folkertdev/one-true-path-experiment/1.0.0

NOTE: this package is not compatible with Elm 0.19.1

Path

Low-level module for constructing svg paths.

This module provides a wrapper around the svg path syntax. A path can be parsed from a string or build up using the svg path primitives, and then converted to a string that can be used in the d attribute to render the path.

Note that this is not the most convenient way of drawing. This package is mainly meant as a primitive to build other packages on top of. If you want to visualize data, have a look at elm-plot and elm-visualization. If you want to draw geometry, check out opensolid.

For more information on svg paths, see the MDN documentation.

Data Layout

A path is a list of subpaths that are drawn in order. A subpath consists of a MoveTo instruction followed by a list of DrawTo instructions.

This package only supports absolute coordinates and instructions, but it is possible to parse strings with relative intructions. When a path is parsed, the first MoveTo instruction is always interpreted as absolute (this is in accordance with the spec), thus making sure that there is always an absolute cursor position.

The constructors are exposed, so if you need an escape hatch it is available. As always though, never reach for it when there are other options.

Example

myPoints : List (Float, Float)

-- connect all the points with a straight line
linear : List Coordinate -> Path
linear points =
    case points of
        [] ->
            []

        p::ps ->
            [ subpath (moveTo p) [ lineTo ps ] ]


main =
    Svg.svg [ width "400", height "400" ]
        [ Path.svgPath (linear myPoints) [ fill "none" ]
        ]

Data Structures

type alias Coordinate =
( Float, Float )

A 2-tuple of floats representing a position in space

type alias Path =
List SubPath

A path is a list of SubPaths.

type alias SubPath =
{ moveto : MoveTo
, drawtos : List DrawTo
}

A subpath consists of a MoveTo instruction followed by a list of DrawTo instructions

Constructing Paths

subpath : MoveTo -> List DrawTo -> SubPath

Construct a subpath

subpath (moveTo (10,0)) [ lineTo [ (42, 73) ] ]
parse : String -> Result Error Path

Parse a path string into a Path

parse "M0,0 l42,73"
    --> Ok [{ moveto = MoveTo Absolute (0,0), drawtos = [ LineTo Relative  [(42, 73)]]}]

Only accepts valid complete subpaths (a sequences of a move followed by zero or more draws). Relative instructions are converted to absolute ones. The types and constructors in the output are described here.

The parser uses elm-tools/parser. The error type is Parser.Error.

Creating SVG

svgPath : Path -> List (Attribute msg) -> Svg msg

Construct an svg path element from a Path with the given attributes

toString : Path -> String

Turn a MixedPath into a String. The result is ready to be used with the d attribute.

Path.toString [ subpath (moveTo (0,0)) [ lineBy ( 42, 73 ) ] ]
    --> "M0,0 l42,73"

Modifying Paths

mapCoordinate : (Coordinate -> Coordinate) -> Path -> Path

Manipulate the coordinates in your SVG. This can be useful for scaling the svg.

-- make the image twice as big in the x direction
[ subpath (moveTo (10,0)) [ lineTo [ (42, 42) ] ] ]
    |> mapCoordinate (\(x,y) -> (2 * x, y))
         --> [ subpath (moveTo (20,0)) [ lineTo [ (84, 42) ] ] ]
type alias CursorState =
{ start : Coordinate
, cursor : Coordinate
}

Contains the start of the current subpath and the current cursor position.

mapWithCursorState : (CursorState -> DrawTo -> b) -> Path -> List b

Map over the DrawTos in a path with access to their starting point.

Many mathematical operations (length, derivative, curvature) are only possible when a segment is fully specified. A DrawTo on its own misses its starting point - the current cursor position. This function makes the cursor position and the start of the current subpath available when mapping.

updateCursorState : DrawTo -> CursorState -> CursorState

Given a cursor state, simulate the effect that a DrawTo has on that cursor state

Moving the cursor

type MoveTo
= MoveTo Coordinate

Constructors for MoveTo instructions

moveTo : Coordinate -> MoveTo

Move to a position on the canvas without drawing.

Drawing on the canvas

type DrawTo
= LineTo (List Coordinate)
| Horizontal (List Float)
| Vertical (List Float)
| CurveTo (List ( Coordinate, Coordinate, Coordinate ))
| SmoothCurveTo (List ( Coordinate, Coordinate ))
| QuadraticBezierCurveTo (List ( Coordinate, Coordinate ))
| SmoothQuadraticBezierCurveTo (List Coordinate)
| EllipticalArc (List EllipticalArcArgument)
| ClosePath

Constructors for DrawTo instructions

Straight lines

lineTo : List Coordinate -> DrawTo

Draw a series of line segments to absolute positions. The L instruction.

horizontalTo : List Float -> DrawTo

Specific version of lineTo that only moves horizontally. The H instruction.

verticalTo : List Float -> DrawTo

Specific version of lineTo that only moves vertically. The V instruction

Close Path

closePath : DrawTo

Draw a straight line from the cursor position to the starting position of the path. The Z instruction.

Quadratic Beziers

quadraticCurveTo : List ( Coordinate, Coordinate ) -> DrawTo

A quadratic bezier. The Q instruction.

quadraticCurveExtendTo : List Coordinate -> DrawTo

A smooth extension to a quadratic bezier segment. The T instruction.

Cubic Beziers

cubicCurveTo : List ( Coordinate, Coordinate, Coordinate ) -> DrawTo

A cubic bezier. The C instruction.

cubicCurveExtendTo : List ( Coordinate, Coordinate ) -> DrawTo

A smooth extension to a cubic bezier segment. The S instruction.

Arcs

arcTo : List EllipticalArcArgument -> DrawTo

An elliptical arc. The A instruction.

{ radii : ( Float, Float )
, xAxisRotate : Float
, arcFlag : ArcFlag
, direction : Direction
, target : Coordinate
}

The arguments for an Arc

type alias Direction =
Direction

Determine which arc to draw

clockwise : Direction

Corresponds to a sweep flag of 1

counterClockwise : Direction

Corresponds to a sweep flag of 0

type alias ArcFlag =
ArcFlag

Determine which arc to draw

largestArc : ArcFlag

Corresponds to an arc flag of 1

smallestArc : ArcFlag

Corresponds to an arc flag of 0

Internal

Some internal functions that need to be exposed in order to use them in tests.

toAbsoluteMoveTo : CursorState -> MoveTo -> ( MoveTo, CursorState )

Exposed for testing

toAbsoluteDrawTo : CursorState -> DrawTo -> ( DrawTo, CursorState )

Exposed for testing

toMixedDrawTo : DrawTo -> DrawTo

Exposed for testing purposes

toMixedMoveTo : MoveTo -> MoveTo

Exposed for testing purposes