A Vector3d represents a quantity such as a displacement or velocity in 3D,
and is defined by its X, Y and Z 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 Point3d
and Direction3d than Vector3d, and much code can avoid working directly with
Vector3d values at all!
The vector with components (0,0,0).
Although there are no predefined constants for the vectors with components
(1,0,0), (0,1,0) and (0,0,1), in most cases you will actually want their
Direction3d versions Direction3d.x,
Direction3d.y and Direction3d.z.
Construct a unitless Vector3d value from its X, Y and Z components. See
also fromUnitless.
The remaining functions all construct a Vector3d from X, Y and Z components
given in specific units. Functions like Vector3d.xyz are more useful in
generic code, but these functions are useful for quickly creating hardcoded
constant values, e.g.
vector =
Vector3d.meters 2 3 1These 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, y and
z fields measured in meters then you could write a decoder like
Decode.map3 Vector3d.meters
(Decode.field "x" Decode.float)
(Decode.field "y" Decode.float)
(Decode.field "z" Decode.float)There are some additional constructors below for vectors with physics-related units (speed, acceleration and force).
Construct a vector from its X, Y and Z components.
vector =
Vector3d.xyz
(Length.meters 2)
(Length.meters 1)
(Length.meters 3)Construct a vector given its local components within a particular frame:
frame =
Frame3d.atOrigin
|> Frame3d.rotateAround Axis3d.z
(Angle.degrees 45)
Vector3d.xyzIn frame
(Speed.feetPerSecond 1)
(Speed.feetPerSecond 0)
(Speed.feetPerSecond 2)
--> Vector3d.xyz
--> (Speed.feetPerSecond 0.7071)
--> (Speed.feetPerSecond 0.7071)
--> (Speed.feetPerSecond 2)Construct a vector from the first given point to the second.
startPoint =
Point3d.meters 1 1 1
endPoint =
Point3d.meters 4 5 6
Vector3d.from startPoint endPoint
--> Vector3d.meters 3 4 5Construct a vector with the given length in the given direction.
Vector3d.withLength (Length.meters 5) Direction3d.y
--> Vector3d.meters 0 5 0Construct a 3D vector lying on a sketch plane by providing a 2D vector specified in XY coordinates within the sketch plane.
vector2d =
Vector2d.meters 2 3
Vector3d.on SketchPlane3d.xy vector2d
--> Vector3d.meters 2 3 0
Vector3d.on SketchPlane3d.yz vector2d
--> Vector3d.meters 0 2 3
Vector3d.on SketchPlane3d.zx vector2d
--> Vector3d.meters 3 0 2A slightly more complex example:
tiltedSketchPlane =
SketchPlane3d.xy
|> SketchPlane3d.rotateAround Axis3d.x
(Angle.degrees 45)
Vector3d.on tiltedSketchPlane <|
Vector2d.meters 1 1
--> Vector3d.meters 1 0.7071 0.7071Construct a 3D vector lying on a sketch plane by providing its 2D components within the sketch plane:
Vector3d.xyOn SketchPlane3d.xy
(Length.meters 2)
(Length.meters 3)
--> Vector3d.meters 2 3 0
Vector3d.xyOn SketchPlane3d.zx
(Length.meters 2)
(Length.meters 3)
--> Vector3d.meters 3 0 2Construct a 3D vector lying on a sketch plane by providing its 2D polar components within the sketch plane:
Vector3d.rThetaOn SketchPlane3d.xy
(Length.meters 2)
(Angle.degrees 45)
--> Vector3d.meters 1.4142 1.4142 0
Vector3d.rThetaOn SketchPlane3d.yz
(Length.meters 2)
(Angle.degrees 30)
--> Vector3d.meters 0 1.732 1Construct an arbitrary vector perpendicular to the given vector. The exact length and direction of the resulting vector are not specified, but it is guaranteed to be perpendicular to the given vector and non-zero (unless the given vector is itself zero).
Vector3d.perpendicularTo (Vector3d.meters 3 0 0)
--> Vector3d.meters 0 0 -3
Vector3d.perpendicularTo (Vector3d.meters 1 2 3)
--> Vector3d.meters 0 -3 2
Vector3d.perpendicularTo Vector3d.zero
--> Vector3d.zeroConstruct a vector by interpolating from the first given vector to the second, based on a parameter that ranges from zero to one.
startVector =
Vector3d.meters 1 2 4
endVector =
Vector3d.meters 1 3 8
Vector3d.interpolateFrom startVector endVector 0.25
--> Vector3d.meters 1 2.25 5Partial application may be useful:
interpolatedVector : Float -> Vector3d
interpolatedVector =
Vector3d.interpolateFrom startVector endVector
List.map interpolatedVector [ 0, 0.5, 1 ]
--> [ Vector3d.meters 1 2 4
--> , Vector3d.meters 1 2 6
--> , Vector3d.meters 1 2 8
--> ]You can pass values less than zero or greater than one to extrapolate:
interpolatedVector -0.5
--> Vector3d.meters 1 2 2
interpolatedVector 1.25
--> Vector3d.meters 1 2 9These functions are useful for interoperability with other Elm code that uses
plain Float tuples or records to represent vectors.
Construct a Vector3d from a tuple of Float values, by specifying what units those values are
in.
Vector3d.fromTuple Length.meters ( 2, 3, 1 )
--> Vector3d.meters 2 3 1Convert a Vector3d to a tuple of Float values, by specifying what units you want the result
to be in.
vector =
Vector3d.feet 2 3 1
Vector3d.toTuple Length.inInches vector
--> ( 24, 36, 12 )Construct a Vector3d from a record with Float fields, by specifying what
units those fields are in.
Vector3d.fromRecord Length.inches { x = 24, y = 36, z = 12 }
--> Vector3d.feet 2 3 1Convert a Vector3d to a record with Float fields, by specifying what units you want the
result to be in.
vector =
Vector3d.meters 2 3 1
Vector3d.toRecord Length.inCentimeters vector
--> { x = 200, y = 300, z = 100 }These functions allow zero-overhead conversion of vectors to and from records
with x, y and z 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 =
Vector3d.meters 6 8 4
displacement |> Vector3d.per (Duration.seconds 2)
--> Vector3d.xyz
--> (Speed.metersPerSecond 3)
--> (Speed.metersPerSecond 4)
--> (Speed.metersPerSecond 2)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 =
Vector3d.xy
(Pixels.pixelsPerSecond 200)
(Pixels.pixelsPerSecond 50)
(Pixels.pixelsPerSecond 100)
velocity |> Vector3d.for (Duration.seconds 0.1)
--> Vector3d.pixels 20 5 10Get the X, Y and Z components of a vector as a tuple.
Vector3d.components (Vector3d.meters 2 3 1)
--> ( Length.meters 2
--> , Length.meters 3
--> , Length.meters 1
--> )Get the X component of a vector.
Vector3d.xComponent (Vector3d.meters 1 2 3)
--> Length.meters 1Get the Y component of a vector.
Vector3d.yComponent (Vector3d.meters 1 2 3)
--> Length.meters 2Get the Z component of a vector.
Vector3d.zComponent (Vector3d.meters 1 2 3)
--> Length.meters 3Find the component of a vector in an arbitrary direction, for example
verticalSpeed =
Vector3d.componentIn upDirection velocityThis is more general and flexible than using xComponent, yComponent or
zComponent, all of which can be expressed in terms of componentIn; for
example,
Vector3d.zComponent vectoris equivalent to
Vector3d.componentIn Direction3d.z vectorGet the length (magnitude) of a vector.
Vector3d.length (Vector3d.meters 2 1 2)
--> Length.meters 3Attempt to find the direction of a vector. In the case of a zero vector,
returns Nothing.
Vector3d.direction (Vector3d.meters 3 0 3)
--> Just (Direction3d.xz (Angle.degrees 45))
Vector3d.direction Vector3d.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 =
Vector3d.meters 2 1 3
secondVector =
Vector3d.meters 2.0002 0.9999 3.0001
Vector3d.equalWithin (Length.millimeters 1)
firstVector
secondVector
--> True
Vector3d.equalWithin (Length.microns 1)
firstVector
secondVector
--> FalseCompare two Vector3d values lexicographically: first by X component, then
by Y, then by Z. Can be used to provide a sort order for Vector3d values.
Alias for scaleBy since a Vector3d can be kind of thought of
as either a 'mathematical' or 'geometric' object.
Divide a vector by the given value.
Find the sum of two vectors.
firstVector =
Vector3d.meters 1 2 3
secondVector =
Vector3d.meters 4 5 6
firstVector |> Vector3d.plus secondVector
--> Vector3d.meters 5 7 9Find the difference between two vectors (the second vector minus the first).
firstVector =
Vector3d.meters 5 6 7
secondVector =
Vector3d.meters 1 1 1
firstVector |> Vector3d.minus secondVector
--> Vector3d.meters 4 5 6Note the argument order: v1 - v2 would be written as
v1 |> Vector3d.minus v2which is the same as
Vector3d.minus v2 v1but the opposite of
Vector3d.minus v1 v2Find the dot product of two vectors.
firstVector =
Vector3d.meters 1 0 2
secondVector =
Vector3d.meters 3 4 5
firstVector |> Vector3d.dot secondVector
--> Area.squareMeters 13Find the cross product of two vectors.
firstVector =
Vector3d.meters 2 0 0
secondVector =
Vector3d.meters 0 3 0
firstVector |> Vector3d.cross secondVector
--> Vector3d.xyz
--> Quantity.zero
--> Quantity.zero
--> (Area.squareMeters 6)Note the argument order: v1 x v2 would be written as
v1 |> Vector3d.cross v2which is the same as
Vector3d.cross v2 v1but the opposite of
Vector3d.cross v1 v2Note that the cross product of two vectors with length units will be a vector with area units!
Find the sum of a list of vectors.
Shorthand for Vector3d.scaleBy 2.
Shorthand for Vector3d.scaleBy 0.5.
Multiply a scalar and a vector, resulting in a vector with units Product
scalarUnits vectorUnits:
forceVector =
Vector3d.product mass accelerationVectorIf you just want to scale a vector by a certain amount, you can use
scaleBy instead.
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.)
Multiply a vector by a unitless quantity, leaving the units unchanged.
Divide a vector with units Product units1 units2 by a scalar with units
units1, resulting in a vector with units units2.
accelerationVector =
forceVector |> Vector3d.over massDivide 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.
Divide a vector by a unitless quantity, leaving the units unchanged.
Note that for all transformations, only the orientation of the given axis or plane is relevant, since vectors are position-independent. Think of transforming a vector as placing its tail on the relevant axis or plane and then transforming its tip.
Reverse the direction of a vector, negating its components.
Vector3d.reverse (Vector3d.meters 1 -3 2)
--> Vector3d.meters -1 3 -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 =
Vector3d.meters 3 0 4
Vector3d.normalize vector
--> Vector3d.meters 0.6 0 0.8
Vector3d.normalize Vector3d.zero
--> Vector3d.zeroWarning: Vector3d.direction is safer since it forces you to explicitly
consider the case where the given vector is zero. Vector3d.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
Vector3d.direction vector
|> Maybe.map Direction3d.toVector
|> Maybe.withDefault Vector3d.zero(which is functionally equivalent to Vector3d.normalize vector) is too high.
Scale the length of a vector by a given scale.
Vector3d.scaleBy 3 (Vector3d.meters 1 2 3)
--> Vector3d.meters 3 6 9(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.)
Scale a vector to a given length.
Vector3d.scaleTo (Length.meters 25) (Vector3d.meters 0 3 4)
--> Vector3d.meters 0 15 20Scaling a zero vector will always result in a zero vector.
Rotate a vector around a given direction by a given angle.
vector =
Vector3d.meters 2 0 1
vector
|> Vector3d.rotateAround Direction3d.x
(Angle.degrees 90)
--> Vector3d.meters 2 -1 0
vector
|> Vector3d.rotateAround Direction3d.z
(Angle.degrees 45)
--> Vector3d.meters 1.4142 1.4142 1Mirror a vector across a plane.
vector =
Vector3d.meters 1 2 3
Vector3d.mirrorAcross Plane3d.xy vector
--> Vector3d.meters 1 2 -3
Vector3d.mirrorAcross Plane3d.yz vector
--> Vector3d.meters -1 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 =
Vector3d.meters 1 2 3
Vector3d.projectionIn Direction3d.x vector
--> Vector3d.meters 1 0 0
Vector3d.projectionIn Direction3d.z vector
--> Vector3d.meters 0 0 3Project a vector orthographically onto a plane. Conceptually, this means splitting the original vector into a portion parallel to the plane (perpendicular to the plane's normal direction) and a portion perpendicular to it (parallel to its normal direction), then returning the parallel (in-plane) portion.
vector =
Vector3d.meters 2 1 3
Vector3d.projectOnto Plane3d.xy vector
--> Vector3d.meters 2 1 0
Vector3d.projectOnto Plane3d.xz vector
--> Vector3d.meters 2 0 3Convert 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 =
Vector3d.meters 2 3 1
resolution : Quantity Float (Rate Pixels Meters)
resolution =
Pixels.pixels 100 |> Quantity.per (Length.meters 1)
worldVector |> Vector3d.at resolution
--> Vector3d.pixels 200 300 100Convert 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 =
Vector3d.pixels 200 300 100
resolution : Quantity Float (Rate Pixels Meters)
resolution =
Pixels.pixels 50 |> Quantity.per (Length.meters 1)
screenVector |> Vector3d.at_ resolution
--> Vector3d.meters 4 6 2Like other transformations, coordinate transformations of vectors depend only on the orientations of the relevant frames/sketch planes, not their positions.
For the examples, assume the following definition of a local coordinate frame, one that is rotated 30 degrees counterclockwise around the Z axis from the global XYZ frame:
rotatedFrame =
Frame3d.atOrigin |> Frame3d.rotateAround Axis3d.z (Angle.degrees 30)Take a vector defined in global coordinates, and return it expressed in local coordinates relative to a given reference frame.
vector =
Vector3d.meters 2 0 3
Vector3d.relativeTo rotatedFrame vector
--> Vector3d.meters 1.732 -1 3Take a vector defined in local coordinates relative to a given reference frame, and return that vector expressed in global coordinates.
vector =
Vector3d.meters 2 0 3
Vector3d.placeIn rotatedFrame vector
--> Vector3d.meters 1.732 1 3Project a vector into a given sketch plane. Conceptually, this finds the orthographic projection of the vector onto the plane and then expresses the projected vector in 2D sketch coordinates.
vector =
Vector3d.meters 2 1 3
Vector3d.projectInto SketchPlane3d.xy vector
--> Vector2d.meters 2 1
Vector3d.projectInto SketchPlane3d.yz vector
--> Vector2d.meters 1 3
Vector3d.projectInto SketchPlane3d.zx vector
--> Vector2d.meters 3 2These 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
Vector3d.feetPerSecond 0 5 0and a force of 10 newtons in the negative X direction could be written as
Vector3d.newtons -10 0 0These 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, Y and Z components as Float values. The
values must be in whatever units the resulting vector is considered to use
(usually meters or pixels). You should generally use something safer such as
meters, fromPixels, xyz,
fromRecord etc.
Extract a vector's raw X, Y and Z coordinates 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.