ianmackenzie / elm-geometry-prerelease / 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 =
Geometry.Types.Point3d

Constants

origin : Point3d

The point (0, 0, 0).

Point3d.origin
--> Point3d.fromCoordinates ( 0, 0, 0 )

Constructors

fromCoordinates : ( Basics.Float, Basics.Float, Basics.Float ) -> Point3d

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

point =
    Point3d.fromCoordinates ( 2, 1, 3 )

fromCoordinatesIn : Geometry.Types.Frame3d -> ( Basics.Float, Basics.Float, Basics.Float ) -> Point3d

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

frame =
    Frame3d.atPoint
        (Point3d.fromCoordinates ( 1, 1, 1 ))

Point3d.fromCoordinatesIn frame ( 1, 2, 3 )
--> Point3d.fromCoordinates ( 2, 3, 4 )

This is shorthand for using Point3d.placeIn;

Point3d.fromCoordinatesIn frame localCoordinates

is equivalent to

Point3d.fromCoordinates localCoordinates
    |> Point3d.placeIn frame

midpoint : Point3d -> Point3d -> Point3d

Construct a point halfway between two other points.

p1 =
    Point3d.fromCoordinates ( 1, 1, 1 )

p2 =
    Point3d.fromCoordinates ( 3, 7, 9 )

Point3d.midpoint p1 p2
--> Point3d.fromCoordinates ( 2, 4, 5 )

centroid : List Point3d -> Maybe Point3d

Find the centroid of a list of points. Returns Nothing if the list is empty.

p0 =
    Point3d.origin

p1 =
    Point3d.fromCoordinates ( 1, 0, 1 )

p2 =
    Point3d.fromCoordinates ( 0, 1, 1 )

Point3d.centroid [ p0, p1, p2 ]
--> Just (Point3d.fromCoordinates ( 0.3333, 0.3333, 0.6667 ))

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

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.fromCoordinates ( 1, 2, 4 )

endPoint =
    Point3d.fromCoordinates ( 1, 2, 8 )

Point3d.interpolateFrom startPoint endPoint 0.25
--> Point3d.fromCoordinates ( 1, 2, 5 )

Partial application may be useful:

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

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

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

interpolatedPoint -0.5
--> Point3d.fromCoordinates ( 1, 2, 2 )

interpolatedPoint 1.25
--> Point3d.fromCoordinates ( 1, 2, 9 )

along : Geometry.Types.Axis3d -> Basics.Float -> Point3d

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

Point3d.along Axis3d.z 2
--> Point3d.fromCoordinates ( 0, 0, 2 )

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

horizontalAxis =
    Axis3d.withDirection Direction3d.negativeX
        (Point3d.fromCoordinates ( 1, 1, 1 ))

Point3d.along horizontalAxis 3
--> Point3d.fromCoordinates ( -2, 1, 1 )

Point3d.along horizontalAxis -3
--> Point3d.fromCoordinates ( 4, 1, 1 )

on : Geometry.Types.SketchPlane3d -> Point2d -> Point3d

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.fromCoordinates ( 2, 1 )
--> Point3d.fromCoordinates ( 2, 1, 0 )

Point3d.on SketchPlane3d.xz <|
    Point2d.fromCoordinates ( 2, 1 )
--> Point3d.fromCoordinates ( 2, 0, 1 )

The sketch plane can have any position and orientation:

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

Point3d.on tiltedSketchPlane <|
    Point2d.fromCoordinates ( 2, 1 )
--> Point3d.fromCoordinates ( 12, 10.7071, 10.7071 )

circumcenter : Point3d -> Point3d -> Point3d -> Maybe Point3d

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.fromCoordinates ( 1, 0, 0 ))
    (Point3d.fromCoordinates ( 0, 1, 0 ))
    (Point3d.fromCoordinates ( 0, 0, 1 ))
--> Just (Point3d.fromCoordinates (0.33, 0.33, 0.33))

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

