The SVG arc command consists of
A rx ry x-axis-rotation large-arc-flag sweep-flag x y
- rx: Ellipse radius of x-axis
- ry: Ellipse radius of y-axis
- x-axis-rotation: Coordinate system rotation in degrees
- large-arc-flag: Flag if large or small circle should be taken
- sweep-flag: Flag on which side of the line between start and end should the circle is drawn
- x: The final x-coordinate
- y: The final y-coordinate
For the case that \(r_x\neq r_y\), we can only sample from the path and interpolate it linearly. But for the case that \(r_x=r_y\), it’s possible to improve the gcode by using G2 and G3. In general, the syntax of G2 and G3 are
G2 I<offset> J<offset> R<radius> [X<pos>] [Y<pos>]
G3 I<offset> J<offset> R<radius> [X<pos>] [Y<pos>]
- I: An offset from the current X position to use as the arc center
- Y: An offset from the current Y position to use as the arc center
- R: The radius from the current XY position to use as the arc center
- X: The final x-coordinate
- Y: The final y-coordinate
The combination (I, J) or R are exclusive. As an example, an arc can be drawn like this with (I, J) offset or via radius R:
Derivation
In this derivation, the use of the R parameter is ignored, and the gcode is fed with the (I, J) tuple.
Let’s say \(\mathbf{S}\) is the absolute starting point of the SVG arc path and \(\mathbf{E}\) the absolute ending point of the SVG arc path. The points are connected with the vector \(\mathbf{a}=\mathbf{E}-\mathbf{S}\).
The point \(\mathbf{M} = \frac{1}{2}(\mathbf{S}+\mathbf{E})\) is the mid-point between start and end. On this point, we can construct an orthogonal vector \(\mathbf{v}\) to our desired center \(\mathbf{C}\). The length \(|\mathbf{v}|\) can be determined using Pythagorean theorem:
\[|\mathbf{v}| = \sqrt{r^2 - \frac{1}{4}|\mathbf{a}|^2}\]
Now the vector \(\mathbf{v}\) can be found with the perp operator:
\[\mathbf{v} = \mathbf{\hat{a}}^\perp |\mathbf{v}|\]
Finally, the center (I, J) can be found with
\[\begin{array}{rl} (I, J) =& \mathbf{M} \pm \mathbf{v} - \mathbf{S}\\ =& \mathbf{M} \pm \mathbf{\hat{a}}^\perp |\mathbf{v}| - \mathbf{S}\\ =& \frac{1}{2}(\mathbf{S}+\mathbf{E}) - \mathbf{S} \pm \frac{\mathbf{{a}}^\perp}{|\mathbf{a}|} \sqrt{r^2 - \frac{1}{4}|\mathbf{a}|^2}\\ =& \frac{1}{2}(\mathbf{E} - \mathbf{S}) \pm \mathbf{{a}}^\perp \sqrt{\frac{r^2}{|\mathbf{a}|^2} - \frac{1}{4}}\\ =& \frac{1}{2}\left(\mathbf{a} \pm \mathbf{{a}}^\perp \sqrt{\frac{4r^2}{\mathbf{a}\cdot\mathbf{a}} - 1}\right)\\ \end{array}\]
For which the positive part of the equation is used when the sweep-flag and large-arc-flag differ \(f_S\neq f_A\) and the negative part is used for \(f_S=f_A\).
The remaining question is to decide if we need to draw the circle clockwise or counter-clockwise, which is determined by the direction the circle is drawn - or simply if the sweep-flag \(f_S=1\).
Pseudo-Code
function arcToGCode(S, E, r, fS, fA) {
a = {
x: E.x - S.x,
y: E.y - S.y
}
aP = fS != fA ? {
x: -a.y,
y: a.x
} : {
x: a.y,
y: -a.x
}
w = sqrt(max(0, 4 * r * r / (a.x * a.x + a.y * a.y) - 1))
I = (a.x + aP.x * w) / 2
J = (a.y + aP.y * w) / 2
if (fS != 1)
return "G2 X" + E.x + " Y" + E.y + " I" + I + " J" + J
else
return "G3 X" + E.x + " Y" + E.y + " I" + I + " J" + J
}