An alternative view on paths that is convenient for mathematical operations.
When we look at a path as a list of elemental Segment
s, it becomes easier to reason about it.
The segment data type has four segment types:
line
straigt line segmentquadratic
a quadratic bezier curve segmentcubic
a cubic bezier curve segmentarc
an elliptical arc segmentAll four of these are mathematically well-defined primitives. We can uniformly apply functions like:
angle
between two segmentsderivative
or curvaturereverse
reverse a segment - this can be used to let the browser fill your svg correctlySegment
can also be ArcLengthParameterized
, which makes operations based on arc length possible.
For instance, the total arc length or the location after walking some distance over the segment.
These operations are backed by the great OpenSolid package, and in turn back many of the operations
in SubPath
.
The four types of segments.
line : ( Basics.Float, Basics.Float ) -> ( Basics.Float, Basics.Float ) -> Segment coordinates
Make a line segment
quadratic : ( Basics.Float, Basics.Float ) -> ( Basics.Float, Basics.Float ) -> ( Basics.Float, Basics.Float ) -> Segment coordinates
Make a quadratic bezier segment
cubic : ( Basics.Float, Basics.Float ) -> ( Basics.Float, Basics.Float ) -> ( Basics.Float, Basics.Float ) -> ( Basics.Float, Basics.Float ) -> Segment coordinates
Make a cubic bezier segment
ellipticalArc : ( Basics.Float, Basics.Float ) -> Path.LowLevel.EllipticalArcArgument -> Segment coordinates
Make an elliptic arc segment
at : Basics.Float -> Segment coordinates -> ( Basics.Float, Basics.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 coordinates -> Segment coordinates -> Basics.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 : Basics.Float -> Segment coordinates -> ( Basics.Float, Basics.Float )
Get the derivative at a point on the curve, only defined in the range [0, 1].
import Vector2
import LowLevel.Command exposing
( EllipticalArcArgument
, smallestArc
, largestArc
, clockwise
)
derivativeAt 0.5 (line (0,0) (1,1))
|> Vector2.normalize
--> Vector2.normalize (1,1)
argument : EllipticalArcArgument
argument =
{ target = ( 5, 5 )
, radii = ( 5, 5 )
, xAxisRotate = 0
, arcFlag = smallestArc
, direction = clockwise
}
derivativeAt 0.5 (arc (0,0) argument)
|> Vector2.normalize
--> Vector2.normalize (1,1)
derivativeAtFirst : Segment coordinates -> ( Basics.Float, Basics.Float )
The derivative at the starting point of the segment
derivativeAtFinal : Segment coordinates -> ( Basics.Float, Basics.Float )
The derivative at the ending point of the segment
firstPoint : Segment coordinates -> ( Basics.Float, Basics.Float )
Extract the first point from a segment
finalPoint : Segment coordinates -> ( Basics.Float, Basics.Float )
Extract the final point from a segment
reverse : Segment coordinates -> Segment coordinates
Reverse a line segment
Opaque type for the arc length parameterization of a segment
arcLengthParameterized : Basics.Float -> Segment coordinates -> Maybe (ArcLengthParameterized coordinates)
arcLength : ArcLengthParameterized coordinates -> Basics.Float
pointAlong : ArcLengthParameterized coordinates -> Basics.Float -> ( Basics.Float, Basics.Float )
tangentAlong : ArcLengthParameterized coordinates -> Basics.Float -> Maybe ( Basics.Float, Basics.Float )
parameterValueToArcLength : ArcLengthParameterized coordinates -> Basics.Float -> Basics.Float
arcLengthToParameterValue : ArcLengthParameterized coordinates -> Basics.Float -> Basics.Float
toDrawTo : Segment coordinates -> LowLevel.Command.DrawTo
Convert a segment to a drawto instruction. forgets the starting point.
toSegment : LowLevel.Command.CursorState -> LowLevel.Command.DrawTo -> List (Segment coordinates)
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, clockwise, largestArc)
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 (0,0)
{ target = (10, 0)
, radii = (5,5)
, xAxisRotate = 90
, arcFlag = largestArc
, direction = clockwise
}
]
toSegment start drawto --> expected
toCursorState : Segment coordinates -> LowLevel.Command.CursorState
Convert a Segment
to a CursorState
toCursorState (line (0,0) (10, 10))
--> { start = (0,0) , cursor = (10, 10) , previousControlPoint = Nothing }