ianmackenzie / elm-geometry / Point3d

A Point3d represents a position in 3D space and is defined by its X, Y and Z coordinates. This module contains a variety of point-related functionality, such as

Points are distinct from vectors but interact with them in well-defined ways; you can translate a point by a vector to result in a new point, or you can compute the vector from one point to another, but you cannot 'add' two points like you can add two vectors.


type alias Point3d units coordinates =
Geometry.Types.Point3d units coordinates

Constants

origin : Point3d units coordinates

The point with coordinates (0, 0, 0).

Literals

unitless : Basics.Float -> Basics.Float -> Basics.Float -> Point3d Quantity.Unitless coordinates

Construct a unitless Point3d value from its X, Y and Z coordinates. See also fromUnitless.

meters : Basics.Float -> Basics.Float -> Basics.Float -> Point3d Length.Meters coordinates

pixels : Basics.Float -> Basics.Float -> Basics.Float -> Point3d Pixels coordinates

millimeters : Basics.Float -> Basics.Float -> Basics.Float -> Point3d Length.Meters coordinates

centimeters : Basics.Float -> Basics.Float -> Basics.Float -> Point3d Length.Meters coordinates

inches : Basics.Float -> Basics.Float -> Basics.Float -> Point3d Length.Meters coordinates

feet : Basics.Float -> Basics.Float -> Basics.Float -> Point3d Length.Meters coordinates

Constructors

xyz : Quantity Basics.Float units -> Quantity Basics.Float units -> Quantity Basics.Float units -> Point3d units coordinates

Construct a point from its X, Y and Z coordinates.

point =
    Point3d.xyz
        (Length.meters 2)
        (Length.meters 1)
        (Length.meters 3)

xyzIn : Geometry.Types.Frame3d units globalCoordinates { defines : localCoordinates } -> Quantity Basics.Float units -> Quantity Basics.Float units -> Quantity Basics.Float units -> Point3d units globalCoordinates

Construct a point given its local coordinates within a particular frame:

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

Point3d.xyzIn frame
    (Length.meters 1)
    (Length.meters 2)
    (Length.meters 3)
--> Point3d.meters 2 3 4

midpoint : Point3d units coordinates -> Point3d units coordinates -> Point3d units coordinates

Construct a point halfway between two other points.

Point3d.midpoint
    (Point3d.meters 1 1 1)
    (Point3d.meters 3 7 9)
--> Point3d.meters 2 4 5

interpolateFrom : Point3d units coordinates -> Point3d units coordinates -> Basics.Float -> Point3d units coordinates

Construct a point by interpolating from the first given point to the second, based on a parameter that ranges from zero to one.

startPoint =
    Point3d.meters 1 2 4

endPoint =
    Point3d.meters 1 2 8

Point3d.interpolateFrom startPoint endPoint 0.25
--> Point3d.meters 1 2 5

Partial application may be useful:

interpolatedPoint : Float -> Point3d
interpolatedPoint =
    Point3d.interpolateFrom startPoint endPoint

List.map interpolatedPoint [ 0, 0.5, 1 ]
--> [ Point3d.meters 1 2 4
--> , Point3d.meters 1 2 6
--> , Point3d.meters 1 2 8
--> ]

You can pass values less than zero or greater than one to extrapolate:

interpolatedPoint -0.5
--> Point3d.meters 1 2 2

interpolatedPoint 1.25
--> Point3d.meters 1 2 9

along : Geometry.Types.Axis3d units coordinates -> Quantity Basics.Float units -> Point3d units coordinates

Construct a point along an axis at a particular distance from the axis' origin point.

Point3d.along Axis3d.z (Length.meters 2)
--> Point3d.meters 0 0 2

Positive and negative distances are interpreted relative to the direction of the axis:

horizontalAxis =
    Axis3d.withDirection Direction3d.negativeX
        (Point3d.meters 1 1 1)

Point3d.along horizontalAxis (Length.meters 3)
--> Point3d.meters -2 1 1