Properties

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

Get the coordinates of a point as a tuple.

( x, y, z ) =
    Point3d.coordinates point

xCoordinate : Point3d -> Basics.Float

Get the X coordinate of a point.

Point3d.fromCoordinates ( 2, 1, 3 )
    |> Point3d.xCoordinate
--> 2

yCoordinate : Point3d -> Basics.Float

Get the Y coordinate of a point.

Point3d.fromCoordinates ( 2, 1, 3 )
    |> Point3d.yCoordinate
--> 1

zCoordinate : Point3d -> Basics.Float

Get the Z coordinate of a point.

Point3d.fromCoordinates ( 2, 1, 3 )
    |> Point3d.zCoordinate
--> 3

Comparison

equalWithin : Basics.Float -> Point3d -> Point3d -> 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.fromCoordinates ( 2, 1, 3 )

secondPoint =
    Point3d.fromCoordinates ( 2.0002, 0.9999, 3.0001 )

Point3d.equalWithin 1e-3 firstPoint secondPoint
--> True

Point3d.equalWithin 1e-6 firstPoint secondPoint
--> False

Measurement

distanceFrom : Point3d -> Point3d -> Basics.Float

Find the distance from the first point to the second.

p1 =
    Point3d.fromCoordinates ( 1, 1, 2 )

p2 =
    Point3d.fromCoordinates ( 2, 3, 4 )

Point3d.distanceFrom p1 p2
--> 3

Partial application can be useful:

points =
    [ Point3d.fromCoordinates ( 3, 4, 5 )
    , Point3d.fromCoordinates ( 10, 10, 10 )
    , Point3d.fromCoordinates ( -1, 2, -3 )
    ]

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

squaredDistanceFrom : Point3d -> Point3d -> Basics.Float

Find the square of the distance from one point to another. squaredDistanceFrom is slightly faster than distanceFrom, so for example

Point3d.squaredDistanceFrom p1 p2
    > (tolerance * tolerance)

is equivalent to but slightly more efficient than

Point3d.distanceFrom p1 p2 > tolerance

since the latter requires a square root under the hood. In many cases, however, the speed difference will be negligible and using distanceFrom is much more readable!

signedDistanceAlong : Geometry.Types.Axis3d -> Point3d -> Basics.Float

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.fromCoordinates ( 1, 0, 0 ))

point =
    Point3d.fromCoordinates ( 3, 3, 3 )

Point3d.signedDistanceAlong axis point
--> 2

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

distanceFromAxis : Geometry.Types.Axis3d -> Point3d -> Basics.Float

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

point =
    Point3d.fromCoordinates ( -3, 4, 0 )

Point3d.distanceFromAxis Axis3d.x point
--> 4

Point3d.distanceFromAxis Axis3d.y point
--> 3

Point3d.distanceFromAxis Axis3d.z point
--> 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.

squaredDistanceFromAxis : Geometry.Types.Axis3d -> Point3d -> Basics.Float

Find the square of the perpendicular distance of a point from an axis. As with distanceFrom/squaredDistanceFrom this is slightly more efficient than distanceFromAxis since it avoids a square root.

signedDistanceFrom : Geometry.Types.Plane3d -> Point3d -> Basics.Float

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.fromCoordinates ( 1, 2, 3 ))

point =
    Point3d.fromCoordinates ( 3, 3, 3 )

Point3d.signedDistanceFrom plane point
--> 1

Point3d.signedDistanceFrom plane Point3d.origin
--> -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
--> -1

Point3d.signedDistanceFrom flippedPlane Point3d.origin
--> 2

Transformations

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

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.fromCoordinates ( 1, 1, 1 )

point =
    Point3d.fromCoordinates ( 1, 2, 3 )

Point3d.scaleAbout centerPoint 3 point
--> Point3d.fromCoordinates ( 1, 4, 7 )

