Thanks to a question on StackOverflow, I did some playing with Java’s Graphics2D. Below is some code to generate an offset of a polyline, i.e. given a polyline (a string of connected line segments), generate a polygon that represents a thick version of that line.
Clearly, the Java guys took some shortcuts here, because the polygon ends up including a lot of interior segments that you don’t really want, etc. However, filling it using a nonzero winding rule covers a lot of sins. And I can understand why they committed those sins, since it’s kinda hard to do in general. Read the CGAL page about offsetting for an intro. Sometime I’ll have to investigate the shortcut methods in the OpenJDK source; looking at this output, I’m definitely curious.
import java.awt.BasicStroke; import java.awt.Shape; import java.awt.geom.Path2D; import java.awt.geom.PathIterator; public class StrokePath { public static void main(String[] args) { // set line width to 6, use bevel for line joins BasicStroke bs = new BasicStroke(6.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL); // create a path for the input Path2D p = new Path2D.Float(); p.moveTo(50.0, 50.0); p.lineTo(65.0, 100.0); p.lineTo(70.0, 60.0); p.lineTo(120.0, 65.0); p.lineTo(40.0, 200.0); // create outline of wide lines by stroking the path with the stroke Shape s = bs.createStrokedShape(p); // output each of the segments of the stroked path for the output polygon PathIterator pi = s.getPathIterator(null); while (!pi.isDone()) { pi.next(); double[] coords = new double[6]; int type = pi.currentSegment(coords); switch (type) { case PathIterator.SEG_LINETO: System.out.println(String.format("SEG_LINETO %f,%f", coords[0], coords[1])); break; case PathIterator.SEG_CLOSE: System.out.println("SEG_CLOSE"); break; case PathIterator.SEG_MOVETO: System.out.println(String.format("SEG_MOVETO %f,%f", coords[0], coords[1])); break; default: System.out.println("*** More complicated than LINETO... Maybe should use FlatteningPathIterator? ***"); break; } } } }