Status: Implemented
Visualize is a pipeline of pure values: data is mapped by scales, turned into geometry by shapes, assembled into a backend-agnostic scene graph (the IR), and finally serialised by a backend. This document settles the pipeline, the three IR modules, the backend behaviour and its resolution, the Visualize.Render entry point, and the SVG element layer that faces HEEx templates.
1. The pipeline
data ──► Visualize.Scale ──► Visualize.Shape ──► Visualize.IR.Element ──► Visualize.Backend ──► Visualize.Render
domain → range path strings or scene graph of SVG | Canvas iolist | String
IR.Path elements + transforms (+ encoders)- Scales (
Visualize.Scale, 03-scales) map domain values to range values. They are structs;apply/2performs the mapping. - Shapes (
Visualize.Shape, 04-shapes-and-curves) turn scaled data into geometry.generate/2returns an SVG path-data string;generate_path/2returns aVisualize.IR.Path. - The IR (
Visualize.IR.*, §2) is the scene graph:Elementnodes carrying attributes, style, an optionalTransform, children, text content or aPath.Visualize.Axis.generate/1and the layouts emit IR. - Backends (
Visualize.Backend, §3) serialise the IR.Visualize.Backend.SVGandVisualize.Backend.Canvasimplement the behaviour.Visualize.Backend.CanvasBinary,Visualize.Backend.HybridandVisualize.Backend.CanvasIncrementalare encoders with their own entry points, not behaviour implementations; they are specified in 09-rendering-backends. - Render (
Visualize.Render, §4) chooses a backend and dispatches on the IR's shape.
Every stage is a pure function of its inputs. No stage holds process state; the only process in the library is the force simulation (06-layouts).
2. The intermediate representation
2.1 Visualize.IR.Element
An element is %Visualize.IR.Element{type, attrs, style, transform, children, content, path, clip}. clip is nil or %{shape: element, keep: :inside | :outside}: the element is drawn only where the shape is (:inside) or only where it is not (:outside), the shape being a :path, :rect, :circle, :ellipse or :polygon element whose style is ignored — how a stroke is kept inside or outside an edge (spec/14 §5.6). A backend that can clip MUST honour it (spec/09 §2.3, §3.3); one that cannot draws the element unclipped and says so. type MUST be one of :path, :rect, :circle, :ellipse, :line, :polyline, :polygon, :text, :group, :image, :defs, :clip_path, :linear_gradient, :radial_gradient, :stop, :filter, :fe_drop_shadow, :fe_gaussian_blur, :tspan, :root, :title, :desc. attrs is a map of geometric attributes; style is a map whose recognised keys are :fill, :stroke, :stroke_width, :stroke_linecap (:butt | :round | :square), :stroke_linejoin (:miter | :round | :bevel), :stroke_dasharray, :opacity, :fill_opacity, :stroke_opacity, :font_size, :font_family, :font_weight, :font_style, :text_anchor (:start | :middle | :end), :dominant_baseline, :class, :blend (a mix-blend-mode word), :effect (:none | :shadow | :blur) with :effect_radius; :fill and :stroke accept a string, :none or :current_color. Gradient stops additionally use :stop_color and :stop_opacity. Backends MUST pass unrecognised style keys through unchanged. Constructors with a default style argument default it to %{}.
| Function | Contract |
|---|---|
Visualize.IR.Element.new/1 | As new/2 with no options. |
Visualize.IR.Element.new/2 | Builds an element of the given type from options :attrs (default %{}), :style (%{}), :transform (nil), :children ([]), :content (nil), :path (nil). |
Visualize.IR.Element.path/1 | As path/2 with an empty style. |
Visualize.IR.Element.path/2 | Wraps a Visualize.IR.Path in a :path element with the given style. |
Visualize.IR.Element.group/0 | An empty :group. |
Visualize.IR.Element.group/1 | A :group with the given children. |
Visualize.IR.Element.group/2 | A :group with children and options :style, :transform, :attrs. |
Visualize.IR.Element.rect/4 | As rect/5 with an empty style. |
Visualize.IR.Element.rect/5 | A :rect with attrs x, y, width, height. |
Visualize.IR.Element.circle/3 | As circle/4 with an empty style. |
Visualize.IR.Element.circle/4 | A :circle with attrs cx, cy, r. |
Visualize.IR.Element.ellipse/4 | As ellipse/5 with an empty style. |
Visualize.IR.Element.ellipse/5 | An :ellipse with attrs cx, cy, rx, ry. |
Visualize.IR.Element.line/4 | As line/5 with an empty style. |
Visualize.IR.Element.line/5 | A :line with attrs x1, y1, x2, y2. |
Visualize.IR.Element.polyline/1 | As polyline/2 with an empty style. |
Visualize.IR.Element.polyline/2 | A :polyline whose points attr is a list of {x, y} tuples. |
Visualize.IR.Element.polygon/1 | As polygon/2 with an empty style. |
Visualize.IR.Element.polygon/2 | A :polygon whose points attr is a list of {x, y} tuples. |
Visualize.IR.Element.text/2 | A :text element from content and a keyword list; takes :x, :y, :dx, :dy into attrs and :style into style. |
Visualize.IR.Element.text/3 | As text/4 with an empty style. |
Visualize.IR.Element.text/4 | A :text element with content and attrs x, y. |
Visualize.IR.Element.tspan/2 | tspan(content, attrs): a :tspan — one line of a :text, a child of it — with the given attrs (x, dy). A :text with :tspan children and no content is a text of several lines (spec/14 §7.1). |
Visualize.IR.Element.image/5 | As image/6 with an empty style. |
Visualize.IR.Element.image/6 | An :image with attrs href, x, y, width, height. |
Visualize.IR.Element.defs/0 | An empty :defs. |
Visualize.IR.Element.defs/1 | A :defs with the given children. |
Visualize.IR.Element.clip_path/2 | A :clip_path with attr id and the given children. |
Visualize.IR.Element.linear_gradient/1 | As linear_gradient/2 with no options. |
Visualize.IR.Element.linear_gradient/2 | A :linear_gradient with attr id, optional :x1, :y1, :x2, :y2 attrs, and children from :stops (default []). |
Visualize.IR.Element.radial_gradient/1 | As radial_gradient/2 with no options. |
Visualize.IR.Element.radial_gradient/2 | A :radial_gradient with attr id, optional :cx, :cy, :r, :fx, :fy attrs, and children from :stops. |
Visualize.IR.Element.stop/2 | As stop/3 with no options. |
Visualize.IR.Element.stop/3 | A :stop with attr offset, style stop_color, and stop_opacity when :opacity is given. |
Visualize.IR.Element.filter/2 | filter(id, children): a :filter with attr id and the given primitives. |
Visualize.IR.Element.drop_shadow/3 | drop_shadow(dx, dy, radius): a :fe_drop_shadow with attrs dx, dy, std_deviation and flood_opacity 0.35. |
Visualize.IR.Element.gaussian_blur/1 | gaussian_blur(radius): a :fe_gaussian_blur with attr std_deviation. |
Visualize.IR.Element.title/1 | A :title with the given text as content: the accessible name of the element it is the first child of. |
Visualize.IR.Element.desc/1 | A :desc with the given text as content: the accessible description. |
Visualize.IR.Element.root/2 | As root/3 with no options. |
Visualize.IR.Element.root/3 | A :root with attrs width, height, view_box (default "0 0 #{width} #{height}") and children from :children, plus the responsive options of D-54: :preserve_aspect_ratio sets the attr of that name (rendered preserveAspectRatio, spec/09 §2.4), absent by default; responsive: true sets it to "xMidYMid meet" unless given and adds style: "width:100%;height:auto", so the <svg> keeps width and height as its intrinsic size and aspect ratio but fills the width of its container. Every root carries role: "img" (D-56). The accessibility options: :title and :description prepend a title/1 and a desc/1 child, each with an id, and set aria_labelledby (rendered aria-labelledby) to those ids in that order; :id is the prefix of the child ids (<id>-title, <id>-desc) and is also set on the root, and defaults to vis- followed by the lower-case base-36 :erlang.phash2/1 of {title, description}, so equal charts share ids and different ones do not; with neither text no child, no aria_labelledby and no id are added. |
Visualize.IR.Element.put_attr/3 | The element with one attribute set: an atom key, or a string for an attribute the IR has no name for, such as data-node (spec/14 §4.7). |
Visualize.IR.Element.append/2 | Appends one element or a list of elements to children. |
Visualize.IR.Element.prepend/2 | Prepends one element or a list of elements to children. |
Visualize.IR.Element.set_transform/2 | Replaces the element's transform. |
Visualize.IR.Element.translate/3 | Adds a translate operation: creates the transform when nil, otherwise appends to it. |
Visualize.IR.Element.set_style/2 | Merges the given style over the existing style. |
Visualize.IR.Element.clip/3 | clip(element, shape, keep): the element with clip set to the shape and :inside or :outside. |
Visualize.IR.Element.set_attrs/2 | Merges the given attrs over the existing attrs. |
Visualize.IR.Element.datum/2 | Writes the datum attributes of spec/10 §12.2 over the element's attrs, under string keys: "data-datum", the field names space-separated in order, and "data-datum-<field>" per field with _ as - and to_string/1 of the value, a nil value omitted; the fields are a map (written in Enum.sort/1 order) or a keyword list (in its order). What TooltipHook reads (D-75). |
Visualize.IR.Element.set_content/2 | Sets the text content. |
Visualize.IR.Element.translate_group/3 | Wraps an element or list of elements in a :group carrying Transform.translate(x, y). |
Visualize.IR.Element implements String.Chars (renders through Visualize.Backend.SVG) and, when Phoenix.HTML.Safe is loaded, Phoenix.HTML.Safe (D-7).
2.2 Visualize.IR.Path
A path is %Visualize.IR.Path{commands, metadata}; metadata is a free map for producers and is not serialised. Commands are tuples tagged with the SVG command letter — absolute upper-case, relative lower-case: {:M|:m, x, y}, {:L|:l, x, y}, {:H|:h, x}, {:V|:v, y}, {:C|:c, x1, y1, x2, y2, x, y}, {:S|:s, x2, y2, x, y}, {:Q|:q, x1, y1, x, y}, {:T|:t, x, y}, {:A|:a, rx, ry, rotation, large_arc, sweep, x, y} and the bare atom :Z. large_arc and sweep MUST be 0 or 1. Builders append in call order. A path is built in time proportional to its commands (#414): the appenders above put one command at the end of a list, which is the cost of the list, so a builder that draws one command per datum makes its commands in order and hands them to from_commands/1 at the end — a path of 50,000 commands took six seconds to append one at a time and the drawing of a large chart was quadratic in its points.
| Function | Contract |
|---|---|
Visualize.IR.Path.new/0 | An empty path. |
Visualize.IR.Path.from_commands/1 | A path of the commands given, in the order given: what a builder that draws per datum ends with, so building costs the list and not a square of it (#414). |
Visualize.IR.Path.move_to/3 | Appends {:M, x, y}. |
Visualize.IR.Path.move_to_rel/3 | Appends {:m, dx, dy}. |
Visualize.IR.Path.line_to/3 | Appends {:L, x, y}. |
Visualize.IR.Path.line_to_rel/3 | Appends {:l, dx, dy}. |
Visualize.IR.Path.horizontal_to/2 | Appends {:H, x}. |
Visualize.IR.Path.horizontal_to_rel/2 | Appends {:h, dx}. |
Visualize.IR.Path.vertical_to/2 | Appends {:V, y}. |
Visualize.IR.Path.vertical_to_rel/2 | Appends {:v, dy}. |
Visualize.IR.Path.curve_to/7 | Appends a cubic Bézier {:C, x1, y1, x2, y2, x, y}. |
Visualize.IR.Path.curve_to_rel/7 | Appends {:c, ...} with relative coordinates. |
Visualize.IR.Path.smooth_curve_to/5 | Appends {:S, x2, y2, x, y}. |
Visualize.IR.Path.smooth_curve_to_rel/5 | Appends {:s, ...}. |
Visualize.IR.Path.quad_to/5 | Appends a quadratic Bézier {:Q, x1, y1, x, y}. |
Visualize.IR.Path.quad_to_rel/5 | Appends {:q, ...}. |
Visualize.IR.Path.smooth_quad_to/3 | Appends {:T, x, y}. |
Visualize.IR.Path.smooth_quad_to_rel/3 | Appends {:t, dx, dy}. |
Visualize.IR.Path.arc_to/8 | Appends {:A, rx, ry, x_rotation, large_arc, sweep, x, y}; rotation in degrees. |
Visualize.IR.Path.arc_to_rel/8 | Appends {:a, ...} with a relative end point. |
Visualize.IR.Path.close/1 | Appends :Z. |
Visualize.IR.Path.empty?/1 | true iff there are no commands. |
Visualize.IR.Path.command_count/1 | The number of commands. |
Visualize.IR.Path.concat/1 | One path whose commands are the concatenation of the given paths' commands, in order; metadata is dropped. |
Visualize.IR.Path.append/2 | The first path's commands followed by the second's; metadata is dropped. |
Visualize.IR.Path.expand_smooth/1 | Rewrites S/s/T/t as C/c/Q/q (D-36): the first control point is the reflection about the current point of the previous command's last control point when that command was a cubic (for S/s) or a quadratic (for T/t), and the current point otherwise; a relative result keeps its deltas relative. Every other command is unchanged. |
Visualize.IR.Path.absolute/1 | Every command as an absolute M, L, C, Q, A or Z: relative commands resolved against the current point (a leading m is absolute), H/V as L, smooth curves expanded as expand_smooth/1 does, arcs keeping radii, rotation and flags. Draws the same figure; this is what a target without relative commands replays. |
Visualize.IR.Path.transform/2 | Applies an affine matrix {a, b, c, d, e, f} (Visualize.IR.Transform.matrix/0), or a Visualize.IR.Transform folded with Visualize.IR.Transform.to_matrix/1, to the path (D-35). Absolute end and control points get the full map {a·x + c·y + e, b·x + d·y + f}; relative deltas get the linear part only. The current point is tracked through every command (:Z returns to the subpath start; a leading m is absolute, as SVG reads it): H/h stay single-axis when b = 0 and V/v when c = 0, otherwise they become L/l to the mapped point. An arc's end point is mapped; its radii and rotation are those of the exact image of its ellipse under the linear part (the singular values and left rotation of the 2×2 product), in canonical form rx ≥ ry, rotation in [0, 180), a circle keeping its given rotation; the sweep flag flips when the determinant is negative; large_arc is untouched. |
Visualize.IR.Path.to_string/1 | The SVG d string: commands concatenated with no separators, coordinates comma-separated, e.g. "M10,20L100,200Z"; an arc prints Arx,ry,rot,large,sweep,x,y with the two flags verbatim. Every number goes through format_number/1. |
Visualize.IR.Path.format_number/1 | The one number formatter of every serialiser (D-12, D-32): an integer prints as-is; a float prints with at most four decimals, trailing zeros and a trailing point removed, never in exponent notation, and never as -0 (a zero that rounds negative prints 0). |
to_string/1 is the only d serialisation: Visualize.Backend.SVG.path_data/1, Visualize.SVG.Path.to_string/1 and the String.Chars implementation for Visualize.IR.Path (defined alongside Visualize.SVG.Path) delegate to it (D-12, D-34).
2.3 Visualize.IR.Transform
A transform is %Visualize.IR.Transform{operations}, an ordered list applied left to right as SVG does: {:translate, x, y}, {:rotate, angle}, {:rotate, angle, cx, cy}, {:scale, sx, sy}, {:skew_x, angle}, {:skew_y, angle}, {:matrix, a, b, c, d, e, f}. Angles are degrees.
| Function | Contract |
|---|---|
Visualize.IR.Transform.new/0 | An empty transform. |
Visualize.IR.Transform.translate/2 | A new transform with a single translate. |
Visualize.IR.Transform.translate/3 | Appends a translate to the given transform. |
Visualize.IR.Transform.rotate/1 | A new transform with a single rotate. |
Visualize.IR.Transform.rotate/2 | Appends a rotate about the origin. |
Visualize.IR.Transform.rotate/4 | Appends a rotate about {cx, cy}. |
Visualize.IR.Transform.scale/1 | A new transform with a single uniform scale {:scale, s, s}. |
Visualize.IR.Transform.scale/2 | Two numbers: a new transform with {:scale, sx, sy}. A transform and one number: appends a uniform scale to it (D-1). |
Visualize.IR.Transform.scale/3 | Appends {:scale, sx, sy}. |
Visualize.IR.Transform.skew_x/2 | Appends {:skew_x, angle}. |
Visualize.IR.Transform.skew_y/2 | Appends {:skew_y, angle}. |
Visualize.IR.Transform.matrix/7 | Appends {:matrix, a, b, c, d, e, f} for the matrix [a c e; b d f; 0 0 1]. |
Visualize.IR.Transform.empty?/1 | true iff there are no operations. |
Visualize.IR.Transform.concat/2 | The first transform's operations followed by the second's. |
Visualize.IR.Transform.to_matrix/1 | The operations folded into one affine matrix {a, b, c, d, e, f} for [a c e; b d f; 0 0 1], composed as SVG composes the attribute string: M = Op₁ · Op₂ · … · Opₙ, so the last operation applies to a point first. translate is [1 0 tx; 0 1 ty], scale [sx 0 0; 0 sy 0], rotate(a) [cos −sin 0; sin cos 0], rotate(a, cx, cy) is translate(cx, cy) rotate(a) translate(−cx, −cy), skew_x(a) [1 tan 0; 0 1 0], skew_y(a) [1 0 0; tan 1 0], matrix as given; angles in degrees. An empty transform is the identity. |
Visualize.IR.Transform.multiply/2 | The product m1 · m2 of two matrices: applying the result is applying m2 first, then m1. |
Visualize.IR.Transform.apply_matrix/2 | apply_matrix({a, b, c, d, e, f}, {x, y}) is {a·x + c·y + e, b·x + d·y + f}. |
Visualize.IR.Transform.to_string/1 | The SVG transform attribute: operations space-separated, e.g. "translate(100,50) rotate(45) scale(2)". A scale with equal factors prints the single-argument form. Numbers format with Visualize.IR.Path.format_number/1, so -0 never prints. This is the only transform serialisation; Visualize.Backend.SVG emits it (D-34). |
3. The backend behaviour
3.1 Callbacks
Visualize.Backend is a behaviour. An implementation MUST define:
path_data(Visualize.IR.Path.t()) :: String.t() | [tuple()]— the backend's native path data (thedstring for SVG; a list of drawing commands for Canvas).render_path(Visualize.IR.Path.t(), keyword()) :: output— a rendered path; options MAY include:style.render_element(Visualize.IR.Element.t(), keyword()) :: output— a rendered element tree.render_scene([Visualize.IR.Element.t()], keyword()) :: output— a rendered list of elements; the built-in backends wrap the result withwrap_root/2unlesswrap: false.
and MAY define wrap_root(output, keyword()) :: output, which wraps content in a root container (<svg> for SVG; canvas setup for Canvas) using options :width (default 800), :height (default 600) and :view_box. output is an iolist, string, map or command list as the backend chooses. The callback implementations on Visualize.Backend.SVG and Visualize.Backend.Canvas are specified in 09-rendering-backends.
3.2 Resolution and configuration
| Function | Contract |
|---|---|
Visualize.Backend.default/0 | The module from config :visualize, default_backend: resolved through resolve/1; Visualize.Backend.SVG when unset. |
Visualize.Backend.resolve/1 | nil → default/0; :svg → Visualize.Backend.SVG; :canvas → Visualize.Backend.Canvas; any other atom is returned as a module unchanged. |
config :visualize, default_backend: value accepts the same values as resolve/1. There is deliberately no per-process override (D-2): the backend is either the :backend option at the call site or the application configuration.
4. Visualize.Render
Visualize.Render dispatches a renderable — a Visualize.IR.Path, a Visualize.IR.Element, or a list of elements — to render_path/2, render_element/2 or render_scene/2 respectively. Every function takes an options keyword list; :backend (:svg, :canvas, or a module) selects the backend and all other options pass through to it.
| Function | Contract |
|---|---|
Visualize.Render.render/1 | As render/2 with the default backend. |
Visualize.Render.render/2 | Renders to the backend's native output (an iolist for the built-in backends). |
Visualize.Render.to_string/1 | As to_string/2 with the default backend. |
Visualize.Render.to_string/2 | render/2 converted with IO.iodata_to_binary/1. |
Visualize.Render.to_svg/1 | As to_svg/2. |
Visualize.Render.to_svg/2 | render/2 with backend: :svg forced. |
Visualize.Render.to_svg_string/1 | As to_svg_string/2. |
Visualize.Render.to_svg_string/2 | to_string/2 with backend: :svg forced. |
Visualize.Render.to_canvas/1 | As to_canvas/2. |
Visualize.Render.to_canvas/2 | render/2 with backend: :canvas forced. |
Visualize.Render.to_canvas_string/1 | As to_canvas_string/2. |
Visualize.Render.to_canvas_string/2 | to_string/2 with backend: :canvas forced. |
Visualize.Render.path_data/1 | As path_data/2 with the default backend. |
Visualize.Render.path_data/2 | The selected backend's path_data/1 for the path. |
Visualize.Render.to_png/1 | As to_png/2 with no options. |
Visualize.Render.to_png/2 | A root Visualize.IR.Element, or an SVG document as a binary, rasterised to a PNG by the resvg command-line tool, run as a port: {:ok, png, warnings}, the warnings resvg printed ({:missing_family, list} for each font-family list no font resolves, {:rasterizer, line} for any other line, [] when there are none), {:error, :no_rasterizer} with no resvg binary configured or on the PATH, {:error, {:rasterizer_version, found, required}} for a binary older than the supported minimum, {:error, :css_references} when the SVG holds a var(-- reference, {:error, :timeout} when the render outlasts :timeout, {:error, {:rasterizer, message}} when resvg refuses the document. Options :scale, :background, :timeout, and the font options :font_dirs, :system_fonts, :generic_families and :font_family; 09-rendering-backends §9. |
Visualize.Render.to_png!/1 | As to_png!/2 with no options. |
Visualize.Render.to_png!/2 | As to_png/2, returning the PNG binary of {:ok, png, []} and raising RuntimeError on the {:error, _} branch and when there is a warning: a complete picture or none. |
to_png/2 is the one function of the module that produces no backend output: it renders an IR root with to_svg_string/2 and hands the SVG to the rasteriser of 09-rendering-backends §9 (D-121). Its input is narrower than a renderable, because a PNG is a whole document: a :root element (Visualize.IR.Element.root/3, or Visualize.Chart.generate/2 with root: true), or the SVG string such an element renders to (Visualize.Chart.render/2 with root: true).
5. The SVG element layer
Visualize.SVG.Element predates the IR and remains the HEEx-facing struct: %Visualize.SVG.Element{tag, attrs, children, content} mirrors an SVG element one-to-one, with tag an SVG tag atom and attrs a map rendered as attributes. It implements String.Chars and, when Phoenix is loaded, Phoenix.HTML.Safe via Visualize.SVG.Renderer.render/1, so it interpolates into a template as an iolist without escaping. from_ir/1 is the bridge from the IR: the scene graph is built with Visualize.IR.Element, converted once, and embedded.
5.1 Visualize.SVG.Element
from_ir/1 maps element types to tags (:group → :g, :root → :svg, :clip_path → :clipPath, :linear_gradient → :linearGradient, :radial_gradient → :radialGradient, others unchanged), copies attrs, adds d from the path via Visualize.IR.Path.to_string/1, adds transform via Visualize.IR.Transform.to_string/1, converts every style key to its hyphenated SVG attribute name (:stroke_width → :"stroke-width" and so on) and every style atom value to its SVG keyword (:none → "none", :current_color → "currentColor", :start, :middle, :end, :butt, :round, :square, :miter, :bevel, :bold). Style attributes overwrite geometric attrs of the same name. A :root element gains xmlns="http://www.w3.org/2000/svg" and its view_box attr becomes the viewBox key (D-11, D-34); a :polyline/:polygon whose points is a list of {x, y} tuples has it formatted as Visualize.Backend.SVG formats points (spec/09 §2.6). Conversion is recursive over children. The result renders, through Visualize.SVG.Renderer, to exactly the markup Visualize.Backend.SVG.render_element/1 emits for the same IR (D-34); test/visualize/svg/bridge_test.exs holds both to it.
Every constructor accepts attrs as a map or keyword list. The camel-case names (clipPath, linearGradient, radialGradient) are deliberate: they are the SVG tag names.
5.2 Visualize.SVG
The fluent builder over Visualize.SVG.Element. Attrs are accepted as keyword lists or maps.
| Function | Contract |
|---|---|
Visualize.SVG.new/0 | As new/1 with no attrs. |
Visualize.SVG.new/1 | A root :svg element with xmlns and the given attrs. |
Visualize.SVG.append/2 | Appends an SVG.Element child; or, when the second argument is a tag atom, as append/3 with no attrs. |
Visualize.SVG.append/3 | Creates Element.new(tag, attrs) and appends it. |
Visualize.SVG.append_all/2 | Appends each element of the list in order. |
Visualize.SVG.group/0 | As group/1 with no attrs. |
Visualize.SVG.group/1 | A :g element. |
Visualize.SVG.translate/3 | A :g with transform="translate(x,y)" containing the given element or elements. |
Visualize.SVG.render/1 | Visualize.SVG.Renderer.render/1: the tree as an iolist. |
Visualize.SVG.to_string/1 | Visualize.SVG.Renderer.render_to_string/1. |
Visualize.SVG.rect/0 | As rect/1 with no attrs. |
Visualize.SVG.rect/1 | A :rect element. |
Visualize.SVG.circle/0 | As circle/1 with no attrs. |
Visualize.SVG.circle/1 | A :circle element. |
Visualize.SVG.line/0 | As line/1 with no attrs. |
Visualize.SVG.line/1 | A :line element. |
Visualize.SVG.path/0 | As path/1 with no attrs. |
Visualize.SVG.path/1 | A :path element. |
Visualize.SVG.text/1 | As text/2 with no attrs. |
Visualize.SVG.text/2 | A :text element with the given content and attrs. |
Visualize.SVG.polyline/0 | As polyline/1 with no attrs. |
Visualize.SVG.polyline/1 | A :polyline element. |
Visualize.SVG.polygon/0 | As polygon/1 with no attrs. |
Visualize.SVG.polygon/1 | A :polygon element. |
5.3 Visualize.SVG.Path
A compatibility façade: every builder delegates to Visualize.IR.Path and operates on Visualize.IR.Path structs (there is no separate SVG.Path struct). New code SHOULD use Visualize.IR.Path directly.
5.4 Visualize.SVG.Renderer
Serialises an SVG.Element tree. Attributes with a nil value are omitted; the rest are emitted sorted by their SVG name from attribute_name/1, values escaped (&, ", <, >); text content is escaped (&, <, >); child-less, content-less circle, ellipse, line, path, polygon, polyline, rect, image, use, stop, feDropShadow and feGaussianBlur elements are self-closing. The iolist form is what Phoenix.HTML.Safe returns. Visualize.Backend.SVG renders its tags, attributes and text through the same functions, so the two serialisers cannot drift (D-34).
| Function | Contract |
|---|---|
Visualize.SVG.Renderer.render/1 | The element tree as an iolist. |
Visualize.SVG.Renderer.render_to_string/1 | render/1 converted to a binary. |
Visualize.SVG.Renderer.attribute_name/1 | The SVG attribute name for a key (D-11): a snake_case atom hyphenates (:stroke_width → "stroke-width", :clip_path → "clip-path") unless it is one of the camel-cased SVG attributes, which map to their spelling — :view_box → "viewBox", :preserve_aspect_ratio, :gradient_units, :gradient_transform, :pattern_units, :pattern_transform, :clip_path_units, :mask_units, :std_deviation (stdDeviation), :marker_width, :marker_height, :marker_units, :ref_x, :ref_y, :spread_method, :text_length, :length_adjust; an atom already camel-cased (:viewBox) and a binary key pass through unchanged. |