ianmackenzie / elm-geometry / Frame3d

A Frame3d has an origin point and a set of X, Y and Z directions (which are always perpendicular to each other). It can be thought of as:


type alias Frame3d units coordinates defines =
Geometry.Types.Frame3d units coordinates defines

The type parameters of a Frame3d indicate what units and coordinate systems it's defined in, and what coordinate system (if any) it itself defines. A concrete Frame3d type might look like

type alias Frame =
    Frame3d Meters World { defines : Local }

which can be read as "a Frame3d defined in meters in world coordinates, which itself defines local coordinates". For frames that don't define a local coordinate system, you could use

type alias Frame =
    Frame3d Meters World {}

Many functions in this module don't care about the third type argument (whether it's a record with a defines field like in the first example, an empty record like in the second example, or even something else entirely) but functions like placeIn and relativeTo expect the { defines : localCoordinates } pattern.

Constants

atOrigin : Frame3d units coordinates defines

The global XYZ frame, centered at the origin.

Frame3d.originPoint Frame3d.atOrigin
--> Point3d.origin

Frame3d.xDirection Frame3d.atOrigin
--> Direction3d.x

Frame3d.yDirection Frame3d.atOrigin
--> Direction3d.y

Frame3d.zDirection Frame3d.atOrigin
--> Direction3d.z

Constructors

The withXDirection, withYDirection and withZDirection functions all construct a new Frame3d with the given axis direction, having the given origin point. The other two directions will be chosen arbitrarily. This can be useful when constructing 'scratch' frames where (for example) you want a particular Z direction but the specific X/Y directions are unimportant:

zDirection =
    Direction3d.xz (Angle.degrees 60)

frame =
    Frame3d.withZDirection zDirection Point3d.origin

Frame3d.zDirection frame
--> Direction3d.xz (Angle.degrees 60)

Frame3d.originPoint frame
--> Point3d.origin

Frame3d.xDirection frame
--> Direction3d.xz (Angle.degrees -30)

Frame3d.yDirection frame
--> Direction3d.y

No guarantees are given about the other two directions other than that the three directions will be mutually perpendicular, and will be oriented so that the resulting frame is right-handed.

withXDirection : Direction3d coordinates -> Point3d units coordinates -> Frame3d units coordinates defines

Construct a frame with the given origin point and X direction.

withYDirection : Direction3d coordinates -> Point3d units coordinates -> Frame3d units coordinates defines

Construct a frame with the given origin point and Y direction.

withZDirection : Direction3d coordinates -> Point3d units coordinates -> Frame3d units coordinates defines

Construct a frame with the given origin point and Z direction.

atPoint : Point3d units coordinates -> Frame3d units coordinates defines

Construct a frame aligned with the global XYZ frame but with the given origin point.

frame =
    Frame3d.atPoint
        (Point3d.meters 2 1 3)

Frame3d.originPoint frame
--> Point3d.meters 2 1 3

Frame3d.xDirection frame
--> Direction3d.x

Frame3d.yDirection frame
--> Direction3d.y

Frame3d.zDirection frame
--> Direction3d.z

copy : Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

Create a 'fresh copy' of a frame: one with the same origin point and X/Y/Z 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 really just returns the value you passed in, but with a different type.

unsafe : { originPoint : Point3d units coordinates, xDirection : Direction3d coordinates, yDirection : Direction3d coordinates, zDirection : Direction3d coordinates } -> Frame3d units coordinates defines

Construct a frame directly from its origin point and X, Y and Z directions:

frame =
    Frame3d.unsafe
        { originPoint = Point3d.meters 2 1 3
        , xDirection =
            Direction3d.unsafe
                { x = 0.8, y = 0.6, z = 0 }
        , yDirection =
            Direction3d.unsafe
                { x = -0.6, y = 0.8, z = 0 }
        , zDirection =
            Direction3d.unsafe
                { x = 0, y = 0, z = 1 }
        }

In this case you must be careful to ensure that the X, Y and Z directions are perpendicular. (You will likely also want to make sure that they form a right-handed coordinate system.) To construct sets of mutually perpendicular directions, Direction3d.orthonormalize, Direction3d.orthogonalize, or Direction3d.perpendicularBasis may be useful.

From axes

fromXAxis : Axis3d units coordinates -> Frame3d units coordinates defines

Construct a Frame3d with the given X axis. Perpendicular Y and Z directions will be chosen arbitrarily.

fromYAxis : Axis3d units coordinates -> Frame3d units coordinates defines

Construct a Frame3d with the given Y axis. Perpendicular X and Z directions will be chosen arbitrarily.

fromZAxis : Axis3d units coordinates -> Frame3d units coordinates defines

Construct a Frame3d with the given Z axis. Perpendicular X and Y directions will be chosen arbitrarily.

Properties

originPoint : Frame3d units coordinates defines -> Point3d units coordinates

