Creating and Editing SVG Graphics

  • 7/15/2012

An Example of Building Complex Shapes

This section shows how you can use the pen-down command M to make more complex shapes with <path>.

The following code creates two paths, with one apparently drawn inside the other (in the sense that the coordinates of one are contained inside the polygon defined by the other):

<path d="M 100,350 300,100 500,350" fill="none" stroke="black" stroke-width="20"/>
<path d="M 250,320 250,220 350,220 350,320" fill="none" stroke="black" stroke-width="20"/>

The figure contains two paths: one with three points, the other with four. Note how the triangle encompasses the rectangle.

httpatomoreillycomsourcemspimages1261191.png

Next, we add the simple z subcommand (shown below in bold) at the end of each of the strings, which closes the path by drawing a final line back to the path’s starting point. After we do that, the paths will be closed rather than left open between endpoints.

<path d="M 100,350 300,100 500,350 z" fill="none" stroke="black" stroke-width="20"/>
<path d="M 250,320 250,220 350,220 350,320 z" fill="none" stroke="black" stroke-width="20"
/>

The new rendered SVG image looks like this:

httpatomoreillycomsourcemspimages1261193.png

Alternatively, you could create the preceding image using only a single path object (see http://www.w3techcourses.com/svg_images/onepath.svg), as follows:

<path d="M 100,350 300,100 500,350 z
               M 250,320 250,220 350,220 350,320 z"
          fill="none" stroke="black" stroke-width="20"/>

This method can save a bit on markup, but is a little harder. However, there are some additional benefits to this approach that are worth considering. By combining the two shapes above into one compound path, you can define the fill rule of that path as evenodd. The net effect of this is that the shape’s fill color will not be applied to the interior region (although it would be applied to regions inside the interior region). Try this combined code:

<path d="M 100,350 300,100 500,350 z
M 250,320 250,220 350,220 350,320 z"
  fill="#ff8" stroke="black" stroke-width="15" fill-rule="evenodd"/>

You can see the advantage in the next graphic. The rectangles that underlie the triangle are visible through the rectangular hole in the shape. This effect would be difficult to produce if the two parts of this compound path were separate paths, because to be visible, the rectangle would have to be on top of the triangle—but in that case, nothing inside it other than the triangle itself would be visible.

httpatomoreillycomsourcemspimages1261195.png

We have just demonstrated how to create a complex vector graphic shape using a single SVG path element that contains a yellow triangle with a rectangular hole showing pink and green rectangles underneath. The next section discusses creating shapes using Bézier curves.

Quadratic Bézier Curves: The Q Subcommand

I became aware of Bézier curves in the mid-1980s when I discovered that Adobe Illustrator had the ability to draw amazing curves quickly. You can find good treatment of the subject on Wikipedia, at http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_curves.

Here’s basically how a quadratic Bézier curve works in SVG. You define an initial point (e.g., 100,200) using a pen-down command. From there, you set a course heading toward the next point; however, instead of actually moving to the next point, you just aim in that direction. So, for example, while “M 100 200 L 200 400” will make you actually arrive at the point (200,400), “M 100 200 Q 200 400…” will merely point you in that direction. Ultimately, you also need a final destination, which is the final coordinate pair required for a quadratic Bézier curve. In the example that follows, the command “M 100,200 L 200,400 300,200” draws a red path between (and reaching each of) the three points indicated. But simply replacing the L with a Q (i.e., “M 100,200 Q 200,400 300,200”) produces a curve that passes through both endpoints and is a tangent to the associated lines of the allied line path at the endpoints of the segments.

Bézier Curve Example

This example clearly shows how the quadratic Bézier curve is created.

<path d="M 100 200 Q 200,400 300,200" fill="none" stroke="blue" />
<path d="M 100 200 L 200,400 300,200" fill="none" stroke="red"/>

While an infinite number of curves are tangent to both the line “M 100 200 L 200 300” at (100,200) and “M 200 400 L 300 200” at (300,200), only one quadratic shares these properties, even if you allow for rotations (in the sense of parametric equations) of the quadratic. That is, those three points in the plane uniquely define a specific curve. Likewise, any three noncollinear points in the plane determine one quadratic Bézier curve.

Revisiting the earlier example, which modified the fill rule to produce an empty space in the middle of the curve, you can draw the same curve with quadratic splines instead of lines to see the effect.

Here’s an example of a graphic that uses a quadratic spline:

<path fill-rule="evenodd"
  d="M 70 140 L 150,0 200,100 L 40,100 100,0 L 170,140 70 140"/>

<path fill="red" fill-rule="evenodd"
  d="M 70 140 Q 150,0 200,100 Q 40,100 100,0 Q 170,140 70 140"/>
httpatomoreillycomsourcemspimages1261199.png

Note how the above example

<path id="H" fill="#bbb" fill-rule="evenodd"
  d="M 70 140 L 150,0 200,100 L 40,100 100,0 L 170,140 70 140"/>

can have its Ls modified to Qs:

<path id="X" fill="#b42" fill-rule="evenodd"
  d="M 70 140 Q 150,0 200,100 Q 40,100 100,0 Q 170,140 70 140"/>

That produces a shape similar to the following (we’ve changed the colors and added in identifiers to the paths for easy reference in the text here):

httpatomoreillycomsourcemspimages1261201.png

The figure shows two paths produced from the preceding code. Both paths have the same points, but one is linear (id=“H”) and the other is quadratic (id=“X”).

Observe that the angles of the reddish shape (X) at which the curves actually meet are sharp rather than rounded. Let’s look more closely. If you’re familiar with trefoil knots (see http://en.wikipedia.org/wiki/Trefoil_knot), then that is the sort of shape we’ll be aiming toward.

First, observe that if the desired shape were to pass through any of the six points of the linear path H, then in order for the parts of the curve that meet there to be smooth, and for any of them to be tangent to lines of H, the new curve would have to extend beyond the bounds of X. You could extend the lines of X into a larger equilateral triangle and then work on building your trefoil knot. You could do this with cubic Bézier curves by defining a curve that passes through the same three endpoints (http://www.w3techcourses.com/svg_images/lineOutCub.svg) that it already does, but that is guided by the control points consisting of the three points of the circumscribed triangle (shown in the next figure as the light green line).

<path fill="#c53" fill-rule="evenodd" opacity=".5"
  d="M 70 140 C 17.5 ,140 150,0 200,100 C 220, 140 40,100 100,0 C 127,-47 170,140 70 140"/>
httpatomoreillycomsourcemspimages1261203.png

As a final example, the following demonstrates how to stitch Bézier curves together smoothly. For this to happen, the slopes of the lines at either side of a segment’s endpoint must be the same.

Notice that the brown and blue paths share the same beginning points and endpoints, initial and final control points, and midpoints (150,200). They differ only in terms of the control points surrounding the midpoint. The blue path aims toward (100,100) and then changes direction toward (200,300), passing through the midpoint on its way and tangent to the line, as shown. Because the three relevant points, (100,100), (150,200), and (200,300), are collinear, the slopes of both segments are the same at the point where they meet, implying that the curve is smooth (continuously differentiable) at that point.

Creating Smooth Curves: The S and T Subcommands

These shortcut commands help with creating smooth curves, and they require fewer data points than constructing cubic and quadratic Bézier curves without these shortcut commands. This is because one of the Bézier curve points is used simply as a reference point, which is then reflected to create a smooth curve.

You use the S command to draw a smooth cubic Bézier spline segment from the current point to a new point (x,y). The previous segment must also be a smooth cubic Bézier spline, and that second control point is then reused via reflection relative to the current point as the segment’s first control point. The second control must be explicitly specified.

You use the T command to draw a smooth quadratic Bézier spline segment from the current point to a new point (x,y). The previous segment must also be a smooth quadratic Bézier spline, and that control point is then reused via reflection relative to the current point.

The following image demonstrates the automatic reflection process for both these commands:

httpatomoreillycomsourcemspimages1261207.png

As you have seen, the <path> element can express both simple and complex shapes using the L, H, V, Q, and C commands. The geometric calculations involved are quite complex, which is why vector-drawing programs such as Inkscape, Illustrator, SVG-Edit, and Visio are very helpful in the SVG design process.

Elliptical Arc Example

One other often-used path command is the elliptical arc command (A), which allows you to quickly draw subsets of ellipses or intersecting ellipses. The arc subcommand of the <path> element has the following syntax: A rx ry XAR large-arc-flag sweep-flag x y.

The arc begins at the current point (which is determined by the last coordinate specified) and ends at (x,y), as demonstrated below:

httpatomoreillycomsourcemspimages1261209.png

You now have the choice of four elliptical arc segments: two small ones and two large ones. These arc segments can have a positive angular orientation (clockwise) or a negative orientation. The large-arc-flag (fl) controls the angular orientation of the larger arc segment via fl = 0 : small, fl = 1 : large. The sweep-flag (fs) controls the angular orientation analogously, via fs = 0 : positive, and fs = 1 : negative.

Using this elliptical arc information, here’s the code to create a simple spiral:

<svg width="600" height="400" viewBox="0 0 400 300">
<path stroke="darkslategray" stroke-width="6" fill="none"
  stroke-linecap="round"
  d="M50,100
    A100,50 0 0 1 250,100
    A80,40 0 0 1 90,100
    A60,30 0 0 1 210,100
    A40,20 0 0 1 130,100
    A20,10 0 0 1 170,100" />
</svg>

That code produces the following spiral:

httpatomoreillycomsourcemspimages1261211.png

Table 2-2 provides a quick reference for the path commands and properties.

Table 2-2 Path Commands

Commands

Parameters

Instruction

M, m

x, y

Move to a new point (x,y).

L, l

x, y

Draw a line from the current point to a new point (x,y).

H, h

x

Draw a horizontal line from the current point to a new point (x,current-point-y).

V, v

y

Draw a vertical line from the current point to a new point (current-point-x,y).

A, a

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

Draw an elliptical arc from the current point to a new point (x,y). The arc belongs to an ellipse that has radii rx and ry and a rotation with respect to the positive x-axis of x-axis-rotation (in degrees). If large-arc-flag is 0 (zero), then the small arc (less than 180 degrees) is drawn. A value of 1 results in the large arc (greater than 180 degrees) being drawn. If sweep-flag is 0, then the arc is drawn in a negative angular direction (counterclockwise); if it is 1, then the arc is drawn in a positive angular direction (clockwise).

Q, q

x1, y1

x, y

Draw a quadratic Bézier curve from the current point to a new point (x,y) using (x1,y1) as the control point.

T, t

x, y

Draw a smooth quadratic Bézier curve segment from the current point to a new point (x,y). The control point is computed automatically as the reflection of the control point on the previous command relative to the current point. If there is no previous command or if the previous command was not a Q, q, T, or t, the control point is coincident with the current point.

C, c

x1, y1

x2, y2

x, y

Draw a cubic Bézier curve from the current point to a new point (x,y) using (x1,y1) and (x2,y2) as control points.

S, s

x2, y2

x, y

Draw a smooth cubic Bézier curve segment from the current point to a new point (x,y). The first control point is computed automatically as the reflection of the control point on the previous command relative to the current point. If there is no previous command or if the previous command was not a C, c, S, or s, the first control point is coincident with the current point. (x2,y2) is the second control point.

Relative vs. Absolute Path Coordinates

This next example uses a mixture of MoveTo (M), Vertical (V), LineTo (L), Bézier (Q), HorizontalTo (H), and ClosePath (Z) commands to generate a fairly elegant shape, as shown on the left of the following image. The example on the right requires less spatial brain power to generate the same shape because it uses relative versions of commands (i.e., lowercase commands). The coordinates of the new point are relative to the position of the previous point (40,80).

httpatomoreillycomsourcemspimages1261213.png