Point3d.scaleAbout centerPoint 0.5 point
--> Point3d.fromCoordinates ( 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 -> Basics.Float -> Point3d -> Point3d

Rotate a point around an axis by a given angle (in radians).

axis =
    Axis3d.x

angle =
    degrees 45

point =
    Point3d.fromCoordinates ( 3, 1, 0 )

Point3d.rotateAround axis angle point
--> Point3d.fromCoordinates ( 3, 0.7071, 0.7071 )

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

translateBy : Vector3d -> Point3d -> Point3d

Translate a point by a given displacement.

point =
    Point3d.fromCoordinates ( 3, 4, 5 )

displacement =
    Vector3d.fromComponents ( 1, 2, 3 )

Point3d.translateBy displacement point
--> Point3d.fromCoordinates ( 4, 6, 8 )

translateIn : Direction3d -> Basics.Float -> Point3d -> Point3d

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

point =
    Point3d.fromCoordinates ( 3, 4, 5 )

point |> Point3d.translateIn Direction3d.x 2
--> Point3d.fromCoordinates ( 5, 4, 5 )

point |> Point3d.translateIn Direction3d.y 2
--> Point3d.fromCoordinates ( 3, 6, 5 )

The distance can be negative:

Point3d.translateIn Direction3d.x -2
--> Point3d.fromCoordinates ( 1, 4, 5 )

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

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

point =
    Point3d.fromCoordinates ( 1, 2, 3 )

-- Plane3d.xy is the plane Z=0
Point3d.mirrorAcross Plane3d.xy point
--> Point3d.fromCoordinates ( 1, 2, -3 )

-- Plane3d.yz is the plane X=0
Point3d.mirrorAcross Plane3d.yz point
--> Point3d.fromCoordinates ( -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.fromCoordinates ( 0, 0, 2 )

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

Find the orthographic projection of a point onto a plane:

point =
    Point3d.fromCoordinates ( 1, 2, 3 )

Point3d.projectOnto Plane3d.xy point
--> Point3d.fromCoordinates ( 1, 2, 0 )

Point3d.projectOnto Plane3d.yz point
--> Point3d.fromCoordinates ( 0, 2, 3 )

The plane does not have to pass through the origin:

offsetPlane =
    Plane3d.offsetBy 1 Plane3d.xy

Point3d.projectOnto offsetPlane point
--> Point3d.fromCoordinates ( 1, 2, 1 )

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

Project a point perpendicularly onto an axis.

point =
    Point3d.fromCoordinates ( 1, 2, 3 )

Point3d.projectOntoAxis Axis3d.x
--> Point3d.fromCoordinates ( 1, 0, 0 )

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

Point3d.projectOntoAxis verticalAxis
--> Point3d.fromCoordinates ( 0, 1, 3 )

Coordinate conversions

relativeTo : Geometry.Types.Frame3d -> Point3d -> Point3d

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

localFrame =
    Frame3d.atPoint
        (Point3d.fromCoordinates ( 1, 2, 3 ))

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

Point3d.relativeTo localFrame
    (Point3d.fromCoordinates ( 1, 1, 1 ))
--> Point3d.fromCoordinates ( 0, -1, -2 )

placeIn : Geometry.Types.Frame3d -> Point3d -> Point3d

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.fromCoordinates ( 1, 2, 3 ))

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

Point3d.placeIn localFrame
    (Point3d.fromCoordinates ( 0, -1, -2 ))
--> Point3d.fromCoordinates ( 1, 1, 1 )

projectInto : Geometry.Types.SketchPlane3d -> Point3d -> Point2d

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.fromCoordinates ( 2, 1, 3 )

Point3d.projectInto SketchPlane3d.xy point
--> Point2d.fromCoordinates ( 2, 1 )

Point3d.projectInto SketchPlane3d.yz point
--> Point2d.fromCoordinates ( 1, 3 )

Point3d.projectInto SketchPlane3d.zx point
--> Point2d.fromCoordinates ( 3, 2 )