Point3d.along horizontalAxis (Length.meters -3)
--> Point3d.meters 4 1 1

on : Geometry.Types.SketchPlane3d units coordinates3d { defines : coordinates2d } -> Point2d units coordinates2d -> Point3d units coordinates3d

Construct a 3D point lying on a sketch plane by providing a 2D point specified in XY coordinates within the sketch plane.

Point3d.on SketchPlane3d.xy (Point2d.meters 2 1)
--> Point3d.meters 2 1 0

Point3d.on SketchPlane3d.xz (Point2d.meters 2 1)
--> Point3d.meters 2 0 1

The sketch plane can have any position and orientation:

tiltedSketchPlane =
    SketchPlane3d.xy
        |> SketchPlane3d.rotateAround Axis3d.x
            (Angle.degrees 45)
        |> SketchPlane3d.moveTo
            (Point3d.meters 10 10 10)

Point3d.on tiltedSketchPlane (Point2d.meters 2 1)
--> Point3d.meters 12 10.7071 10.7071

xyOn : Geometry.Types.SketchPlane3d units coordinates3d { defines : coordinates2d } -> Quantity Basics.Float units -> Quantity Basics.Float units -> Point3d units coordinates

Construct a 3D point lying on a sketch plane by providing its 2D coordinates within that sketch plane:

Point3d.xyOn SketchPlane3d.xy
    (Length.meters 2)
    (Length.meters 1)
--> Point3d.meters 2 1 0

Point3d.xyOn SketchPlane3d.xz
    (Length.meters 2)
    (Length.meters 1)
--> Point3d.meters 2 0 1

rThetaOn : Geometry.Types.SketchPlane3d units coordinates3d { defines : coordinates2d } -> Quantity Basics.Float units -> Angle -> Point3d units coordinates

Construct a 3D point lying on a sketch plane by providing its 2D polar coordinates within that sketch plane:

Point3d.rThetaOn SketchPlane3d.xy
    (Length.meters 2)
    (Angle.degrees 45)
--> Point3d.meters 1.4142 1.4142 0

Point3d.rThetaOn SketchPlane3d.yz
    (Length.meters 2)
    (Angle.degrees 30)
--> Point3d.meters 0 1.732 1

circumcenter : Point3d units coordinates -> Point3d units coordinates -> Point3d units coordinates -> Maybe (Point3d units coordinates)

Attempt to find the circumcenter of three points; this is the center of the circle that passes through all three points. If the three given points are collinear, returns Nothing.

Point3d.circumcenter
    (Point3d.meters 1 0 0)
    (Point3d.meters 0 1 0)
    (Point3d.meters 0 0 1)
--> Just (Point3d.meters 0.33 0.33 0.33)

Point3d.circumcenter
    Point3d.origin
    (Point3d.meters 1 0 0)
    (Point3d.meters 2 0 0)
--> Nothing

Interop

These functions are useful for interoperability with other Elm code that uses plain Float tuples or records to represent points.

fromTuple : (Basics.Float -> Quantity Basics.Float units) -> ( Basics.Float, Basics.Float, Basics.Float ) -> Point3d units coordinates

Construct a Point3d from a tuple of Float values, by specifying what units those values are in.

Point3d.fromTuple Length.meters ( 2, 3, 1 )
--> Point3d.meters 2 3 1

toTuple : (Quantity Basics.Float units -> Basics.Float) -> Point3d units coordinates -> ( Basics.Float, Basics.Float, Basics.Float )

Convert a Point3d to a tuple of Float values, by specifying what units you want the result to be in.

point =
    Point3d.feet 2 3 1

Point3d.toTuple Length.inInches point
--> ( 24, 36, 12 )

fromRecord : (Basics.Float -> Quantity Basics.Float units) -> { x : Basics.Float, y : Basics.Float, z : Basics.Float } -> Point3d units coordinates

Construct a Point3d from a record with Float fields, by specifying what units those fields are in.

Point3d.fromRecord Length.inches
    { x = 24, y = 36, z = 12 }
--> Point3d.feet 2 3 1

