opensolid/geometry/2.1.0

NOTE: this package is not compatible with Elm 0.19.1

OpenSolid.CubicSpline3d

CubicSpline3d

A CubicSpline3d is a cubic Bézier curve in 3D defined by four control points. This module contains functionality for

  • Evaluating points and derivatives along a spline
  • Scaling, rotating, translating or mirroring a spline
  • Converting a spline between local and global coordinates in different reference frames
type alias CubicSpline3d =
CubicSpline3d

Constructors

fromControlPoints : ( Point3d, Point3d, Point3d, Point3d ) -> CubicSpline3d

Construct a spline from its four control points:

exampleSpline =
    CubicSpline3d.fromControlPoints
        ( Point3d.fromCoordinates ( 1, 1, 1 )
        , Point3d.fromCoordinates ( 3, 1, 1 )
        , Point3d.fromCoordinates ( 3, 3, 1 )
        , Point3d.fromCoordinates ( 3, 3, 3 )
        )
hermite : ( Point3d, Vector3d ) -> ( Point3d, Vector3d ) -> CubicSpline3d

Construct a spline in Hermite form, from the position and derivative values at its start and end points, like so:

Hermite cubic spline

The spline is based on a parameter that ranges from 0 to 1; as a result, in most cases the length of each derivative vector should be roughly equal to the length of the resulting spline.

on : SketchPlane3d -> CubicSpline2d -> CubicSpline3d

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

CubicSpline3d.on SketchPlane3d.xz <|
    CubicSpline2d.fromControlPoints
        ( Point2d.fromCoordinates ( 1, 1 )
        , Point2d.fromCoordinates ( 3, 4 )
        , Point2d.fromCoordinates ( 5, 1 )
        , Point2d.fromCoordinates ( 7, 4 )
        )
--> CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 1, 0, 1 )
-->     , Point3d.fromCoordinates ( 3, 0, 4 )
-->     , Point3d.fromCoordinates ( 5, 0, 1 )
-->     , Point3d.fromCoordinates ( 7, 0, 4 )
-->     )
fromQuadraticSpline : QuadraticSpline3d -> CubicSpline3d

Convert a quadratic spline into the equivalent cubic spline (every quadratic spline can be represented exactly as a cubic spline).

quadraticSpline =
    QuadraticSpline3d.fromControlPoints
        ( Point3d.fromCoordinates ( 0, 0, 0  )
        , Point3d.fromCoordinates ( 3, 0, 0 )
        , Point3d.fromCoordinates ( 3, 3, 0 )
        )


CubicSpline3d.fromQuadraticSpline quadraticSpline
--> CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 0, 0, 0 )
-->     , Point3d.fromCoordinates ( 2, 0, 0 )
-->     , Point3d.fromCoordinates ( 3, 1, 0 )
-->     , Point3d.fromCoordinates ( 3, 3, 0 )
-->     )

Properties

controlPoints : CubicSpline3d -> ( Point3d, Point3d, Point3d, Point3d )

Get the control points of a spline as a tuple.

( p1, p2, p3, p4 ) =
    CubicSpline3d.controlPoints exampleSpline




--> p1 = Point3d.fromCoordinates ( 1, 1, 1 )
--> p2 = Point3d.fromCoordinates ( 3, 1, 1 )
--> p3 = Point3d.fromCoordinates ( 3, 3, 1 )
--> p4 = Point3d.fromCoordinates ( 3, 3, 3 )
startPoint : CubicSpline3d -> Point3d

Get the start point of a spline. This is equal to the spline's first control point.

CubicSpline3d.startPoint exampleSpline
--> Point3d.fromCoordinates ( 1, 1, 1 )
endPoint : CubicSpline3d -> Point3d

Get the end point of a spline. This is equal to the spline's last control point.

CubicSpline3d.endPoint exampleSpline
--> Point3d.fromCoordinates ( 3, 3, 3 )
startDerivative : CubicSpline3d -> Vector3d

Get the start derivative of a spline. This is equal to three times the vector from the spline's first control point to its second.

CubicSpline3d.startDerivative exampleSpline
--> Vector3d.fromComponents ( 6, 0, 0 )
endDerivative : CubicSpline3d -> Vector3d

Get the end derivative of a spline. This is equal to three times the vector from the spline's third control point to its fourth.

CubicSpline3d.endDerivative exampleSpline
--> Vector3d.fromComponents ( 0, 0, 6 )

Evaluation

pointOn : CubicSpline3d -> Float -> Point3d

Get a point along a spline, based on a parameter that ranges from 0 to 1. A parameter value of 0 corresponds to the start point of the spline and a value of 1 corresponds to the end point.

CubicSpline3d.pointOn exampleSpline 0
--> Point3d.fromCoordinates ( 1, 1, 1 )


CubicSpline3d.pointOn exampleSpline 0.5
--> Point3d.fromCoordinates ( 2.75, 2, 1.25 )


CubicSpline3d.pointOn exampleSpline 1
--> Point3d.fromCoordinates ( 3, 3, 3 )
derivative : CubicSpline3d -> Float -> Vector3d

