Slide 1: Slide 2: This is Minard's famous map of Napolean's march on Moscow (and back). The important feature for this talk is the thick band that represents the size of Napoleon's army. The map was drawn in the 1840s(?) so did not benefit from software support. Thing is, it is not clear that modern software can help with drawing that thick band ! Slide 3: Modern graphics systems typically have a set of line drawing primitives consisting of line segments ... Slide 4: ... polylines ... Slide 5: ... and smooth curves. This is an X-spline, which is a flexible curve primitive that draws a line relative to control points with a shape parameter that controls whether the line approximates a control point ... Slide 6: ... or interpolates a control point ... Slide 7: ... or is discontinuous at a control point. Slide 8: Graphics systems also provide control over the style of line endings (round, square, butt) and line joins (round, mitre, bevel). Slide 9: The difficulty with Minard's map is that the thick band is a line with varying width. Slide 10: Note that it is NOT just a series of line segments with different widths because the line joins matter. Slide 11: All of which is an excuse to wonder: wouldn't it be fun to have a variable-width line primitive ? Hence the 'vwline' package. Slide 12: There are many different approaches to generating a variable-width line. The 'vwline' package provides several functions for drawing a variable-width line relative to (x, y) locations or control points. Slide 13: In all cases, an algorithm is used to generate the outline of a variable-width line (a polygon or path) and then that outline is filled in. Slide 14: grid.vwcurve() is based on a series of points. We calculate a "perpendicular" at each point and then connect up the ends of the perpendiculars. Slide 15: The example on the left is based on just four points and the width changes smoothly between points, but the line itself has sharp corners. The example on the right is based on lots of points so the line appears smooth and the widths change smoothly. Slide 16: grid.vwXspline() is based on a set of control points. We generate new control points based on perpendiculars at the original control points, then connect up the curves through the new control points. Slide 17: These curves are always smooth, though the result is less predictable. The image on the left is based on just five control points. The image on the right is one main line with lots of smaller lines drawn outward from its extremities (in addition to drawing variable-width lines, we can query them for their boundary points) Slide 18: grid.vwline() is based on a set of points. We generate a thick line for each segment and then calculate a "join" for the outside and inside of each corner. Slide 19: Both of these lines are based on the same set of points - we have just changed the line ending and line join styles. The left image shows examples of "round" ends and joins, though these are not as simple as circle segments (as is the case for fixed-width lines). The right image shows an example of "stepped" changes in line width (with "square" line ends and "mitre" line joins). Slide 20: grid.brushXspline() is based on a smooth line relative to a set of control points. A brush is placed at points along the smooth line (perpendicular to the line), then each "segment" is created from a convex hull around consecutuive brushes. The end result is a union of all of the segments. Slide 21: The left image shows that we can place the brushes at relatively wide intervals to create a "broken" variable-width line. The right image is based on a circular X-spline with a half-brush that only draws "inside" the circle (at variable widths). Slide 22: grid.offsetXspline() is based on a smooth line relative to control points. The smooth line is a series of very short straight lines. The offset curve for the original X-spline is used to calculate the width at each vertex on the "smooth" line. The width of the line is specified as an X-spline (where 'x' is distance along the line and 'y' is the width). Slide 23: The left image shows that this mathematical (rather than heuristic) approach is better at coping with tight corners with rapidly changing width. The right image shows a width X-spline that smoothly decreases to zero then back up again, and repeats. Slide 24: Less than a year after doing this work, someone came up with a problem (on R-help) that makes use of this solution! The problem here is that the filled bars are only filled (so that the bottoms of the bars align with zero on the y-axis), but the unfilled bars are stroked with thick lines, which "bleeds" below zero and makes the bars look uneven. What we want is to have the borders on the unfilled bars only visible "inside" the bars. Slide 25: Variable-width lines are one way to achieve this, by drawing a line (clockwise) around the border of the bar with a zero width on the left and a thick width on the right. Slide 26: The grid.vwline() function, with stepWidth=TRUE and linejoin="mitre" can produce a variable-width line that changes width in abrupt steps. This gets us quite close to what Minard did, though I have to admit it is still not quite there. Notice though that the "return" line has variable-width on only one side! We can do that! Slide 27: Slide 28: