Scalable Vector Graphics (SVG)

Bitmap images like PNG or AV1 are the standard on the web, but they lose detail during pixel upscaling. Alternatively, you can define geometric primitives in the SVG format. This is not perfect either, e.g. for fonts, but we have to start somewhere.

Geometric Primitives

To start [tutorial], you need to imagine a canvas of some size, e.g. 100x100, so you know the size of things to define. Lets start with a rectangle, defined completely inline within this html document.

<svg width="100" height="100"> <rect x="0" y="0" width="100" height="100" style="stroke-width:4; stroke:#36a; fill:#dde; opacity:0.9" /> </svg>

Already you can see that e.g. the stroke width is relative to the canvas size, so it will look good scaled up. Lets have a circle/ellipse next:

<ellipse cx="30" cy="30" rx="20" ry="20" style="stroke-width:8; stroke:#0f9; fill:yellow; stroke-opacity:0.5" /> <ellipse cx="30" cy="50" rx="20" ry="20" style="stroke-width:1; stroke:#09f; fill:yellow; fill-opacity:0.8" />

... and note that stroke width is added to both sides of the stroke line, so the lower circle has a bigger "inner fill". Next up are (poly)lines:

<polyline points="10,10 10,80 30,40 30,90" style="stroke:red; fill:none" /> <line x1="50" y1="20" x2="80" y2="30" style="stroke:blue; stroke-width:10" />

At least the polyline can also have a fill color, and is not a closed polygon by default. And line ends are rectangular, not rounded caps. Onwards with path, a command-based, super-powered version of polyline:

<path d="M 10 90 q +40 -80 +70 -10" style="stroke-width:7; stroke:orange; fill:none" />

... which reads as "move (uppercase M: absolute) to (10, 90), then build a quadratic curve (lowercase q: relative) with control point at (+40, -80) and ending at (+70, -10), both relative to the last absolute point (10, 90)".

Line transition points can be adorned by marker [tutorial] elements.

<defs> <marker id="arr" orient="auto-start-reverse" markerWidth="10" markerHeight="7" refX="0" refY="3.5"> <polygon points="0 7, 0 0, 10 3.5" style="fill-opacity: 0.5"/> </marker> </defs> <polyline marker-end="url(#arr)" points="50 20, 80 60" style="stroke:blue; stroke-width:2" /> <polyline marker-start="url(#arr)" marker-end="url(#arr)" points="10 10, 10 90, 90 90" style="stroke:#36a; stroke-width:1; fill:none;" />

Look at the polygon first: a 10x7 triangle pointing to the right. Now look at the marker: A 10x7 rect where the reference point is 0 (at the left) and 3.5 (vertically in the middle) of the 10x7 triangle. The orientation can be auto for repeat and auto-start-reverse for e.g. xy-axis.

On the polylines at the bottom, note that some style attributes like stroke-width carry over to the arrow head, while others like stroke color do not.

Fonts

Text is interesting because anchoring to a point is an issue.

<circle cx="50" cy="50" r="5" style="fill:yellow" /> <text x="50" y="50" style="fill:#0c9; font-size:8pt">My Text</text> My Text

... where the default anchoring is to the bottom left. If we rotate the text:

<circle cx="50" cy="50" r="5" style="fill:yellow" /> <text x="50" y="50" transform="rotate(90 50,50)" style="fill:#063; font-size:8pt">My Text</text> My Text

... rotation is clockwise, unlike in regular math. Since the size of the rendered text box is unknown, the most sensible rotation axis is probably at the lower left corner, the same coordinate as the text itself.

Re-using Shapes

Use a shape multiple times with grouping [tutorial] [tricks].

<defs> <g id="duck" style="stroke:black"> <circle cx="0" cy="0" r="20" /> <circle cx="15" cy="-15" r="10" /> <polygon points="25 -10, 25 -18, 35 -14" style="fill:orange"/> </g> </defs> <use xlink:href="#duck" x="30" y="70" style="fill:none" /> <use xlink:href="#duck" x="140" y="160" style="fill:yellow; transform:scale(.5)"/>

A group has sub-shapes much like the line markers seen previously. Any style attributes defined in the defs cannot be overwritten later, so rather be less specific here. Below, use the group repeatedly with different styles.

Note that scaling by half means specifying all attributes with a factor of two, e.g. the planned coordinate (70, 80) became (140, 160), and also the stroke width is halved if left unspecified.

Conclusion

SVGs are a nice and compact way of painting with geometric primitives, and particularly suited for programming. I hope you liked this first introduction, and happy painting!

EOF (Jun:2022)