Interpreting svg paths created in inkscape - svg

I am trying to regenerate svg paths in a script using xml files created by inkscape. I understand how to interpret the d tag in the xml file, but I am having trouble with the transformations baked into the file.
My test file has a path on it that I simplified down to a small triangle to make it easier to work with. The simple svg file from inkscape looks like this:
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="svg6530"
viewBox="0 0 33 134"
height="134"
width="33">
<defs
id="defs6532" />
<metadata
id="metadata6535">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="display:inline"
transform="translate(0,-918.36216)"
id="layer1">
<path
id="path7149"
d="m 0.10475398,1040.2201 1.79370822,0 1.4230759,-1.6612"
style="fill:#000000" />
</g>
</svg>
The path is relative (lowercase m)
It has a transform:translate of (0,-918.36216) applied to it.
The view box is 0 0 33 134
height = 134
width = 33
the path is: "m 0.10475398,1040.2201 1.79370822,0 1.4230759,-1.6612"
All units are pixels.
If I look at the path in inkscape I see the following 3 points in the document:
0.105,12.142
1.898,12.142
3.322,13.80
These are the three points I'd like to recreate in a script.
If I start with
"M 0.105,12.142 1.898,12.142 3.322,13.80"
I get exactly what I want, but I can't figure out how to get to this from:
d="m 0.10475398,1040.2201 1.79370822,0 1.4230759,-1.6612"

The path string:
m 0.10475398,1040.2201 1.79370822,0 1.4230759,-1.6612
is equivalent to
M 0.10475398,1040.2201 l 1.79370822,0 l 1.4230759,-1.6612
or a move and two relative lines.
If we convert the relative line coordinates to absolute (and round to three decimal places for simplicity) we get:
M 0.105,1040.220 L (0.105+1.794),(1040.220+0) L 0.105+1.794+1.423),(1040.220+0-1.661)
or
M 0.105,1040.220 L 1.899,1040.220 L 3.322,1038.559
Now you are also applying a transform to the path. It's a translation by (0,-918.362). If if we now apply that to your path, we get:
M 0.105,(1040.220 - 918.362) L 1.899,(1040.220 - 918.362) L 3.322,(1038.559 - 918.362)
or:
M 0.105,121.858 L 1.899,121.858 L 3.322,120.197
In its UI, Inkscape is flipping the Y coordinates from the SVG convention, where Y=0 is at the top, to the cartesian convention where Y=0 is at the bottom.
So for the final step, if we subtract all the Y coordinates from 134 (the height of the document), we get:
M 0.105,12.142 L 1.899,12.142 L 3.322,13.807
Which is the string you were after (with minor rounding differences).
tl;dr: Convert to absolute coordinates, apply the transform, flip the Y coords.

Related

change 0,0 position of svg path

I have a svg path as below:
'M12,2 C 8.13,2 5,5.13 5,9 c 0,5.25 7,13 7,13s7,-7.75 7,-13C19,5.13 15.87,2 12,2zM7,9c0,-2.76 2.24,-5 5,-5s5,2.24 5,5c0,2.88 -2.88,7.19 -5,9.88C9.92,16.21 7,11.85 7,9z M12,9m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0'
Now the tip point at (12,22), I would like to keep shape the same but the tip point at (0,0).
Which tool can do such position shift?
One possible solution would be using this tool to convert the path to to all-relative path commands.
For example convert:
M12,2 C 8.13,2 5,5.13 5,9 c 0,5.25 7,13 7,13s7,-7.75 7,-13C19,5.13 15.87,2 12,2zM7,9c0,-2.76 2.24,-5 5,-5s5,2.24 5,5c0,2.88 -2.88,7.19 -5,9.88C9.92,16.21 7,11.85 7,9z M12,9m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0
to:
M12,2c-3.87,0,-7,3.13,-7,7c0,5.25,7,13,7,13s7,-7.75,7,-13c0,-3.87,-3.13,-7,-7,-7zm-5,7c0,-2.76,2.24,-5,5,-5s5,2.24,5,5c0,2.88,-2.88,7.19,-5,9.88c-2.08,-2.67,-5,-7.03,-5,-9.88zm5,0m-2.5,0a2.5,2.5,0,1,1,5,0a2.5,2.5,0,1,1,-5,0
Now in order to move it in the 0,0 just change the first two values after the initial M command from 12,2 to 0,-18.
The first value is 0 and represents the x value of both tue top and the tip. The second value is -18 and represents the y value of the starting point of the path located almost at the top. In order to get this value I did: 2 (actual y coordinate of the point) - 20 (height of the path).
In order to get the height of the path I'm using getBBox()
console.log(g.getBBox())
svg{width:30vh;border:solid}
<svg viewBox="-7 -18 14 20">
<path id="g" d="M0,-18c-3.87,0,-7,3.13,-7,7c0,5.25,7,13,7,13s7,-7.75,7,-13c0,-3.87,-3.13,-7,-7,-7zm-5,7c0,-2.76,2.24,-5,5,-5s5,2.24,5,5c0,2.88,-2.88,7.19,-5,9.88c-2.08,-2.67,-5,-7.03,-5,-9.88zm5,0m-2.5,0a2.5,2.5,0,1,1,5,0a2.5,2.5,0,1,1,-5,0"/>
</svg>

