ianmackenzie / elm-geometry / Vector2d

A Vector2d represents a quantity such as a displacement or velocity in 2D, and is defined by its X and Y components. This module contains a variety of vector-related functionality, such as

Note that unlike in many other geometry packages where vectors are used as a general-purpose data type, elm-geometry has separate data types for vectors, directions and points. In most code it is actually more common to use Point2d and Direction2d than Vector2d, and much code can avoid working directly with Vector2d values at all!


type alias Vector2d units coordinates =
Geometry.Types.Vector2d units coordinates

Constants

zero : Vector2d units coordinates

The vector with components (0,0).

Literals

unitless : Basics.Float -> Basics.Float -> Vector2d Quantity.Unitless coordinates

Construct a unitless Vector2d value from its X and Y components. See also fromUnitless.

meters : Basics.Float -> Basics.Float -> Vector2d Length.Meters coordinates

pixels : Basics.Float -> Basics.Float -> Vector2d Pixels coordinates

millimeters : Basics.Float -> Basics.Float -> Vector2d Length.Meters coordinates

centimeters : Basics.Float -> Basics.Float -> Vector2d Length.Meters coordinates

inches : Basics.Float -> Basics.Float -> Vector2d Length.Meters coordinates

Constructors

xy : Quantity Basics.Float units -> Quantity Basics.Float units -> Vector2d units coordinates

Construct a vector from its X and Y components.

vector =
    Vector2d.xy (Length.meters 2) (Length.meters 3)

xyIn : Geometry.Types.Frame2d frameUnits globalCoordinates { defines : localCoordinates } -> Quantity Basics.Float units -> Quantity Basics.Float units -> Vector2d units globalCoordinates

Construct a vector given its local components within a particular frame:

rotatedFrame =
    Frame2d.atOrigin
        |> Frame2d.rotateBy (Angle.degrees 45)

Vector2d.xyIn rotatedFrame
    (Length.meters 2)
    (Length.meters 0)
--> Vector2d.xy
-->     (Length.meters 1.4142)
-->     (Length.meters 1.4142)

rTheta : Quantity Basics.Float units -> Angle -> Vector2d units coordinates

Construct a vector from a length and angle. The angle is measured counterclockwise from the positive X direction.

Vector2d.rTheta (Length.meters 2) (Angle.degrees 135)
-->Vector2d.meters -1.4142 1.4142

rThetaIn : Geometry.Types.Frame2d frameUnits globalCoordinates { defines : localCoordinates } -> Quantity Basics.Float units -> Angle -> Vector2d units globalCoordinates

Construct a vector given its local polar components within a particular frame:

rotatedFrame =
    Frame2d.atOrigin
        |> Frame2d.rotateBy (Angle.degrees 45)

Vector2d.rThetaIn rotatedFrame
    (Length.meters 1)
    (Angle.degrees 0)
--> Vector2d.meters 0.7071 0.7071

from : Geometry.Types.Point2d units coordinates -> Geometry.Types.Point2d units coordinates -> Vector2d units coordinates

Construct a vector from the first given point to the second.

startPoint =
    Point2d.meters 1 1

endPoint =
    Point2d.meters 4 5

Vector2d.from startPoint endPoint
--> Vector2d.meters 3 4

withLength : Quantity Basics.Float units -> Geometry.Types.Direction2d coordinates -> Vector2d units coordinates

Construct a vector with the given length in the given direction.

Vector2d.withLength (Length.meters 5) Direction2d.y
--> Vector2d.meters 0 5

perpendicularTo : Vector2d units coordinates -> Vector2d units coordinates

Construct a vector perpendicular to the given vector, by rotating the given vector 90 degrees counterclockwise. The constructed vector will have the same length as the given vector. Alias for Vector2d.rotateCounterclockwise.

Vector2d.perpendicularTo (Vector2d.meters 1 0)
--> Vector2d.meters 0 1

Vector2d.perpendicularTo (Vector2d.meters 0 2)
--> Vector2d.meters -2 0

