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!
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.
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.
Construct a vector from its X and Y components.
vector =
Vector2d.xy (Length.meters 2) (Length.meters 3)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)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.4142Construct 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.7071Construct 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 4Construct a vector with the given length in the given direction.
Vector2d.withLength (Length.meters 5) Direction2d.y
--> Vector2d.meters 0 5Construct 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.zeroConstruct 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 3Partial 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 15These functions are useful for interoperability with other Elm code that uses
plain Float tuples or records to represent vectors.
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 3Convert 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 )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 3Convert 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 }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.
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 5Multiply 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 5Get the X component of a vector.
Vector2d.xComponent (Vector2d.meters 2 3)
--> Length.meters 2Get the Y component of a vector.
Vector2d.yComponent (Vector2d.meters 2 3)
--> Length.meters 3Find the component of a vector in an arbitrary direction, for example
forwardSpeed =
Vector2d.componentIn forwardDirection velocityThis is more general and flexible than using xComponent or yComponent, both
of which can be expressed in terms of componentIn; for example,
Vector2d.xComponent vectoris equivalent to
Vector2d.componentIn Direction2d.x vectorGet the length (magnitude) of a vector.
Vector2d.length (Vector2d.meters 3 4)
--> Length.meters 5Attempt 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
--> NothingCompare 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
--> FalseCompare two Vector2d values lexicographically: first by X component, then
by Y. Can be used to provide a sort order for Vector2d values.
Find the sum of two vectors.
firstVector =
Vector2d.meters 1 2
secondVector =
Vector2d.meters 3 4
firstVector |> Vector2d.plus secondVector
--> Vector2d.meters 4 6Find 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 3Note the argument order - v1 - v2 would be written as
v1 |> Vector2d.minus v2which is the same as
Vector2d.minus v2 v1but the opposite of
Vector2d.minus v1 v2Find the dot product of two vectors.
firstVector =
Vector2d.meters 1 2
secondVector =
Vector2d.meters 3 4
firstVector |> Vector2d.dot secondVector
--> Area.squareMeters 11Find 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 v2which is the same as
Vector2d.cross v2 v1but the opposite of
Vector2d.cross v1 v2Some examples:
firstVector =
Vector2d.feet 2 0
secondVector =
Vector2d.feet 0 3
firstVector |> Vector2d.cross secondVector
--> Area.squareFeet 6
secondVector |> Vector2d.cross firstVector
--> Area.squareFeet -6
firstVector |> Vector2d.cross firstVector
--> Area.squareFeet 0Convert 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 300Convert 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 6Note 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 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 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.zeroWarning: 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.
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.)
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 0Rotate the given vector 90 degrees clockwise;
Vector2d.rotateClockwise vectoris equivalent to
Vector2d.rotateBy (Angle.degrees -90) vectorbut is more efficient.
Rotate the given vector 90 degrees counterclockwise;
Vector2d.rotateCounterclockwise vectoris equivalent to
Vector2d.rotateBy (Angle.degrees 90) vectorbut is more efficient.
Mirror a vector across a given axis.
vector =
Vector2d.meters 2 3
Vector2d.mirrorAcross Axis2d.y vector
--> Vector2d.meters -2 3The 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 -3Find 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 3Project 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 0This is equivalent to finding the projection in the axis' direction.
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)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 -1Take 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 1These 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.
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.
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.