6

I'm trying to do something that i though would be pretty simple: Replacing all Arcs in an SVG path with Cubic Bezier Curves.

This: http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes doesn't really help me as it doesn't really say anything about conversion.

I know how to make simple Arcs, but the SVG Arcs do have a lot of parameters.

So what i need is basically just an algorithm that takes:

rx ry x-axis-rotation large-arc-flag sweep-flag x y

(and also the start point of the arc)

and calculates:

x1 y1 x2 y2 x y

(of course, the start point, x and y keep the same values...)

Does anybody know something like this?

Thanks in advance! :-)

Vogel Vogel
  • 271
  • 1
  • 4
  • 11

4 Answers4

6

Turns out this is impossible. I mean, mathematically, at least. You can't do circular arcs with plain cubic Bezier curves, you're always going to have an error, so depending on the arc's angle, you're going to probably need more than one Bezier curve.

With that said, cubic Bezier curves work quite well for quarter-circle arcs, so if you have arcs smaller than that, you can use a single curve, and if you have wider arcs, you can simply divide it by a sensible number (between quarter and half circle? Use two curves. Between half and three quarts? Use three. Full circle? Four curves. Pretty simple).

So, how much work is this? Turns out, if you have to do it from scratch: a fair bit of work, but you can just jump to the "okay so what is the final formula I need" and then it becomes relatively simple.

If we have angle phi, then the cubic curve approximation of your arc, provided we align the start of our arc with the x-axis (so that the arc starts at y=0, and runs counter clockwise from there), and we have an arc radius R, is:

start coordinate = {
  x: R,
  y: 0
}

control point 1 = {
  x: R,
  y: R * 4/3 * tan( phi / 4)
}

control point 2 = {
  x: R * ( cos(phi) + 4/3 * tan( phi/4 ) * sin(phi) ),
  y: R * ( sin(phi) - 4/3 * tan( phi/4 ) * cos(phi) ),
}

end coordinate = {
  x: R * cos(phi),
  y: R * sin(phi)
}

Maths! But really just "plug in angle, get the coordinates we need". Simple!

But what if our arc is not aligned to the x-axis? We apply a dumb "rotate + translate" to put align our arc, and then we run the rotation/translation in reverse when we're done. Explained here.

Mike 'Pomax' Kamermans
  • 40,046
  • 14
  • 84
  • 126
  • 1
    This looks way too easy, why would we just "align" the start of the arc? It has to be exactly where the last segment ended... Also, approximations are okay, the "dividing into pieces" way of making it work is totally fine :) – Vogel Vogel May 16 '15 at 19:37
  • 1
    The trick is that you're not going for "exact". Being off at the 2nd or 3rd decimal place on a computer screen, which rounds to integers, is absolutely irrelevant. You need to align the arc if you want to use these specific formulae. If you don't, you'll need to work out a lot of maths (see the dark box in the Primer section linked) that can be bypassed very efficiently with an RT, then abstracting the s/c1/c2/e coordinates based on the aligned arc, and then applying the inverse RT. – Mike 'Pomax' Kamermans May 16 '15 at 19:53
  • 1
    Sounds good :) Still, if i had a point like (5,7) i could just do the translation (0,-7) and I'm at the x-axis. No need to rotate :/ – Vogel Vogel May 16 '15 at 19:56
  • 1
    you'll need to move it slightly differently, because you need `start` to end up on (`r`, 0) (so on the x-axis, which means y-coordinate is 0, and on the x coordinate that corresponds to the arc's radius). – Mike 'Pomax' Kamermans May 16 '15 at 19:59
  • 2
    What makes it even more complicated is that the arc is not part of a circle, but an ellipse. So it actually has two radii rx and ry. Still, assuming a single radius of 3, something like (5,7) could just be translated by (-2, -7) to become (3, 0) and there is no need to rotate(?) :( – Vogel Vogel May 16 '15 at 20:04
  • 3
    for the ellipse mapping you'd need to find the major/minor axes, scale the ellipse along the minor axis to a circle, do the arc approximation, and then scale back along the minor axis. Bit more work. – Mike 'Pomax' Kamermans May 16 '15 at 20:12
5

Most SVG rendering libraries have to do this because 2D graphics libraries don't seem to directly support arcs that are rotated with respect to the X axis.

So you could look up the code in, say, Batik. Or look at the arcTo() method in my SVG library (which also borrows from Batik):

https://github.com/BigBadaboom/androidsvg/blob/master/androidsvg/src/main/java/com/caverock/androidsvg/SVGAndroidRenderer.java#L2598

It's Java, but should be easily converted to JS.

Paul LeBeau
  • 81,507
  • 8
  • 120
  • 146
2

You can also look at this function from SnapSVG which might have been used somehow by Adobe Illustrator (it is hosted by a user named "adobe-webplatform"), to convert arc to cubic commands. It's also used in SVGO(ptimizer).

I'm still trying to decypher it, but the standard is actually pretty helpfull for that.

cdoublev
  • 451
  • 4
  • 13
0

You can check those links:

Arc to Qubic by Dmitry Baranovskiy And also the same implementation by Jarek Foksa.

Based on W3.org

Ievgen Naida
  • 3,596
  • 6
  • 64
  • 117