Vector2d.perpendicularTo (Vector2d.meters 3 1)
--> Vector2d.meters -1 3

Vector2d.perpendicularTo Vector2d.zero
--> Vector2d.zero

interpolateFrom : Vector2d units coordinates -> Vector2d units coordinates -> Basics.Float -> Vector2d units coordinates

Construct a vector by interpolating from the first given vector to the second, based on a parameter that ranges from zero to one.

startVector =
    Vector2d.zero

endVector =
    Vector2d.meters 8 12

Vector2d.interpolateFrom startVector endVector 0.25
--> Vector2d.meters 2 3

Partial application may be useful:

interpolatedVector : Float -> Vector2d
interpolatedVector =
    Vector2d.interpolateFrom startVector endVector

List.map interpolatedVector [ 0, 0.5, 1 ]
--> [ Vector2d.meters 0 0
--> , Vector2d.meters 4 6
--> , Vector2d.meters 8 12
--> ]

You can pass values less than zero or greater than one to extrapolate:

interpolatedVector -0.5
--> Vector2d.meters -4 -6

interpolatedVector 1.25
--> Vector2d.meters 10 15

Interop

These functions are useful for interoperability with other Elm code that uses plain Float tuples or records to represent vectors.

fromTuple : (Basics.Float -> Quantity Basics.Float units) -> ( Basics.Float, Basics.Float ) -> Vector2d units coordinates

Construct a Vector2d from a tuple of Float values, by specifying what units those values are in.

Vector2d.fromTuple Length.meters ( 2, 3 )
--> Vector2d.meters 2 3

toTuple : (Quantity Basics.Float units -> Basics.Float) -> Vector2d units coordinates -> ( Basics.Float, Basics.Float )

Convert a Vector2d to a tuple of Float values, by specifying what units you want the result to be in.

vector =
    Vector2d.feet 2 3

Vector2d.toTuple Length.inInches vector
--> ( 24, 36 )

fromRecord : (Basics.Float -> Quantity Basics.Float units) -> { x : Basics.Float, y : Basics.Float } -> Vector2d units coordinates

Construct a Vector2d from a record with Float fields, by specifying what units those fields are in.

Vector2d.fromRecord Length.inches { x = 24, y = 36 }
--> Vector2d.feet 2 3

toRecord : (Quantity Basics.Float units -> Basics.Float) -> Vector2d units coordinates -> { x : Basics.Float, y : Basics.Float }

Convert a Vector2d to a record with Float fields, by specifying what units you want the result to be in.

vector =
    Vector2d.meters 2 3

Vector2d.toRecord Length.inCentimeters vector
--> { x = 200, y = 300 }

Zero-copy conversions

These functions allow zero-overhead conversion of vectors to and from records with x and y Float fields, useful for efficient interop with other code that represents vectors as plain records.

fromMeters : { x : Basics.Float, y : Basics.Float } -> Vector2d Length.Meters coordinates

toMeters : Vector2d Length.Meters coordinates -> { x : Basics.Float, y : Basics.Float }

fromPixels : { x : Basics.Float, y : Basics.Float } -> Vector2d Pixels coordinates

toPixels : Vector2d Pixels coordinates -> { x : Basics.Float, y : Basics.Float }

fromUnitless : { x : Basics.Float, y : Basics.Float } -> Vector2d Quantity.Unitless coordinates

toUnitless : Vector2d Quantity.Unitless coordinates -> { x : Basics.Float, y : Basics.Float }

Rates of change

per : Quantity Basics.Float independentUnits -> Vector2d dependentUnits coordinates -> Vector2d (Quantity.Rate dependentUnits independentUnits) coordinates

Construct a vector representing a rate of change such as a speed:

displacement =
    Vector2d.meters 6 8

velocity =
    displacement |> Vector2d.per (Duration.seconds 2)

-- Get the magnitude of the velocity (the speed)
Vector2d.length velocity
--> Speed.metersPerSecond 5

