]> granicus.if.org Git - graphviz/commitdiff
arrows: miter_point: add fallback to bevel
authorMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Thu, 15 Sep 2022 17:01:54 +0000 (19:01 +0200)
committerMagnus Jacobsson <Magnus.Jacobsson@berotec.se>
Tue, 11 Oct 2022 19:45:27 +0000 (21:45 +0200)
The 'l' and 'r' arrow shape modifiers make the arrow tip have a sharp
angle and causes the normal miter point to exceed the 'miter-limit'
and SVG renderers to fall back to a bevelled corner.

lib/common/arrows.c

index 979d0ad76c8182c48dea915f62bae57b01a42801..705fca7da25d7b1ef06fb6dab9db918e117c9deb 100644 (file)
@@ -459,6 +459,28 @@ static pointf miter_point(pointf base_left, pointf P, pointf base_right,
   const double theta = beta_rev - alpha + (beta_rev - alpha <= -M_PI ? 2 * M_PI : 0);
   assert(theta >= 0 && theta <= M_PI && "theta out of range");
 
+  // check if the miter limit is exceeded according to
+  // https://www.w3.org/TR/SVG2/painting.html#StrokeMiterlimitProperty
+  const double stroke_miterlimit = 4.0;
+  const double normalized_miter_length = 1.0 / sin(theta / 2.0);
+
+  if (normalized_miter_length > stroke_miterlimit)  {
+    // fall back to bevel
+    const double sinBeta = dyB / hypotB;
+    const double sinBetaMinusPi = -sinBeta;
+    const double cosBetaMinusPi = -cosBeta;
+    const pointf P2 = {P.x + penwidth / 2.0 * sinBetaMinusPi,
+                       P.y - penwidth / 2.0 * cosBetaMinusPi};
+
+    // the bevel is the triangle formed from the three points P, P1 and P2 so
+    // a good anough approximation of the miter point in this case is the
+    // crossing of P-P3 with P1-P2 which is the same as the midpoint between
+    // P1 and P2
+    const pointf Pbevel = {(P1.x + P2.x) / 2, (P1.y + P2.y) / 2};
+
+    return Pbevel;
+  }
+
   // length between P1 and P3 (and between P2 and P3)
   const double l = penwidth / 2.0 / tan(theta / 2.0);