Explanation of SVG path? - svg

I have an SVG path that I'm trying to dynamically create, but the resources I've found on SVG paths don't describe that path I have.
This is the path:
m41.5 1.5v15l-40 10v100h60v-100l-10-10v-15z
I don't even understand the start of it - the m41.6; m moves the pen to a coordinate relative to the last known position. I assume this is the origin? but everywhere I've found says that the syntax is m x,y. If I supply m41.6,0 instead, the svg just disappears - opening in Inkscape doesn't show it either.
I need a step-by-step explanation please of each term.
The full SVG is:
<svg id="svg11" version="1.1" viewBox="0 0 63 128" xmlns="http://www.w3.org/2000/svg">
<path d="m41.5 1.5v15l-40 10v100h60v-100l-10-10v-15z" fill="#ffd42a"/>
</svg>

the resources I've found on SVG paths don't describe that path I have.
Then I guess you didn't consider reading the SVG specification? :)
Try reading the Paths section of the SVG specification . It's all explained quite clearly there.
Explanation
m41.5 1.5
Move to (41.5, 1.5). m is a relative move normally, but the spec says:
If a relative moveto (m) appears as the first element of the path, then it is treated as a pair of absolute coordinates.
Coordinates can be separated by a comma, whitespace, both, or even neither. For example, M1.5.5 is a valid path command, since a coord can't have more than one decimal point, and leading zeros are optional.
M 1.5,0.5, M1.5 0.5, M1.5, 0.5, M 1.5 .5, and M1.5.5 are all equivalent.
v15
Draw a line vertically (downward) by 15 units
l-40 10
Draw a line left by 40 and down by 10 units.
v100
Draw a line down by 100 units
h60
Draw a line right by 60 units
v-100
Draw a line upwards by 100 units
l-10-10
Draw a line diagonally up and left by (10,10)
v-15
Draw a line vertically upwards by 15 units
z
Close the path (ie back to 41.5,1.5)

The path can be rewritten as:
m41.5,1.5 v15 l-40,10 v100 h60 v-100 l-10,-10 v-15 z
or
m41.5 1.5 v15 l-40 10 v100 h60 v-100 l-10 -10 v-15 z
This better separates the individual pen movements which I was confused by when they were all merged together in the original version.

Related

How do SVG/EPS (vectors) clip a path by another path?

I try to understand how SVG programs (like browsers) draw a shape by the given paths. I struggle to understand how a path is drawn and one clips part of a shape.
For example, consider the letter Å, and A with a top ring. The SVG code is like
<svg viewBox="0 0 1800 1800" xmlns="http://www.w3.org/2000/svg">
<path
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M426 822q0 -47 -24 -71.5t-80 -24.5q-104 0 -104 96q0 46 25.5 71t78.5 25q56 0 80 -25t24 -71z
M319 515h-1l-85 -264h169z
M374 822q0 51 -52 51q-50 0 -50 -51q0 -49 50 -49q52 0 52 49z
" />
</svg>
JSFIDDLE
First line: draws the body of an A.
Second line: draws a top circle.
Third line: clips a triangle from the first line.
Fourth line: clips a small circle from the second line.
My question is: how do SVG programs understand to draw a shape by the second line, but clip a shape from an existing shape?
Obviously the answer is: if the path is within another path, it clips otherwise it draws.
There are two reasons that I think this not the whole picture:
It needs huge calculations to find if a path is within another path.
The order of lines is not important (the clipping path does not necessarily come after the drawing path).
This is, of course, not limited to SVG, as other vector formats such as EPS does the same.
To add a pragmatic perspective, please read the question as: how can we parse (in any programming language) the above d element to find out which path is drawing (black) and which is clipping (white) out of the four paths given in the above SVG?
Broadly speaking, you don't parse the paths at all.
Instead you 'scan convert' each path to a series of rectangles, at the current resolution. Each reactangle may be as little as one pixel high, and there may be more than one rectangle at a given y value.
Do this for both the path to be filled or stroked and the path to apply as a clip, then intersect the series of rectangles. This is, obviously, a much simpler task. Then fill the rectangles.
This does, of course, produce a result which is only correct at a given resolution. Change the resolution and you get a different series of rectangles. However it produces the correct output at a decent speed. Intersecting two arbitrary paths to produce a new arbitrary path is obviously a much more complex task, and for the purpose of drawing the result, not one we need to perform.
In the next examples I'm using the path for the letter A in your example.
In the first svg element the letter A is drawn from right to left while the hole of is drawn in the opposite dirrection: you get the "clipping".
In the second example I've reversed the part that is drawing the hole. Now this part is drawn in the same direction as the main part of the letter A. Now you won't get the "clipping"
In the third example I'm using the reversed path as before but I'm adding fill-rule="evenodd" Now the hole is clipped since the "fill-rule attribute is a presentation attribute defining the algorithm to use to determine the inside part of a shape".
svg{width:30%;border:solid}
<svg viewBox="-40 -40 900 900" xmlns="http://www.w3.org/2000/svg">
<path
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M319 515h-1l-85 -264h169z
" />
</svg>
<svg viewBox="-40 -40 900 900" xmlns="http://www.w3.org/2000/svg">
<path
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M319,515L402,251L233,251L318,515L319,515z
" />
</svg>
<svg viewBox="-40 -40 900 900" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M319,515L402,251L233,251L318,515L319,515z
" />
</svg>
Whether a given path is "filled" or "clipped" depends on the "winding" algorithm being used, which is determined by the SVG fill-rule property, which defaults to nonzero.
For the nonzero mode:
This rule determines the "insideness" of a point on the canvas by drawing a ray from that point to infinity in any direction and then examining the places where a segment of the shape crosses the ray. Starting with a count of zero, add one each time a path segment crosses the ray from left to right and subtract one each time a path segment crosses the ray from right to left. After counting the crossings, if the result is zero then the point is outside the path. Otherwise, it is inside. The following drawing illustrates the nonzero rule:
The nonzero default is why you'll often hear that rotational direction is important, because clockwise creates fills and counterclockwise creates clips. (This is how it works in GeoJSON as well.)
For the evenodd mode:
This rule determines the "insideness" of a point on the canvas by drawing a ray from that point to infinity in any direction and counting the number of path segments from the given shape that the ray crosses. If this number is odd, the point is inside; if even, the point is outside. The following drawing illustrates the evenodd rule:
The evenodd mode is a simpler to understand for basic shapes with holes, but not as flexible for clipping arbitrary chunks out that may not be completely isolated as islands.
Here's a great article called Understanding the SVG fill-rule Property that explains it further with code examples.