toRecord : (Quantity Basics.Float units -> Basics.Float) -> Point3d units coordinates -> { x : Basics.Float, y : Basics.Float, z : Basics.Float }

Convert a Point3d to a record with Float fields, by specifying what units you want the result to be in.

point =
    Point3d.meters 2 3 1

Point3d.toRecord Length.inCentimeters point
--> { x = 200, y = 300, z = 100 }

Zero-copy conversions

These functions allow zero-overhead conversion of points to and from records with x, y and z Float fields, useful for efficient interop with other code that represents points as plain records.

fromMeters : { x : Basics.Float, y : Basics.Float, z : Basics.Float } -> Point3d Length.Meters coordinates

toMeters : Point3d Length.Meters coordinates -> { x : Basics.Float, y : Basics.Float, z : Basics.Float }

fromPixels : { x : Basics.Float, y : Basics.Float, z : Basics.Float } -> Point3d Pixels coordinates

toPixels : Point3d Pixels coordinates -> { x : Basics.Float, y : Basics.Float, z : Basics.Float }

fromUnitless : { x : Basics.Float, y : Basics.Float, z : Basics.Float } -> Point3d Quantity.Unitless coordinates

toUnitless : Point3d Quantity.Unitless coordinates -> { x : Basics.Float, y : Basics.Float, z : Basics.Float }

Properties

coordinates : Point3d units coordinates -> ( Quantity Basics.Float units, Quantity Basics.Float units, Quantity Basics.Float units )

Get the X, Y and Z coordinates of a point as a tuple.

Point3d.coordinates (Point3d.meters 2 3 1)
--> ( Length.meters 2
--> , Length.meters 3
--> , Length.meters 1
--> )

xCoordinate : Point3d units coordinates -> Quantity Basics.Float units

Get the X coordinate of a point.

Point3d.xCoordinate (Point3d.meters 2 1 3)
--> Length.meters 2

yCoordinate : Point3d units coordinates -> Quantity Basics.Float units

Get the Y coordinate of a point.

Point3d.yCoordinate (Point3d.meters 2 1 3)
--> Length.meters 1

zCoordinate : Point3d units coordinates -> Quantity Basics.Float units

Get the Z coordinate of a point.

Point3d.zCoordinate (Point3d.meters 2 1 3)
--> Length.meters 3

coordinatesIn : Geometry.Types.Frame3d units globalCoordinates { defines : localCoordinates } -> Point3d units globalCoordinates -> ( Quantity Basics.Float units, Quantity Basics.Float units, Quantity Basics.Float units )

Get the X, Y and Z coordinates of a point relative to a given frame, as a tuple; these are the coordinates the point would have as viewed by an observer in that frame.

xCoordinateIn : Geometry.Types.Frame3d units globalCoordinates { defines : localCoordinates } -> Point3d units globalCoordinates -> Quantity Basics.Float units

Find the X coordinate of a point relative to a given frame.

yCoordinateIn : Geometry.Types.Frame3d units globalCoordinates { defines : localCoordinates } -> Point3d units globalCoordinates -> Quantity Basics.Float units

Find the Y coordinate of a point relative to a given frame.

zCoordinateIn : Geometry.Types.Frame3d units globalCoordinates { defines : localCoordinates } -> Point3d units globalCoordinates -> Quantity Basics.Float units

Find the Z coordinate of a point relative to a given frame.

Comparison

equalWithin : Quantity Basics.Float units -> Point3d units coordinates -> Point3d units coordinates -> Basics.Bool

Compare two points within a tolerance. Returns true if the distance between the two given points is less than the given tolerance.

firstPoint =
    Point3d.meters 2 1 3

secondPoint =
    Point3d.meters 2.0002 0.9999 3.0001

Point3d.equalWithin (Length.millimeters 1)
    firstPoint
    secondPoint
--> True

Point3d.equalWithin (Length.microns 1)
    firstPoint
    secondPoint
--> False

lexicographicComparison : Point3d units coordinates -> Point3d units coordinates -> Basics.Order

