ianmackenzie / elm-geometry / Direction3d

A Direction3d represents a direction like 'up' or 'north' or 'forwards'. They are represented using X, Y and Z components, and can be converted to vectors if necessary, but should be thought of as conceptually different. Directions have several uses, such as:


type alias Direction3d coordinates =
Geometry.Types.Direction3d coordinates

Constants

x : Direction3d coordinates

Synonym for Direction3d.positiveX.

y : Direction3d coordinates

Synonym for Direction3d.positiveY.

z : Direction3d coordinates

Synonym for Direction3d.positiveZ.

positiveX : Direction3d coordinates

The positive X direction.

negativeX : Direction3d coordinates

The negative X direction.

positiveY : Direction3d coordinates

The positive Y direction.

negativeY : Direction3d coordinates

The negative Y direction.

positiveZ : Direction3d coordinates

The positive Z direction.

negativeZ : Direction3d coordinates

The negative Z direction.

Constructors

from : Geometry.Types.Point3d units coordinates -> Geometry.Types.Point3d units coordinates -> Maybe (Direction3d coordinates)

Attempt to construct the direction from the first given point to the second. If the two points are coincident, returns Nothing.

point =
    Point3d.meters 1 0 1

Direction3d.from Point3d.origin point
--> Just (Direction3d.xz (Angle.degrees 45))

Direction3d.from point Point3d.origin
--> Just (Direction3d.xz (Angle.degrees -135))

Direction3d.from point point
--> Nothing

In a plane

xy : Angle -> Direction3d coordinates

Construct a direction in the XY plane given an angle measured from positive X towards positive Y:

Direction3d.xy (Angle.degrees 0)
--> Direction3d.positiveX

Direction3d.xy (Angle.degrees 90)
--> Direction3d.positiveY

Direction3d.xy (Angle.degrees 180)
--> Direction3d.negativeX

yx : Angle -> Direction3d coordinates

Construct a direction in the XY plane given an angle measured from positive Y towards positive X:

Direction3d.yx (Angle.degrees 0)
--> Direction3d.positiveY

Direction3d.yx (Angle.degrees 90)
--> Direction3d.positiveX

Direction3d.yx (Angle.degrees -90)
--> Direction3d.negativeX

yz : Angle -> Direction3d coordinates

Construct a direction in the YZ plane given an angle measured from positive Y towards positive Z.

zy : Angle -> Direction3d coordinates

Construct a direction in the YZ plane given an angle measured from positive Z towards positive Y.

zx : Angle -> Direction3d coordinates

Construct a direction in the XZ plane given an angle measured from positive Z towards positive X.

xz : Angle -> Direction3d coordinates

Construct a direction in the XZ plane given an angle measured from positive X towards positive Z.

on : Geometry.Types.SketchPlane3d units coordinates3d { defines : coordinates2d } -> Direction2d coordinates2d -> Direction3d coordinates3d

Construct a 3D direction lying on a sketch plane by providing a 2D direction specified in XY coordinates within the sketch plane. This is a generalized version of Direction3d.xy, Direction3d.yz etc.; for example,

Direction3d.xy (Angle.degrees 45)

is equivalent to

Direction3d.on SketchPlane3d.xy (Direction2d.degrees 45)

From azimuth and elevation

xyZ : Angle -> Angle -> Direction3d coordinates

Construct a direction given:

See here for an illustration of these two angles (except that this function measures azimuth from positive X towards positive Y, not negative Y towards positive X). Note that Direction3d.xyZ is a generalized version of Direction3d.xy;

Direction3d.xy angle

is equivalent to

Direction3d.xyZ angle (Angle.degrees 0)

yzX : Angle -> Angle -> Direction3d coordinates

Construct a direction given:

This is generally not as useful as Direction3d.xyZ or Direction3d.zxY.

zxY : Angle -> Angle -> Direction3d coordinates

Construct a direction given:

This may be useful if you like to use positive Y as your global up vector.

fromAzimuthInAndElevationFrom : Geometry.Types.SketchPlane3d units coordinates3d defines -> Angle -> Angle -> Direction3d coordinates3d

Construct a direction given:

This is a generalized version of Direction3d.xyZ, Direction3d.yzX and Direction3d.zxY; for example,

Direction3d.xyZ azimuth elevation

is equivalent to

Direction3d.fromAzimuthInAndElevationFrom
    SketchPlane3d.xy
    azimuth
    elevation

