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!
Geometry.Types.Vector2d
zero : Vector2d
The zero vector.
Vector2d.zero
--> Vector2d.fromComponents ( 0, 0 )
fromComponents : ( Basics.Float, Basics.Float ) -> Vector2d
Construct a vector from its X and Y components.
vector =
Vector2d.fromComponents ( 2, 3 )
fromPolarComponents : ( Basics.Float, Basics.Float ) -> Vector2d
Construct a vector from a length and angle. The angle is measured counterclockwise from the positive X direction.
Vector2d.fromPolarComponents ( 2, degrees 135 )
-->Vector2d.fromComponents ( -1.4142, 1.4142 )
from : Geometry.Types.Point2d -> Geometry.Types.Point2d -> Vector2d
Construct a vector from the first given point to the second.
startPoint =
Point2d.fromCoordinates ( 1, 1 )
endPoint =
Point2d.fromCoordinates ( 4, 5 )
Vector2d.from startPoint endPoint
--> Vector2d.fromComponents ( 3, 4 )
withLength : Basics.Float -> Geometry.Types.Direction2d -> Vector2d
Construct a vector with the given length in the given direction.
Vector2d.withLength 5 Direction2d.y
--> Vector2d.fromComponents ( 0, 5 )
perpendicularTo : Vector2d -> Vector2d
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.fromComponents ( 1, 0 ))
--> Vector2d.fromComponents ( 0, 1 )
Vector2d.perpendicularTo
(Vector2d.fromComponents ( 0, 2 ))
--> Vector2d.fromComponents ( -2, 0 )
Vector2d.perpendicularTo
(Vector2d.fromComponents ( 3, 1 ))
--> Vector2d.fromComponents ( -1, 3 )
Vector2d.perpendicularTo Vector2d.zero
--> Vector2d.zero
interpolateFrom : Vector2d -> Vector2d -> Basics.Float -> Vector2d
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.fromComponents ( 8, 12 )
Vector2d.interpolateFrom startVector endVector 0.25
--> Vector2d.fromComponents ( 2, 3 )
Partial application may be useful:
interpolatedVector : Float -> Vector2d
interpolatedVector =
Vector2d.interpolateFrom startVector endVector
List.map interpolatedVector [ 0, 0.5, 1 ]
--> [ Vector2d.fromComponents ( 0, 0 )
--> , Vector2d.fromComponents ( 4, 6 )
--> , Vector2d.fromComponents ( 8, 12 )
--> ]
You can pass values less than zero or greater than one to extrapolate:
interpolatedVector -0.5
--> Vector2d.fromComponents ( -4, -6 )
interpolatedVector 1.25
--> Vector2d.fromComponents ( 10, 15 )
components : Vector2d -> ( Basics.Float, Basics.Float )
Extract the components of a vector.
Vector2d.components (Vector2d.fromComponents ( 2, 3 ))
--> ( 2, 3 )
This combined with Elm's built-in tuple destructuring provides a convenient way to extract both the X and Y components of a vector in one line of code:
( x, y ) =
Vector2d.components vector
xComponent : Vector2d -> Basics.Float
Get the X component of a vector.
Vector2d.xComponent (Vector2d.fromComponents ( 2, 3 ))
--> 2
yComponent : Vector2d -> Basics.Float
Get the Y component of a vector.
Vector2d.yComponent (Vector2d.fromComponents ( 2, 3 ))
--> 3
polarComponents : Vector2d -> ( Basics.Float, Basics.Float )
Get the polar components (length, polar angle) of a vector.
Vector2d.polarComponents
(Vector2d.fromComponents ( 1, 1 ))
--> ( 1.4142, degrees 45 )
length : Vector2d -> Basics.Float
Get the length (magnitude) of a vector.
Vector2d.length (Vector2d.fromComponents ( 3, 4 ))
--> 5
squaredLength : Vector2d -> Basics.Float
Get the squared length of a vector. squaredLength
is slightly faster than
length
, so for example
Vector2d.squaredLength vector > tolerance * tolerance
is equivalent to but slightly more efficient than
Vector2d.length vector > tolerance
since the latter requires a square root under the hood. In many cases, however,
the speed difference will be negligible and using length
is much more
readable!
direction : Vector2d -> Maybe Geometry.Types.Direction2d
Attempt to find the direction of a vector. In the case of a zero vector,
return Nothing
.
Vector2d.direction (Vector2d.fromComponents ( 3, 3 ))
--> Just (Direction2d.fromAngle (degrees 45))
Vector2d.direction Vector2d.zero
--> Nothing
lengthAndDirection : Vector2d -> Maybe ( Basics.Float, Geometry.Types.Direction2d )
Attempt to find the length and direction of a vector. In the case of a zero
vector, returns Nothing
.
vector =
Vector2d.fromComponents ( 1, 1 )
Vector2d.lengthAndDirection vector
--> Just
--> ( 1.4142
--> , Direction2d.fromAngle (degrees 45)
--> )
Vector2d.lengthAndDirection Vector2d.zero
--> Nothing
equalWithin : Basics.Float -> Vector2d -> Vector2d -> 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.fromComponents ( 1, 2 )
secondVector =
Vector2d.fromComponents ( 0.9999, 2.0002 )
Vector2d.equalWithin 1e-3 firstVector secondVector
--> True
Vector2d.equalWithin 1e-6 firstVector secondVector
--> False
componentIn : Geometry.Types.Direction2d -> Vector2d -> Basics.Float
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
sum : Vector2d -> Vector2d -> Vector2d
Find the sum of two vectors.
firstVector =
Vector2d.fromComponents ( 1, 2 )
secondVector =
Vector2d.fromComponents ( 3, 4 )
Vector2d.sum firstVector secondVector
--> Vector2d.fromComponents ( 4, 6 )
difference : Vector2d -> Vector2d -> Vector2d
Find the difference between two vectors (the first vector minus the second).
firstVector =
Vector2d.fromComponents ( 5, 6 )
secondVector =
Vector2d.fromComponents ( 1, 3 )
Vector2d.difference firstVector secondVector
--> Vector2d.fromComponents ( 4, 3 )
dotProduct : Vector2d -> Vector2d -> Basics.Float
Find the dot product of two vectors.
firstVector =
Vector2d.fromComponents ( 1, 2 )
secondVector =
Vector2d.fromComponents ( 3, 4 )
Vector2d.dotProduct firstVector secondVector
--> 11
crossProduct : Vector2d -> Vector2d -> Basics.Float
Find the scalar 'cross product' of two vectors in 2D. This is defined as
crossProduct firstVector secondVector =
let
( x1, y1 ) =
components firstVector
( x2, y2 ) =
components secondVector
in
x1 * y2 - y1 * x2
and is useful in many of the same ways as the 3D cross product:
Some examples:
firstVector =
Vector2d.fromComponents ( 2, 0 )
secondVector =
Vector2d.fromComponents ( 0, 3 )
Vector2d.crossProduct firstVector secondVector
--> 6
Vector2d.crossProduct secondVector firstVector
--> -6
Vector2d.crossProduct firstVector firstVector
--> 0
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 -> Vector2d
Reverse the direction of a vector, negating its components.
Vector2d.reverse (Vector2d.fromComponents ( -1, 2 ))
--> Vector2d.fromComponents ( 1, -2 )
normalize : Vector2d -> Vector2d
Normalize a vector to have a length of one. Zero vectors are left as-is.
vector =
Vector2d.fromComponents ( 3, 4 )
Vector2d.normalize vector
--> Vector2d.fromComponents ( 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 -> Vector2d
Scale the length of a vector by a given scale.
Vector2d.scaleBy 3 (Vector2d.fromComponents ( 1, 2 ))
--> Vector2d.fromComponents ( 3, 6 )
rotateBy : Basics.Float -> Vector2d -> Vector2d
Rotate a vector counterclockwise by a given angle (in radians).
Vector2d.fromComponents ( 1, 1 )
|> Vector2d.rotateBy (degrees 45)
--> Vector2d.fromComponents ( 0, 1.4142 )
Vector2d.fromComponents ( 1, 0 )
|> Vector2d.rotateBy pi
--> Vector2d.fromComponents ( -1, 0 )
rotateClockwise : Vector2d -> Vector2d
Rotate the given vector 90 degrees clockwise;
Vector2d.rotateClockwise vector
is equivalent to
Vector2d.rotateBy (degrees -90) vector
but is more efficient.
rotateCounterclockwise : Vector2d -> Vector2d
Rotate the given vector 90 degrees counterclockwise;
Vector2d.rotateCounterclockwise vector
is equivalent to
Vector2d.rotateBy (degrees 90) vector
but is more efficient.
mirrorAcross : Geometry.Types.Axis2d -> Vector2d -> Vector2d
Mirror a vector across a given axis.
vector =
Vector2d.fromComponents ( 2, 3 )
Vector2d.mirrorAcross Axis2d.y vector
--> Vector2d.fromComponents ( -2, 3 )
The position of the axis doesn't matter, only its orientation:
horizontalAxis =
Axis2d.withDirection Direction2d.x
(Point2d.fromCoordinates ( 100, 200 ))
Vector2d.mirrorAcross horizontalAxis vector
--> Vector2d.fromComponents ( 2, -3 )
projectionIn : Geometry.Types.Direction2d -> Vector2d -> Vector2d
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.fromComponents ( 2, 3 )
Vector2d.projectionIn Direction2d.x vector
--> Vector2d.fromComponents ( 2, 0 )
Vector2d.projectionIn Direction2d.y vector
--> Vector2d.fromComponents ( 0, 3 )
projectOnto : Geometry.Types.Axis2d -> Vector2d -> Vector2d
Project a vector onto an axis.
Vector2d.projectOnto Axis2d.y
(Vector2d.fromComponents ( 3, 4 ))
--> Vector2d.fromComponents ( 0, 4 )
Vector2d.projectOnto Axis2d.x
(Vector2d.fromComponents ( -1, 2 ))
--> Vector2d.fromComponents ( -1, 0 )
This 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.rotateBy (degrees 30) Frame2d.xy
relativeTo : Geometry.Types.Frame2d -> Vector2d -> Vector2d
Take a vector defined in global coordinates, and return it expressed in local coordinates relative to a given reference frame.
Vector2d.fromComponents ( 2, 0 )
|> Vector2d.relativeTo rotatedFrame
--> Vector2d.fromComponents ( 1.732, -1 )
placeIn : Geometry.Types.Frame2d -> Vector2d -> Vector2d
Take a vector defined in local coordinates relative to a given reference frame, and return that vector expressed in global coordinates.
Vector2d.fromComponents ( 2, 0 )
|> Vector2d.placeIn rotatedFrame
--> Vector2d.fromComponents ( 1.732, 1 )