Compare two Point3d values lexicographically: first by X coordinate, then by Y, then by Z. Can be used to provide a sort order for Point3d values.

Measurement

distanceFrom : Point3d units coordinates -> Point3d units coordinates -> Quantity Basics.Float units

Find the distance from the first point to the second.

Point3d.distanceFrom
    (Point3d.meters 1 1 2)
    (Point3d.meters 2 3 4)
--> Length.meters 3

Partial application can be useful:

points =
    [ Point3d.meters 3 4 5
    , Point3d.meters 10 10 10
    , Point3d.meters -1 2 -3
    ]

points
    |> Quantity.sortBy
        (Point3d.distanceFrom Point3d.origin)
--> [ Point3d.meters -1 2 -3
--> , Point3d.meters 3 4 5
--> , Point3d.meters 10 10 10
--> ]

signedDistanceAlong : Geometry.Types.Axis3d units coordinates -> Point3d units coordinates -> Quantity Basics.Float units

Determine how far along an axis a particular point lies. Conceptually, the point is projected perpendicularly onto the axis, and then the distance of this projected point from the axis' origin point is measured. The result will be positive if the projected point is ahead the axis' origin point and negative if it is behind, with 'ahead' and 'behind' defined by the direction of the axis.

axis =
    Axis3d.withDirection Direction3d.x
        (Point3d.meters 1 0 0)

Point3d.signedDistanceAlong axis (Point3d.meters 3 3 3)
--> Length.meters 2

Point3d.signedDistanceAlong axis Point3d.origin
--> Length.meters -1

distanceFromAxis : Geometry.Types.Axis3d units coordinates -> Point3d units coordinates -> Quantity Basics.Float units

Find the perpendicular (nearest) distance of a point from an axis.

point =
    Point3d.meters -3 4 0

Point3d.distanceFromAxis Axis3d.x point
--> Length.meters 4

Point3d.distanceFromAxis Axis3d.y point
--> Length.meters 3

Point3d.distanceFromAxis Axis3d.z point
--> Length.meters 5

Note that unlike in 2D, the result is always positive (unsigned) since there is no such thing as the left or right side of an axis in 3D.

signedDistanceFrom : Geometry.Types.Plane3d units coordinates -> Point3d units coordinates -> Quantity Basics.Float units

Find the perpendicular distance of a point from a plane. The result will be positive if the point is 'above' the plane and negative if it is 'below', with 'up' defined by the normal direction of the plane.

plane =
    Plane3d.withNormalDirection Direction3d.y
        (Point3d.meters 1 2 3)

Point3d.signedDistanceFrom plane (Point3d.meters 3 3 3)
--> Length.meters 1

Point3d.signedDistanceFrom plane Point3d.origin
--> Length.meters -2

This means that flipping a plane (reversing its normal direction) will also flip the sign of the result of this function:

flippedPlane =
    Plane3d.reverseNormal plane

Point3d.signedDistanceFrom flippedPlane point
--> Length.meters -1

Point3d.signedDistanceFrom flippedPlane Point3d.origin
--> Length.meters 2

Transformations

scaleAbout : Point3d units coordinates -> Basics.Float -> Point3d units coordinates -> Point3d units coordinates

Perform a uniform scaling about the given center point. The center point is given first and the point to transform is given last. Points will contract or expand about the center point by the given scale. Scaling by a factor of 1 is a no-op, and scaling by a factor of 0 collapses all points to the center point.

centerPoint =
    Point3d.meters 1 1 1

point =
    Point3d.meters 1 2 3

Point3d.scaleAbout centerPoint 3 point
--> Point3d.meters 1 4 7

Point3d.scaleAbout centerPoint 0.5 point
--> Point3d.meters 1 1.5 2

Avoid scaling by a negative scaling factor - while this may sometimes do what you want it is confusing and error prone. Try a combination of mirror and/or rotation operations instead.

rotateAround : Geometry.Types.Axis3d units coordinates -> Angle -> Point3d units coordinates -> Point3d units coordinates

Rotate a point around an axis by a given angle.

