+ /* TODO: Maybe print a warning if one of the above limitations
+ applies to the given polygon. However, this is quite complicated
+ to check. */
+
+ const Real epsilon = 0.01;
+
+#ifndef NDEBUG
+ /* remove consecutive duplicate points */
+ for (int i = 0; i < points.size (); i++)
+ {
+ int next_i = (i + 1) % points.size ();
+ Real d = (points[i] - points[next_i]).length ();
+ if (d < epsilon)
+ programming_error ("Polygon should not have duplicate points");
+ }
+#endif
+
+ /* special cases: degenerated polygons */
+ if (points.size () == 0)
+ return Stencil ();
+ if (points.size () == 1)
+ return dot (points[0], 0.5 * blotdiameter);
+ if (points.size () == 2)
+ return Line_interface::make_line (blotdiameter, points[0], points[1]);
+
+ /* shrink polygon in size by 0.5 * blotdiameter */
+ Array<Offset> shrunk_points;
+ shrunk_points.set_size (points.size ());
+ bool ccw = 1; // true, if three adjacent points are counterclockwise ordered
+ for (int i = 0; i < points.size (); i++)
+ {
+ int i0 = i;
+ int i1 = (i + 1) % points.size ();
+ int i2 = (i + 2) % points.size ();
+ Offset p0 = points[i0];
+ Offset p1 = points[i1];
+ Offset p2 = points[i2];
+ Offset p10 = p0 - p1;
+ Offset p12 = p2 - p1;
+ if (p10.length () != 0.0)
+ { // recompute ccw
+ Real phi = p10.arg ();
+ // rotate (p2 - p0) by (-phi)
+ Offset q = complex_multiply (p2 - p0, complex_exp (Offset (1.0, -phi)));
+
+ if (q[Y_AXIS] > 0)
+ ccw = 1;
+ else if (q[Y_AXIS] < 0)
+ ccw = 0;
+ else {} // keep ccw unchanged
+ }
+ else {} // keep ccw unchanged
+ Offset p10n = (1.0 / p10.length ()) * p10; // normalize length to 1.0
+ Offset p12n = (1.0 / p12.length ()) * p12;
+ Offset p13n = 0.5 * (p10n + p12n);
+ Offset p14n = 0.5 * (p10n - p12n);
+ Offset p13;
+ Real d = p13n.length () * p14n.length (); // distance p3n to line (p1..p0)
+ if (d < epsilon)
+ // special case: p0, p1, p2 are on a single line => build
+ // vector orthogonal to (p2-p0) of length 0.5 blotdiameter
+ {
+ p13[X_AXIS] = p10[Y_AXIS];
+ p13[Y_AXIS] = -p10[X_AXIS];
+ p13 = (0.5 * blotdiameter / p13.length ()) * p13;
+ }
+ else
+ p13 = (0.5 * blotdiameter / d) * p13n;
+ shrunk_points[i1] = p1 + ((ccw) ? p13 : -p13);
+ }
+
+ /* build scm expression and bounding box */
+ SCM shrunk_points_scm = SCM_EOL;
+ Box box;
+ for (int i = 0; i < shrunk_points.size (); i++)
+ {
+ SCM x = scm_from_double (shrunk_points[i][X_AXIS]);
+ SCM y = scm_from_double (shrunk_points[i][Y_AXIS]);
+ shrunk_points_scm = scm_cons (x, scm_cons (y, shrunk_points_scm));
+ box.add_point (points[i]);
+ }
+ SCM polygon_scm = scm_list_n (ly_symbol2scm ("polygon"),
+ ly_quote_scm (shrunk_points_scm),
+ scm_from_double (blotdiameter),
+ SCM_BOOL_T,
+ SCM_UNDEFINED);
+
+ Stencil polygon = Stencil (box, polygon_scm);
+ shrunk_points.clear ();
+ return polygon;