ianmackenzie / elm-geometry-prerelease / Arc2d

An Arc2d is a section of a circle, defined by its center point, start point and swept angle (the counterclockwise angle from the start point to the end point). This module includes functionality for


type alias Arc2d =
Geometry.Types.Arc2d

Constructors

from : Point2d -> Point2d -> Basics.Float -> Arc2d

Construct an arc with from the first given point to the second, with the given swept angle.

p1 =
    Point2d.fromCoordinates ( 2, 1 )

p2 =
    Point2d.fromCoordinates ( 1, 2 )

arc1 =
    Arc2d.from p1 p2 (degrees 90)

Arc2d.centerPoint arc1
--> Point2d.fromCoordinates ( 1, 1 )

arc2 =
    Arc2d.from p1 p2 (degrees -90)

Arc2d.centerPoint arc2
--> Point2d.fromCoordinates ( 2, 2 )

arc3 =
    Arc2d.from p1 p2 (degrees 180)

Arc2d.centerPoint arc3
--> Point2d.fromCoordinates ( 1.5, 1.5 )

arc4 =
    Arc2d.from p1 p2 (degrees -180)

Arc2d.centerPoint arc4
--> Point2d.fromCoordinates ( 1.5, 1.5 )

arc5 =
    Arc2d.from p1 p2 (degrees 45)

Arc2d.centerPoint arc5
--> Point2d.fromCoordinates ( 0.2929, 0.2929 )

with : { centerPoint : Point2d, radius : Basics.Float, startAngle : Basics.Float, sweptAngle : Basics.Float } -> Arc2d

Construct an arc with the given center point, radius, start angle and swept angle:

arc =
    Arc2d.with
        { centerPoint =
            Point2d.fromCoordinates ( 2, 0 )
        , radius = 1
        , startAngle = degrees 45
        , sweptAngle = degrees -90
        }

Arc2d.startPoint arc
--> Point2d.fromCoordinates ( 2.7071, 0.7071 )

Arc2d.endPoint arc
--> Point2d.fromCoordinates ( 2.7071, -0.7071 )

sweptAround : Point2d -> Basics.Float -> Point2d -> Arc2d

Construct an arc by sweeping (rotating) a given start point around a given center point by a given angle. The center point to sweep around is given first and the start point to be swept is given last.

exampleArc =
    Point2d.fromCoordinates ( 3, 1 )
        |> Arc2d.sweptAround
            (Point2d.fromCoordinates ( 1, 1 ))
            (degrees 90)

Arc2d.endPoint exampleArc
--> Point2d.fromCoordinates ( 1, 3 )

Note that the 'actual' form of this function is

arc =
    Arc2d.sweptAround centerPoint sweptAngle startPoint

but it is generally written using the pipe operator |> (as in the first example) to improve readability:

arc =
    startPoint
        |> Arc2d.sweptAround centerPoint sweptAngle

A positive swept angle means that the arc is formed by rotating the start point counterclockwise around the center point. A negative swept angle results in a clockwise arc instead.

throughPoints : Point2d -> Point2d -> Point2d -> Maybe Arc2d

Attempt to construct an arc that starts at the first given point, passes through the second given point and ends at the third given point:

Arc2d.throughPoints
    Point2d.origin
    (Point2d.fromCoordinates ( 1, 0 ))
    (Point2d.fromCoordinates ( 0, 1 ))
--> Just
-->     (Point2d.origin
-->         |> Arc2d.sweptAround
-->             (Point2d.fromCoordinates ( 0.5, 0.5 ))
-->             (degrees 270)
-->     )

Arc2d.throughPoints
    (Point2d.fromCoordinates ( 1, 0 ))
    Point2d.origin
    (Point2d.fromCoordinates ( 0, 1 ))
--> Just
-->     (Point2d.fromCoordinates ( 1, 0 )
-->         |> Arc2d.sweptAround
-->             (Point2d.fromCoordinates ( 0.5, 0.5 ))
-->             (degrees -180)
-->     )

If the three points are collinear, returns Nothing:

Arc2d.throughPoints
    Point2d.origin
    (Point2d.fromCoordinates ( 1, 0 ))
    (Point2d.fromCoordinates ( 2, 0 ))
--> Nothing

Arc2d.throughPoints
    Point2d.origin
    Point2d.origin
    (Point2d.fromCoordinates ( 1, 0 ))
--> Nothing

withRadius : Basics.Float -> Arc.SweptAngle.SweptAngle -> Point2d -> Point2d -> Maybe Arc2d

Attempt to construct an arc with the given radius between the given start and end points. Note that this is only possible if the given radius is large enough! For any given valid radius, start point and end point, there are four possible results, so the SweptAngle argument is used to specify which arc to create. For example:

p1 =
    Point2d.fromCoordinates ( 1, 0 )

p2 =
    Point2d.fromCoordinates ( 0, 1 )

Arc2d.withRadius 1 SweptAngle.smallPositive p1 p2
--> Just
-->     (Point2d.fromCoordinates ( 1, 0 )
-->         |> Arc2d.sweptAround Point2d.origin
-->             (degrees 90)
-->     )