Exporting designs in apps as vector (XML/svg) file?

My ultimate goal is to export designs created in mobile apps as vector graphics. Say I have a list of points of corners of shapes and the respective color that goes inside each shape and this design is being displayed on a mobile app (iOS and Android because cocos2d-x is being used). Is it possible to convert this information into a vector file (SVG file which is essentially an XML file)?
SVG contains a path element that stores the lines that make up a shape's path. The d attribute stores a path string, and the style attribute stores the path's styling, such as stroke and fill. To export a shape to SVG there are several things you should care about:
The size of the SVG in pixels (the width and height attributes).
Example:<svg width='640px' height='640px'
The size of the shape in pixels (the viewBox attribute: left, right, width, height).
Example:viewBox='0 0 100 100'
The color used to stroke the shape and the stroke width.
Example:style='stroke:red;stroke-width:1px' or style='stroke:none;'
The color used to fill each shape.
Example:style='fill:blue;' or style='fill:none;'
The shape of the path.
Example:d='M10 10L20 20 L30 10Z'
Each path is divided into commands. The M command moves the pen,
the L command draws to a new position, and the Z command closes the shape.
There can be any number of paths in a single SVG file.
Example SVG file:
<svg width='640px' height='640px' viewBox='0 0 100 100'
xmlns='http://www.w3.org/2000/svg'>
<path style='stroke:red;fill:none' d='M10 10L20 20 L30 10Z'/>
</svg>

SVG path spec: moveTo and implicit lineTo

