0

Short version: How can I convert an SVG elliptical arc to a QPainterPath arc?

Long version: Using SVG++ my code receives a callback in parsing SVG files when it comes curves, ellipses or circles.

void onPathEllipticalArcTo(double rx, double ry, double x_axis_rotation, bool large_arc_flag, bool sweep_flag, double x, double y);

where:

  • rx is the X radius of the ellipse
  • ry is the Y radius of the ellipse
  • x_axis_rotation is effectively the rotation of the ellipses in related to the X-axis
  • large_arc_flag indicates whether we mean the outer or the inner arc
  • sweep_flag indicates the direction of the arc
  • x and y are the end point coordinates of the arc

See this SVG article on elliptical arc curves for an illustration on what these parameters mean. Note that the starting point of the curve is passed to my program using a simple moveTo(x,y) instruction sent ahead of the above instruction.

All of this needs to be translated into a call to QPainterPath::arcTo() [c++] [python].

The problem is, the way in which both achieve the arc is done rather differently as you can see. A QPainterPath::arcTo() expects:

  • The rectangle in which the curve will fit
  • The starting angle of the curve
  • The sweep length of the curve

See the linked article(s) for an illustration on how this is achieved.

I think there is a fair amount of maths/geometry involved in this conversion.

My (naive) approach so far comes down to: enter image description here

In this image (see earlier SVG callback information):

  • [A] are the coordinates that SVG++ have us moveTo() before we draw the arc.
  • [B] are the coordinates that SVG++ will have us draw the arc to (i.e. x and y params).
  • rx and ry are obviously the radii of the ellipse.
  • x_axis_rotation is 0 degrees.
  • large_arc_flag would be 0/false.
  • sweep_flag would be 0/false.

To be able to call QPainterPath::arcTo() I need to work out the shaded rectangle. The width of this rectangle is of course very easily calculated. The starting angle of the curve will conveniently be zero deg and sweep angle in this instance will be 180 deg (but it may not be if A and B are not on the same Y coordinate).

But how do I calculate the height of the rectangle?

If it helps in explaining the maths to us, let:

  • A = { 2, 3}
  • B = { 6, 3}
  • rx ~= 2.45
  • ry ~= 4.28

Maybe your solution would demonstrate also that it works even when [A] and [B] are not on the same X-axis or the same Y-axis?

The ultimate goal is that my application needs to be able to draw an arc upon a received SVG instruction. I am aware that there is a chance that I can achieve this in a much simpler way than I suggest here?

Important: I cannot use class QSvgRenderer. Apparently it is too limited in functionality for the complexity of SVG that we parse? So I am told.

Note: there is an error in my logic relating to QPainterPath::arcTo(). This does not invalidate the comments thus far. Once I have a proven solution then I will update this question with the correct logic and an enhanced graph.

iwarv
  • 335
  • 4
  • 13
  • Seems https://stackoverflow.com/a/12329083/844416 is what you need – MBo Oct 03 '22 at 08:47
  • Hmm the centre Y on the illustrated arc would indeed lead to to the height of the rectangle. That helps me in the short term, so thanks. But this would not work if [A] and [B] are not aligned horizontally or vertically, right? – iwarv Oct 03 '22 at 08:53
  • That approach works with rotated ellipses, but I don't see that QPainterPath::arcTo is able to use rotation. Perhaps rotation transform should be applied to canvas? (I know nothing about QPainter). I know how to draw ellipse arcs with Bezier cubic curve. If you won't find solution with arcTo, it might be useful... – MBo Oct 03 '22 at 09:12
  • Sorry, I just figured out that the linked article does _not_ calculate the midway point of the arc. It calculates the centre of the ellipse that is described by the arc. That's not going to give me my rectangle. – iwarv Oct 04 '22 at 08:34
  • But having ellipse rectangle and its sizes you know bounding rectangle - as far as I understand, you need rectangle for ellipse rather than for arc itself: https://doc.qt.io/qt-6/images/qpainterpath-arcto.png – MBo Oct 04 '22 at 08:45
  • Of course: height = y[A] - (y[H] - ry) – iwarv Oct 04 '22 at 08:58
  • if your Qt painterpath arc is axis aligned ellipse then there is no way of using it as SVG elliptic arcs can be rotated (unless you have a way to rotate the output)... instead see [Express SVG arc as series of curves](https://stackoverflow.com/a/59300224/2521214) and use Bezier or whatever other cubic you have at disposal instead... – Spektre Oct 05 '22 at 06:11

0 Answers0