Arc2d.withRadius 1 SweptAngle.smallNegative p1 p2
--> Just
-->     (Point2d.fromCoordinates ( 1, 0 )
-->         |> Arc2d.sweptAround
-->             (Point2d.fromCoordinates ( 1, 1 ))
-->             (degrees -90)
-->     )

Arc2d.withRadius 1 SweptAngle.largePositive p1 p2
--> Just
-->     (Point2d.fromCoordinates ( 1, 0 )
-->         |> Arc2d.sweptAround
-->             (Point2d.fromCoordinates ( 1, 1 ))
-->             (degrees 270)
-->     )

Arc2d.withRadius 1 SweptAngle.largeNegative p1 p2
--> Just
-->     (Point2d.fromCoordinates ( 1, 0 )
-->         |> Arc2d.sweptAround Point2d.origin
-->             (degrees -270)
-->     )

Arc2d.withRadius 2 SweptAngle.smallPositive p1 p2
--> Just
-->     (Point2d.fromCoordinates ( 1, 0 )
-->         |> Arc2d.sweptAround
-->             (Point2d.fromCoordinates
-->                 ( -0.8229, -0.8229 )
-->             )
-->             (degrees 41.4096)
-->     )

If the start and end points are coincident or the distance between them is more than twice the given radius, returns Nothing:

-- p1 and p2 are too far apart to be connected by an
-- arc of radius 0.5
Arc2d.withRadius 0.5 SweptAngle.smallPositive p1 p2
--> Nothing

Note that this means it is dangerous to use this function to construct 180 degree arcs (half circles), since in this case due to numerical roundoff the distance between the two given points may appear to be slightly more than twice the given radius. In this case it is safer to use Arc2d.from, such as (for a counterclockwise arc):

halfCircle =
    Arc2d.from firstPoint secondPoint (degrees 180)

(Use degrees -180 for a clockwise arc.)

Properties

centerPoint : Arc2d -> Point2d

Get the center point of an arc.

Arc2d.centerPoint exampleArc
--> Point2d.fromCoordinates ( 1, 1 )

radius : Arc2d -> Basics.Float

Get the radius of an arc.

Arc2d.radius exampleArc
--> 2

startPoint : Arc2d -> Point2d

Get the start point of an arc.

Arc2d.startPoint exampleArc
--> Point2d.fromCoordinates ( 3, 1 )

endPoint : Arc2d -> Point2d

Get the end point of an arc.

Arc2d.endPoint exampleArc
--> Point2d.fromCoordinates ( 1, 3 )

sweptAngle : Arc2d -> Basics.Float

Get the swept angle of an arc in radians.

Arc2d.sweptAngle exampleArc
--> 1.5708

The result will be positive for a counterclockwise arc and negative for a clockwise one.

Evaluation

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

Get the point along an arc at a given parameter value:

Arc2d.pointOn exampleArc ParameterValue.zero
--> Point2d.fromCoordinates ( 3, 1 )

Arc2d.pointOn exampleArc ParameterValue.half
--> Point2d.fromCoordinates ( 2.4142, 2.4142 )

Arc2d.pointOn exampleArc ParameterValue.one
--> Point2d.fromCoordinates ( 1, 3 )

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

Get points along an arc at a given set of parameter values:

exampleArc |> Arc2d.pointsAt (ParameterValue.steps 2)
--> [ Point2d.fromCoordinates ( 3, 1 )
--> , Point2d.fromCoordinates ( 2.4142, 2.4142 )
--> , Point2d.fromCoordinates ( 1, 3 )
--> ]


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 Arc2d.tangentDirection and can be constructed using Arc2d.nondegenerate.

nondegenerate : Arc2d -> Result Point2d Nondegenerate

Attempt to construct a nondegenerate arc from a general Arc2d. If the arc is in fact degenerate (consists of a single point), returns an Err with that point.

Arc2d.nondegenerate exampleArc
--> Ok nondegenerateExampleArc

fromNondegenerate : Nondegenerate -> Arc2d

Convert a nondegenerate arc back to a general Arc2d.

Arc2d.fromNondegenerate nondegenerateExampleArc
--> exampleArc

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

Get the tangent direction to a nondegenerate arc at a given parameter value:

Arc2d.tangentDirection nondegenerateExampleArc
    ParameterValue.zero
--> Direction2d.fromAngle (degrees 90)

Arc2d.tangentDirection nondegenerateExampleArc
    ParameterValue.half
--> Direction2d.fromAngle (degrees 135)

Arc2d.tangentDirection nondegenerateExampleArc
    ParameterValue.one
--> Direction2d.fromAngle (degrees 180)

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

Get tangent directions to a nondegenerate arc at a given set of parameter values:

nondegenerateExampleArc
    |> Arc2d.tangentDirectionsAt
        (ParameterValue.steps 2)
--> [ Direction2d.fromAngle (degrees 90)
--> , Direction2d.fromAngle (degrees 135)
--> , Direction2d.fromAngle (degrees 180)
--> ]

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

Get both the point and tangent direction of a nondegenerate arc at a given parameter value:

Arc2d.sample nondegenerateExampleArc
    ParameterValue.zero
