folkertdev/one-true-path-experiment/3.0.0

NOTE: this package is not compatible with Elm 0.19.1

Segment

An alternative view on paths that is convenient for mathematical operations.

When we look at a path as a list of elemental Segments, it becomes easier to reason about it. The segment data type has four segment types:

  • line straigt line segment
  • quadratic a quadratic bezier curve segment
  • cubic a cubic bezier curve segment
  • arc an elliptical arc segment

All four of these are mathematically well-defined, and there is a wide range of operations that we can perform on them:

These operations are backed by the great OpenSolid package, and in turn back many of the operations in SubPath.

type Segment
= LineSegment LineSegment2d
| Quadratic QuadraticSpline2d
| Cubic CubicSpline2d
| Arc EndpointParameterization

The four types of segments.

line : ( Float, Float ) -> ( Float, Float ) -> Segment

Make a line segment

quadratic : ( Float, Float ) -> ( Float, Float ) -> ( Float, Float ) -> Segment

Make a quadratic bezier segment

( Float, Float )
-> ( Float, Float )
-> ( Float, Float )
-> ( Float, Float )
-> Segment

Make a cubic bezier segment

arc : ( Float, Float ) -> EllipticalArcArgument -> Segment

Make an elliptic arc segment

Operations

at : Float -> Segment -> ( Float, Float )

Get the location at a point on the curve, only defined in the range [0, 1].

at 0.5 (line (0,0) (10, 0)) --> ( 5, 0 )


at 0.5 (quadratic (0,0) (5, 10) (10, 0)) --> ( 5, 5 )
angle : Segment -> Segment -> Float

The signed angle (in radians) between the end of segment1 and the start of segment2

a : Segment
a = line ( 0, 0 ) ( 1, 0 )


b : Segment
b = line ( 0, 0 ) ( 0, 1 )


angle a b --> degrees 90


angle b a --> degrees -90
derivativeAt : Float -> Segment -> ( Float, Float )

Get the derivative at a point on the curve, only defined in the range [0, 1].

import LowLevel.Command exposing
    ( EllipticalArcArgument
    , largestArc
    , clockwise
    )


derivativeAt 0.5 (line (0,0) (1,1))
    --> (0.7071067811865475,0.7071067811865475)


argument : EllipticalArcArgument
argument =
    { target = ( 10, 0 )
    , radii = ( 5, 5 )
    , xAxisRotate = 0
    , arcFlag = largestArc
    , direction = clockwise
    }


derivativeAt 0.5 (arc (0,0)  argument) --> ( 1, 0)
derivativeAtFirst : Segment -> Float2

The derivative at the starting point of the segment

derivativeAtFinal : Segment -> Float2

The derivative at the ending point of the segment

firstPoint : Segment -> Vec2 Float

Extract the first point from a segment

finalPoint : Segment -> Vec2 Float

Extract the final point from a segment

reverse : Segment -> Segment

Reverse a line segment

Conversion

toDrawTo : Segment -> DrawTo

Convert a segment to a drawto instruction. forgets the starting point.

toSegment : CursorState -> DrawTo -> List Segment

Convert a drawto into a segment

This function needs the previous segment to the starting point and (for bezier curves) the control points

import LowLevel.Command exposing (DrawTo(EllipticalArc), CursorState)


start : CursorState
start = { start = (0,0), cursor = (0,0), previousControlPoint = Nothing }


drawto : DrawTo
drawto =
        EllipticalArc
            [ { target = (10, 0)
              , radii = (5,5)
              , xAxisRotate = 90
              , arcFlag = largestArc
              , direction = clockwise
              }
            ]


expected : List Segment
expected =
     [ Arc
        { start = (0,0)
        , end = (10, 0)
        , radii = (5,5)
        , xAxisRotate = pi / 2
        , arcFlag = largestArc
        , direction = clockwise
        }
      ]


toSegment start drawto --> expected
toCursorState : Segment -> CursorState

Convert a Segment to a CursorState

toCursorState (line (0,0) (10, 10))
    --> { start = (0,0) , cursor = (10, 10) , previousControlPoint = Nothing }