for : Quantity Basics.Float independentUnits -> Vector2d (Quantity.Rate dependentUnits independentUnits) coordinates -> Vector2d dependentUnits coordinates

Multiply a rate of change vector by an independent quantity to get a total vector. For example, multiply a velocity by a duration to get a total displacement:

velocity =
    Vector2d.xy
        (Pixels.pixelsPerSecond 200)
        (Pixels.pixelsPerSecond 50)

velocity |> Vector2d.for (Duration.seconds 0.1)
--> Vector2d.pixels 20 5

Properties

components : Vector2d units coordinates -> ( Quantity Basics.Float units, Quantity Basics.Float units )

Get the X and Y components of a vector as a tuple.

Vector2d.components (Vector2d.meters 2 3)
--> ( Length.meters 2, Length.meters 3 )

xComponent : Vector2d units coordinates -> Quantity Basics.Float units

Get the X component of a vector.

Vector2d.xComponent (Vector2d.meters 2 3)
--> Length.meters 2

yComponent : Vector2d units coordinates -> Quantity Basics.Float units

Get the Y component of a vector.

Vector2d.yComponent (Vector2d.meters 2 3)
--> Length.meters 3

componentIn : Geometry.Types.Direction2d coordinates -> Vector2d units coordinates -> Quantity Basics.Float units

Find the component of a vector in an arbitrary direction, for example

forwardSpeed =
    Vector2d.componentIn forwardDirection velocity

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

Vector2d.xComponent vector

is equivalent to

Vector2d.componentIn Direction2d.x vector

length : Vector2d units coordinates -> Quantity Basics.Float units

Get the length (magnitude) of a vector.

Vector2d.length (Vector2d.meters 3 4)
--> Length.meters 5

direction : Vector2d units coordinates -> Maybe (Geometry.Types.Direction2d coordinates)

Attempt to find the direction of a vector. In the case of a zero vector, return Nothing.

Vector2d.direction (Vector2d.meters 3 3)
--> Just (Direction2d.degrees 45)

Vector2d.direction Vector2d.zero
--> Nothing

Comparison

equalWithin : Quantity Basics.Float units -> Vector2d units coordinates -> Vector2d units coordinates -> Basics.Bool

Compare two vectors within a tolerance. Returns true if the difference between the two given vectors has magnitude less than the given tolerance.

firstVector =
    Vector2d.meters 1 2

secondVector =
    Vector2d.meters 0.9999 2.0002

Vector2d.equalWithin (Length.millimeters 1)
    firstVector
    secondVector
--> True

Vector2d.equalWithin (Length.microns 1)
    firstVector
    secondVector
--> False

lexicographicComparison : Vector2d units coordinates -> Vector2d units coordinates -> Basics.Order

Compare two Vector2d values lexicographically: first by X component, then by Y. Can be used to provide a sort order for Vector2d values.

Arithmetic

multiplyBy : Basics.Float -> Vector2d units coordinates -> Vector2d units coordinates

Alias for scaleBy since a Vector2d can be kind of thought of as either a 'mathematical' or 'geometric' object.

divideBy : Basics.Float -> Vector2d units coordinates -> Vector2d units coordinates

Divide a vector by the given value.

plus : Vector2d units coordinates -> Vector2d units coordinates -> Vector2d units coordinates

Find the sum of two vectors.

firstVector =
    Vector2d.meters 1 2

secondVector =
    Vector2d.meters 3 4

firstVector |> Vector2d.plus secondVector
--> Vector2d.meters 4 6

minus : Vector2d units coordinates -> Vector2d units coordinates -> Vector2d units coordinates

Find the difference between two vectors (the second vector minus the first).

firstVector =
    Vector2d.meters 5 6

secondVector =
    Vector2d.meters 1 3

firstVector |> Vector2d.minus secondVector
--> Vector2d.meters 4 3

Note the argument order: v1 - v2 would be written as

v1 |> Vector2d.minus v2

which is the same as

Vector2d.minus v2 v1

but the opposite of

Vector2d.minus v1 v2