axis =
    Axis3d.x

angle =
    Angle.degrees 45

point =
    Point3d.meters 3 1 0

Point3d.rotateAround axis angle point
--> Point3d.meters 3 0.7071 0.7071

Rotation direction is given by the right-hand rule, counterclockwise around the direction of the axis.

translateBy : Vector3d units coordinates -> Point3d units coordinates -> Point3d units coordinates

Translate a point by a given displacement.

point =
    Point3d.meters 3 4 5

displacement =
    Vector3d.meters 1 2 3

Point3d.translateBy displacement point
--> Point3d.meters 4 6 8

In more mathematical terms, this is 'point plus vector'. For 'point minus point' (giving the vector from one point to another), there is Vector3d.from.

translateIn : Direction3d coordinates -> Quantity Basics.Float units -> Point3d units coordinates -> Point3d units coordinates

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

point =
    Point3d.meters 3 4 5

point
    |> Point3d.translateIn Direction3d.x
        (Length.meters 2)
--> Point3d.meters 5 4 5

point
    |> Point3d.translateIn Direction3d.y
        (Length.meters 2)
--> Point3d.meters 3 6 5

The distance can be negative:

point
    |> Point3d.translateIn Direction3d.x
        (Length.meters -2)
--> Point3d.meters 1 4 5

mirrorAcross : Geometry.Types.Plane3d units coordinates -> Point3d units coordinates -> Point3d units coordinates

Mirror a point across a plane. The result will be the same distance from the plane but on the opposite side.

point =
    Point3d.meters 1 2 3

-- Plane3d.xy is the plane Z=0
Point3d.mirrorAcross Plane3d.xy point
--> Point3d.meters 1 2 -3

-- Plane3d.yz is the plane X=0
Point3d.mirrorAcross Plane3d.yz point
--> Point3d.meters -1 2 3

The plane does not have to pass through the origin:

-- offsetPlane is the plane  Z=1
offsetPlane =
    Plane3d.offsetBy 1 Plane3d.xy

-- The origin point is 1 unit below the offset
-- plane, so its mirrored copy is one unit above
Point3d.mirrorAcross offsetPlane Point3d.origin
--> Point3d.meters 0 0 2

projectOnto : Geometry.Types.Plane3d units coordinates -> Point3d units coordinates -> Point3d units coordinates

Find the orthographic projection of a point onto a plane:

point =
    Point3d.meters 1 2 3

Point3d.projectOnto Plane3d.xy point
--> Point3d.meters 1 2 0

Point3d.projectOnto Plane3d.yz point
--> Point3d.meters 0 2 3

The plane does not have to pass through the origin:

offsetPlane =
    Plane3d.offsetBy 1 Plane3d.xy

Point3d.projectOnto offsetPlane point
--> Point3d.meters 1 2 1

projectOntoAxis : Geometry.Types.Axis3d units coordinates -> Point3d units coordinates -> Point3d units coordinates

Project a point perpendicularly onto an axis.

point =
    Point3d.meters 1 2 3

Point3d.projectOntoAxis Axis3d.x
--> Point3d.meters 1 0 0

verticalAxis =
    Axis3d.withDirection Direction3d.z
        (Point3d.meters 0 1 2)

Point3d.projectOntoAxis verticalAxis
--> Point3d.meters 0 1 3

Unit conversions

at : Quantity Basics.Float (Quantity.Rate destinationUnits sourceUnits) -> Point3d sourceUnits coordinates -> Point3d destinationUnits coordinates

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

worldPoint =
    Point3d.meters 2 3 1

resolution : Quantity Float (Rate Pixels Meters)
resolution =
    Pixels.pixels 100 |> Quantity.per (Length.meters 1)

worldPoint |> Point3d.at resolution
--> Point3d.pixels 200 300 100

at_ : Quantity Basics.Float (Quantity.Rate sourceUnits destinationUnits) -> Point3d sourceUnits coordinates -> Point3d destinationUnits coordinates

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

screenPoint =
    Point3d.pixels 200 300 100