Perpendicular directions

perpendicularTo : Direction3d coordinates -> Direction3d coordinates

Construct an arbitrary direction perpendicular to the given direction. The exact resulting direction is not specified, but it is guaranteed to be perpendicular to the given direction.

Direction3d.perpendicularTo Direction3d.x
--> Direction3d.negativeZ

Direction3d.perpendicularTo Direction3d.y
--> Direction3d.positiveZ

direction =
    Direction3d.xz (Angle.degrees 60)

Direction3d.perpendicularTo direction
--> Direction3d.xz (Angle.degrees -30)

perpendicularBasis : Direction3d coordinates -> ( Direction3d coordinates, Direction3d coordinates )

Construct a pair of directions that are perpendicular to each other and both perpendicular to the given direction.

The given direction and the two returned directions will form a right-handed system (that is, a right-handed Frame3d could be constructed by using the given direction as the X direction and the two returned directions as the Y and Z directions, or the given direction as the Z direction and the two returned directions as the X and Y directions).

Direction3d.perpendicularBasis Direction3d.x
--> ( Direction3d.negativeZ
--> , Direction3d.positiveY
--> )

Direction3d.perpendicularBasis Direction3d.y
--> ( Direction3d.positiveZ
--> , Direction3d.positiveX
--> )

orthonormalize : Vector3d units1 coordinates -> Vector3d units2 coordinates -> Vector3d units3 coordinates -> Maybe ( Direction3d coordinates, Direction3d coordinates, Direction3d coordinates )

Attempt to form a set of three mutually perpendicular directions from the three given vectors by performing Gram-Schmidt normalization:

If any of the given vectors are zero, any two of them are parallel, or the three are coplanar, returns Nothing.

Direction3d.orthonormalize
    (Vector3d.meters 3 3 0)
    (Vector3d.meters 0 2 0)
    (Vector3d.meters 1 2 3)
--> Just
-->     ( Direction3d.xy (Angle.degrees 45)
-->     , Direction3d.xy (Angle.degrees 135)
-->     , Direction3d.z
-->     )

-- Three vectors in the XY plane:
Direction3d.orthonormalize
    (Vector3d.meters 2 0 0)
    (Vector3d.meters 3 1 0)
    (Vector3d.meters 4 2 0)
--> Nothing

orthogonalize : Direction3d coordinates -> Direction3d coordinates -> Direction3d coordinates -> Maybe ( Direction3d coordinates, Direction3d coordinates, Direction3d coordinates )

Attempt to form a set of three mutually perpendicular directions from the three given directions;

Direction3d.orthogonalize
    xDirection
    yDirection
    zDirection

is equivalent to

Direction3d.orthonormalize
    (Direction3d.toVector xDirection)
    (Direction3d.toVector yDirection)
    (Direction3d.toVector zDirection)

Conversion

toVector : Direction3d coordinates -> Vector3d Quantity.Unitless coordinates

Convert a direction to a unitless vector of length 1.

Direction3d.toVector Direction3d.y
--> Vector3d.unitless 0 1 0

Properties

components : Direction3d coordinates -> ( Basics.Float, Basics.Float, Basics.Float )

Get the X, Y and Z components of a direction as a tuple.

Direction3d.yz (Angle.degrees 135)
    |> Direction3d.components
--> ( 0, -0.7071, 0.7071 )

xComponent : Direction3d coordinates -> Basics.Float

Get the X component of a direction.

Direction3d.xComponent Direction3d.x
--> 1

Direction3d.xComponent Direction3d.y
--> 0

yComponent : Direction3d coordinates -> Basics.Float

Get the Y component of a direction.

Direction3d.yComponent Direction3d.y
--> 1

Direction3d.yComponent Direction3d.z
--> 0

zComponent : Direction3d coordinates -> Basics.Float

Get the Z component of a direction.

Direction3d.zComponent Direction3d.z
--> 1

Direction3d.zComponent Direction3d.x
--> 0

componentIn : Direction3d coordinates -> Direction3d coordinates -> Basics.Float

Find the component of one direction in another direction. This is equal to the cosine of the angle between the directions, or equivalently the dot product of the two directions converted to unit vectors.

direction =
    Direction3d.xz (Angle.degrees 60)

Direction3d.componentIn Direction3d.x direction
--> 0.5

Direction3d.componentIn Direction3d.z direction
--> 0.866

Direction3d.componentIn direction direction
--> 1