Get the deriative value at a point along a spline, based on a parameter that ranges from 0 to 1. A parameter value of 0 corresponds to the start derivative of the spline and a value of 1 corresponds to the end derivative.

CubicSpline3d.derivative exampleSpline 0
--> Vector3d.fromComponents ( 6, 0, 0 )


CubicSpline3d.derivative exampleSpline 0.5
--> Vector3d.fromComponents ( 1.5, 3, 1.5 )


CubicSpline3d.derivative exampleSpline 1
--> Vector3d.fromComponents ( 0, 0, 6 )

Note that the derivative interpolates linearly from end to end.

evaluate : CubicSpline3d -> Float -> ( Point3d, Vector3d )

Evaluate a spline at a given parameter value, returning the point on the spline at that parameter value and the derivative with respect to that parameter value;

CubicSpline3d.evaluate spline t

is equivalent to

( CubicSpline3d.pointOn spline t
, CubicSpline3d.derivative spline t
)

but is more efficient.

Transformations

reverse : CubicSpline3d -> CubicSpline3d

Reverse a spline so that the start point becomes the end point, and vice versa.

CubicSpline3d.reverse exampleSpline
--> CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 3, 3, 3 )
-->     , Point3d.fromCoordinates ( 3, 3, 1 )
-->     , Point3d.fromCoordinates ( 3, 1, 1 )
-->     , Point3d.fromCoordinates ( 1, 1, 1 )
-->     )
scaleAbout : Point3d -> Float -> CubicSpline3d -> CubicSpline3d

Scale a spline about the given center point by the given scale.

CubicSpline3d.scaleAbout Point3d.origin 2 exampleSpline
--> CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 2, 2, 2 )
-->     , Point3d.fromCoordinates ( 6, 2, 2 )
-->     , Point3d.fromCoordinates ( 6, 6, 2 )
-->     , Point3d.fromCoordinates ( 6, 6, 6 )
-->     )
rotateAround : Axis3d -> Float -> CubicSpline3d -> CubicSpline3d

Rotate a spline counterclockwise around a given axis by a given angle (in radians).

exampleSpline
    |> CubicSpline3d.rotateAround Axis3d.z (degrees 90)
--> CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( -1, 1, 1 )
-->     , Point3d.fromCoordinates ( -1, 3, 1 )
-->     , Point3d.fromCoordinates ( -3, 3, 1 )
-->     , Point3d.fromCoordinates ( -3, 3, 3 )
-->     )
translateBy : Vector3d -> CubicSpline3d -> CubicSpline3d

Translate a spline by a given displacement.

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


CubicSpline3d.translateBy displacement exampleSpline
--> CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 3, 4, 2 )
-->     , Point3d.fromCoordinates ( 5, 4, 2 )
-->     , Point3d.fromCoordinates ( 5, 6, 2 )
-->     , Point3d.fromCoordinates ( 5, 6, 4 )
-->     )
mirrorAcross : Plane3d -> CubicSpline3d -> CubicSpline3d

Mirror a spline across a plane.

CubicSpline3d.mirrorAcross Plane3d.xy exampleSpline
--> CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 1, 1, -1 )
-->     , Point3d.fromCoordinates ( 3, 1, -1 )
-->     , Point3d.fromCoordinates ( 3, 3, -1 )
-->     , Point3d.fromCoordinates ( 3, 3, -3 )
-->     )
projectOnto : Plane3d -> CubicSpline3d -> CubicSpline3d

Find the orthographic projection of a spline onto a plane.

CubicSpline3d.projectOnto Plane3d.xy exampleSpline
--> CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 1, 1, 0 )
-->     , Point3d.fromCoordinates ( 3, 1, 0 )
-->     , Point3d.fromCoordinates ( 3, 3, 0 )
-->     , Point3d.fromCoordinates ( 3, 3, 0 )
-->     )

Coordinate conversions

relativeTo : Frame3d -> CubicSpline3d -> CubicSpline3d

Take a spline 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 ))


CubicSpline3d.relativeTo localFrame exampleSpline
--> CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 0, -1, -2 )
-->     , Point3d.fromCoordinates ( 2, -1, -2 )
-->     , Point3d.fromCoordinates ( 2, 1, -2 )
-->     , Point3d.fromCoordinates ( 2, 1, 0 )
-->     )
placeIn : Frame3d -> CubicSpline3d -> CubicSpline3d

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

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


CubicSpline3d.placeIn localFrame exampleSpline
--> CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 2, 3, 4 )
-->     , Point3d.fromCoordinates ( 4, 3, 4 )
-->     , Point3d.fromCoordinates ( 4, 5, 4 )
-->     , Point3d.fromCoordinates ( 4, 5, 6 )
-->     )
projectInto : SketchPlane3d -> CubicSpline3d -> CubicSpline2d

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

exampleSpline
    |> CubicSpline3d.projectInto SketchPlane3d.yz
--> CubicSpline2d.fromControlPoints
-->     ( Point2d.fromCoordinates ( 1, 1 )
-->     , Point2d.fromCoordinates ( 1, 1 )
-->     , Point2d.fromCoordinates ( 3, 1 )
-->     , Point2d.fromCoordinates ( 3, 3 )
-->     )

