ianmackenzie/elm-geometry/4.0.0

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

  • Adding or subtracting vectors
  • Finding the lengths of vectors
  • Rotating vectors
  • Converting vectors between different coordinate systems

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 =
Vector2d units coordinates

Constants

zero : Vector2d units coordinates

The vector with components (0,0).

Although there are no predefined constants for the vectors with components (1,0) and (0,1), in most cases you will actually want their Direction2d versions Direction2d.x and Direction2d.y.

Literals

unitless : Float -> Float -> Vector2d Unitless coordinates

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

The remaining functions all construct a Vector2d from X and Y components given in specific units. Functions like Vector2d.xy are more useful in generic code, but these functions are useful for quickly creating hardcoded constant values, e.g.

vector =
    Vector2d.meters 2 3

These functions may also be useful when decoding vectors from JSON - for example if you had some JSON where a vector was encoded as an object with x and y fields measured in meters then you could write a decoder like

Decode.map2 Vector2d.meters
    (Decode.field "x" Decode.float)
    (Decode.field "y" Decode.float)
meters : Float -> Float -> Vector2d Meters coordinates
pixels : Float -> Float -> Vector2d Pixels coordinates
millimeters : Float -> Float -> Vector2d Meters coordinates
centimeters : Float -> Float -> Vector2d Meters coordinates
inches : Float -> Float -> Vector2d Meters coordinates
feet : Float -> Float -> Vector2d Meters coordinates

There are some additional constructors below for vectors with physics-related units (speed, acceleration and force).

Constructors

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

Construct a vector from its X and Y components.

vector =
    Vector2d.xy (Length.meters 2) (Length.meters 3)
Frame2d
frameUnits
globalCoordinates
{ defines : localCoordinates }
-> Quantity Float units
-> Quantity 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 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
Frame2d
frameUnits
globalCoordinates
{ defines : localCoordinates }
-> Quantity 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
Point2d units coordinates
-> 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
Quantity Float units
-> 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
Vector2d units coordinates
-> Vector2d units coordinates
-> 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.

(Float -> Quantity Float units)
-> ( Float, 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
(Quantity Float units -> Float)
-> Vector2d units coordinates
-> ( Float, 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 )
(Float -> Quantity Float units)
-> { x : Float
, y : 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
(Quantity Float units -> Float)
-> Vector2d units coordinates
-> { x : Float
, y : 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.

{ x : Float
, y : Float
}
-> Vector2d Meters coordinates
Vector2d Meters coordinates
-> { x : Float
, y : Float
}
{ x : Float
, y : Float
}
-> Vector2d Pixels coordinates
Vector2d Pixels coordinates
-> { x : Float
, y : Float
}
{ x : Float
, y : Float
}
-> Vector2d Unitless coordinates
Vector2d Unitless coordinates
-> { x : Float
, y : Float
}

Rates of change

per :
Quantity Float independentUnits
-> Vector2d dependentUnits coordinates
-> Vector2d (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 Float independentUnits
-> Vector2d (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

Vector2d units coordinates
-> ( Quantity Float units, Quantity 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 Float units

Get the X component of a vector.

Vector2d.xComponent (Vector2d.meters 2 3)
--> Length.meters 2
yComponent : Vector2d units coordinates -> Quantity Float units

Get the Y component of a vector.

Vector2d.yComponent (Vector2d.meters 2 3)
--> Length.meters 3
Direction2d coordinates
-> Vector2d units coordinates
-> Quantity 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 Float units

Get the length (magnitude) of a vector.

Vector2d.length (Vector2d.meters 3 4)
--> Length.meters 5
direction : Vector2d units coordinates -> Maybe (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

Quantity Float units
-> Vector2d units coordinates
-> Vector2d units coordinates
-> 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 -> 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 : 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 : Float -> Vector2d units coordinates -> Vector2d units coordinates

Divide a vector by the given value.

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
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 Float (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
Vector2d units2 coordinates
-> Vector2d units1 coordinates
-> Quantity Float (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:

  • Its length is equal to the product of the lengths of the two given vectors and the sine of the angle between them, so it can be used as a metric to determine if two vectors are nearly parallel.
  • The sign of the result indicates the direction of rotation from the first vector to the second (positive indicates a counterclockwise rotation and negative indicates a clockwise rotation), similar to how the direction of the 3D cross product indicates the direction of rotation.

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

Quantity Float scalarUnits
-> Vector2d vectorUnits coordinates
-> Vector2d (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.

Quantity Float scalarUnits
-> Vector2d vectorUnits coordinates
-> Vector2d (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.)

Quantity Float Unitless
-> Vector2d units coordinates
-> Vector2d units coordinates

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

Quantity Float units1
-> Vector2d (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
Quantity Float units2
-> Vector2d (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.

Quantity Float 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 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 : 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.)

Quantity 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.

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
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
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 Float (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 Float (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)
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
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 : Float -> Float -> Vector2d MetersPerSecond coordinates
feetPerSecond : Float -> Float -> Vector2d MetersPerSecond coordinates
kilometersPerHour : Float -> Float -> Vector2d MetersPerSecond coordinates
milesPerHour : Float -> Float -> Vector2d MetersPerSecond coordinates

Acceleration

metersPerSecondSquared : Float -> Float -> Vector2d MetersPerSecondSquared coordinates
feetPerSecondSquared : Float -> Float -> Vector2d MetersPerSecondSquared coordinates
gees : Float -> Float -> Vector2d MetersPerSecondSquared coordinates

Force

newtons : Float -> Float -> Vector2d Newtons coordinates
kilonewtons : Float -> Float -> Vector2d Newtons coordinates
meganewtons : Float -> Float -> Vector2d Newtons coordinates
pounds : Float -> Float -> Vector2d Newtons coordinates
kips : Float -> Float -> Vector2d 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.

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

Vector2d units coordinates
-> { x : Float
, y : 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.