A Frame2d has an origin point and a pair of X and Y directions (which are
always perpendicular to each other). It can be thought of as:
relativeTo
and placeIn functions that convert values of that type from global
coordinates to local coordinates in a particular frame, and vice versa.Frame2d could be
used to define the position and orientation of a spaceship in a 2D game.
Movement of the ship would then be done by translating and rotating the
frame.The global XY frame, centered at the origin.
Frame2d.originPoint Frame2d.atOrigin
--> Point2d.origin
Frame2d.xDirection Frame2d.atOrigin
--> Direction2d.x
Frame2d.yDirection Frame2d.atOrigin
--> Direction2d.yConstruct a frame aligned with the global XY frame but with the given origin point.
frame =
Frame2d.atPoint (Point2d.meters 2 3)
Frame2d.originPoint frame
--> Point2d.meters 2 3
Frame2d.xDirection frame
--> Direction2d.x
Frame2d.yDirection frame
--> Direction2d.yConstruct a frame with the given angle and origin point. The angle is the amount the returned frame will be rotated relative to the global XY frame, or equivalently the angle of the frame's X direction;
Frame2d.withAngle angle pointis equivalent to
Frame2d.withXDirection
(Direction2d.fromAngle givenAngle)
pointConstruct a frame with the given X axis direction, having the given origin point. The Y axis direction will be constructed by rotating the given X direction 90 degrees counterclockwise:
frame =
Frame2d.withXDirection (Direction2d.degrees 30)
(Point2d.meters 2 3)
Frame2d.yDirection frame
--> Direction2d.degrees 120Construct a frame with the given Y axis direction, having the given origin point. The X axis direction will be constructed by rotating the given Y direction 90 degrees clockwise:
frame =
Frame2d.withYDirection (Direction2d.degrees 30)
(Point2d.meters 2 3)
Frame2d.xDirection frame
--> Direction2d.degrees -60Construct a Frame2d given its X axis;
Frame2d.fromXAxis axisis equivalent to
Frame2d.withXDirection (Axis2d.direction axis)
(Axis2d.originPoint axis)Construct a Frame2d given its Y axis;
Frame2d.fromYAxis axisis equivalent to
Frame2d.withYDirection (Axis2d.direction axis)
(Axis2d.originPoint axis)Create a 'fresh copy' of a frame: one with the same origin point and X/Y directions, but that can be used to define a different local coordinate system. Sometimes useful in generic/library code. Despite the name, this is efficient: it doesn't actually copy anything.
Construct a frame directly from its origin point and X and Y directions:
frame =
Frame2d.unsafe
{ originPoint = Point2d.meters 2 3
, xDirection = Direction2d.degrees 45
, yDirection = Direction2d.degrees 135
}In this case you must be careful to ensure that the X and Y directions are
perpendicular. To construct pairs of perpendicular directions,
Direction2d.orthonormalize or
Direction2d.orthogonalize may be useful.
Get the origin point of a given frame.
Frame2d.originPoint Frame2d.atOrigin
--> Point2d.originGet the X direction of a given frame.
Frame2d.xDirection Frame2d.atOrigin
--> Direction2d.xGet the Y direction of a given frame.
Frame2d.yDirection Frame2d.atOrigin
--> Direction2d.yCheck if a frame is right-handed.
Frame2d.isRightHanded Frame2d.atOrigin
--> True
Frame2d.isRightHanded
(Frame2d.reverseX Frame2d.atOrigin)
--> FalseAll predefined frames are right-handed, and most operations on frames preserve
handedness, so about the only ways to end up with a left-handed frame are by
constructing one explicitly with unsafe or by mirroring a right-handed frame.
Get the X axis of a given frame (the axis formed from the frame's origin point and X direction).
Frame2d.xAxis Frame2d.atOrigin
--> Axis2d.xGet the Y axis of a given frame (the axis formed from the frame's origin point and Y direction).
Frame2d.yAxis Frame2d.atOrigin
--> Axis2d.yConvert a frame 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.
Convert a frame 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.
Reverse the X direction of a frame, leaving its Y direction and origin point the same.
point =
Point2d.meters 2 3
frame =
Frame2d.atPoint point |> Frame2d.reverseX
Frame2d.originPoint frame
--> point
Frame2d.xDirection frame
--> Direction2d.negativeX
Frame2d.yDirection frame
--> Direction2d.yNote that this will switch the handedness of the frame.
Reverse the Y direction of a frame, leaving its X direction and origin point the same.
frame =
Frame2d.atPoint (Point2d.meters 2 3)
|> Frame2d.reverseY
Frame2d.originPoint frame
--> Point2d.meters 2 3
Frame2d.xDirection frame
--> Direction2d.x
Frame2d.yDirection frame
--> Direction2d.negativeYNote that this will switch the handedness of the frame.
Move a frame so that it has the given origin point.
point =
Point2d.meters 1 1
Frame2d.atOrigin |> Frame2d.moveTo point
--> Frame2d.atPoint pointRotate a frame counterclockwise by a given angle around the frame's own origin point. The resulting frame will have the same origin point, and its X and Y directions will be rotated by the given angle.
rotatedFrame =
Frame2d.atOrigin
|> Frame2d.rotateBy (Angle.degrees 30)
Frame2d.xDirection rotatedFrame
--> Direction2d.degrees 30
Frame2d.yDirection rotatedFrame
--> Direction2d.degrees 120Rotate a frame counterclockwise around a given point by a given angle. The frame's origin point will be rotated around the given point by the given angle, and its X and Y basis directions will be rotated by the given angle.
rotatedFrame =
Frame2d.atPoint (Point2d.meters 1 1)
|> Frame2d.rotateAround Point2d.origin
(Angle.degrees 45)
Frame2d.originPoint rotatedFrame
--> Point2d.meters 0 1.4142
Frame2d.xDirection rotatedFrame
--> Direction2d.degrees 45
Frame2d.yDirection rotatedFrame
--> Direction2d.degrees 135Translate a frame by a given displacement.
frame =
Frame2d.atPoint (Point2d.meters 2 3)
displacement =
Vector2d.meters 1 1
Frame2d.translateBy displacement frame
--> Frame2d.atPoint (Point2d.meters 3 4)Translate a frame in a given direction by a given distance;
Frame2d.translateIn direction distanceis equivalent to
Frame2d.translateBy
(Vector2d.withLength distance direction)Translate a frame along one of its own axes by a given distance.
The first argument is a function that returns the axis to translate along, given
the current frame. The majority of the time this argument will be either
Frame2d.xAxis or Frame2d.yAxis. The second argument is the distance to
translate along the given axis.
This function is convenient when constructing frames via a series of transformations. For example,
frame =
Frame2d.atPoint (Point2d.meters 2 0)
|> Frame2d.rotateBy (Angle.degrees 45)
|> Frame2d.translateAlongOwn Frame2d.xAxis
(Length.meters 2)means "construct a frame at the point (2, 0), rotate it around its own origin point by 45 degrees, then translate it along its own X axis by 2 meters", resulting in
Frame2d.originPoint frame
--> Point2d.meters 3.4142 1.4142
Frame2d.xDirection frame
--> Direction2d.degrees 45
Frame2d.yDirection frame
--> Direction2d.degrees 135Mirror a frame across an axis.
frame =
Frame2d.atPoint (Point2d.meters 2 3)
mirroredFrame =
Frame2d.mirrorAcross Axis2d.x frame
Frame2d.originPoint mirroredFrame
--> Point2d.meters 2 -3
Frame2d.xDirection mirroredFrame
--> Direction2d.x
Frame2d.yDirection mirroredFrame
--> Direction2d.negativeYNote that this will switch the handedness of the frame.
Take two frames defined in global coordinates, and return the second one expressed in local coordinates relative to the first.
Take one frame defined in global coordinates and a second frame defined in local coordinates relative to the first frame, and return the second frame expressed in global coordinates.
The type parameters of a
Frame2dindicate what units and coordinate systems it's defined in, and what coordinate system (if any) it itself defines. A concreteFrame2dtype might look likewhich can be read as "a
Frame2ddefined in meters in world coordinates, which itself defines local coordinates". For frames that don't define a local coordinate system, you could useMany functions in this module don't care about the third type argument (whether it's a record with a
definesfield like in the first example, an empty record like in the second example, or even something else entirely) but functions likeplaceInandrelativeToexpect the{ defines : localCoordinates }pattern.