Subdivision

bisect : CubicSpline3d -> ( CubicSpline3d, CubicSpline3d )

Split a spline into two roughly equal halves. Equivalent to splitAt 0.5.

CubicSpline3d.bisect exampleSpline
--> ( CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 1, 1, 1 )
-->     , Point3d.fromCoordinates ( 2, 1, 1 )
-->     , Point3d.fromCoordinates ( 2.5, 1.5, 1 )
-->     , Point3d.fromCoordinates ( 2.75, 2, 1.25 )
-->     )
--> , CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 2.75, 2, 1.25 )
-->     , Point3d.fromCoordinates ( 3, 2.5, 1.5 )
-->     , Point3d.fromCoordinates ( 3, 3, 2 )
-->     , Point3d.fromCoordinates ( 3, 3, 3 )
-->     )
--> )
splitAt : Float -> CubicSpline3d -> ( CubicSpline3d, CubicSpline3d )

Split a spline at a particular parameter value (in the range 0 to 1), resulting in two smaller splines.

CubicSpline3d.splitAt 0.75 exampleSpline
--> ( CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 1, 1, 1 )
-->     , Point3d.fromCoordinates ( 2.5, 1, 1 )
-->     , Point3d.fromCoordinates ( 2.88, 2.13, 1 )
-->     , Point3d.fromCoordinates ( 2.97, 2.69, 1.84 )
-->     )
--> , CubicSpline3d.fromControlPoints
-->     ( Point3d.fromCoordinates ( 2.97, 2.69, 1.84 )
-->     , Point3d.fromCoordinates ( 3, 2.88, 2.13 )
-->     , Point3d.fromCoordinates ( 3, 3, 2.5 )
-->     , Point3d.fromCoordinates ( 3, 3, 3 )
-->     )
--> )

Arc length parameterization

A spline that has been parameterized by arc length.

arcLengthParameterized : Float -> CubicSpline3d -> ArcLengthParameterized

Build an arc length parameterization of the given spline, within a given tolerance. Generally speaking, all operations on the resulting ArcLengthParameterized value will be accurate to within the given arc length tolerance.

tolerance =
    1.0e-4


parameterizedSpline =
    CubicSpline3d.arcLengthParameterized
        tolerance
        exampleSpline
arcLength : ArcLengthParameterized -> Float

Find the total arc length of a spline. This will be accurate to within the tolerance given when calling arcLengthParameterized.

arcLength : Float
arcLength =
    CubicSpline3d.arcLength parameterizedSpline


arcLength
--> 4.3303
pointAlong : ArcLengthParameterized -> Float -> Maybe Point3d

Try to get the point along a spline at a given arc length. For example, to get the point a quarter of the way along exampleSpline:

CubicSpline3d.pointAlong parameterizedSpline
    (arcLength / 4)
--> Just <|
-->     Point3d.fromCoordinates
-->         ( 2.0425, 1.2431, 1.0206 )

Note that this is not the same as evaulating at a parameter value of 1/4:

CubicSpline3d.pointOn exampleSpline 0.25
--> Point3d.fromCoordinates ( 2.1563, 1.3125, 1.0313 )

If the given arc length is less than zero or greater than the arc length of the spline, Nothing is returned.

tangentAlong : ArcLengthParameterized -> Float -> Maybe Direction3d

Try to get the tangent direction along a spline at a given arc length. To get the tangent direction a quarter of the way along exampleSpline:

CubicSpline3d.tangentAlong parameterizedSpline
    (arcLength / 4)
--> Just <|
-->     Direction3d.with
-->         { azimuth = degrees 29.0995
-->         , elevation = degrees 3.8713
-->         }

If the given arc length is less than zero or greater than the arc length of the spline (or if the derivative of the spline happens to be exactly zero at the given arc length), Nothing is returned.

arcLengthToParameterValue : ArcLengthParameterized -> Float -> Maybe Float

Try to get the parameter value along a spline at a given arc length. If the given arc length is less than zero or greater than the arc length of the spline, returns Nothing.

CubicSpline3d.arcLengthToParameterValue
    parameterizedSpline
    (arcLength / 4)
--> Just 0.2177
parameterValueToArcLength : ArcLengthParameterized -> Float -> Maybe Float

Try to get the arc length along a spline at a given parameter value. If the given parameter value is less than zero or greater than one, returns Nothing.

CubicSpline3d.parameterValueToArcLength
    parameterizedSpline
    0.25
--> Just 1.2163

Low level

Low level functionality that you are unlikely to need to use directly.

derivativeMagnitude : CubicSpline3d -> Float -> Float

Find the magnitude of the derivative to a spline at a particular parameter value;

CubicSpline3d.derivativeMagnitude spline t

is equivalent to

Vector3d.length (CubicSpline3d.derivative spline t)

but more efficient since it avoids any intermediate Vector3d allocation.

maxSecondDerivativeMagnitude : CubicSpline3d -> Float

Find an upper bound on the magnitude of the second derivative of a spline.