SVG semi-circles and Arc parameter disambiguation - svg

As helpfully explained and demonstrated in this MDN article, SVG arcs are defined in a way that is a little tricky, especially when compared to the JS Canvas arcTo() method.
The problem arises when I want a semi-circle in my path. In a case like this, with an angle of 180°, the two possible circles overlap, and the "large-arc flag" becomes ambiguous because it's explained as "if the arc should be greater than or less than 180 degrees" - it doesn't specify what should happen if the angle is exactly 180 degrees.
Until now I've "resolved" this issue through simple trial-and-error. Take a guess, did the arc go the wrong way? Try changing a flag until it works. There's only four combinations after all, it doesn't take much work.
But I'd just like to further my understanding. In a case like this, how can I know ahead of time which way my arc will go? Is there an easy way to remember this information so I don't have to resort to trial-and-error?
And on a related note, what if I want my "arc" to be a full circle? In a case like this, there are infinitely many possible circles that meet the criteria of "passes through the starting/ending point (same point!) and has radius R", so is this resolvable at all, or should I stick to two 180° semi-circles instead?

Note that sweep-flag defines arc rotation direction. So for 180-degrees arc just choose needed semicircle with this flag. large-arc-flag doesn't influence in this case.
<svg width="325" height="325" xmlns="http://www.w3.org/2000/svg">
<path d="M 0 100
A 50 50, 0, 0, 0, 100 100
L 125 100 Z" fill="green"/>
<path d="M 200 100
A 50 50, 0, 0, 1, 300 100
L 325 100 Z" fill="green"/>
<path d="M 0 200
A 50 50, 0, 1, 0, 100 200
L 125 200 Z" fill="green"/>
<path d="M 200 200
A 50 50, 0, 1, 1, 300 200
L 325 200 Z" fill="green"/>
</svg>

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>

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"

How to convert font size (in px) to the unit used in the document (mm) for svg

I have svg created in inkscape here.
The document size is in mm. For the 2 text fields, the font size is given like this:
style="...;font-size:31.420084px;..."
So it is given in px. Now I want the size of the font related to the document size.
But how should I convert the font-size in px to mm? I would need something like dpi, but the document does not specify anything.
In your case, your viewBox matches your document dimensions:
width="164.28572mm"
height="78.10714mm"
viewBox="0 0 164.28572 78.10714"
that means all unit less values are in mm. Just specify your font-size without units (or in your case when you use css just keep your document untouched and use px).
The confusing part is, that px in svg are always user units and not device pixels, for that reason, your font-size is already given in mm... so font-size:31.420084px in your document is equal to font-size:31.420084mm in a viewBox less document (where user units equal to device pixels)
<svg viewBox="0 0 100 20" width="100mm" height="20mm">
<text x="0" y="12" font-size="12px">12px</text>
</svg>
<svg >
<text x="0" y="12mm" font-size="12mm">12mm</text>
</svg>
Thats where it gets confusing. In the next example "1mm" is equal to 3.6 user units, but because 1 user unit equals 1mm in the real world, one svg mm equals 3.6 real mm ...
<svg viewBox="0 0 50 50" width="50mm" height="50mm">
<text x="0" y="5" font-size="1mm">font-size: 1mm</text>
<text x="0" y="10" font-size="3.6">font-size: 3.6</text>
</svg>
Units in SVG are a bit wired, but well defined...
The SVGLength Interface has a Method convertToSpecifiedUnits.
you can use that to convert between different units in SVG.
var l = svg.createSVGLength()
l.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, 12)
l.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_MM)
out1.innerHTML = l.valueAsString
svg {
width: 0;
height: 0
}
<svg id="svg">
</svg>
12px = <span id="out1"></span>
The standard DPI used in an SVG document is the standard DPI that is specified by the CSS standard. That is 96dpi, or 96 standard CSS pixels per inch.
There are 25.4 mm per inch. So to convert px to mm, the formula will be:
mm = 25.4 * (px / 96)
So if we plug your original 31.420084px into that formula, the result is 8.31mm.
Note that CSS doesn't take into account the real word DPI of the device that you are rendering to. It uses that fixed approximation of 96pixels per inch. So you can't rely on the fact that an element with size 96px or 25.4mm will actually be that size on screen, or when printed.

Interpreting svg paths created in inkscape

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.

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