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 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.
Make a quadratic bezier segment
Make a cubic bezier segment
Make an elliptic arc segment
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 )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 -90Get 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)The derivative at the starting point of the segment
The derivative at the ending point of the segment
Extract the first point from a segment
Extract the final point from a segment
Opaque type for the arc length parameterization of a segment
Convert a segment to a drawto instruction. forgets the starting point.
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 --> expectedConvert a Segment to a CursorState
toCursorState (line (0,0) (10, 10))
--> { start = (0,0) , cursor = (10, 10) , previousControlPoint = Nothing }
The four types of segments.