Backend-agnostic path command representation.
Stores drawing commands as tuples that can be converted to any output format by backends (SVG, Canvas, etc.).
Command Types
- Movement:
{:M, x, y},{:m, dx, dy}(absolute/relative move) - Lines:
{:L, x, y},{:l, dx, dy},{:H, x},{:V, y} - Curves:
{:C, x1, y1, x2, y2, x, y}(cubic bezier) - Arcs:
{:A, rx, ry, rotation, large_arc, sweep, x, y} - Close:
:Z
Examples
iex> alias Visualize.IR.Path
iex> Path.new()
...> |> Path.move_to(10, 20)
...> |> Path.line_to(100, 200)
...> |> Path.close()
%Visualize.IR.Path{commands: [{:M, 10, 20}, {:L, 100, 200}, :Z]}
Summary
Functions
Rewrites every command as an absolute M, L, C, Q, A or Z.
Appends commands from another path.
Arc to absolute position.
Arc to relative position.
Close the path.
Returns the number of commands in the path.
Combines multiple paths into one.
Cubic Bezier curve to absolute position.
Cubic Bezier curve to relative position.
Returns whether the path is empty (has no commands).
Rewrites the smooth curve commands S/s/T/t as C/c/Q/q.
The number formatter every serialiser shares (D-12, D-32): an integer verbatim; a float
with at most four decimals, trailing zeros and point trimmed, never in exponent notation,
and never -0.
A path of the commands given, in the order given (spec/02 §2.2, #414).
Horizontal line to absolute x.
Horizontal line to relative x.
Line to absolute position.
Line to relative position.
Move to absolute position.
Move to relative position.
Creates a new empty path.
Quadratic Bezier curve to absolute position.
Quadratic Bezier curve to relative position.
Smooth cubic Bezier curve to absolute position.
Smooth cubic Bezier curve to relative position.
Smooth quadratic Bezier curve to absolute position.
Smooth quadratic Bezier curve to relative position.
Converts the path to an SVG path data string.
Applies an affine transform to every coordinate of the path.
Vertical line to absolute y.
Vertical line to relative y.
Types
@type command() :: {:M, number(), number()} | {:m, number(), number()} | {:L, number(), number()} | {:l, number(), number()} | {:H, number()} | {:h, number()} | {:V, number()} | {:v, number()} | {:C, number(), number(), number(), number(), number(), number()} | {:c, number(), number(), number(), number(), number(), number()} | {:S, number(), number(), number(), number()} | {:s, number(), number(), number(), number()} | {:Q, number(), number(), number(), number()} | {:q, number(), number(), number(), number()} | {:T, number(), number()} | {:t, number(), number()} | {:A, number(), number(), number(), integer(), integer(), number(), number()} | {:a, number(), number(), number(), integer(), integer(), number(), number()} | :Z
Functions
Rewrites every command as an absolute M, L, C, Q, A or Z.
Relative commands are resolved against the current point (a leading m is absolute, as
SVG reads it), H/V become L, and the smooth commands are expanded as in
expand_smooth/1. An arc keeps its radii, rotation and flags. The result draws the same
figure and is what a target without relative commands, such as Canvas, replays.
Examples
iex> alias Visualize.IR.Path
iex> Path.new() |> Path.move_to(1, 1) |> Path.line_to_rel(2, 0) |> Path.vertical_to(5)
...> |> Path.close() |> Path.absolute()
%Visualize.IR.Path{commands: [{:M, 1, 1}, {:L, 3, 1}, {:L, 3, 5}, :Z]}
Appends commands from another path.
Arc to absolute position.
rx,ry: radii of the ellipsex_rotation: rotation of the ellipse in degreeslarge_arc: 0 or 1, whether to use the larger arcsweep: 0 or 1, direction of the arcx,y: end point
@spec arc_to_rel( t(), number(), number(), number(), integer(), integer(), number(), number() ) :: t()
Arc to relative position.
Close the path.
@spec command_count(t()) :: non_neg_integer()
Returns the number of commands in the path.
Combines multiple paths into one.
Cubic Bezier curve to absolute position.
Cubic Bezier curve to relative position.
Returns whether the path is empty (has no commands).
Rewrites the smooth curve commands S/s/T/t as C/c/Q/q.
The first control point is the reflection of the previous command's last control point
about the current point when that command was a cubic (for S) or a quadratic (for
T), and the current point itself otherwise — SVG's rule. Every other command is kept as
it is, so relative commands stay relative.
Examples
iex> alias Visualize.IR.Path
iex> Path.new() |> Path.move_to(0, 0) |> Path.curve_to(1, 1, 2, 1, 3, 0)
...> |> Path.smooth_curve_to(5, -1, 6, 0) |> Path.expand_smooth()
%Visualize.IR.Path{commands: [{:M, 0, 0}, {:C, 1, 1, 2, 1, 3, 0}, {:C, 4, -1, 5, -1, 6, 0}]}
The number formatter every serialiser shares (D-12, D-32): an integer verbatim; a float
with at most four decimals, trailing zeros and point trimmed, never in exponent notation,
and never -0.
Examples
iex> Visualize.IR.Path.format_number(1.5)
"1.5"
iex> Visualize.IR.Path.format_number(1.0e20)
"100000000000000000000"
iex> Visualize.IR.Path.format_number(-0.00001)
"0"
A path of the commands given, in the order given (spec/02 §2.2, #414).
What a builder drawing one command per datum ends with: the appenders put a command at the end of a list, which costs the list, so appending n commands one at a time costs a square of n — a 50,000-point line took six seconds to build and sixteen milliseconds to encode. A builder collects its commands and makes the path once.
iex> Visualize.IR.Path.from_commands([{:M, 0, 0}, {:L, 10, 5}])
%Visualize.IR.Path{commands: [{:M, 0, 0}, {:L, 10, 5}]}
Horizontal line to absolute x.
Horizontal line to relative x.
Line to absolute position.
Line to relative position.
Move to absolute position.
Move to relative position.
@spec new() :: t()
Creates a new empty path.
Quadratic Bezier curve to absolute position.
Quadratic Bezier curve to relative position.
Smooth cubic Bezier curve to absolute position.
Smooth cubic Bezier curve to relative position.
Smooth quadratic Bezier curve to absolute position.
Smooth quadratic Bezier curve to relative position.
Converts the path to an SVG path data string.
Examples
iex> alias Visualize.IR.Path
iex> Path.new() |> Path.move_to(10, 20) |> Path.line_to(100, 200) |> Path.to_string()
"M10,20L100,200"
@spec transform(t(), Visualize.IR.Transform.matrix() | Visualize.IR.Transform.t()) :: t()
Applies an affine transform to every coordinate of the path.
The transform is a matrix {a, b, c, d, e, f} (Visualize.IR.Transform.matrix/0) or a
Visualize.IR.Transform, which is folded with Visualize.IR.Transform.to_matrix/1.
Absolute end and control points get the full affine map; relative deltas get its linear
part only. H/V/h/v are resolved against the current point and stay single-axis
when the transform keeps that axis (no rotation or skew into it), otherwise they become
L/l. An arc's end point is mapped, its radii and rotation are those of the exact image
ellipse, and its sweep flag flips under a reflection. :Z is untouched.
Examples
iex> alias Visualize.IR.Path
iex> Path.new() |> Path.move_to(1, 2) |> Path.line_to_rel(3, 4) |> Path.horizontal_to(9)
...> |> Path.transform({1, 0, 0, 1, 10, 20})
%Visualize.IR.Path{commands: [{:M, 11, 22}, {:l, 3, 4}, {:H, 19}]}
Vertical line to absolute y.
Vertical line to relative y.