0

Here's my d parameter:

M1 40.00000000000003 69.28203230275508A80 80 0 0 -80 -9.797174393178826e-15

I receive an error:

Error: <path> attribute d: Expected number, "…9.28203230275508A80 80 0 0 -80 -…".

For some reason I only get this error when describing arcs that are greater than 180 degrees, 179' and smaller work just fine.

Update: Seems to be a regex issue surrounding how the path param is generated. Here's the block that's creating it:

let firstArcSection = /(^.+?)L/;

let newArc = firstArcSection.exec( d3.select(this).select('path').attr('d') )[1];
newArc = newArc.replace(/,/g , " ");

if (d.endAngle > 90 * Math.PI/180) {
    let startLoc    = /M(.*?)A/,
        middleLoc   = /A(.*?) 0 [01] 1/,
        endLoc      = / 0 [01] 1 (.*?)$/;
    let newStart = endLoc.exec( newArc )[1];
    let newEnd = startLoc.exec( newArc )[1];
    let middleSec = middleLoc.exec( newArc )[1];

    newArc = `M${newStart}A${middleSec} 0 0 0 ${newEnd}`;
}

The text still shows up on the wrong side of the arc, but there's no error now at least

myrcutio
  • 1,045
  • 9
  • 13
  • Your path's data starts with a `M1`! Is this a typo or is it real? If this is real the number `69.28203230275508` will be interpreted as the beginning of a subsequent `M` command, which then is missing its second coordinate. Hence the error about the expected number. – altocumulus Nov 02 '16 at 00:55
  • It is indeed real. It's using the regex manipulation in this example to generate the path: http://bl.ocks.org/nbremer/b603c3e0f7a74794da87 – myrcutio Nov 02 '16 at 03:37
  • 1
    @altocumulus if M1 is real, the number 69.282... will be interpreted as the beginning of an L command and not an M you are right that the second coordinate is missing from that though. – Robert Longson Nov 02 '16 at 07:33
  • @RobertLongson Do you have any idea why it is interpreted like this? The [spec](https://www.w3.org/TR/SVG/paths.html#PathData) pretty clearly differs from this interpretation *"The command letter can be eliminated on subsequent commands if the same command is used multiple times in a row*". That's where I got it from. A [JSFiddle](https://jsfiddle.net/x1m0dbp5/), however, backs your assumption. Can you shed some light on this? – altocumulus Nov 02 '16 at 09:48
  • 1
    @myrcutio Now we are narrowing in: The regex are looking for substrings containing `0 0 1`, i.e. both the `large-arc-flag` as well as the `sweep-flag` are expected to be `0` (see https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands). For arcs exceeding 180 deg, however, the `large-arc-flag` is flipped to `1`, which won't be matched by the regex anymore. [Adjusting the regex](http://blockbuilder.org/anonymous/e2207823b671eebd9f48801593d1f44f) to `0 [01] 1` will avoid the error but also draw the text on the inside. This has to be corrected but should get you back on track. – altocumulus Nov 02 '16 at 10:03
  • @altocumulus because that's what the specification says for move commands: https://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands – Robert Longson Nov 02 '16 at 11:20
  • @RobertLongson Alright, I am going to take this for granted, although this is a contradiction in the specification. Thank you for the clarification. – altocumulus Nov 02 '16 at 11:27
  • @altocumulus I think you're right that the `0 0 1` checking is throwing it off somehow. I'm still getting the error with the `[01]` change in the regex but manually modifying the resulting path seems to silence it. I'm thinking it may have something to do with the `0 0 0 ` in the concatenation line missing a separator at the beginning. – myrcutio Nov 02 '16 at 17:39
  • Have you actually looked at this [example](http://blockbuilder.org/anonymous/e2207823b671eebd9f48801593d1f44f) I linked to in my above comment, where I got it working without the error? – altocumulus Nov 02 '16 at 17:42
  • Yep, I think it has to do with some of the path data ending in 80 right before the `0 1 1`. It's actually matching the `0 [01] 1` twice, since the regex doesn't have a space prior to it. Here's what I'm using now: `let startLoc = /M(.*?)A/, middleLoc = /A(.*?) 0 [01] 1/, endLoc = / 0 [01] 1 (.*?)$/;` – myrcutio Nov 02 '16 at 17:53

1 Answers1

0

Found it! Big thanks to @altocumulus for identifying the wayward regex. The regex parsing the original arc path was matching too much (80 0 1) in cases of positions ending in 0 and not enough (0 1 1) in cases where the large-arc-flag was set. Also I discovered that in some cases d3 created arcs with rounding errors that resulted in two 180' arcs within the same pie chart. Here's the working code:

let firstArcSection = /(^.+?)L/;

let newArc = firstArcSection.exec( d3.select(this).select('path').attr('d') )[1];
newArc = newArc.replace(/,/g , " ");

let middleLoc = /A(.*?) 0 ([01]) 1/;
let middleSec = middleLoc.exec( newArc );

if (d.endAngle > 90 * Math.PI/180 && (middleSec[2] == '0' || d.startAngle > Math.PI / 2 )) {
    let startLoc    = /M(.*?)A/,
        endLoc      = / 0 [01] 1 (.*?)$/;
    let newStart = endLoc.exec( newArc );
    let newEnd = startLoc.exec( newArc );

    newArc = `M${newStart[1]}A${middleSec[1]} 0 0 0 ${newEnd[1]}`;
}
myrcutio
  • 1,045
  • 9
  • 13
  • 1
    It would be much safer and simpler to use the [SVG DOM](https://www.w3.org/TR/SVG/paths.html#InterfaceSVGPathSeg). No parsing or regex would be required if you did that. And it would be much faster. – Robert Longson Nov 02 '16 at 22:21
  • Not sure I follow, do you have a jsfiddle that shows the same transformation in action? – myrcutio Nov 03 '16 at 00:38
  • There's plenty of stuff if you search for it. This is the first thing I came across. http://www.programcreek.com/java-api-examples/index.php?api=org.w3c.dom.svg.SVGPathSeg – Robert Longson Nov 03 '16 at 05:37
  • The coordinate interfaces in the java examples aren't available in JS or d3, unless I'm missing something. All the search results for reversing path arc start and end points (in javascript) lead to solutions in line with the regex version above. I'd be happy to learn about any other tools which can do this, but "svg dom" by itself is not enough to go on. – myrcutio Nov 03 '16 at 16:08
  • you're missing something, there's lots of information out there. http://stackoverflow.com/questions/8053487/scripting-path-data-in-svg-reading-and-modifying – Robert Longson Nov 03 '16 at 20:08
  • Ok, now we're getting somewhere, though that link has some deprecated methods. I found SVGDOM.getTotalLength and SVGDOM.getPointAtLength, which gives me starting and ending points. Could you clarify which function then creates a new reversed arc from those two points? – myrcutio Nov 04 '16 at 18:56
  • there is no such "reverse an arc" function but there is a function to get the current arc values which you can then modify to create your reversed arc. – Robert Longson Nov 04 '16 at 19:17
  • Perhaps you could provide a solution that illustrates this? – myrcutio Nov 04 '16 at 20:13
  • Perhaps you could provide a [mcve] I could correct. – Robert Longson Nov 04 '16 at 22:19