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, OpenSolid 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 zero vector.
Vector2d.zero
--> Vector2d.fromComponents ( 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 vector from its X and Y components.
vector =
Vector2d.fromComponents ( 2, 3 )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 )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 )Construct a vector with the given length in the given direction.
Vector2d.with { length = 5, direction = Direction2d.y }
--> Vector2d.fromComponents ( 0, 5 )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.
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.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.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 )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 vectorGet the X component of a vector.
Vector2d.xComponent (Vector2d.fromComponents ( 2, 3 ))
--> 2Get the Y component of a vector.
Vector2d.yComponent (Vector2d.fromComponents ( 2, 3 ))
--> 3Get the polar components (length, polar angle) of a vector.
Vector2d.polarComponents
(Vector2d.fromComponents ( 1, 1 ))
--> ( 1.4142, degrees 45 )Get the length (magnitude) of a vector.
Vector2d.length (Vector2d.fromComponents ( 3, 4 ))
--> 5Get the squared length of a vector. squaredLength is slightly faster than
length, so for example
Vector2d.squaredLength vector > tolerance * toleranceis equivalent to but slightly more efficient than
Vector2d.length vector > tolerancesince 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!
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
--> NothingAttempt 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
--> 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.fromComponents ( 1, 2 )
secondVector =
Vector2d.fromComponents ( 0.9999, 2.0002 )
Vector2d.equalWithin 1e-3 firstVector secondVector
--> True
Vector2d.equalWithin 1e-6 firstVector secondVector
--> FalseFind 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 vectorFind the sum of two vectors.
firstVector =
Vector2d.fromComponents ( 1, 2 )
secondVector =
Vector2d.fromComponents ( 3, 4 )
Vector2d.sum firstVector secondVector
--> Vector2d.fromComponents ( 4, 6 )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 )Find the dot product of two vectors.
firstVector =
Vector2d.fromComponents ( 1, 2 )
secondVector =
Vector2d.fromComponents ( 3, 4 )
Vector2d.dotProduct firstVector secondVector
--> 11Find 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 * x2and 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
--> 0Note 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.flip (Vector2d.fromComponents ( -1, 2 ))
--> Vector2d.fromComponents ( 1, -2 )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.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.fromComponents ( 1, 2 ))
--> Vector2d.fromComponents ( 3, 6 )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 )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.with
{ originPoint =
Point2d.fromCoordinates ( 100, 200 )
, direction = Direction2d.x
}
Vector2d.mirrorAcross horizontalAxis vector
--> Vector2d.fromComponents ( 2, -3 )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 )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.
Functions for transforming vectors between local and global coordinates in different coordinate frames. 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.xyTake 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 )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 )