dot : Vector2d units2 coordinates -> Vector2d units1 coordinates -> Quantity Basics.Float (Quantity.Product units1 units2)

Find the dot product of two vectors.

firstVector =
    Vector2d.meters 1 2

secondVector =
    Vector2d.meters 3 4

firstVector |> Vector2d.dot secondVector
--> Area.squareMeters 11

cross : Vector2d units2 coordinates -> Vector2d units1 coordinates -> Quantity Basics.Float (Quantity.Product units1 units2)

Find the scalar 'cross product' of two vectors in 2D. This is useful in many of the same ways as the 3D cross product:

Note the argument order: v1 x v2 would be written as

v1 |> Vector2d.cross v2

which is the same as

Vector2d.cross v2 v1

but the opposite of

Vector2d.cross v1 v2

Note that the cross product of two vectors with length units will be a vector with area units!

sum : List (Vector2d units coordinates) -> Vector2d units coordinates

Find the sum of a list of vectors.

twice : Vector2d units coordinates -> Vector2d units coordinates

Shorthand for Vector2d.scaleBy 2.

half : Vector2d units coordinates -> Vector2d units coordinates

Shorthand for Vector2d.scaleBy 0.5.

Vector/scalar products

product : Quantity Basics.Float scalarUnits -> Vector2d vectorUnits coordinates -> Vector2d (Quantity.Product scalarUnits vectorUnits) coordinates

Multiply a scalar and a vector, resulting in a vector with units Product scalarUnits vectorUnits:

forceVector =
    Vector2d.product mass accelerationVector

If you just want to scale a vector by a certain amount, you can use scaleBy instead.

times : Quantity Basics.Float scalarUnits -> Vector2d vectorUnits coordinates -> Vector2d (Quantity.Product vectorUnits scalarUnits) coordinates

Multiply a vector by a scalar quantity, resulting in a vector with units Product vectorUnits scalarUnits. (To the compiler Product a b and Product b a are different unit types, so sometimes you will have to swap from product to times or vice versa to make the types work out.)

timesUnitless : Quantity Basics.Float Quantity.Unitless -> Vector2d units coordinates -> Vector2d units coordinates

Multiply a vector by a unitless quantity, leaving the units unchanged.

over : Quantity Basics.Float units1 -> Vector2d (Quantity.Product units1 units2) coordinates -> Vector2d units2 coordinates

Divide a vector with units Product units1 units2 by a scalar with units units1, resulting in a vector with units units2.

accelerationVector =
    forceVector |> Vector2d.over mass

over_ : Quantity Basics.Float units2 -> Vector2d (Quantity.Product units1 units2) coordinates -> Vector2d units1 coordinates

Divide a vector with units Product units1 units2 by a scalar with units units2, resulting in a vector with units units1. Provided for consistency with elm-units but shouldn't be needed in most cases.

overUnitless : Quantity Basics.Float Quantity.Unitless -> Vector2d units coordinates -> Vector2d units coordinates

Divide a vector by a unitless quantity, leaving the units unchanged.

Transformations

Note that for mirrorAcross and projectOnto, only the direction of the axis affects the result, since vectors are position-independent. Think of mirroring/projecting a vector across/onto an axis as moving the vector so its tail is on the axis, then mirroring/projecting its tip across/onto the axis.

reverse : Vector2d units coordinates -> Vector2d units coordinates

Reverse the direction of a vector, negating its components.

Vector2d.reverse (Vector2d.meters -1 2)
--> Vector2d.meters 1 -2

(This could have been called negate, but reverse is more consistent with the naming used in other modules.)

normalize : Vector2d units coordinates -> Vector2d Quantity.Unitless coordinates

Normalize a vector to have a length of one. Zero vectors are left as-is.

vector =
    Vector2d.meters 3 4

Vector2d.normalize vector
--> Vector2d.meters 0.6 0.8

Vector2d.normalize Vector2d.zero
--> Vector2d.zero

