ianmackenzie / elm-geometry-prerelease / Direction2d

A Direction2d represents a direction like 'up' or 'north' or 'forwards'. They are represented using X and Y 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 Direction2d =
Geometry.Types.Direction2d

Constants

x : Direction2d

Synonym for Direction2d.positiveX.

y : Direction2d

Synonym for Direction2d.positiveY.

positiveX : Direction2d

The positive X direction.

Direction2d.components Direction2d.positiveX
--> ( 1, 0 )

negativeX : Direction2d

The negative X direction.

Direction2d.components Direction2d.negativeX
--> ( -1, 0 )

positiveY : Direction2d

The positive Y direction.

Direction2d.components Direction2d.positiveY
--> ( 0, 1 )

negativeY : Direction2d

The negative Y direction.

Direction2d.components Direction2d.negativeY
--> ( 0, -1 )

Constructors

from : Geometry.Types.Point2d -> Geometry.Types.Point2d -> Maybe Direction2d

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

point =
    Point2d.fromCoordinates ( 1, 1 )

Direction2d.from Point2d.origin point
--> Just (Direction2d.fromAngle (degrees 45))

Direction2d.from point Point2d.origin
--> Just (Direction2d.fromAngle (degrees -135))

Direction2d.from point point
--> Nothing

perpendicularTo : Direction2d -> Direction2d

Construct a direction perpendicular to the given direction, by rotating the given direction 90 degrees counterclockwise. Synonym for rotateCounterclockwise.

Direction2d.perpendicularTo Direction2d.x
--> Direction2d.y

Direction2d.perpendicularTo Direction2d.y
--> Direction2d.negativeX

orthonormalize : Vector2d -> Vector2d -> Maybe ( Direction2d, Direction2d )

Attempt to form a pair of perpendicular directions from the two given vectors by performing Gram-Schmidt normalization:

If either of the given vectors are zero, or if the two vectors are parallel, returns Nothing.

Direction2d.orthonormalize
    (Vector2d.fromComponents ( 3, 3 ))
    (Vector2d.fromComponents ( 0, -2 ))
--> Just
-->     ( Direction2d.fromAngle (degrees 45)
-->     , Direction2d.fromAngle (degrees -45)
-->     )

Direction2d.orthonormalize
    (Vector2d.fromComponents ( 3, 3 ))
    (Vector2d.fromComponents ( -2, -2 ))
--> Nothing

orthogonalize : Direction2d -> Direction2d -> Maybe ( Direction2d, Direction2d )

Attempt to form a pair of perpendicular directions from the two given directions;

Direction2d.orthogonalize xDirection yDirection

is equivalent to

Direction2d.orthonormalize
    (Direction2d.toVector xDirection)
    (Direction2d.toVector yDirection)

unsafe : ( Basics.Float, Basics.Float ) -> Direction2d

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

Direction2d.unsafe ( 1, 0 )

Direction2d.unsafe ( 0, -1 )

Direction2d.unsafe ( 0.6, 0.8 )

are all valid but

Direction2d.unsafe ( 2, 0 )

Direction2d.unsafe ( 1, 1 )

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

Conversions

fromAngle : Basics.Float -> Direction2d

Construct a direction from an angle in radians, given counterclockwise from the positive X direction.

Direction2d.fromAngle 0
--> Direction2d.x

Direction2d.fromAngle (degrees 90)
--> Direction2d.y

Direction2d.fromAngle (degrees -90)
--> Direction2d.negativeY

toAngle : Direction2d -> Basics.Float

Convert a direction to a polar angle (the counterclockwise angle in radians from the positive X direction). The result will be in the range -π to π.

Direction2d.toAngle Direction2d.x
--> 0

Direction2d.toAngle Direction2d.y
--> degrees 90

Direction2d.toAngle Direction2d.negativeY
--> degrees -90

Properties

components : Direction2d -> ( Basics.Float, Basics.Float )

Get the components of a direction as a tuple (the components it would have as a unit vector, also know as its direction cosines).

( x, y ) =
    Direction2d.components direction

xComponent : Direction2d -> Basics.Float

Get the X component of a direction.

Direction2d.xComponent Direction2d.x
--> 1

Direction2d.xComponent Direction2d.y
--> 0

yComponent : Direction2d -> Basics.Float

Get the Y component of a direction.

