ianmackenzie / elm-geometry-prerelease / EllipticalArc2d

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.


type alias EllipticalArc2d =
Geometry.Types.EllipticalArc2d

Constructors

with : { centerPoint : Point2d, xDirection : Direction2d, xRadius : Basics.Float, yRadius : Basics.Float, startAngle : Basics.Float, sweptAngle : Basics.Float } -> EllipticalArc2d

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
        }

90 degree elliptical arc

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
    }

180 degree inclined elliptical arc

fromEndpoints : { startPoint : Point2d, endPoint : Point2d, xRadius : Basics.Float, yRadius : Basics.Float, xDirection : Direction2d, sweptAngle : Arc.SweptAngle.SweptAngle } -> Maybe EllipticalArc2d

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.

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.

Properties

startAngle : EllipticalArc2d -> Basics.Float

The start angle of an elliptical arc is the value of the ellipse parameter at the start point of the arc.

EllipticalArc2d.startAngle exampleArc
--> 0

sweptAngle : EllipticalArc2d -> Basics.Float

The 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 90

startPoint : EllipticalArc2d -> Point2d

Get the start point of an elliptical arc.

EllipticalArc2d.startPoint exampleArc
--> Point2d.fromCoordinates ( 2, 0 )

centerPoint : EllipticalArc2d -> Point2d

axes : EllipticalArc2d -> Frame2d

xAxis : EllipticalArc2d -> Axis2d

yAxis : EllipticalArc2d -> Axis2d

xDirection : EllipticalArc2d -> Direction2d

yDirection : EllipticalArc2d -> Direction2d

xRadius : EllipticalArc2d -> Basics.Float

yRadius : EllipticalArc2d -> Basics.Float

Evaluation

pointOn : EllipticalArc2d -> Curve.ParameterValue.ParameterValue -> Point2d

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 )

pointsAt : List Curve.ParameterValue.ParameterValue -> EllipticalArc2d -> List Point2d

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 )
--> ]


type Nondegenerate

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.

nondegenerate : EllipticalArc2d -> Result Point2d 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 nondegenerateExampleArc

fromNondegenerate : Nondegenerate -> EllipticalArc2d

Convert a nondegenerate elliptical arc back to a general EllipticalArc2d.

EllipticalArc2d.fromNondegenerate
    nondegenerateExampleArc
--> exampleArc

tangentDirection : Nondegenerate -> Curve.ParameterValue.ParameterValue -> Direction2d

Get 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)

tangentDirectionsAt : List Curve.ParameterValue.ParameterValue -> Nondegenerate -> List Direction2d

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)
--> ]

sample : Nondegenerate -> Curve.ParameterValue.ParameterValue -> ( Point2d, Direction2d )

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)
--> )

samplesAt : List Curve.ParameterValue.ParameterValue -> Nondegenerate -> List ( Point2d, Direction2d )

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)
-->   )
--> ]

Transformations

reverse : EllipticalArc2d -> EllipticalArc2d

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
-->     }

scaleAbout : Point2d -> Basics.Float -> EllipticalArc2d -> EllipticalArc2d

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
-->     }

rotateAround : Point2d -> Basics.Float -> EllipticalArc2d -> EllipticalArc2d

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
-->     }

translateBy : Vector2d -> EllipticalArc2d -> EllipticalArc2d

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
-->     }

translateIn : Direction2d -> Basics.Float -> EllipticalArc2d -> EllipticalArc2d

Translate an elliptical arc in a given direction by a given distance;

EllipticalArc2d.translateIn direction distance

is equivalent to

EllipticalArc2d.translateBy
    (Vector2d.withLength distance direction)

mirrorAcross : Axis2d -> EllipticalArc2d -> EllipticalArc2d

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 )

Coordinate conversions

relativeTo : Frame2d -> EllipticalArc2d -> EllipticalArc2d

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
-->     }

placeIn : Frame2d -> EllipticalArc2d -> EllipticalArc2d

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
-->     }

Arc length parameterization


type ArcLengthParameterized

An elliptical arc that has been parameterized by arc length.

arcLengthParameterized : { maxError : Basics.Float } -> EllipticalArc2d -> ArcLengthParameterized

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 }

arcLength : ArcLengthParameterized -> Basics.Float

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

pointAlong : ArcLengthParameterized -> Basics.Float -> Maybe Point2d

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

tangentDirectionAlong : ArcLengthParameterized -> Basics.Float -> Maybe Direction2d

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.

sampleAlong : ArcLengthParameterized -> Basics.Float -> Maybe ( Point2d, Direction2d )

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.

Low level

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.

arcLengthParameterization : ArcLengthParameterized -> Curve.ArcLengthParameterization.ArcLengthParameterization

fromArcLengthParameterized : ArcLengthParameterized -> EllipticalArc2d

Differentiation

You are unlikely to need to use these functions directly, but they are useful if you are writing low-level geometric algorithms.

firstDerivative : EllipticalArc2d -> Curve.ParameterValue.ParameterValue -> Vector2d

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 )

firstDerivativesAt : List Curve.ParameterValue.ParameterValue -> EllipticalArc2d -> List Vector2d

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 )
--> ]

maxSecondDerivativeMagnitude : EllipticalArc2d -> Basics.Float

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