resolution : Quantity Float (Rate Pixels Meters)
resolution =
    Pixels.pixels 50 |> Quantity.per (Length.meters 1)

screenPoint |> Point3d.at_ resolution
--> Point3d.meters 4 6 2

Coordinate conversions

relativeTo : Geometry.Types.Frame3d units globalCoordinates { defines : localCoordinates } -> Point3d units globalCoordinates -> Point3d units localCoordinates

Take a point defined in global coordinates, and return it expressed in local coordinates relative to a given reference frame.

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

Point3d.relativeTo localFrame (Point3d.meters 4 5 6)
--> Point3d.meters 3 3 3

Point3d.relativeTo localFrame (Point3d.meters 1 1 1)
--> Point3d.meters 0 -1 -2

placeIn : Geometry.Types.Frame3d units globalCoordinates { defines : localCoordinates } -> Point3d units localCoordinates -> Point3d units globalCoordinates

Take a point defined in local coordinates relative to a given reference frame, and return that point expressed in global coordinates.

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

Point3d.placeIn localFrame (Point3d.meters 3 3 3)
--> Point3d.meters 4 5 6

Point3d.placeIn localFrame (Point3d.meters 0 -1 -2)
--> Point3d.meters 1 1 1

projectInto : Geometry.Types.SketchPlane3d units coordinates3d { defines : coordinates2d } -> Point3d units coordinates3d -> Point2d units coordinates2d

Project a point into a given sketch plane. Conceptually, this finds the orthographic projection of the point onto the plane and then expresses the projected point in 2D sketch coordinates.

point =
    Point3d.meters 2 1 3

Point3d.projectInto SketchPlane3d.xy point
--> Point2d.meters 2 1

Point3d.projectInto SketchPlane3d.yz point
--> Point2d.meters 1 3

Point3d.projectInto SketchPlane3d.zx point
--> Point2d.meters 3 2

Centroid calculation

centroid : Point3d units coordinates -> List (Point3d units coordinates) -> Point3d units coordinates

Find the centroid (average) of one or more points, by passing the first point and then all remaining points. This allows this function to return a Point3d instead of a Maybe Point3d. You would generally use centroid within a case expression:

case points of
    [] ->
        -- some default behavior

    first :: rest ->
        let
            centroid =
                Point3d.centroid first rest
        in
        ...

Alternatively, you can use centroidN instead.

centroidOf : (a -> Point3d units coordinates) -> a -> List a -> Point3d units coordinates

Like centroid, but lets you work with any kind of data as long as a point can be extracted/constructed from it. For example, to get the centroid of a bunch of vertices:

type alias Vertex =
    { position : Point3d Meters World
    , color : Color
    , id : Int
    }

vertexCentroid =
    Point3d.centroidOf .position
        firstVertex
        [ secondVertex
        , thirdVertex
        ]

centroid3 : Point3d units coordinates -> Point3d units coordinates -> Point3d units coordinates -> Point3d units coordinates

Find the centroid of three points;

Point3d.centroid3 p1 p2 p3

is equivalent to

Point3d.centroid p1 [ p2, p3 ]

but is more efficient.

centroidN : List (Point3d units coordinates) -> Maybe (Point3d units coordinates)

Find the centroid of a list of N points. If the list is empty, returns Nothing. If you know you have at least one point, you can use centroid instead to avoid the Maybe.

Advanced

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

unsafe : { x : Basics.Float, y : Basics.Float, z : Basics.Float } -> Point3d units coordinates

Construct a point from its raw X, Y and Z coordinates as Float values. The values must be in whatever units the resulting point is considered to use (usually meters or pixels). You should generally use something safer such as meters, fromPixels, xyz, fromRecord etc.

unwrap : Point3d units coordinates -> { x : Basics.Float, y : Basics.Float, z : Basics.Float }

Extract a point's raw X, Y and Z coordinates as Float values. These values will be in whatever units the point has (usually meters or pixels). You should generally use something safer such as toMeters, toRecord, xCoordinate etc.