SVG - How to apply a gradient transform matrix to a linear gradient brush?

I'm writing a SVG viewer application in c++ language. I actually face a transform issue in several SVG files that I cannot figure out.
Considering the following SVG file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg id="svg9686" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="90mm" width="145mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 145 90" xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs id="defs9680">
<linearGradient id="linearGradient6593-0" gradientUnits="userSpaceOnUse" x1="74.658" y1="-398.92" x2="75.519" y2="-485.7" gradientTransform="matrix(1.0069 0 0 1.19 1.4571 709.77)">
<stop id="stop6595" stop-color="#be245a" offset="0"/>
<stop id="stop6600" stop-color="#e46e6f" offset=".48408"/>
<stop id="stop6597" stop-color="#f1a769" offset="1"/>
</linearGradient>
</defs>
<g id="layer1" transform="translate(-7.631 -139.36)">
<rect id="rect3690-4-2-09-4-2-8-0" height="90" width="145" y="139.36" x="7.631" fill="url(#linearGradient6593-0)"/>
</g>
</svg>
This is basically a rectangle filled with a gradient brush, from orange to magenta. The rectangle size is 90x145, located at coordinates [0, 0] after all the transformations are applied.
If I well understood the theory, to draw the rectangle correctly, I should process the following steps:
Calculate the gradient brush bounding box, given by the x1, y1, x2 and y2 values, in the local document coordinates system. This should be done by applying the given gradient transform matrix to the points computed from x1, y1, x2 and y2
As the gradient units are declared to be "user space on use", calculate the brush from the linear gradient tag values and the transformed bounding box
Transform the rectangle coordinates to fill, to also put it in the document coordinates system
Using the previously created brush, draw the rectangle at his transformed coordinates
Applying the above described process, I expected to reach the following result:
But I get this result:
If I manually change all the values in the above source file, to remove all transformations and apply all the values in the document coordinates, the linear gradient is filled correctly in my rectangle. For that reason, I would be really thankful if somebody could explain to me
What I'm doing wrong in my process?
How I should calculate the linear gradient values?
How should I apply the gradient matrix to the given values? (i.e. I expected that applying the matrix to the values should transform them in the document system coordinates, so the transformed values should roughly give x1 = 0, y1 = 0, x2 = 90 and y2 = 145 as result, but it's not the case)
NOTE a demonstration in mathematical form would be welcome
First, using the term "bounding box" for the gradient I think is not helpfull. The four values x1, x2, y1, y2 describe a vector onto which the gradient stops are matched, and to which the gradient normal is perpendicular (before applying any transform). A "box" has no meaningfull relation to the gradient properties.
The gradient vector could be visualized as a line element
<line x1="74.658" y1="-398.92" x2="75.519" y2="-485.7" />
The first step is to apply gradientTransform="matrix(1.0069 0 0 1.19 1.4571 709.77)". The resulting line would be drawn as
<line x1="76.6302" y1="235.055" x2="77.4972" y2="131.787" />
(Since the transformation introduces no skew, the gradient normal is still perpendicular to that line.)
At this point, the gradient is applied to the rectangle in its local coordinate system
<rect width="145" height="90" x="7.631" y="139.36"/>
<line x1="76.6302" y1="235.055" x2="77.4972" y2="131.787" />
Only after that, the final transform="translate(-7.631 -139.36)" is applied to both the rectangle and the vector:
<rect width="145" height="90" x="7.631" y="139.36"/>
<line x1="70" y1="95.7" x2="69.87" y2="-7.57" />
Note that this would even be true if the transform had been applied to the rect directly, instead of an enclosing group. Applying the transform to an element is always the last operation to perform.
Where you went wrong, I think, is interpreting userSpaceOnUse as the final coordinate system after applying transforms to the rectangle. But what it is, is the coordinate system
in place at the time when the gradient element is referenced,
thus before further transformations.

how to split one path into two paths in svg

I'm very new to svg syntax and I want to know how I can split a path into two paths. actually I have something like this:
M Xm Ym ... C Xc1 Yc1 Xc2 Yc2 Xc3 Yc3 (*) C Xd1 Yd1 Xd2 Yd2 Xd3 Yd3 C ...
(*) is where I want to split the path
and I want to convert it to two paths like this:
M Am Bm ... C Ac1 Bc1 Ac2 Bc2 Ac3 Bc3
and
M An Bn C Ad1 Bd1 Ad2 Bd2 Ad3 Bd3 ...
How to calculate A and B numbers by X and Y nums?
If you can rely on the path commands being absolute (ie capital letters like 'C' rather than 'c'), then it is easy.
M Xm Ym ... C Xc1 Yc1 Xc2 Yc2 Xc3 Yc3 (*) C Xd1 Yd1 Xd2 Yd2 Xd3 Yd3 C ...
would become
M Xm Ym ... C Xc1 Yc1 Xc2 Yc2 Xc3 Yc3
and
M Xc3 Yc3 C Xd1 Yd1 Xd2 Yd2 Xd3 Yd3 C ...
That is, just use the last coordinate pair from the previous path command.
However be aware that, if the path has a fill, splitting it like this may mess up the fill.
If the path has relative path commands (eg. c) - particularly the command before the split - then you will need to do a lot more work. You will need to work out what that last coordinate is in absolute terms before you can use them in the inserted M command.
Example:
<svg width="200" height="200" viewBox="0 0 20 20">
<path transform="translate(10,10)"
d="M -10,0
C -10,-5.5 -5.5,-10 0,-10
C 5.5,-10 10,-5.5 10,0"/>
</svg>
<svg width="200" height="200" viewBox="0 0 20 20">
<path transform="translate(10,10)" fill="red"
d="M -10,0
C -10,-5.5 -5.5,-10 0,-10"/>
<path transform="translate(10,10)" fill="green"
d="M 0,-10
C 5.5,-10 10,-5.5 10,0"/>
</svg>

Trouble creating a closed line chart in svg

I've got a react component which is creating an svg line chart (I'm not using a library, just creating the svg itself).
Problem is, when I add a final point to the path to return to the starting point, I have a strange 45degree angle showing up at the end of the chart.
Can anybody explain why this is not nicely closed?
Here's an example https://jsfiddle.net/7svavrmu/1/
From what I understand, the final L 0 300 should be returning the path to the origin.
Here's the code
<svg width="300" height="67.40652464075235">
<path fill="blue" stroke="black"
d="M 0 40.32613081539207
L 0.15306122448979592 40.990776224724726
L 0.25510204081632654 41.834373941621585
L 0.30612244897959184 62.31225269212592
L 299.0816326530612 45.84534164491692
L 299.33673469387753 65.256033885832
L 299.48979591836735 45.314084715607414
L 300 45.27080004137377 L 0 300 "></path>
</svg>
In SVG paths, each letter is an instruction and the following numbers are the coordinates for that instruction.
Your path ends at a strange location, L 0 300 is the bottom left location but way off the viewport, you need to "draw" the bottom part of your graph by removing the last instruction and adding L 300 67 (bottom right corner) and L 0 67 (bottom left corner). Putting it all together your path needs to look like this:
d="M 0 40.32613081539207
L 0.15306122448979592 40.990776224724726
L 0.25510204081632654 41.834373941621585
L 0.30612244897959184 62.31225269212592
L 299.0816326530612 45.84534164491692
L 299.33673469387753 65.256033885832
L 299.48979591836735 45.314084715607414
L 300 45.27080004137377 L 300 67 L 0 67"