Get the origin point of a given frame.

xDirection : Frame3d units coordinates defines -> Direction3d coordinates

Get the X direction of a given frame.

yDirection : Frame3d units coordinates defines -> Direction3d coordinates

Get the Y direction of a given frame.

zDirection : Frame3d units coordinates defines -> Direction3d coordinates

Get the Z direction of a given frame.

isRightHanded : Frame3d units coordinates defines -> Basics.Bool

Check if a frame is right-handed.

Frame3d.isRightHanded Frame3d.atOrigin
--> True

Frame3d.isRightHanded
    (Frame3d.reverseZ Frame3d.atOrigin)
--> False

All 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.

Axes

xAxis : Frame3d units coordinates defines -> Axis3d units coordinates

Get the X axis of a given frame (the axis formed from the frame's origin point and X direction).

yAxis : Frame3d units coordinates defines -> Axis3d units coordinates

Get the Y axis of a given frame (the axis formed from the frame's origin point and Y direction).

zAxis : Frame3d units coordinates defines -> Axis3d units coordinates

Get the Z axis of a given frame (the axis formed from the frame's origin point and Z direction).

Planes

The following functions all return planes with the same origin point as the given frame, but with varying normal directions. In each case the normal direction of the resulting plane is given by the cross product of the two indicated basis directions (assuming a right-handed frame); for example,

Frame3d.xyPlane Frame3d.atOrigin
--> Plane3d.through Point3d.origin
-->     Direction3d.positiveZ

since the cross product of the X and Y basis directions of a frame is equal to its Z basis direction. And since reversing the order of arguments in a cross product reverses the sign of the result,

Frame3d.yxPlane Frame3d.atOrigin
--> Plane3d.through Point3d.origin
-->     Direction3d.negativeZ

xyPlane : Frame3d units coordinates defines -> Plane3d units coordinates

Get a plane with normal direction equal to the frame's positive Z direction.

yxPlane : Frame3d units coordinates defines -> Plane3d units coordinates

Get a plane with normal direction equal to the frame's negative Z direction.

yzPlane : Frame3d units coordinates defines -> Plane3d units coordinates

Get a plane with normal direction equal to the frame's positive X direction.

zyPlane : Frame3d units coordinates defines -> Plane3d units coordinates

Get a plane with normal direction equal to the frame's negative X direction.

zxPlane : Frame3d units coordinates defines -> Plane3d units coordinates

Get a plane with normal direction equal to the frame's positive Y direction.

xzPlane : Frame3d units coordinates defines -> Plane3d units coordinates

Get a plane with normal direction equal to the frame's negative Y direction.

Sketch planes

These functions all form a SketchPlane3d from two axes of the given frame. The X and Y axes of the sketch plane will correspond to the two indicated axes. For example,

yzSketchPlane =
    Frame3d.yzSketchPlane Frame3d.atOrigin

SketchPlane3d.originPoint yzSketchPlane
--> Point3d.origin

SketchPlane3d.xDirection yzSketchPlane
--> Direction3d.y

SketchPlane3d.yDirection yzSketchPlane
--> Direction3d.z

Note that this can be confusing - for example, a local X coordinate in the above sketch plane corresponds to a global Y coordinate, and a local Y coordinate corresponds to a global Z coordinate!

xySketchPlane : Frame3d units coordinates defines3d -> SketchPlane3d units coordinates defines2d

Form a sketch plane from the given frame's X and Y axes.

yxSketchPlane : Frame3d units coordinates defines3d -> SketchPlane3d units coordinates defines2d

Form a sketch plane from the given frame's Y and X axes.

yzSketchPlane : Frame3d units coordinates defines3d -> SketchPlane3d units coordinates defines2d

Form a sketch plane from the given frame's Y and Z axes.

zySketchPlane : Frame3d units coordinates defines3d -> SketchPlane3d units coordinates defines2d

Form a sketch plane from the given frame's Z and Y axes.

zxSketchPlane : Frame3d units coordinates defines3d -> SketchPlane3d units coordinates defines2d

Form a sketch plane from the given frame's Z and X axes.

xzSketchPlane : Frame3d units coordinates defines3d -> SketchPlane3d units coordinates defines2d

Form a sketch plane from the given frame's X and Z axes.

Transformations

reverseX : Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

Reverse the X direction of a frame. Note that this will switch the handedness of the frame.

reverseY : Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

Reverse the Y direction of a frame. Note that this will switch the handedness of the frame.

reverseZ : Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

Reverse the Z direction of a frame. Note that this will switch the handedness of the frame.

moveTo : Point3d units coordinates -> Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

Move a frame so that it has the given origin point but unchanged orientation.

point =
    Point3d.meters 2 1 3

Frame3d.atOrigin |> Frame3d.moveTo point
--> Frame3d.atPoint point

rotateAround : Axis3d units coordinates -> Angle -> Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

Rotate a frame around an axis by a given angle. The frame's origin point and basis directions will all be rotated around the given axis.

frame =
    Frame3d.atPoint (Point3d.meters 2 1 3)

rotatedFrame =
    frame
        |> Frame3d.rotateAround Axis3d.z
            (Angle.degrees 90)

Frame3d.originPoint rotatedFrame
--> Point3d.meters -1 2 3

Frame3d.xDirection rotatedFrame
--> Direction3d.y

Frame3d.yDirection rotatedFrame
--> Direction3d.negativeX

Frame3d.zDirection rotatedFrame
--> Direction3d.z

rotateAroundOwn : (Frame3d units coordinates defines1 -> Axis3d units coordinates) -> Angle -> Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

Rotate a frame around one of its own axes by a given angle.

The first argument is a function that returns the axis to rotate around, given the current frame. The majority of the time this will be either Frame3d.xAxis, Frame3d.yAxis or Frame3d.zAxis. Compare the following to the above example for rotateAround:

frame =
    Frame3d.atPoint (Point3d.meters 2 1 3)

rotatedFrame =
    frame
        |> Frame3d.rotateAroundOwn Frame3d.zAxis
            (Angle.degrees 90)

Frame3d.originPoint rotatedFrame
--> Point3d.meters 2 1 3

Frame3d.xDirection rotatedFrame
--> Direction3d.y

Frame3d.yDirection rotatedFrame
--> Direction3d.negativeX

Frame3d.zDirection rotatedFrame
--> Direction3d.z

Since the rotation is done around the frame's own Z axis (which passes through the frame's origin point), the origin point remains the same after rotation.

In this example the frame's Z axis has the same orientation as the global Z axis so the frame's basis directions are rotated the same way, but in more complex examples involving rotated frames a rotation around (for example) the frame's own Z axis may be completely different from a rotation around the global Z axis.

translateBy : Vector3d units coordinates -> Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

Translate a frame by a given displacement.

frame =
    Frame3d.atPoint (Point3d.meters 2 1 3)

displacement =
    Vector3d.meters 1 1 1

Frame3d.translateBy displacement frame
--> Frame3d.atPoint (Point3d.meters 3 2 4)

translateIn : Direction3d coordinates -> Quantity Basics.Float units -> Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

Translate a frame in a given direction by a given distance.

translateAlongOwn : (Frame3d units coordinates defines1 -> Axis3d units coordinates) -> Quantity Basics.Float units -> Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

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 will be either Frame3d.xAxis, Frame3d.yAxis or Frame3d.zAxis.

This function is convenient when constructing frames via a series of transformations. For example,

point =
    Point3d.meters 2 0 0

frame =
    Frame3d.atPoint point
        |> Frame3d.rotateAroundOwn Frame3d.zAxis
            (Angle.degrees 45)
        |> Frame3d.translateAlongOwn Frame3d.xAxis
            (Length.meters 2)

means "construct a frame at the point (2, 0, 0), rotate it around its own Z axis counterclockwise by 45 degrees, then translate it along its own (rotated) X axis by 2 meters", resulting in

Frame3d.originPoint frame
--> Point3d.meters 3.4142 1.4142 0

Frame3d.xDirection frame
--> Direction3d.xy (Angle.degrees 45)

Frame3d.yDirection frame
--> Direction3d.xy (Angle.degrees 135)

Frame3d.zDirection frame
--> Direction3d.z

mirrorAcross : Plane3d units coordinates -> Frame3d units coordinates defines1 -> Frame3d units coordinates defines2

Mirror a frame across a plane.

frame =
    Frame3d.atPoint (Point3d.meters 2 1 3)

mirroredFrame =
    Frame3d.mirrorAcross Plane3d.xy frame

Frame3d.originPoint mirroredFrame
--> Point3d.meters 2 1 -3

Frame3d.xDirection mirroredFrame
--> Direction3d.x

Frame3d.yDirection mirroredFrame
--> Direction3d.y

Frame3d.zDirection mirroredFrame
--> Direction3d.negativeZ

Note that this will switch the handedness of the frame.

Unit conversions

at : Quantity Basics.Float (Quantity.Rate units2 units1) -> Frame3d units1 coordinates defines -> Frame3d units2 coordinates defines

Convert 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.

at_ : Quantity Basics.Float (Quantity.Rate units1 units2) -> Frame3d units1 coordinates defines -> Frame3d units2 coordinates defines

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.

Coordinate conversions

relativeTo : Frame3d units globalCoordinates { defines : localCoordinates } -> Frame3d units globalCoordinates defines -> Frame3d units localCoordinates defines

Take two frames defined in global coordinates, and return the second one expressed in local coordinates relative to the first.

placeIn : Frame3d units globalCoordinates { defines : localCoordinates } -> Frame3d units localCoordinates defines -> Frame3d units globalCoordinates defines

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.