I am trying to write a little SVG path parser / normalizer and got one last issue with the spec:
As far as I understood the most commands support additional implicit commands and when they do so and are in relative mode, the "current point" will be updated after the last implicit command, not in between them.
But the "moveTo" command is something special and allows implicit "lineTo" commands. While the "lineTo" command it self will only update the "current point" after the last implicit command:
Draw a line from the current point to the given (x,y) coordinate which
becomes the new current point. L (uppercase) indicates that absolute
coordinates will follow; l (lowercase) indicates that relative
coordinates will follow. A number of coordinates pairs may be
specified to draw a polyline. At the end of the command, the new
current point is set to the final set of coordinates provided.
I am not sure what the "moveTo" with additional "lineTo" does.
Extract of the SVG Path Spec:
Start a new sub-path at the given (x,y) coordinate. M (uppercase)
indicates that absolute coordinates will follow; m (lowercase)
indicates that relative coordinates will follow. If a moveto is
followed by multiple pairs of coordinates, the subsequent pairs are
treated as implicit lineto commands. Hence, implicit lineto commands
will be relative if the moveto is relative, and absolute if the moveto
is absolute. If a relative moveto (m) appears as the first element of
the path, then it is treated as a pair of absolute coordinates. In
this case, subsequent pairs of coordinates are treated as relative
even though the initial moveto is interpreted as an absolute moveto.
Especially the last sentence is confusing.
And even worse, in SVGTiny Path Spec they wrote another description, while almost everything else is the same:
A new sub-path at the given (x,y) coordinate shall be started. This
shall also establish a new current point at the given coordinate. If a
relative 'moveto' (m) appears as the first element of the 'path', then
it shall treated as a pair of absolute coordinates. If a 'moveto' is
followed by multiple pairs of coordinates, the subsequent pairs shall
be treated as implicit 'lineto' commands.
Does it mean that the "current point" is updated in between (which would be inconsistent to everything else) or was it just a ambiguous description which they corrected in newer versions?
Well it all seems perfectly clear to me.
Here's an illustration of the two modes being used to draw two square (100px × 100px) boxes. The first uses absolute coordinates, and the second uses relative coordinates. As the spec states, when the first coordinate is specified with a lower-case 'm', then it is treated as an absolute coordinate, but all the coordinates that follow it are treated as relative.
<svg widtn="250" height="140" viewBox="0 0 250 140">
<g fill="none" stroke-width="5">
<!-- 1. Move with implicit LineTo (absolute) -->
<path d="M10,10 110,10 110,110 10,110z" stroke="blue" />
<!-- 2. Move with implicit LineTo (relative) -->
<path d="m120,10 100,0 0,100 -100,0z" stroke="red" />
</g>
</svg>

SVG path element rendered inconsistently between different versions of Chrome

Good morning,
I've created a chord diagram using D3. But I've encountered a problem with the output that causes paths to be rendered poorly in some versions of Chrome.
Here's an example of a problematic path generated by D3:
<svg height="1000px" width="1000px">
<g transform="translate(400,400)">
<path d="M329.2336690603744,-46.49130195040491A332.5,332.5 0 0,1 329.2336694247276,-46.491299370194035Q 0,0 -25.421977592957564,-331.5267305290222A332.5,332.5 0 0,1 -25.42197499477598,-331.5267307282548Q 0,0 329.2336690603744,-46.49130195040491Z" class="chord" fill="#c8cfdc" stroke-width="1px" stroke="#000000"></path>
</g>
</svg>
In most browsers, I see a single arc, which is what I'd expect. But on my dev machine running Chrome version 36.0.1985.125 on Ubuntu 14.04 I see the arc on top of a big gray circle. The big circle kind of ruins the rest of the diagram.
Is there anything particularly problematic about this path's d attribute that could cause it to get painted inconsistently by the browser?
Many thanks.
Here's an image of what I'm seeing when it goes wrong:
Expanding on #jshanley's comment, the breakdown of the path data is as follows (long decimals trimmed for readability):
d="M 329,-46
//Move the pen to the starting point (329,-46)
A 332.5,332.5 0 0,1 329,-46
//draw a circular arc (radius in both directions 332.5 with 0 degrees rotation),
//in a clockwise direction taking the shortest route (flags 0,1)
//ending at point (329,-46).
//In a normal chord diagram, this is the edge of the chord, which follows the
//arc of the large circle.
//However, in this case the start and end points are the same
//and nothing should be drawn
Q 0,0 -25,-331
//draw a quadratic curve to point (-25, -331) using control point (0,0)
//this is the curve you see, connecting different points on the large circle
A 332.5,332.5 0 0,1 -25,-331
//another arc with the same start and end points, which shouldn't be drawn
Q 0,0 329,-46
//Another quadratic curve, the exact same shape as the previous one
//but going back in the opposite direction;
//this is what causes the curve to look like a single line, no fill
Z"
//close the shape
This is a definite bug in the Ubuntu Chrome version you're using. Path arc segments that start and end at the same point are supposed to be skipped, regardless of what the flag settings are, because they are not clearly defined. Even if the browser wasn't automatically skipping it, you'd think they would still respect the "short arc" flag and draw a zero-length arc.
If support for that particular browser version is important, you'll need to add in an error-check in your code, so that you don't draw the chords at all when they have zero width on both ends, or manually edit the path data to remove the empty arc commands.

Make stroke-width of a SVG path depend on position

I have a big (12 MB) SVG file containing many curved paths all having the same stroke width. Now I'd like to transform the stroke-width in a way that it depends on some mathematical function which takes the coordinates of the path segment (or even better: of the actual points on the path segment) as input.
The only way i found so far is to cut the paths in segments using inkscape and then modifying the stroke-width using the etree library for python. But what I really want is continuously decreasing/increasing stroke width like in inkscapes calligraphy tool.

Resources