Direction2d.yComponent Direction2d.x
--> 0

Direction2d.yComponent Direction2d.y
--> 1

Comparison

equalWithin : Basics.Float -> Direction2d -> Direction2d -> Basics.Bool

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

firstDirection =
    Direction2d.fromAngle (degrees 45)

secondDirection =
    Direction2d.fromAngle (degrees 47)

Direction2d.equalWithin (degrees 5)
    firstDirection
    secondDirection
--> True

Direction2d.equalWithin (degrees 1)
    firstDirection
    secondDirection
--> False

Measurement

componentIn : Direction2d -> Direction2d -> 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 =
    Direction2d.fromAngle (degrees 60)

Direction2d.componentIn Direction2d.x direction
--> 0.5

Direction2d.componentIn direction direction
--> 1

Direction2d.componentIn Direction2d.x Direction2d.y
--> 0

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

Direction2d.xComponent direction

is equivalent to

Direction2d.componentIn Direction2d.x direction

angleFrom : Direction2d -> Direction2d -> Basics.Float

Find the counterclockwise angle in radians from the first direction to the second. The result will be in the range -π to π.

referenceDirection =
    Direction2d.fromAngle (degrees 30)

Direction2d.angleFrom referenceDirection Direction2d.y
--> degrees 60

Direction2d.angleFrom referenceDirection Direction2d.x
--> degrees -30

Conversion

toVector : Direction2d -> Vector2d

Convert a direction to a unit vector.

Direction2d.toVector Direction2d.x
--> Vector2d.fromComponents ( 1, 0 )

Transformations

reverse : Direction2d -> Direction2d

Reverse a direction.

Direction2d.reverse Direction2d.y
--> Direction2d.negativeY

rotateClockwise : Direction2d -> Direction2d

Rotate a direction by 90 degrees clockwise.

Direction2d.rotateClockwise Direction2d.y
--> Direction2d.x

Direction2d.rotateClockwise Direction2d.x
--> Direction2d.negativeY

rotateCounterclockwise : Direction2d -> Direction2d

Rotate a direction by 90 degrees counterclockwise.

Direction2d.rotateClockwise Direction2d.x
--> Direction2d.y

Direction2d.rotateClockwise Direction2d.y
--> Direction2d.negativeX

rotateBy : Basics.Float -> Direction2d -> Direction2d

Rotate a direction counterclockwise by a given angle (in radians).

Direction2d.rotateBy pi Direction2d.x
--> Direction2d.negativeX

Direction2d.rotateBy (degrees 45) Direction2d.y
--> Direction2d.fromAngle (degrees 135)

mirrorAcross : Geometry.Types.Axis2d -> Direction2d -> Direction2d

Mirror a direction across a particular axis. Note that only the direction of the axis affects the result, since directions are position-independent.

slopedAxis =
    Axis2d.through
        (Point2d.fromCoordinates ( 100, 200 ))
        (Direction2d.fromAngle (degrees 45))

Direction2d.mirrorAcross slopedAxis Direction2d.x
--> Direction2d.y

Direction2d.mirrorAcross slopedAxis Direction2d.y
--> Direction2d.x

Coordinate conversions

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

For the examples, assume the following frames have been defined:

upsideDownFrame =
    Frame2d
        { originPoint = Point2d.origin
        , xDirection = Direction2d.positiveX
        , yDirection = Direction2d.negativeY
        }

rotatedFrame =
    Frame2d.rotateBy (degrees 30) Frame2d.xy

relativeTo : Geometry.Types.Frame2d -> Direction2d -> Direction2d

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

Direction2d.relativeTo upsideDownFrame Direction2d.y
--> Direction2d.negativeY

Direction2d.relativeTo rotatedFrame Direction2d.x
--> Direction2d.fromAngle (degrees -30)

Direction2d.relativeTo rotatedFrame Direction2d.y
--> Direction2d.fromAngle (degrees 60)

placeIn : Geometry.Types.Frame2d -> Direction2d -> Direction2d

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

Direction2d.placeIn upsideDownFrame Direction2d.y
--> Direction2d.negativeY

Direction2d.placeIn rotatedFrame Direction2d.x
--> Direction2d.fromAngle (degrees 30)

Direction2d.placeIn rotatedFrame Direction2d.y
--> Direction2d.fromAngle (degrees 120)