Dev diary: SVG markup optimizations
1 SVG optimizations
So far, L-systems generated by Malsys as SVG were rendered in very primitive way. Every line segment was one tag of SVG line element. Thanks to GZip compression the files were not that huge because average compression ratio was more than 90%. The files looked like code in Code listing 1. There were many sources of data redundancies and ineffectiveness:
- Ending point of many line segments was often starting point of the very next one so lines could be concatenated to polyline element,
- coordinates had often unnecessary too many digits like 0.333333333 because of used double precision floating point format,
- some color codes could be shortened like #FFFFFF to #FFF, and finally
- colors and stroke widths were often the same so groups could be used to optimize the markup.
Following text talks about mentioned SVG markup optimizations together with some stats.
Code listing 1: Original non-optimized SVG code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC ...> <svg xmlns="http://www.w3.org/2000/svg" viewBox="-3 -169.277 582 172.277" ...> <line x1="0" y1="0" x2="64" y2="0" stroke="#000000" stroke-width="2" /> <line x1="64" y1="0" x2="96" y2="-55.426" stroke="#000000" stroke-width="2" /> <line x1="96" y1="-55.426" x2="128" y2="0" stroke="#000000" stroke-width="2" /> <line x1="128" y1="0" x2="192" y2="0" stroke="#000000" stroke-width="2" /> ... <line x1="384" y1="0" x2="448" y2="0" stroke="#000000" stroke-width="2" /> <line x1="448" y1="0" x2="480" y2="-55.426" stroke="#000000" stroke-width="2" /> <line x1="480" y1="-55.426" x2="512" y2="0" stroke="#000000" stroke-width="2" /> <line x1="512" y1="0" x2="576" y2="0" stroke="#000000" stroke-width="2" /> </svg> |
1.1 Lines to paths
The biggest source of redundancy are lines. SVG has very nice tag called path which enables to concatenate consequent line segments. Path tag works that you can specify move (M) and line (L) actions similarly to L-systems' MoveTo and DrawTo actions. Code showed in the introduction Code listing 1 is optimized to code in Code listing 2.
This brings huge savings among L-systems which are one line with same thickness and color. This optimization is enabled by default but it can be toggled with optimizeSvg
settable property of SvgRenderer2D
.
Code listing 2: SVG optimized by converting lines to paths.
1 2 3 4 5 6 7 8 9 10 | <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC ...> <svg xmlns="http://www.w3.org/2000/svg" viewBox="-3 -169.277 582 172.277" ...> <path stroke="#000000" stroke-width="2" d=" M 0 0 L 64 0 L 96 -55.426 L 128 0 L 192 0 L 224 -55.426 L 192 -110.9 L 256 -110.9 L 288 -166.3 L 320 -110.9 L 384 -110.9 L 352 -55.426 L 384 0 L 448 0 L 480 -55.426 L 512 0 L 576 0" /> </svg> |
1.2 Coordinates precision
Previously, floating point numbers were always rounded to 3 decimal digits. This was unnecessary for large numbers and negatively affected precision of small numbers. Here are some exaples:
- -87.002 → 87
- -105.579 → -105.6
- 0.005 → 0.004921
Better solution is to round to certain number of significant digits. This will save some characters and even improves the precision. The default number of significant digits was set to 4. Why 4? Empirical experiment on Hexa-Gosper curve which walks on non-whole point locations shown that 3 is not enough (see Figure 1).
It is also possible to change this number to any value with outputSignificantDigitsCount
settable property of SvgRenderer2D
component.
The rounding algorithm was improved to not loose precision if it will not save any characters. See the following example.
- 11564.781 → 11564 (not 11560)
- 20247.025 → 20247 (not 20250)
Since SVG standard supports scientific notation of floating point numbers, number in scientific notation it is placed to places where it save some characters. This is useful only when L-system is very tiny or very huge.
- 0.0005791 → 5791e-6 (not 0.0005791)
- 202478.025 → 2025e2 (not 202478)
- 20001.2 → 2e4 (not 20001, this is neat trick)
1.3 Simplification of some color codes
This is very simple simplification. If hexadecimal code of color has same pair of digits for all three channels R, G and B, they can be expressed as three-digit number instead of six. This might sound like very rare case but many L-systems are just black or solid color.
- #000000 → #000
- #FFBB00 → #FB0
- #33AA00 → #3A0
1.4 Results
Following charts in Figure 2 present size reduction using all described techniques.