An EllipticalArc2d is a section of an Ellipse2d with a start and end point.
This module includes functionality for
The startAngle and sweptAngle values referred to below are not actually
proper angles but instead refer to values of the ellipse parameter.
However, in simple cases you don't need to worry about the difference - if
startAngle and sweptAngle are both multiples of 90 degrees, then you can
treat them as actual angles and everything will behave as you expect.
Construct an elliptical arc from its center point, X direction, X and Y radii, start angle and swept angle. If you pass a negative radius, the absolute value will be used.
For example, to construct a simple 90 degree elliptical arc, you might use
exampleArc =
EllipticalArc2d.with
{ centerPoint = Point2d.origin
, xDirection = Direction2d.x
, xRadius = 2
, yRadius = 1
, startAngle = 0
, sweptAngle = degrees 90
}To make an inclined 180 degree elliptical arc, you might use
EllipticalArc2d.with
{ centerPoint = Point2d.origin
, xDirection = Direction2d.fromAngle (degrees 30)
, xRadius = 2
, yRadius = 1
, startAngle = degrees -90
, sweptAngle = degrees 180
}Attempt to construct an elliptical arc from its endpoints, X direction, and X and Y radii. For any given valid set of these inputs, there are four possible solutions, so you also need to specify which of the four solutions you want - whether the swept angle of the arc should be less than or greater than 180 degrees, and whether the swept angle should be positive (counterclockwise) or negative (clockwise).
The example below is interactive; try dragging either endpoint or the tip of the X direction (or the center point to move the whole arc), clicking on the X or Y radial lines and then scrolling to changet that radius, or clicking/tapping on the various dashed arcs to switch what kind of swept angle to use.
https://ianmackenzie.github.io/elm-geometry/1.0.0/EllipticalArc2d/fromEndpoints.html
This function will return Nothing if no solution can found. Typically this
means that the two endpoints are too far apart, but could also mean that one of
the specified radii was negative or zero, or the two given points were
coincident.
The behavior of this function is very close to the SVG spec,
but when 'out of range' parameters are given this function will simply return
Nothing instead of attempting to degrade gracefully (for example, by
increasing X and Y radius slightly if the given endpoints are slightly too far
apart). Note that this means this function is dangerous to use for 180 degree
arcs, since then slight numerical roundoff can mean the difference between a
solution being found and not - for 180 degree arcs it is safer to use
EllipticalArc2d.with instead.
The start angle of an elliptical arc is the value of the ellipse parameter at the start point of the arc.
EllipticalArc2d.startAngle exampleArc
--> 0The swept angle of an elliptical arc is the difference between values of the ellipse parameter from the start point to the end point of the arc.
EllipticalArc2d.sweptAngle exampleArc
--> degrees 90Get the start point of an elliptical arc.
EllipticalArc2d.startPoint exampleArc
--> Point2d.fromCoordinates ( 2, 0 )Get the end point of an elliptical arc.
EllipticalArc2d.endPoint exampleArc
--> Point2d.fromCoordinates ( 0, 1 )All remaining properties of elliptical arcs are actually just properties of the underlying ellipse; check out the Ellipse2d module for details.
Get the point along an elliptical arc at a given parameter value:
EllipticalArc2d.pointOn exampleArc ParameterValue.zero
--> Point2d.fromCoordinates ( 2, 0 )
EllipticalArc2d.pointOn exampleArc ParameterValue.half
--> Point2d.fromCoordinates ( 1.4142, 0.7071 )
EllipticalArc2d.pointOn exampleArc ParameterValue.one
--> Point2d.fromCoordinates ( 0, 1 )Get points along an elliptical arc at a given set of parameter values:
exampleArc
|> EllipticalArc2d.pointsAt
(ParameterValue.steps 2)
--> [ Point2d.fromCoordinates ( 2, 0 )
--> , Point2d.fromCoordinates ( 1.4142, 0.7071 )
--> , Point2d.fromCoordinates ( 0, 1 )
--> ]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 an arc that is definitely not degenerate. It
is used as input to functions such as EllipticalArc2d.tangentDirection and can
be constructed using EllipticalArc2d.nondegenerate.
Attempt to construct a nondegenerate elliptical arc from a general
EllipticalArc2d. If the arc is in fact degenerate (consists of a single
point), returns an Err with that point.
EllipticalArc2d.nondegenerate exampleArc
--> Ok nondegenerateExampleArcConvert a nondegenerate elliptical arc back to a general EllipticalArc2d.
EllipticalArc2d.fromNondegenerate
nondegenerateExampleArc
--> exampleArcGet the tangent direction to a nondegenerate elliptical arc at a given parameter value:
EllipticalArc2d.tangentDirection nondegenerateExampleArc
ParameterValue.zero
--> Direction2d.fromAngle (degrees 90)
EllipticalArc2d.tangentDirection nondegenerateExampleArc
ParameterValue.half
--> Direction2d.fromAngle (degrees 153.4)
EllipticalArc2d.tangentDirection nondegenerateExampleArc
ParameterValue.one
--> Direction2d.fromAngle (degrees 180)Get tangent directions to a nondegenerate elliptical arc at a given set of parameter values:
nondegenerateExampleArc
|> EllipticalArc2d.tangentDirectionsAt
(ParameterValue.steps 2)
--> [ Direction2d.fromAngle (degrees 90)
--> , Direction2d.fromAngle (degrees 153.4)
--> , Direction2d.fromAngle (degrees 180)
--> ]Get both the point and tangent direction of a nondegenerate elliptical arc at a given parameter value:
EllipticalArc2d.sample nondegenerateExampleArc
ParameterValue.zero
--> ( Point2d.fromCoordinates ( 2, 0 )
--> , Direction2d.fromAngle (degrees 90)
--> )
EllipticalArc2d.sample nondegenerateExampleArc
ParameterValue.half
--> ( Point2d.fromCoordinates ( 1.4142, 0.7071 )
--> , Direction2d.fromAngle (degrees 153.4)
--> )
EllipticalArc2d.sample nondegenerateExampleArc
ParameterValue.one
--> ( Point2d.fromCoordinates ( 0, 1 )
--> , Direction2d.fromAngle (degrees 180)
--> )Get points and tangent directions of a nondegenerate arc at a given set of parameter values:
nondegenerateExampleArc
|> EllipticalArc2d.samplesAt
(ParameterValue.steps 2)
--> [ ( Point2d.fromCoordinates ( 2, 0 )
--> , Direction2d.fromAngle (degrees 90)
--> )
--> , ( Point2d.fromCoordinates ( 1.4142, 0.7071 )
--> , Direction2d.fromAngle (degrees 153.4)
--> )
--> , ( Point2d.fromCoordinates ( 0, 1 )
--> , Direction2d.fromAngle (degrees 180)
--> )
--> ]Reverse the direction of an elliptical arc, so that the start point becomes the end point and vice versa. Does not change the shape of the arc or any properties of the underlying ellipse.
EllipticalArc2d.reverse exampleArc
--> EllipticalArc2d.with
--> { centerPoint = Point2d.origin
--> , xDirection = Direction2d.x
--> , xRadius = 2
--> , yRadius = 1
--> , startAngle = degrees 90
--> , sweptAngle = degrees -90
--> }Scale an elliptical arc about a given point by a given scale.
exampleArc
|> EllipticalArc2d.scaleAbout Point2d.origin 3
--> EllipticalArc2d.with
--> { centerPoint = Point2d.origin
--> , xDirection = Direction2d.x
--> , xRadius = 6
--> , yRadius = 3
--> , startAngle = 0
--> , sweptAngle = degrees 90
--> }Rotate an elliptical arc around a given point by a given angle (in radians).
exampleArc
|> EllipticalArc2d.rotateAround Point2d.origin
(degrees 180)
--> EllipticalArc2d.with
--> { centerPoint = Point2d.origin
--> , xDirection = Direction2d.negativeX
--> , xRadius = 2
--> , yRadius = 1
--> , startAngle = 0
--> , sweptAngle = degrees 90
--> }Translate an elliptical arc by a given displacement.
exampleArc
|> EllipticalArc2d.translateBy
(Vector2d.fromComponents ( 2, 3 ))
--> EllipticalArc2d.with
--> { centerPoint =
--> Point2d.fromCoordinates ( 2, 3 )
--> , xDirection = Direction2d.x
--> , xRadius = 2
--> , yRadius = 1
--> , startAngle = 0
--> , sweptAngle = degrees 90
--> }Translate an elliptical arc in a given direction by a given distance;
EllipticalArc2d.translateIn direction distanceis equivalent to
EllipticalArc2d.translateBy
(Vector2d.withLength distance direction)Mirror an elliptical arc across a given axis.
mirroredArc =
exampleArc
|> EllipticalArc2d.mirrorAcross Axis2d.y
EllipticalArc2d.startPoint mirroredArc
--> Point2d.fromCoordinates ( -2, 0 )
EllipticalArc2d.endPoint mirroredArc
--> Point2d.fromCoordinates ( 0, 1 )Take an elliptical arc defined in global coordinates, and return it expressed in local coordinates relative to a given reference frame.
localFrame =
Frame2d.atPoint (Point2d.fromCoordinates ( 1, 2 ))
EllipticalArc2d.relativeTo localFrame exampleArc
--> EllipticalArc2d.with
--> { centerPoint =
--> Point2d.fromCoordinates ( -1, -2 )
--> , xDirection = Direction2d.x
--> , xRadius = 2
--> , yRadius = 1
--> , startAngle = 0
--> , sweptAngle = degrees 90
--> }Take an elliptical arc considered to be defined in local coordinates relative to a given reference frame, and return that arc expressed in global coordinates.
localFrame =
Frame2d.atPoint (Point2d.fromCoordinates ( 1, 2 ))
EllipticalArc2d.relativeTo localFrame exampleArc
--> EllipticalArc2d.with
--> { centerPoint =
--> Point2d.fromCoordinates ( 1, 2 )
--> , xDirection = Direction2d.x
--> , xRadius = 2
--> , yRadius = 1
--> , startAngle = 0
--> , sweptAngle = degrees 90
--> }An elliptical arc that has been parameterized by arc length.
Build an arc length parameterization of the given elliptical arc, with a
given accuracy. Generally speaking, all operations on the resulting
ArcLengthParameterized value will be accurate to within the specified maximum
error.
parameterizedArc =
exampleArc
|> EllipticalArc2d.arcLengthParameterized
{ maxError = 1.0e-4 }Find the total arc length of an elliptical arc. This will be accurate to
within the tolerance given when calling arcLengthParameterized.
arcLength : Float
arcLength =
EllipticalArc2d.arcLength parameterizedArc
arcLength
--> 2.4221Try to get the point along an elliptical arc at a given arc length. For
example, to get the true midpoint of exampleArc:
EllipticalArc2d.pointAlong parameterizedArc
(arcLength / 2)
--> Just (Point2d.fromCoordinates ( 1.1889, 0.8041 ))Note that this is not the same as evaulating at a parameter value of 0.5:
EllipticalArc2d.pointOn exampleArc
ParameterValue.half
--> Point2d.fromCoordinates ( 1.4142, 0.7071 )If the given arc length is less than zero or greater than the arc length of the
arc, returns Nothing.
Try to get the tangent direction along an elliptical arc at a given arc
length. To get the tangent direction at the midpoint of exampleArc:
EllipticalArc2d.tangentDirectionAlong parameterizedArc
(arcLength / 2)
--> Just (Direction2d.fromAngle (degrees 159.7))If the given arc length is less than zero or greater than the arc length of the
elliptical arc (or if the elliptical arc is degenerate), returns Nothing.
Try to get the point and tangent direction along an elliptical arc at a
given arc length. To get the point and tangent direction at the midpoint of
exampleArc:
EllipticalArc2d.sampleAlong parameterizedArc
(arcLength / 2)
--> Just
--> ( Point2d.fromCoordinates ( 1.1889, 0.8041 )
--> , Direction2d.fromAngle (degrees 159.7)
--> )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 EllipticalArc2d. If you need to do something fancy, you can extract
these two values separately.
You are unlikely to need to use these functions directly, but they are useful if you are writing low-level geometric algorithms.
Get the first derivative of an elliptical arc at a given parameter value:
EllipticalArc2d.firstDerivative exampleArc
ParameterValue.zero
--> Vector2d.fromComponents ( 0, 1.5708 )
EllipticalArc2d.firstDerivative exampleArc
ParameterValue.half
--> Vector2d.fromComponents ( -2.2214, 1.1107 )
EllipticalArc2d.firstDerivative exampleArc
ParameterValue.one
--> Vector2d.fromComponents ( -3.1416, 0 )Evaluate the first derivative of an elliptical arc at a given set of parameter values:
exampleArc
|> EllipticalArc2d.firstDerivativesAt
(ParameterValue.steps 2)
--> [ Vector2d.fromComponents ( 0, 1.5708 )
--> , Vector2d.fromComponents ( -2.2214, 1.1107 )
--> , Vector2d.fromComponents ( -3.1416, 0 )
--> ]Find a conservative upper bound on the magnitude of the second derivative of an elliptical arc. This can be useful when determining error bounds for various kinds of linear approximations.
exampleArc
|> EllipticalArc2d.maxSecondDerivativeMagnitude
--> 4.935