The message in this blog article is very simple: write good specifications. The word ‘good’ is problematic as it is terribly broad, so let’s narrow it down a little bit. A good specification is simple, clear and unambiguous and when it is impossible to avoid ambiguity, it should describe how to resolve any ambiguity. The SVG specification contains a good lesson in how to not do this, which I will talk about in detail. First, let’s talk about the various ways that can be used to specify geometrical figures.
For most geometric figures, there are multiple ways that they can be specified. For example, a line (infinite) or a line segment can be specified by two points (x1, y1) and (x2, y2). In most applications, this is convenient and compact, but let’s consider another way of representing a line:
Slope/intercept (from the formula y = mx + b, where m is the slope, Dx/Dy, and b i s the y intercept)
You should know this well from basic algebra. So we can take that and say that we will represent our line as (m, b). This is nice – it’s more compact than the four points. The problem with it is that m is undefined when the line is vertical. Oops. If we specified slope intercept, we’d need a way to deal with that. Maybe a flag that says that when the flag is true, the formula is to be interpreted as Dy/Dx and m is the x intercept. OK, that fixes that problem, but it makes the spec more complicated.
This can be used to define a line – what about a segment? How about a starting point and a length? Now our spec is (m, b, f, x1, y1, l). This is also ambiguous, because we don’t know which side of the point to extend the length (maybe it’s centered). You see what’s happening? Something that started relatively simply is just getting worse and worse.
Now consider the SVG specification for elliptical arcs – the parameters that define it are (rx, ry, j, large-arc-flag, sweep-flag, x, y) where rx and ry are the radii of the ellipse, j is the a rotation angle relative to the x-axis, large-arc-flag defines whether to use the large piece of the arc or the small piece, sweep-flag defines the which of two elliptical paths to select that could fit the data, and (x, y) are the end point of the arc. The start point is implicitly defined by previous path. To help you out, here is an illustration from the spec:
technically this is a correct way to describe an elliptical arc, but it is wrong in so many ways. First, it is ambiguous. I can provide an ending point and radii for which there are no solutions and if you code up the math to solve for the ellipse, you will only find out about the problem when you need to take the square root of a negative number. No worries, the spec describes what you should do in these cases, in what also turns out to be an ambiguous and overly complicated fashion. Seriously. Check it out. It requires an unnecessary wall of math which turns into a wall of code. In addition, if the start and end points are 180 degrees out of phase, there is no large arc – they’re both the same size. Which do you pick? Finally, the x-axis angle, j, is totally redundant since any shape can be contained within a transform.
To be generous, there is one and only one reason I can think of for this that it’s fairly simple to decide the two points that start and end the arc if you’re hand-coding SVG, but the other ambiguities outweigh this benefit in my opinion.
Instead, they should use the specification (cx, cy, rx, rx, q1, q2, cw), where cx and cy are the coordinates of the center of the ellipse, rx and ry are the radii, q1 and q2 are the start and end angles of the ellipse in degrees, and cw determines if the arc should be drawn clockwise or counterclockwise from q1 to q2. This spec is simpler to implement and impossible to get incorrect values. You could simplify it further and remove rx and ry and instead use a parent scale transform to set the radii, if you wanted.
To a certain extent, I prefer the PDF approach to representing arcs and circles: you don’t. In PDF there are only Bezier curves and the specification is very simple. While drawing a Bezier curve is also simple, it can be tricky to do it efficiently. Also, circles can’t be represented by Beziers without error. Fortunately, Don Lancaster has a great article on how to approximate circles and minimize the error. If you use Atalasoft’s DotPdf toolkit, you’ll get high-level shapes that implement this for you (as well as SVG->PDF conversion). On another day, I’ll share how I turn an elliptical arc into Beziers. In the meantime, write good specifications!
About the AuthorFollow on Twitter More Content by Steve Hawley