A CubicSpline3d
is a cubic Bézier curve
in 3D defined by a start point, end point and two control points. This module
contains functionality for
Geometry.Types.CubicSpline3d
with : { startPoint : Point3d, startControlPoint : Point3d, endControlPoint : Point3d, endPoint : Point3d } -> CubicSpline3d
Construct a spline from its four control points:
exampleSpline =
CubicSpline3d.with
{ startPoint =
Point3d.fromCoordinates ( 1, 1, 1 )
, startControlPoint =
Point3d.fromCoordinates ( 3, 1, 1 )
, endControlPoint =
Point3d.fromCoordinates ( 3, 3, 1 )
, endPoint =
Point3d.fromCoordinates ( 3, 3, 3 )
}
fromEndpoints : { startPoint : Point3d, startDerivative : Vector3d, endPoint : Point3d, endDerivative : Vector3d } -> CubicSpline3d
Construct a spline from a given start point with a given start derivative, to a given end point with a given end derivative, like so:
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.with
{ startPoint =
Point2d.fromCoordinates ( 1, 1 )
, startControlPoint =
Point2d.fromCoordinates ( 3, 4 )
, endControlPoint =
Point2d.fromCoordinates ( 5, 1 )
, endPoint =
Point2d.fromCoordinates ( 7, 4 )
}
--> CubicSpline3d.with
--> { startPoint =
--> Point3d.fromCoordinates ( 1, 0, 1 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 3, 0, 4 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 5, 0, 1 )
--> , endPoint =
--> 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.with
{ startPoint =
Point3d.fromCoordinates ( 0, 0, 0 )
, controlPoint =
Point3d.fromCoordinates ( 3, 0, 0 )
, endPoint =
Point3d.fromCoordinates ( 3, 3, 0 )
}
CubicSpline3d.fromQuadraticSpline quadraticSpline
--> CubicSpline3d.with
--> { startPoint =
--> Point3d.fromCoordinates ( 0, 0, 0 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 2, 0, 0 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 3, 1, 0 )
--> , endPoint =
--> Point3d.fromCoordinates ( 3, 3, 0 )
--> )
startPoint : CubicSpline3d -> Point3d
Get the start point of a spline.
CubicSpline3d.startPoint exampleSpline
--> Point3d.fromCoordinates ( 1, 1, 1 )
endPoint : CubicSpline3d -> Point3d
Get the end point of a spline.
CubicSpline3d.endPoint exampleSpline
--> Point3d.fromCoordinates ( 3, 3, 3 )
startControlPoint : CubicSpline3d -> Point3d
Get the start control point of a spline (the control point next to the start point).
endControlPoint : CubicSpline3d -> Point3d
Get the end control point of a spline (the control point next to the end point).
startDerivative : CubicSpline3d -> Vector3d
Get the start derivative of a spline. This is equal to three times the vector from the spline's start point to its start control point.
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 end control point to its end point.
CubicSpline3d.endDerivative exampleSpline
--> Vector3d.fromComponents ( 0, 0, 6 )
boundingBox : CubicSpline3d -> BoundingBox3d
Compute a bounding box for a given spline. It is not guaranteed that the result will be the smallest possible bounding box, since for efficiency the bounding box is computed from the spline's control points (which cover a larger volume than the spline itself).
CubicSpline3d.boundingBox exampleSpline
--> BoundingBox3d.fromExtrema
--> { minX = 1
--> , maxX = 3
--> , minY = 1
--> , maxY = 3
--> , minZ = 1
--> , maxZ = 3
--> }
pointOn : CubicSpline3d -> Curve.ParameterValue.ParameterValue -> Point3d
Get a point at a given parameter value.
CubicSpline3d.pointOn exampleSpline ParameterValue.zero
--> Point3d.fromCoordinates ( 1, 1, 1 )
CubicSpline3d.pointOn exampleSpline ParameterValue.half
--> Point3d.fromCoordinates ( 2.75, 2, 1.25 )
CubicSpline3d.pointOn exampleSpline ParameterValue.one
--> Point3d.fromCoordinates ( 3, 3, 3 )
pointsAt : List Curve.ParameterValue.ParameterValue -> CubicSpline3d -> List Point3d
Get points along a spline at a given set of parameter values.
exampleSpline
|> CubicSpline3d.pointsAt
(ParameterValue.steps 2)
--> [ Point3d.fromCoordinates ( 1, 1, 1 )
--> , Point3d.fromCoordinates ( 2.75, 2, 1.25 )
--> , Point3d.fromCoordinates ( 3, 3, 3 )
--> ]
If a curve has zero length (consists of just a single point), then we say that it is 'degenerate'. Some operations such as computing tangent directions are not defined on degenerate curves.
A Nondegenerate
value represents a spline that is definitely not degenerate.
It is used as input to functions such as CubicSpline3d.tangentDirection
and
can be constructed using CubicSpline3d.nondegenerate
.
nondegenerate : CubicSpline3d -> Result Point3d Nondegenerate
Attempt to construct a nondegenerate spline from a general CubicSpline3d
.
If the spline is in fact degenerate (consists of a single point), returns an
Err
with that point.
CubicSpline3d.nondegenerate exampleSpline
--> Ok nondegenerateExampleSpline
fromNondegenerate : Nondegenerate -> CubicSpline3d
Convert a nondegenerate spline back to a general CubicSpline3d
.
CubicSpline3d.fromNondegenerate
nondegenerateExampleSpline
--> exampleSpline
tangentDirection : Nondegenerate -> Curve.ParameterValue.ParameterValue -> Direction3d
Get the tangent direction to a nondegenerate spline at a given parameter value:
CubicSpline3d.tangentDirection
nondegenerateExampleSpline
ParameterValue.zero
--> Direction3d.x
CubicSpline3d.tangentDirection
nondegenerateExampleSpline
ParameterValue.half
--> Direction3d.fromAzimuthAndElevation
--> (degrees 63.43)
--> (degrees 24.09)
CubicSpline3d.tangentDirection
nondegenerateExampleSpline
ParameterValue.one
--> Direction3d.z
tangentDirectionsAt : List Curve.ParameterValue.ParameterValue -> Nondegenerate -> List Direction3d
Get tangent directions to a nondegenerate spline at a given set of parameter values:
nondegenerateExampleSpline
|> CubicSpline3d.tangentDirectionsAt
(ParameterValue.steps 2)
--> [ Direction3d.x
--> , Direction3d.fromAzimuthAndElevation
--> (degrees 63.43)
--> (degrees 24.09)
--> , Direction3d.z
--> ]
sample : Nondegenerate -> Curve.ParameterValue.ParameterValue -> ( Point3d, Direction3d )
Get both the point and tangent direction of a nondegenerate spline at a given parameter value:
CubicSpline3d.sample nondegenerateExampleSpline
ParameterValue.half
--> ( Point3d.fromCoordinates ( 2.75, 2, 1.25 )
--> , Direction3d.fromAzimuthAndElevation
--> (degrees 63.43)
--> (degrees 24.09)
--> )
samplesAt : List Curve.ParameterValue.ParameterValue -> Nondegenerate -> List ( Point3d, Direction3d )
Get points and tangent directions of a nondegenerate spline at a given set of parameter values:
nondegenerateExampleSpline
|> CubicSpline3d.samplesAt
(ParameterValue.steps 2)
--> [ ( Point3d.fromCoordinates ( 1, 1, 1 )
--> , Direction3d.x
--> )
--> , ( Point3d.fromCoordinates ( 2.75, 2, 1.25 )
--> , Direction3d.fromAzimuthAndElevation
--> (degrees 63.43)
--> (degrees 24.09)
--> )
--> , ( Point3d.fromCoordinates ( 3, 3, 3 )
--> , Direction3d.z
--> )
--> ]
reverse : CubicSpline3d -> CubicSpline3d
Reverse a spline so that the start point becomes the end point, and vice versa.
CubicSpline3d.reverse exampleSpline
--> CubicSpline3d.with
--> { startPoint =
--> Point3d.fromCoordinates ( 3, 3, 3 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 3, 3, 1 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 3, 1, 1 )
--> , endPoint =
--> Point3d.fromCoordinates ( 1, 1, 1 )
--> }
scaleAbout : Point3d -> Basics.Float -> CubicSpline3d -> CubicSpline3d
Scale a spline about the given center point by the given scale.
CubicSpline3d.scaleAbout Point3d.origin 2 exampleSpline
--> CubicSpline3d.with
--> { startPoint =
--> Point3d.fromCoordinates ( 2, 2, 2 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 6, 2, 2 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 6, 6, 2 )
--> , endPoint =
--> Point3d.fromCoordinates ( 6, 6, 6 )
--> }
rotateAround : Axis3d -> Basics.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.with
--> { startPoint =
--> Point3d.fromCoordinates ( -1, 1, 1 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( -1, 3, 1 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( -3, 3, 1 )
--> , endPoint =
--> 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.with
--> { startPoint =
--> Point3d.fromCoordinates ( 3, 4, 2 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 5, 4, 2 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 5, 6, 2 )
--> , endPoint =
--> Point3d.fromCoordinates ( 5, 6, 4 )
--> }
translateIn : Direction3d -> Basics.Float -> CubicSpline3d -> CubicSpline3d
Translate a spline in a given direction by a given distance;
CubicSpline3d.translateIn direction distance
is equivalent to
CubicSpline3d.translateBy
(Vector3d.withLength distance direction)
mirrorAcross : Plane3d -> CubicSpline3d -> CubicSpline3d
Mirror a spline across a plane.
CubicSpline3d.mirrorAcross Plane3d.xy exampleSpline
--> CubicSpline3d.with
--> { startPoint =
--> Point3d.fromCoordinates ( 1, 1, -1 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 3, 1, -1 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 3, 3, -1 )
--> , endPoint =
--> 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.with
--> { startPoint =
--> Point3d.fromCoordinates ( 1, 1, 0 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 3, 1, 0 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 3, 3, 0 )
--> , endPoint =
--> Point3d.fromCoordinates ( 3, 3, 0 )
--> }
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.with
--> { startPoint =
--> Point3d.fromCoordinates ( 0, -1, -2 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 2, -1, -2 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 2, 1, -2 )
--> , endPoint =
--> 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.with
--> { startPoint =
--> Point3d.fromCoordinates ( 2, 3, 4 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 4, 3, 4 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 4, 5, 4 )
--> , endPoint =
--> 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.with
--> { startPoint =
--> Point2d.fromCoordinates ( 1, 1 )
--> , startControlPoint =
--> Point2d.fromCoordinates ( 1, 1 )
--> , endControlPoint =
--> Point2d.fromCoordinates ( 3, 1 )
--> , endPoint =
--> Point2d.fromCoordinates ( 3, 3 )
--> }
bisect : CubicSpline3d -> ( CubicSpline3d, CubicSpline3d )
Split a spline into two roughly equal halves.
CubicSpline3d.bisect exampleSpline
--> ( CubicSpline3d.with
--> { startPoint =
--> Point3d.fromCoordinates ( 1, 1, 1 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 2, 1, 1 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 2.5, 1.5, 1 )
--> , endPoint =
--> Point3d.fromCoordinates ( 2.75, 2, 1.25 )
--> }
--> , CubicSpline3d.with
--> { startPoint =
--> Point3d.fromCoordinates ( 2.75, 2, 1.25 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 3, 2.5, 1.5 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 3, 3, 2 )
--> , endPoint =
--> Point3d.fromCoordinates ( 3, 3, 3 )
--> }
--> )
Equivalent to CubicSpline3d.splitAt ParameterValue.half
.
splitAt : Curve.ParameterValue.ParameterValue -> CubicSpline3d -> ( CubicSpline3d, CubicSpline3d )
Split a spline at a particular parameter value, resulting in two smaller splines.
parameterValue =
ParameterValue.clamped 0.75
CubicSpline3d.splitAt parameterValue exampleSpline
--> ( CubicSpline3d.with
--> { startPoint =
--> Point3d.fromCoordinates ( 1, 1, 1 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 2.5, 1, 1 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 2.88, 2.13, 1 )
--> , endPoint =
--> Point3d.fromCoordinates ( 2.97, 2.69, 1.84 )
--> }
--> , CubicSpline3d.with
--> { startPoint =
--> Point3d.fromCoordinates ( 2.97, 2.69, 1.84 )
--> , startControlPoint =
--> Point3d.fromCoordinates ( 3, 2.88, 2.13 )
--> , endControlPoint =
--> Point3d.fromCoordinates ( 3, 3, 2.5 )
--> , endPoint =
--> Point3d.fromCoordinates ( 3, 3, 3 )
--> }
--> )
A spline that has been parameterized by arc length.
arcLengthParameterized : { maxError : Basics.Float } -> CubicSpline3d -> ArcLengthParameterized
Build an arc length parameterization of the given spline, with a given
accuracy. Generally speaking, all operations on the resulting
ArcLengthParameterized
value will be accurate to within the specified maximum
error.
parameterizedSpline =
exampleSpline
|> CubicSpline3d.arcLengthParameterized
{ maxError = 1.0e-4 }
arcLength : ArcLengthParameterized -> Basics.Float
Find the total arc length of a spline:
arcLength =
CubicSpline3d.arcLength parameterizedSpline
arcLength
--> 4.3303
In this example, the result will be accurate to within 1.0e-4
since that was
the tolerance used when constructing parameterizedSpline
.
pointAlong : ArcLengthParameterized -> Basics.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
(ParameterValue.clamped 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, returns Nothing
.
tangentDirectionAlong : ArcLengthParameterized -> Basics.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.tangentDirectionAlong parameterizedSpline
(0.25 * arcLength)
--> Just
--> (Direction3d.fromAzimuthAndElevation
--> (degrees 29.1)
--> (degrees 3.871)
--> )
If the given arc length is less than zero or greater than the arc length of the
spline (or if the spline is degenerate), returns Nothing
.
sampleAlong : ArcLengthParameterized -> Basics.Float -> Maybe ( Point3d, Direction3d )
Try to get the point and tangent direction along a spline at a given arc
length. To get the point and tangent direction a quarter of the way along
exampleSpline
:
CubicSpline3d.sampleAlong parameterizedSpline
(0.25 * arcLength)
--> Just
--> ( Point3d.fromCoordinates
--> ( 2.0425, 1.2431, 1.0206 )
--> , Direction3d.fromAzimuthAndElevation
--> (degrees 29.1)
--> (degrees 3.871)
--> )
If the given arc length is less than zero or greater than the arc length of the
spline (or if the spline is degenerate), returns Nothing
.
An ArcLengthParameterized
value is a combination of an
ArcLengthParameterization
and an
underlying CubicSpline3d
. If you need to do something fancy, you can extract
these two values separately.
arcLengthParameterization : ArcLengthParameterized -> Curve.ArcLengthParameterization.ArcLengthParameterization
fromArcLengthParameterized : ArcLengthParameterized -> CubicSpline3d
You are unlikely to need to use these functions directly, but they are useful if you are writing low-level geometric algorithms.
firstDerivative : CubicSpline3d -> Curve.ParameterValue.ParameterValue -> Vector3d
Get the first derivative of a spline at a given parameter value.
CubicSpline3d.derivative exampleSpline
ParameterValue.zero
--> Vector3d.fromComponents ( 6, 0, 0 )
CubicSpline3d.derivative exampleSpline
ParameterValue.half
--> Vector3d.fromComponents ( 1.5, 3, 1.5 )
CubicSpline3d.derivative exampleSpline
ParameterValue.one
--> Vector3d.fromComponents ( 0, 0, 6 )
firstDerivativesAt : List Curve.ParameterValue.ParameterValue -> CubicSpline3d -> List Vector3d
Evaluate the first derivative of a spline at a range of parameter values.
exampleSpline
|> CubicSpline3d.firstDerivativesAt
(ParameterValue.steps 2)
--> [ Vector3d.fromComponents ( 6, 0, 0 )
--> , Vector3d.fromComponents ( 1.5, 3, 1.5 )
--> , Vector3d.fromComponents ( 0, 0, 6 )
--> ]
secondDerivative : CubicSpline3d -> Curve.ParameterValue.ParameterValue -> Vector3d
Get the second derivative 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 of the spline and a value of 1 corresponds to the end.
CubicSpline3d.secondDerivative exampleSpline
ParameterValue.zero
--> Vector3d.fromComponents ( -12, 12, 0 )
CubicSpline3d.secondDerivative exampleSpline
ParameterValue.half
--> Vector3d.fromComponents ( -6, 0, 6 )
CubicSpline3d.secondDerivative exampleSpline
ParameterValue.one
--> Vector3d.fromComponents ( 0, -12, 12 )
secondDerivativesAt : List Curve.ParameterValue.ParameterValue -> CubicSpline3d -> List Vector3d
Evaluate the second derivative of a spline at a range of parameter values.
exampleSpline
|> CubicSpline3d.secondDerivativesAt
(ParameterValue.steps 2)
--> [ Vector3d.fromComponents ( -12, 12, 0 )
--> , Vector3d.fromComponents ( -6, 0, 6 )
--> , Vector3d.fromComponents ( 0, -12, 12 )
--> ]
thirdDerivative : CubicSpline3d -> Vector3d
Get the third derivative of a spline (for a cubic spline, this is a constant).
maxSecondDerivativeMagnitude : CubicSpline3d -> Basics.Float
Find a conservative upper bound on the magnitude of the second derivative of a spline. This can be useful when determining error bounds for various kinds of linear approximations.