--> ( Point2d.fromCoordinates ( 3, 1 )
--> , Direction2d.fromAngle (degrees 90)
--> )

Arc2d.sample nondegenerateExampleArc
    ParameterValue.half
--> ( Point2d.fromCoordinates ( 2.4142, 2.4142 )
--> , Direction2d.fromAngle (degrees 135)
--> )

Arc2d.sample nondegenerateExampleArc
    ParameterValue.one
--> ( Point2d.fromCoordinates ( 1, 3 )
--> , 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
    |> Arc2d.samplesAt (ParameterValue.steps 2)
--> [ ( Point2d.fromCoordinates ( 3, 1 )
-->   , Direction2d.fromAngle (degrees 90)
-->   )
--> , ( Point2d.fromCoordinates ( 2.4142, 2.4142 )
-->   , Direction2d.fromAngle (degrees 135)
-->   )
--> , ( Point2d.fromCoordinates ( 1, 3 )
-->   , Direction2d.fromAngle (degrees 180)
-->   )
--> ]

Linear approximation

toPolyline : { maxError : Basics.Float } -> Arc2d -> Polyline2d

Approximate an arc as a polyline, within a given tolerance:

exampleArc |> Arc2d.toPolyline { maxError = 0.1 }
--> Polyline2d.fromVertices
-->     [ Point2d.fromCoordinates ( 3, 1 )
-->     , Point2d.fromCoordinates ( 2.732, 2 )
-->     , Point2d.fromCoordinates ( 2, 2.732 )
-->     , Point2d.fromCoordinates ( 1, 3 )
-->     ]

In this example, every point on the returned polyline will be within 0.1 units of the original arc.

Transformations

reverse : Arc2d -> Arc2d

Reverse the direction of an arc, so that the start point becomes the end point and vice versa.

Arc2d.reverse exampleArc
--> Point2d.fromCoordinates ( 1, 3 )
-->     |> Arc2d.sweptAround
-->         (Point2d.fromCoordinates ( 1, 1 ))
-->         (degrees -90)

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

Scale an arc about a given point by a given scale.

point =
    Point2d.fromCoordinates ( 0, 1 )

Arc2d.scaleAbout point 2 exampleArc
--> Point2d.fromCoordinates ( 6, 1 )
-->     |> Arc2d.sweptAround
-->         (Point2d.fromCoordinates ( 2, 1 ))
-->         (degrees 90)

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

Rotate an arc around a given point by a given angle.

Arc2d.rotateAround Point2d.origin (degrees 90)
--> Point2d.fromCoordinates ( -1, 3 )
-->     |> Arc2d.sweptAround
-->         (Point2d.fromCoordinates ( -1, 1 ))
-->         (degrees 90)

translateBy : Vector2d -> Arc2d -> Arc2d

Translate an arc by a given displacement.

displacement =
    Vector2d.fromComponents ( 2, 3 )

Arc2d.translateBy displacement exampleArc
--> Point2d.fromCoordinates ( 5, 4 )
-->     |> Arc2d.sweptAround
-->         (Point2d.fromCoordinates ( 3, 4 ))
-->         (degrees 90)

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

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

Arc2d.translateIn direction distance

is equivalent to

Arc2d.translateBy
    (Vector2d.withLength distance direction)

mirrorAcross : Axis2d -> Arc2d -> Arc2d

Mirror an arc across a given axis.

Arc2d.mirrorAcross Axis2d.y exampleArc
--> Point2d.fromCoordinates ( -3, 1 )
-->     |> Arc2d.sweptAround
-->         (Point2d.fromCoordinates ( -1, 1 ))
-->         (degrees -90)

Coordinate conversions

relativeTo : Frame2d -> Arc2d -> Arc2d

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

Arc2d.relativeTo localFrame exampleArc
--> Point2d.fromCoordinates ( 2, -1 )
-->     |> Arc2d.sweptAround
-->         (Point2d.fromCoordinates ( 0, -1 ))
-->         (degrees 90)

placeIn : Frame2d -> Arc2d -> Arc2d

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

Arc2d.placeIn localFrame exampleArc
--> Point2d.fromCoordinates ( 4, 3 )
-->     |> Arc2d.sweptAround
-->         (Point2d.fromCoordinates ( 2, 3 ))
-->         (degrees 90)

Differentiation

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

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

Get the first derivative of an arc at a given parameter value:

Arc2d.firstDerivative exampleArc ParameterValue.zero
--> Vector2d.fromComponents ( 0, 3.1416 )

Arc2d.firstDerivative exampleArc ParameterValue.half
--> Vector2d.fromComponents ( -2.2214, 2.2214 )

Arc2d.firstDerivative exampleArc ParameterValue.one
--> Vector2d.fromComponents ( -3.1416, 0 )

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

Evaluate the first derivative of an arc at a given set of parameter values:

exampleArc
    |> Arc2d.firstDerivativesAt
        (ParameterValue.steps 2)
--> [ Vector2d.fromComponents ( 0, 3.1416 )
--> , Vector2d.fromComponents ( -2.2214, 2.2214 )
--> , Vector2d.fromComponents ( -3.1416, 0 )
--> ]