SVG path element rendered inconsistently between different versions of Chrome - svg

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.

Related

Explanation of SVG path?

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.

SVG to PDF rendering goes wrong - are there errors in my header?

I have a couple of SVG images that I want to paste together to make a big graphic.
First, let me present you with my problem:
This is one of the symbols, placed in a grid. The grid is, for convenience, with unit-less 100 distance from line to line.
If I render it to pdf, it looks like this instead:
Those symbols have a completely wrong size for the grid (They are much larger, mostly) and they are badly positioned if I use them raw.
So my treatment is, I scale them and position them correctly in relation to the grid, then I make a rectangle around them that encompasses them completely and makes the symbol-handling easier.
That rectangle is perfectly fitting to my grid. In my case, for this symbol, it is a rectangle encompassing the six squares around the symbol. I did this, because the symbol can be rotated by the user and the rotation is done from symbol point of view; any transform after the rotation is from the rotated point of view. So I made an attempt to de-couple the transformations by wrapping them.
Finally, I move the rectangle to a user-defined place and rotate it as the user wants to have it. So far, so well, it was extensively tested in google chrome and works reliably. In Google Chrome.
Now I wanted to translate it to pdf for printing. And after conversion, the symbol is placed in the wrong position.
I am guessing (as I made several tests) that the error is somewhere in my header.
Could someone of you please check the headers that I add and tell me if and where I did something wrong? For example there is a view box starting at -100, otherwise the symbol would be cut and wrongly placed. Is there another way to "rectangularize" any arbitrary symbol? Or is it generally the wrong way to do these kinds of things?
Here are the changes that I added around the svg symbol code. Innermost changes are applied first.
<!-- move the rectangle to the right place and rotate it as the user wishes-->
<g transform="translate(300.0, 400.0) rotate(0,150.0, 50.0) ">
<!-- a rectangle around the symbol, perfectly fitting to the grid-->
<svg x="0" y="0" width="300.0" height="200.0" version = '2.0'
xmlns="http://www.w3.org/2000/svg" viewBox="-100.0 0 300.0 200.0">
<!-- scaled to correct size, placed inside the grid as it should be -->
<g transform = "translate(-10.000, 73.614) scale(0.229,0.229)" >
<!-- original symbol, wrong size -->
<svg width="523" height="230" version = '2.0' xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 523 230">
.... lots of svg paths here ...
</svg>
</g>
</svg>
</g>

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.

Examples of polygons drawn by path vs polygon in SVG

I would like to learn SVG, and am trying to learn how the same image can be rendered by using either the point (with polygon) or by dynamically by paths (path).
I would like a few examples of the SAME polygon (triangle, square, and pentagon are enough to begin) in BOTH SVG polygon AND SVG path, so that I can compare the code. I can find individual images drawn by either, but none that are the SAME.
It's trivial: You can basically take the points attribute of a polygon and turn it into a path's d attribute by prepending M and appending z.
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
<polygon points="20,20 100,20 100,100 30,110"/>
<path d="M20,20 100,20 100,100 30,110z" fill="green" transform="translate(100,0)"/>
</svg>
Both can create shapes.
Polygon will automatically close the shape for
you (by returning to the first point) after drawing at least three
sides, and is composed of a series of connected straight lines, which
means it does not scale well.
Paths can use straight OR curved lines, and do not auto-close the
shape for you. Path is probably the most powerful basic shape element
in SVG.
Source

stroke-width doesn't scale; aspect ratio problem?

I have one or more path elements inside a g element that I am scaling to fit inside a grid rectangle. The transform is applied to the g element. My transform works in that all the points end up at the right places, but I discovered that I have to adjust the stroke-width of the path to get a readable line.
The problem is that if the scale involves a large shift in the aspect ratio, I end up with some segments of the path being heavier weight than others, depending on their orientation.
Here is a typical transform that my code computed:
scale(0.1875, -0.010397820616798718) translate(-1149000, -96174)
In this case I end up changing the stroke-width from 9px to about 48px. Segments close to the horizontal end up thin, those close to the vertical are thick.
Is there any easy way to end with all the segments with the same rendered width?
Have you looked at setting the vector-effect attribute to non-scaling-stroke?
<line vector-effect="non-scaling-stroke" stroke="black" stroke-width="5"
x1="32" y1="50" x2="32" y2="350"/>
http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke
UPDATE
The best solution I can think of is to manually transform the coordinates of your path.
vector-effect="non-scaling-vector" is not consistently supported. My versions of Firefox and Safari do not support it, but my Chrome browser does.
In the SVG standard there is no way of specifying a transformation for the stroke independantly. (A stroke-transform attribute would be nice - like the windows GDI+ drawing system, where a Pen object has it's own local transformation).
Until this changes, the best solution I can think of is to manually work out the coordinates of your path - that is the svg has no transform elements; the coordinates are already transformed.

Resources