Warning: Vector2d.direction is safer since it forces you to explicitly consider the case where the given vector is zero. Vector2d.normalize is primarily useful for cases like generating WebGL meshes, where defaulting to a zero vector for degenerate cases is acceptable, and the overhead of something like

Vector2d.direction vector
    |> Maybe.map Direction2d.toVector
    |> Maybe.withDefault Vector2d.zero

(which is functionally equivalent to Vector2d.normalize vector) is too high.

scaleBy : Basics.Float -> Vector2d units coordinates -> Vector2d units coordinates

Scale the length of a vector by a given scale.

Vector2d.scaleBy 3 (Vector2d.meters 1 2)
--> Vector2d.meters 3 6

(This could have been called multiply or times, but scaleBy was chosen as a more geometrically meaningful name and to be consistent with the scaleAbout name used in other modules.)

scaleTo : Quantity Basics.Float units2 -> Vector2d units1 coordinates -> Vector2d units2 coordinates

Scale a vector to a given length.

Vector2d.scaleTo (Length.meters 25) (Vector2d.meters 3 4)
--> Vector2d.meters 15 20

Scaling a zero vector will always result in a zero vector.

rotateBy : Angle -> Vector2d units coordinates -> Vector2d units coordinates

Rotate a vector counterclockwise by a given angle.

Vector2d.meters 1 1
    |> Vector2d.rotateBy (Angle.degrees 45)
--> Vector2d.meters 0 1.4142

Vector2d.meters 1 0
    |> Vector2d.rotateBy (Angle.radians pi)
--> Vector2d.meters -1 0

rotateClockwise : Vector2d units coordinates -> Vector2d units coordinates

Rotate the given vector 90 degrees clockwise;

Vector2d.rotateClockwise vector

is equivalent to

Vector2d.rotateBy (Angle.degrees -90) vector

but is more efficient.

rotateCounterclockwise : Vector2d units coordinates -> Vector2d units coordinates

Rotate the given vector 90 degrees counterclockwise;

Vector2d.rotateCounterclockwise vector

is equivalent to

Vector2d.rotateBy (Angle.degrees 90) vector

but is more efficient.

mirrorAcross : Geometry.Types.Axis2d axisUnits coordinates -> Vector2d units coordinates -> Vector2d units coordinates

Mirror a vector across a given axis.

vector =
    Vector2d.meters 2 3

Vector2d.mirrorAcross Axis2d.y vector
--> Vector2d.meters -2 3

The position of the axis doesn't matter, only its orientation:

horizontalAxis =
    Axis2d.withDirection Direction2d.x
        (Point2d.meters 100 200)

Vector2d.mirrorAcross horizontalAxis vector
--> Vector2d.meters 2 -3

projectionIn : Geometry.Types.Direction2d coordinates -> Vector2d units coordinates -> Vector2d units coordinates

Find the projection of a vector in a particular direction. Conceptually, this means splitting the original vector into a portion parallel to the given direction and a portion perpendicular to it, then returning the parallel portion.

vector =
    Vector2d.meters 2 3

Vector2d.projectionIn Direction2d.x vector
--> Vector2d.meters 2 0

Vector2d.projectionIn Direction2d.y vector
--> Vector2d.meters 0 3

projectOnto : Geometry.Types.Axis2d axisUnits coordinates -> Vector2d units coordinates -> Vector2d units coordinates

Project a vector onto an axis.

Vector2d.projectOnto Axis2d.y (Vector2d.meters 3 4)
--> Vector2d.meters 0 4

Vector2d.projectOnto Axis2d.x (Vector2d.meters -1 2)
--> Vector2d.meters -1 0

This is equivalent to finding the projection in the axis' direction.

Unit conversions

at : Quantity Basics.Float (Quantity.Rate destinationUnits sourceUnits) -> Vector2d sourceUnits coordinates -> Vector2d destinationUnits coordinates

Convert a vector from one units type to another, by providing a conversion factor given as a rate of change of destination units with respect to source units.

worldVector =
    Vector2d.meters 2 3