Best way to move a group of SVG elements

I'm looking for the best way (the faster, the cleaner...) to move a group of SVG elements. I have three ways in mind :
do a loop on all elements and, for each of us, change the x and y attributes
group all elements in a svg element and change its x and y attributes
group all elemnts in a g element and apply the method described here : https://stackoverflow.com/a/14036803/2019761
Do you have an idea please ?
You can move svg groups or elements with javascript
// translate svg element
function translate( _element , _x , _y )
{
var transform = _element.transform.baseVal.getItem(0);
var mat = transform.matrix;
mat = mat.translate( _x, _y );
transform.setMatrix( mat );
}
see it in action:
http://www.janvas.com/illustrators_designers_developers/projects/janvas2D_web/examples_EN.php?exampleName=ufo_animation_EN
I think that the better way is to move a group of elements.
If you look the example you can see that the ufo are translated
and the inner motor rotate inside of it.
(all moved elements are groups)
<g xmlns="http://www.w3.org/2000/svg" transform="matrix(1 0 0 1 -12.5067 69.4101)" id="ufo">
<g transform="matrix(1 0 0 1 0 -2.842170943040401e-14)">
<path transform="matrix(1 0 0 1 21.6 2.8)" width="92.34371368613222" height="91.4899957511011" stroke-width="0.83" stroke-miterlimit="3" stroke="none" fill="url(#_1_)" d="M46.1,0 C71.67,0 92… "/>
</g>
<g transform="matrix(0.5 0.86 -0.86 0.5 74.6 24.1)" id="motor">
<path transform="matrix(1 0 0 1 9.7 -2.2)" width="13.11" height="13.5849" stroke-width="0.88" stroke-miterlimit="3" stroke="none" fill="url(#_4_)" d="M6.55,2.8… "/>
</g>
</g>
Interacting with DOM methods involves JS <-> native code overhead. Browser implementers have been working hard to reduce this overhead, but it's never going to be free. If you're doing a lot of it, such as setting x and y on a lot of elements, you may start to see a significant performance impact. In this case setting positional properties just once on an <svg> or <g> container will likely help.
A more significant source of overhead is likely to be the work to repaint for the changes you make. If these changes are for a transform change, and if the transform's value changes multiple times in a short space of time, then most implementations will paint the content of the transformed SVG element into a cached offscreen surface and composite that surface instead of repainting each time. Recompositing can be a lot faster than repainting if the contents of the element are expensive to paint (say it contains a lot of children, or expensive filter effects), so if you're animating the transform of a <g> then you could well see much better performance.

Resources