ianmackenzie/elm-geometry/3.0.0

NOTE: the latest version is 4.0.0

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:

  • Constructing a vector from a length and direction
  • Determining the component of a vector in a particular direction (for example, finding the component of velocity in the up direction to get vertical speed)
  • Determining the angle between two directions
  • Defining the orientation of an axis, plane or reference frame
type alias Direction3d coordinates =
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

Point3d units coordinates
-> 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 :
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:

  • Its azimuth angle in the XY plane, from positive X towards positive Y
  • Its elevation angle from the XY plane towards positive Z

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:

  • Its azimuth angle in the YZ plane, from positive Y towards positive Z
  • Its elevation angle from the YZ plane towards positive X

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

zxY : Angle -> Angle -> Direction3d coordinates

Construct a direction given:

  • Its azimuth angle in the ZX plane, from positive Z towards positive X
  • Its elevation angle from the ZX plane towards positive Y

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

SketchPlane3d units coordinates3d defines
-> Angle
-> Angle
-> Direction3d coordinates3d

Construct a direction given:

  • Its azimuth angle within a given sketch plane, from that sketch plane's positive X direction towards its positive Y diretion
  • Its elevation angle out of that sketch plane, in the sketch plane's norma direction (the cross product of its X and Y directions)

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)
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
--> )
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:

  • The first returned direction will be equal to the direction of the first given vector
  • The second returned direction will be as close as possible to the second given vector while being perpendicular to the first returned direction
  • The third returned direction will be as close as possible to the third given vector while being perpendicular to the first and second returned directions

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
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 Unitless coordinates

Convert a direction to a unitless vector of length 1.

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

Properties

xComponent : Direction3d coordinates -> Float

Get the X component of a direction.

Direction3d.xComponent Direction3d.x
--> 1


Direction3d.xComponent Direction3d.y
--> 0
yComponent : Direction3d coordinates -> Float

Get the Y component of a direction.

Direction3d.yComponent Direction3d.y
--> 1


Direction3d.yComponent Direction3d.z
--> 0
zComponent : Direction3d coordinates -> Float

Get the Z component of a direction.

Direction3d.zComponent Direction3d.z
--> 1


Direction3d.zComponent Direction3d.x
--> 0
componentIn : Direction3d coordinates -> Direction3d coordinates -> 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
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
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 -> 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 Axis3d.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.

Axis3d units coordinates
-> Angle
-> Direction3d coordinates
-> Direction3d coordinates

Rotate a direction around an axis by a given angle.

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

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

offsetAxis =
    Axis3d.withDirection Direction3d.z
        (Point3d.meters 100 200 300)


Direction3d.x
    |> Direction3d.rotateAround offsetAxis
        (Angle.degrees 90)
--> Direction3d.y
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)
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)
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
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
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

Advanced

{ x : Float
, y : Float
, z : 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.

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

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