resolution : Quantity Float (Rate Pixels Meters)
resolution =
    Pixels.pixels 100 |> Quantity.per (Length.meters 1)

worldVector |> Vector2d.at resolution
--> Vector2d.pixels 200 300

at_ : Quantity Basics.Float (Quantity.Rate sourceUnits destinationUnits) -> Vector2d sourceUnits coordinates -> Vector2d destinationUnits coordinates

Convert a vector from one units type to another, by providing an 'inverse' conversion factor given as a rate of change of source units with respect to destination units.

screenVector =
    Vector2d.pixels 200 300

resolution : Quantity Float (Rate Pixels Meters)
resolution =
    Pixels.pixels 50 |> Quantity.per (Length.meters 1)

screenVector |> Vector2d.at_ resolution
--> Vector2d.meters 4 6

Coordinate conversions

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

For the examples, assume the following frame has been defined:

rotatedFrame =
    Frame2d.atOrigin
        |> Frame2d.rotateBy (Angle.degrees 30)

relativeTo : Geometry.Types.Frame2d frameUnits globalCoordinates { defines : localCoordinates } -> Vector2d units globalCoordinates -> Vector2d units localCoordinates

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

Vector2d.meters 2 0
    |> Vector2d.relativeTo rotatedFrame
--> Vector2d.meters 1.732 -1

placeIn : Geometry.Types.Frame2d frameUnits globalCoordinates { defines : localCoordinates } -> Vector2d units localCoordinates -> Vector2d units globalCoordinates

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

Vector2d.meters 2 0
    |> Vector2d.placeIn rotatedFrame
--> Vector2d.meters 1.732 1

Physics

These constructors let you conveniently create vectors with physics-related units such as speed, acceleration and force. For example, a speed of 5 feet per second in the positive Y direction could be written as

Vector2d.feetPerSecond 0 5

and a force of 10 newtons in the negative X direction could be written as

Vector2d.newtons -10 0

Speed

metersPerSecond : Basics.Float -> Basics.Float -> Vector2d Speed.MetersPerSecond coordinates

feetPerSecond : Basics.Float -> Basics.Float -> Vector2d Speed.MetersPerSecond coordinates

kilometersPerHour : Basics.Float -> Basics.Float -> Vector2d Speed.MetersPerSecond coordinates

milesPerHour : Basics.Float -> Basics.Float -> Vector2d Speed.MetersPerSecond coordinates

Acceleration

metersPerSecondSquared : Basics.Float -> Basics.Float -> Vector2d Acceleration.MetersPerSecondSquared coordinates

feetPerSecondSquared : Basics.Float -> Basics.Float -> Vector2d Acceleration.MetersPerSecondSquared coordinates

gees : Basics.Float -> Basics.Float -> Vector2d Acceleration.MetersPerSecondSquared coordinates

Force

newtons : Basics.Float -> Basics.Float -> Vector2d Force.Newtons coordinates

kilonewtons : Basics.Float -> Basics.Float -> Vector2d Force.Newtons coordinates

meganewtons : Basics.Float -> Basics.Float -> Vector2d Force.Newtons coordinates

pounds : Basics.Float -> Basics.Float -> Vector2d Force.Newtons coordinates

kips : Basics.Float -> Basics.Float -> Vector2d Force.Newtons coordinates

Advanced

These functions are unsafe because they require you to track units manually. In general you should prefer other functions instead, but these functions may be useful when writing generic/library code.

unsafe : { x : Basics.Float, y : Basics.Float } -> Vector2d units coordinates

Construct a vector from its raw X and Y components as Float values. The values must be in whatever units the resulting point is considered to use (usually meters or pixels). You should generally use something safer such as meters, fromPixels, xy, fromRecord etc.

unwrap : Vector2d units coordinates -> { x : Basics.Float, y : Basics.Float }

Extract a vector's raw X and Y components as Float values. These values will be in whatever units the vector has (usually meters or pixels). You should generally use something safer such as toMeters, toRecord, xComponent etc.