View Source Scenic.Script (Scenic v0.11.2)
overview
Overview
Scenic.Script is the fundamental "rendering" data structure for Scenic.
Prior to v0.11, drivers received Graphs from the ViewPort and were responsible for transforming them into a list of drawing commands that would ultimately draw the actual picture that is displayed as the Scenic UI.
After some experience, it became apparent that this was a common task for almost all drivers and has been moved into the ViewPort and formalized in Scenic.Script.
Simply put, Scenic.Script produces a list of static drawing commands that are
sent, unmodified, to the drivers through the ViewPort. These scripts are
generated for you when you use Scenic.Scene.push_graph/3
, or you can create
them yourself by using this api.
If you use Scenic.Script directly, you can create more complicated and/or reusable graphics than you could by using Graphs alone.
update-isolation
Update Isolation
Another way to use scripts is to separate large static bits of a graph from that which changes frequently. When a graph is pushed to the ViewPort, the entire thing is compiled into a script, which is sent as a whole to the Drivers.
However if that graph references a script, only the reference to the script is sent, not the contents of the script. This means you can modify the graph frequently without sending the contents of the scripts.
The opposite is also true. You can generate and push the scripts frequently without causing the graphs that reference them to be recompiled or resent.
An example of this might be a graph that implements a chart of some sort. The contents of the chart could be a script that is updated as new data comes in without needing all the chrome around it to be recompiled or sent for every update.
api-patterns
API Patterns
This is a large module with many functions. Most of them are very small, however and simply add individual commands to the script.
The general pattern is that you start by calling Scenic.Script.start(). This simply returns an empty list. Each drawing api adds an element to the head of the list. Then you end with Scenic.Script.finsh(), which reverses the list and performs some optimizations on it.
alias Scenic.Script
my_script =
Script.start()
|> Script.text( "A very simple script" )
|> Script.finish()
After the script is generated, you publish it to the ViewPort through either your
scene's helper api Scenic.Scene.push_script/4
or directly throught the ViewPort api
Scenic.ViewPort.put_script/4
Finally, to get the script to draw, you need to reference it from a graph.
my_graph =
Graph.build()
|> script( "my_script_name" )
It doesn't matter if you push the script before or after you push the graph that references it.
scene =
scene
|> push_script( my_script, "my_script_name" )
|> push_graph( my_graph )
example-the-checkbox-check-mark
Example: the Checkbox check mark
The canonical example is the checkmark symbol used in the Checkbox
component.
This is two lines, with rounded endpoints and rounded joint between them.
It could be described using a Path primitive, but there is no need to
send its instructions every time it is shown or hidden since its shape
doesn't actually change.
The Checkbox component creates and publishes this script using something very similar to the following example.
alias Scenic.Script
# build the checkmark script
chx_script =
Script.start()
|> Script.push_state()
|> Script.join(:round)
|> Script.stroke_width(@border_width + 1)
|> Script.stroke_color(theme.thumb)
|> Script.begin_path()
|> Script.move_to(0, 8)
|> Script.line_to(5, 13)
|> Script.line_to(12, 1)
|> Script.stroke_path()
|> Script.pop_state()
|> Script.finish()
chx_id = scene.id <> "_chk"
scene = push_script(scene, chx_script, chx_id)
Building and using a custom script happens in three parts. First, the script itself
is created using the Scenic.Script
api.
Then the script is published to the ViewPort using Scenic.Scene.push_script/4
with a
unique name.
Later, the graph for the checkbox references this script, which is what triggers it to be drawn.
graph =
Graph.build()
|> script(chx_id, id: :chx, hidden: !checked?)
script-state
Script State
The underlying rendering engine that consumes these scripts and draws the actual
pictures maintains a set of current drawing state, which can be pushed and popped
from a stack via the push_state/1
, pop_state/1
, and pop_push_state/1
apis.
These functions are analogous to the state save/restore APIs in Canvas.
transforms
Transforms
Transforms are applied to the running script by multiplying matrices together. This is very similar to how games position elements of UI. It takes a little getting used to, but is very powerful.
If you want to unroll the most recent applied transform, you should push then pop the state to get back to the previous transform stack.
binary-format
Binary Format
The serialize/3
and deserialize/1
go back and forth from the list format to a binary
representation of the script. The serialize/3
function produces an IO list, and deserialize
goes back into a list of instructions.
There is a standard format to the binary, but that needs to be independently documented. You
can dig into the code behind serialize/3
to see it in action.
Drivers, which normally do the serialization call, can intercept/override any command if they need something specific.
Link to this section Summary
Functions
Add an arc segment to the path using the control points and radius.
Begin a new path.
Add a bezier curve segment to the path using the control points.
Set the current end cap style.
Add a circle to the current path.
Erase the entire drawing field output.
Close the current path.
Transform a binary or io list into a readable script list.
Draw an arc defined by radius and an angle. Can be filled or stroked.
Draw a circle defined by a radius. Can be filled or stroked.
Draw an ellipse defined by two radii. Can be filled or stroked.
draw_flag is a helper function to choose the appropriate fill and/or stroke flag given a map of styles.
Draw a line from a start point to a finish point. Can only be stroked.
Draw a quad defined by four points. Can be filled or stroked.
Draw a rectangle defined by height and width. Can be filled or stroked.
Draw a rounded rectangle defined by height, width, and radius. Can be filled or stroked.
Draw a sector defined by radius and an angle. Can be filled or stroked.
Draw a collection of sprites.
Draw a a block of text. Can be filled.
Draw a a block of text with automatic new lines. Can be filled.
Draw a triangle defined by three points. Can be filled or stroked.
Add an ellipse to the current path.
Set the current fill to an single color.
Set the current fill to an image from the Static asset library.
Set the current fill to linear gradient that goes between two colors.
Fill the current path with the currently selected fill paint.
Set the current fill to radial gradient that goes between two colors.
Set the current fill to an image from an asset stream.
Finish a script, preparing it to be sent to the ViewPort.
Set the current font. Must be a valid reference into your static assets library.
Set the current font size.
Set the current line joint style.
Add a new line segment from the current position to a specified location.
Extract a map of all the media (both static assets and streams) used in a script.
Set the current miter limit for joints.
Move the current draw position without adding a line segment.
Reverts the style/transform state of the script then immediately pushes it again.
Reverts the style/transform state of the script to the most recently pushed state.
Saves the current style/transform state of the script as it is running.
Add a quad to the current path.
Add a quadratic curve segment to the path using the control points.
Add a rectangle to the current path.
Recursively draw a script by reference.
Apply a rotation transform to the current transform stack.
Add a rounded rectangle to the current path.
Apply a scale transform to the current transform stack.
Set the current scissor rect.
Add a sector to the current path.
Transform a script list into a binary IO list.
Transform a script list into a binary IO list with a map like interceptor function.
Transform a script list into a binary IO list with a map_reduce like interceptor function.
Create a new Script.
Set the current stroke to an single color.
Set the current stroke to an image from the Static asset library.
Set the current stroke to linear gradient that goes between two colors.
Stroke the current path with the currently selected stroke width/paint.
Set the current stroke to radial gradient that goes between two colors.
Set the current stroke to an image from an asset stream.
Set the current stroke width.
Set the current horizontal text alignment.
Set the current vertical text alignment.
Apply an arbitrary transform to the current transform stack.
Apply a translation transform to the current transform stack.
Add a triangle to the current path.
Link to this section Types
@type fill_stroke() :: :fill | :stroke | :fill_stroke
@type script_op() :: :push_state | :pop_state | :pop_push_state | {:clear, color :: Scenic.Color.t()} | {:draw_line, {x0 :: number(), y0 :: number(), x1 :: number(), y1 :: number(), :stroke}} | {:draw_quad, {x0 :: number(), y0 :: number(), x1 :: number(), y1 :: number(), x2 :: number(), y2 :: number(), x3 :: number(), y3 :: number(), fill_stroke()}} | {:draw_rect, {width :: number(), height :: number(), fill_stroke()}} | {:draw_rrect, {width :: number(), height :: number(), radius :: number(), fill_stroke()}} | {:draw_sector, {radius :: number(), radians :: number(), fill_stroke()}} | {:draw_arc, {radius :: number(), radians :: number(), fill_stroke()}} | {:draw_circle, {radius :: number(), fill_stroke()}} | {:draw_ellipse, {radius0 :: number(), radius1 :: number(), fill_stroke()}} | {:draw_sprites, {src_id :: Scenic.Assets.Static.id(), cmds :: Scenic.Primitive.Sprites.draw_cmds()}} | {:draw_text, utf8_string :: String.t()} | {:draw_triangle, {x0 :: number(), y0 :: number(), x1 :: number(), y1 :: number(), x2 :: number(), y2 :: number(), fill_stroke()}} | {:script, id :: pos_integer()} | :begin_path | :close_path | :fill_path | :stroke_path | {:move_to, {x :: number(), y :: number()}} | {:line_to, {x :: number(), y :: number()}} | {:arc_to, {x1 :: number(), y1 :: number(), x2 :: number(), y2 :: number(), radius :: number()}} | {:bezier_to, {cp1x :: number(), cp1y :: number(), cp2x :: number(), cp2y :: number(), x :: number(), y :: number()}} | {:quadratic_to, {cpx :: number(), cpy :: number(), x :: number(), y :: number()}} | {:quad, {x0 :: number(), y0 :: number(), x1 :: number(), y1 :: number(), x2 :: number(), y2 :: number(), x3 :: number(), y3 :: number()}} | {:rect, {width :: number(), height :: number()}} | {:rrect, {width :: number(), height :: number(), radius :: number()}} | {:sector, {radius :: number(), radians :: number()}} | {:circle, {radius :: number()}} | {:ellipse, {radius0 :: number(), radius1 :: number()}} | {:triangle, {x0 :: number(), y0 :: number(), x1 :: number(), y1 :: number(), x2 :: number(), y2 :: number()}} | {:scale, {x :: number(), y :: number()}} | {:rotate, radians :: number()} | {:translate, {x :: number(), y :: number()}} | {:transform, {a :: number(), b :: number(), c :: number(), d :: number(), e :: number(), f :: number()}} | {:fill_color, color :: Scenic.Color.t()} | {:fill_linear, {start_x :: number(), start_y :: number(), end_x :: number(), end_y :: number(), color_start :: Scenic.Color.t(), color_end :: Scenic.Color.t()}} | {:fill_radial, {center_x :: number(), center_y :: number(), inner_radius :: number(), outer_radius :: number(), color_start :: Scenic.Color.t(), color_end :: Scenic.Color.t()}} | {:fill_image, image :: Scenic.Assets.Static.id()} | {:fill_stream, id :: Scenic.Assets.Stream.id()} | {:stroke_color, color :: Scenic.Color.t()} | {:stroke_linear, {start_x :: number(), start_y :: number(), end_x :: number(), end_y :: number(), color_start :: Scenic.Color.t(), color_end :: Scenic.Color.t()}} | {:stroke_radial, {center_x :: number(), center_y :: number(), inner_radius :: number(), outer_radius :: number(), color_start :: Scenic.Color.t(), color_end :: Scenic.Color.t()}} | {:stroke_image, image :: Scenic.Assets.Static.id()} | {:stroke_stream, id :: Scenic.Assets.Stream.id()} | {:stroke_width, width :: number()} | {:cap, :butt} | {:cap, :round} | {:cap, :square} | {:join, :bevel} | {:join, :round} | {:join, :miter} | {:miter_limit, limit :: number()} | {:scissor, {width :: number(), height :: number()}} | {:font, id :: Scenic.Assets.Static.id()} | {:font_size, size :: number()} | {:text_align, :left} | {:text_align, :center} | {:text_align, :right} | {:text_base, :top} | {:text_base, :middle} | {:text_base, :alphabetic} | {:text_base, :bottom}
@type t() :: [script_op()]
Link to this section Functions
@spec arc_to( ops :: t(), x1 :: number(), y1 :: number(), x2 :: number(), y2 :: number(), radius :: number() ) :: ops :: t()
Add an arc segment to the path using the control points and radius.
This adds to the current path. It does not fill or stroke anything.
Begin a new path.
@spec bezier_to( ops :: t(), cp1x :: number(), cp1y :: number(), cp2x :: number(), cp2y :: number(), x :: number(), y :: number() ) :: ops :: t()
Add a bezier curve segment to the path using the control points.
This adds to the current path. It does not fill or stroke anything.
Set the current end cap style.
Can be any one of :butt
, :round
, or :square
Add a circle to the current path.
This adds to the current path. It does not fill or stroke anything.
@spec clear(ops :: t(), color :: Scenic.Color.t()) :: ops :: t()
Erase the entire drawing field output.
The clear/1
function is equivalent to setting a color, creating a new rect path
that covers the entire draw field, then filling it. clear/1
does all this in a
single, compact script command.
Close the current path.
This effectively adds a line segment from the current draw position back to where the path was started.
Transform a binary or io list into a readable script list.
This is intended to help with debugging.
Deserialization will only work for scripts serialized using the standard, un-hooked format.
@spec draw_arc( ops :: t(), radius :: number(), radians :: number(), fill_stroke_flags :: fill_stroke() ) :: ops :: t()
Draw an arc defined by radius and an angle. Can be filled or stroked.
Creates a new path and draws it.
@spec draw_circle( ops :: t(), radius :: number(), fill_stroke_flags :: fill_stroke() ) :: ops :: t()
Draw a circle defined by a radius. Can be filled or stroked.
Creates a new path and draws it.
@spec draw_ellipse( ops :: t(), radius0 :: number(), radius1 :: number(), fill_stroke_flags :: fill_stroke() ) :: ops :: t()
Draw an ellipse defined by two radii. Can be filled or stroked.
Creates a new path and draws it.
@spec draw_flag(styles :: map()) :: :fill_stroke | :fill | :stroke | nil
draw_flag is a helper function to choose the appropriate fill and/or stroke flag given a map of styles.
@spec draw_line( ops :: t(), x0 :: number(), y0 :: number(), x1 :: number(), y1 :: number(), :stroke ) :: ops :: t()
Draw a line from a start point to a finish point. Can only be stroked.
Creates a new path and draws it.
@spec draw_quad( ops :: t(), x0 :: number(), y0 :: number(), x1 :: number(), y1 :: number(), x2 :: number(), y2 :: number(), x3 :: number(), y3 :: number(), fill_stroke_flags :: fill_stroke() ) :: ops :: t()
Draw a quad defined by four points. Can be filled or stroked.
Creates a new path and draws it.
@spec draw_rectangle( ops :: t(), width :: number(), height :: number(), fill_stroke_flags :: fill_stroke() ) :: ops :: t()
Draw a rectangle defined by height and width. Can be filled or stroked.
Creates a new path and draws it.
@spec draw_rounded_rectangle( ops :: t(), width :: number(), height :: number(), radius :: number(), fill_stroke_flags :: fill_stroke() ) :: ops :: t()
Draw a rounded rectangle defined by height, width, and radius. Can be filled or stroked.
Creates a new path and draws it.
@spec draw_sector( ops :: t(), radius :: number(), radians :: number(), fill_stroke_flags :: fill_stroke() ) :: ops :: t()
Draw a sector defined by radius and an angle. Can be filled or stroked.
Creates a new path and draws it.
@spec draw_sprites( ops :: t(), image_source :: Scenic.Assets.Static.id(), draw_commands :: Scenic.Primitive.Sprites.draw_cmds() ) :: ops :: t()
Draw a collection of sprites.
Draws one or more subsection from a single source image.
Draw a a block of text. Can be filled.
Creates a new path and draws it.
Draw a a block of text with automatic new lines. Can be filled.
Creates a new path and draws it.
@spec draw_triangle( ops :: t(), x0 :: number(), y0 :: number(), x1 :: number(), y1 :: number(), x2 :: number(), y2 :: number(), fill_stroke_flags :: fill_stroke() ) :: ops :: t()
Draw a triangle defined by three points. Can be filled or stroked.
Creates a new path and draws it.
Add an ellipse to the current path.
This adds to the current path. It does not fill or stroke anything.
@spec fill_color(ops :: t(), color :: Scenic.Color.t()) :: ops :: t()
Set the current fill to an single color.
This only sets the fill paint type. You still need to call fill_path/1
or one of the
draw_* apis to actually draw something.
@spec fill_image(ops :: t(), image :: Scenic.Assets.Static.id()) :: ops :: t()
Set the current fill to an image from the Static asset library.
This image will be automatically repeated both horizontally and vertically.
This only sets the fill paint type. You still need to call fill_path/1
or one of the
draw_* apis to actually draw something.
fill_linear(ops, start_x, start_y, end_x, end_y, color_start, color_end)
View Source@spec fill_linear( ops :: t(), start_x :: number(), start_y :: number(), end_x :: number(), end_y :: number(), color_start :: Scenic.Color.t(), color_end :: Scenic.Color.t() ) :: ops :: t()
Set the current fill to linear gradient that goes between two colors.
This only sets the fill paint type. You still need to call fill_path/1
or one of the
draw_* apis to actually draw something.
Fill the current path with the currently selected fill paint.
fill_radial(ops, center_x, center_y, inner_radius, outer_radius, color_start, color_end)
View Source@spec fill_radial( ops :: t(), center_x :: number(), center_y :: number(), inner_radius :: number(), outer_radius :: number(), color_start :: Scenic.Color.t(), color_end :: Scenic.Color.t() ) :: ops :: t()
Set the current fill to radial gradient that goes between two colors.
This only sets the fill paint type. You still need to call fill_path/1
or one of the
draw_* apis to actually draw something.
@spec fill_stream(ops :: t(), id :: Scenic.Assets.Stream.id()) :: ops :: t()
Set the current fill to an image from an asset stream.
This image will be automatically repeated both horizontally and vertically.
This only sets the fill paint type. You still need to call fill_path/1
or one of the
draw_* apis to actually draw something.
Finish a script, preparing it to be sent to the ViewPort.
This function cleans up the script, which should have been created by first
calling start/0
then and series of calls to Scenic.Script functions that add
commands to the script.
finish/1
cleans up the script, reverses it, and runs an optimization pass.
The resulting script is ready to be stored in the ViewPort.
@spec font(ops :: t(), id :: Scenic.Assets.Static.id()) :: ops :: t()
Set the current font. Must be a valid reference into your static assets library.
Set the current font size.
Set the current line joint style.
Can be any one of :bevel
, :round
, or :miter
Add a new line segment from the current position to a specified location.
This adds to the current path. It does not fill or stroke anything.
Extract a map of all the media (both static assets and streams) used in a script.
Set the current miter limit for joints.
Move the current draw position without adding a line segment.
Reverts the style/transform state of the script then immediately pushes it again.
pop_push_state/1
is for when you have made changes that you want to revert, but then
know you are going to make more changes that you revert again. This is functionally
equivalent to calling pop_state/1
followed immediately by push_state/1
, except that
it is done as a single operation in the script instead of two. This saves drawtime
compute and makes the script smaller. Any adjacent pop/push pairs in the script will
be converted to pop_push in the optimization phase of the finish/1
function.
pop_push_state/1
must be preceded with either push_state/1
and followed by either
pop_state/1
or another pop_push_state/1
Reverts the style/transform state of the script to the most recently pushed state.
This function restores the style and transform states from a stack of states. The idea is that you can make changes, then "pop" back to where the state was before.
push_state/1
must be preceded with either push_state/1
or pop_push_state/1
Saves the current style/transform state of the script as it is running.
This function saves the current style and transform states in a stack of states. The idea is that you can make changes, then "pop" back to where the state was before.
push_state/1
must be paired with an eventual pop_state/1
or pop_push_state/1
@spec quad( ops :: t(), x0 :: number(), y0 :: number(), x1 :: number(), y1 :: number(), x2 :: number(), y2 :: number(), x3 :: number(), y3 :: number() ) :: ops :: t()
Add a quad to the current path.
This adds to the current path. It does not fill or stroke anything.
@spec quadratic_to( ops :: t(), cpx :: number(), cpy :: number(), x :: number(), y :: number() ) :: ops :: t()
Add a quadratic curve segment to the path using the control points.
This adds to the current path. It does not fill or stroke anything.
Add a rectangle to the current path.
This adds to the current path. It does not fill or stroke anything.
Recursively draw a script by reference.
This adds a command that pauses the current script being rendered and recursively draws another script that is referenced by id.
The new script being drawn starts with the currently set draw state and the current state is automatically restored with the script is completed. Then the current script continues drawing.
Apply a rotation transform to the current transform stack.
@spec rounded_rectangle( ops :: t(), width :: number(), height :: number(), radius :: number() ) :: ops :: t()
Add a rounded rectangle to the current path.
This adds to the current path. It does not fill or stroke anything.
Apply a scale transform to the current transform stack.
Set the current scissor rect.
To remove the scissor rect, push the state before you use the scissor, then pop it afterwards.
Add a sector to the current path.
This adds to the current path. It does not fill or stroke anything.
Transform a script list into a binary IO list.
Usually called from a driver.
Returns an IO list.
with {:ok, script} <- ViewPort.get_script_by_id(vp, "my_script_id") do
Scenic.Script.serialize(script)
|> my_render_function()
end
There are times when a driver will want to customize the serialization. For example, GLFW driver wants fixed width names for the streams. So it hooks the serialization of certain commands.
io_list = Script.serialize(script, fn
{:font, id} -> my_serialize_font(id)
{:fill_stream, id} -> my_serialize_fill_stream(id)
{:stroke_stream, id} -> my_serialize_stroke_stream(id)
other -> other
end
@spec serialize( script :: t(), op_fn :: (op_fn :: script_op() -> nil | binary() | iolist() | script_op()) ) :: iolist()
Transform a script list into a binary IO list with a map like interceptor function.
Usually called from a driver.
Returns an IO list.
with {:ok, script} <- ViewPort.get_script(vp, id) do
io_list = Script.serialize(script, fn
{:font, id} -> my_serialize_font(id)
{:fill_stream, id} -> my_serialize_fill_stream(id)
{:stroke_stream, id} -> my_serialize_stroke_stream(id)
other -> other
end)
end
@spec serialize( script :: t(), accumulator :: Enum.acc(), (op_fn :: script_op(), state :: any() -> {nil, any()} | {binary(), any()} | {iolist(), any()} | {script_op(), any()}) ) :: {iolist(), Enum.acc()}
Transform a script list into a binary IO list with a map_reduce like interceptor function.
Usually called from a driver.
Returns an IO list.
{io_list, count} = Script.serialize(script, 0, fn
{:font, id}, c -> {my_serialize_font(id), c+1}
{:fill_stream, id}, c -> {my_serialize_fill_stream(id), c+1}
{:stroke_stream, id}, c -> {my_serialize_stroke_stream(id), c+1}
other, c -> {other, c+1}
end)
@spec start() :: ops :: t()
Create a new Script.
@spec stroke_color(ops :: t(), color :: Scenic.Color.t()) :: ops :: t()
Set the current stroke to an single color.
This only sets the stroke paint type. You still need to call stroke_path/1
or one of the
draw_* apis to actually draw something.
@spec stroke_image(ops :: t(), image :: Scenic.Assets.Static.id()) :: ops :: t()
Set the current stroke to an image from the Static asset library.
This image will be automatically repeated both horizontally and vertically.
This only sets the stroke paint type. You still need to call stroke_path/1
or one of the
draw_* apis to actually draw something.
stroke_linear(ops, start_x, start_y, end_x, end_y, color_start, color_end)
View Source@spec stroke_linear( ops :: t(), start_x :: number(), start_y :: number(), end_x :: number(), end_y :: number(), color_start :: Scenic.Color.t(), color_end :: Scenic.Color.t() ) :: ops :: t()
Set the current stroke to linear gradient that goes between two colors.
This only sets the stroke paint type. You still need to call stroke_path/1
or one of the
draw_* apis to actually draw something.
Stroke the current path with the currently selected stroke width/paint.
stroke_radial(ops, center_x, center_y, inner_radius, outer_radius, color_start, color_end)
View Source@spec stroke_radial( ops :: t(), center_x :: number(), center_y :: number(), inner_radius :: number(), outer_radius :: number(), color_start :: Scenic.Color.t(), color_end :: Scenic.Color.t() ) :: ops :: t()
Set the current stroke to radial gradient that goes between two colors.
This only sets the stroke paint type. You still need to call stroke_path/1
or one of the
draw_* apis to actually draw something.
@spec stroke_stream(ops :: t(), id :: Scenic.Assets.Stream.id()) :: ops :: t()
Set the current stroke to an image from an asset stream.
This image will be automatically repeated both horizontally and vertically.
This only sets the stroke paint type. You still need to call stroke_path/1
or one of the
draw_* apis to actually draw something.
Set the current stroke width.
This only sets the stroke sidth. You still need to call stroke_path/1
or one of the
draw_* apis to actually draw something.
Set the current horizontal text alignment.
Can be any one of :left
, :right
, or :center
Set the current vertical text alignment.
Can be any one of :top
, :middle
, :alphabetic
, or :bottom
@spec transform( ops :: t(), a :: number(), b :: number(), c :: number(), d :: number(), e :: number(), f :: number() ) :: ops :: t()
Apply an arbitrary transform to the current transform stack.
Apply a translation transform to the current transform stack.
@spec triangle( ops :: t(), x0 :: number(), y0 :: number(), x1 :: number(), y1 :: number(), x2 :: number(), y2 :: number() ) :: ops :: t()
Add a triangle to the current path.
This adds to the current path. It does not fill or stroke anything.