direction
    |> Direction3d.componentIn
        (Direction3d.reverse direction)
--> -1

This is more general and flexible than using xComponent, yComponent or zComponent, all of which can be expressed in terms of componentIn; for example,

Direction3d.zComponent direction

is equivalent to

Direction3d.componentIn Direction3d.z direction

angleFrom : Direction3d coordinates -> Direction3d coordinates -> Angle

Find the angle from one direction to another. The result will be in the range 0 to 180 degrees

Direction3d.angleFrom Direction3d.x Direction3d.x
--> Angle.degrees 0

Direction3d.angleFrom Direction3d.x Direction3d.z
--> Angle.degrees 90

Direction3d.angleFrom
    Direction3d.y
    Direction3d.negativeY
--> Angle.degrees 180

azimuthIn : Geometry.Types.SketchPlane3d units coordinates3d { defines : coordinates2d } -> Direction3d coordinates3d -> Angle

Get the azimuth of a direction in a given sketch plane (the angle of that direction projected into the sketch plane), measured from its X axis towards its Y axis (counterclockwise around the sketch plane's normal axis). The result will be in the range -180 degrees to 180 degrees.

Direction3d.x
    |> Direction3d.azimuthIn SketchPlane3d.xy
--> Angle.degrees 0

Direction3d.y
    |> Direction3d.azimuthIn SketchPlane3d.xy
--> Angle.degrees 90

Direction3d.negativeY
    |> Direction3d.azimuthIn SketchPlane3d.xy
--> Angle.degrees -90

Direction3d.negativeX
    |> Direction3d.azimuthIn SketchPlane3d.xy
--> Angle.degrees 180

Vertical directions are considered to have an azimuth of zero:

Direction3d.z |> Direction3d.azimuthIn SketchPlane3d.xy
--> Angle.degrees 0

elevationFrom : Geometry.Types.SketchPlane3d units coordinates3d defines -> Direction3d coordinates3d -> Angle

Get the elevation angle of a direction from a given sketch plane (the angle between the direction and the sketch plane, in the direction of the sketch plane's normal). Directions in (coplanar with) the sketch plane have an elevation angle of zero. The result will be in the range -90 degrees to 90 degrees.

Direction3d.x
    |> Direction3d.elevationFrom SketchPlane3d.xy
--> Angle.degrees 0

Direction3d.y
    |> Direction3d.elevationFrom SketchPlane3d.xy
--> Angle.degrees 0

Direction3d.z
    |> Direction3d.elevationFrom SketchPlane3d.xy
--> Angle.degrees 90

Direction3d.negativeZ
    |> Direction3d.elevationFrom SketchPlane3d.xy
--> Angle.degrees -90

Comparison

equalWithin : Angle -> Direction3d coordinates -> Direction3d coordinates -> Basics.Bool

Compare two directions within an angular tolerance. Returns true if the angle between the two given directions is less than the given tolerance.

rotatedDirection =
    Direction3d.x
        |> Direction3d.rotateAround Direction3d.z
            (Angle.degrees 2)

Direction3d.equalWithin (Angle.degrees 5)
    Direction3d.x
    rotatedDirection
--> True

Direction3d.equalWithin (Angle.degrees 1)
    Direction3d.x
    rotatedDirection
--> False

Transformations

reverse : Direction3d coordinates -> Direction3d coordinates

Reverse a direction.

rotateAround : Direction3d coordinates -> Angle -> Direction3d coordinates -> Direction3d coordinates

Rotate a direction around another direction by a given angle.

Direction3d.y
    |> Direction3d.rotateAround Direction3d.x
        (Angle.degrees 90)
--> Direction3d.z

mirrorAcross : Geometry.Types.Plane3d units coordinates -> Direction3d coordinates -> Direction3d coordinates

Mirror a direction across a plane.

direction =
    Direction3d.xyZ (Angle.degrees 30)
        (Angle.degrees 60)

Direction3d.mirrorAcross Plane3d.xy direction
--> Direction3d.xyZ (Angle.degrees 30)
-->     (Angle.degrees -60)

Note that only the normal direction of the plane affects the result, not the position of its origin point, since directions are position-independent:

Direction3d.mirrorAcross Plane3d.yz direction
--> Direction3d.xyZ (Angle.degrees 150)
-->     (Angle.degrees 60)

offsetPlane =
    Plane3d.offsetBy 10 Plane3d.yz

Direction3d.mirrorAcross offsetPlane direction
--> Direction3d.xyZ (Angle.degrees 150)
-->     (Angle.degrees 60)

projectOnto : Geometry.Types.Plane3d units coordinates -> Direction3d coordinates -> Maybe (Direction3d coordinates)

Find the orthographic projection of a direction onto a plane (renormalized to have unit length). If the given direction is exactly perpendicular to the given plane, returns Nothing.

direction =
    Direction3d.xyZ (Angle.degrees -60)
        (Angle.degrees 0)

Direction3d.projectOnto Plane3d.xy direction
--> Just direction

Direction3d.projectOnto Plane3d.xz direction
--> Just Direction3d.x

Direction3d.projectOnto Plane3d.yz direction
--> Just Direction3d.negativeY

Direction3d.projectOnto Plane3d.xy Direction3d.z
--> Nothing

Coordinate conversions

Like other transformations, coordinate transformations of directions depend only on the orientations of the relevant frames, not their positions.

For the examples, assume the following definition of a local coordinate frame, one that is rotated 30 degrees counterclockwise around the Z axis from the global XYZ frame:

rotatedFrame =
    Frame3d.atOrigin
        |> Frame3d.rotateAround Axis3d.z (Angle.degrees 30)

relativeTo : Geometry.Types.Frame3d units globalCoordinates { defines : localCoordinates } -> Direction3d globalCoordinates -> Direction3d localCoordinates

Take a direction defined in global coordinates, and return it expressed in local coordinates relative to a given reference frame.

Direction3d.relativeTo rotatedFrame Direction3d.x
--> Direction3d.xy (Angle.degrees -30)

Direction3d.relativeTo rotatedFrame Direction3d.y
--> Direction3d.xy (Angle.degrees 60)

Direction3d.relativeTo rotatedFrame Direction3d.z
--> Direction3d.z

placeIn : Geometry.Types.Frame3d units globalCoordinates { defines : localCoordinates } -> Direction3d localCoordinates -> Direction3d globalCoordinates

Take a direction defined in local coordinates relative to a given reference frame, and return that direction expressed in global coordinates.

Direction3d.placeIn rotatedFrame Direction3d.x
--> Direction3d.xy (Angle.degrees 30)

Direction3d.placeIn rotatedFrame Direction3d.y
--> Direction3d.xy (Angle.degrees 120)

Direction3d.placeIn rotatedFrame Direction3d.z
--> Direction3d.z

projectInto : Geometry.Types.SketchPlane3d units coordinates3d { defines : coordinates2d } -> Direction3d coordinates3d -> Maybe (Direction2d coordinates2d)

Project a direction into a given sketch plane. Conceptually, this finds the orthographic projection of the direction onto the plane, re-normalizes it to have unit length, and then expresses the projected direction in 2D sketch coordinates.

This is only possible if the direction is not perpendicular to the sketch plane; if it is perpendicular, Nothing is returned.

direction =
    Direction3d.xy (Angle.degrees -60)

Direction3d.projectInto SketchPlane3d.xy direction
--> Just (Direction2d.degrees -60)

Direction3d.projectInto SketchPlane3d.xz direction
--> Just Direction2d.x

Direction3d.projectInto SketchPlane3d.yz direction
--> Just Direction2d.negativeX

Direction3d.projectInto SketchPlane3d.xy Direction3d.z
--> Nothing

Random generation

random : Random.Generator (Direction3d coordinates)

A random generator for 3D directions.

Advanced

unsafe : { x : Basics.Float, y : Basics.Float, z : Basics.Float } -> Direction3d coordinates

Construct a direction directly from its X, Y and Z components. Note that you must ensure that the sum of the squares of the given components is exactly one:

Direction3d.unsafe { x = 1, y = 0, z = 0 }

Direction3d.unsafe { x = 0, y = -1, z = 0 }

Direction3d.unsafe { x = 0.6, y = 0, z = 0.8 }

are all valid but

Direction3d.unsafe { x = 2, y = 0, z = 0 }

Direction3d.unsafe { x = 1, y = 1, z = 1 }

are not. Instead of using Direction3d.unsafe, it may be easier to use constructors like Direction3d.on or Direction3d.xyZ (which will always result in a valid direction), or start with existing directions and transform them as necessary.

unwrap : Direction3d coordinates -> { x : Basics.Float, y : Basics.Float, z : Basics.Float }

Extract the X, Y and Z components of a direction as a record.