Commits

Barry Schwartz  committed eace128

Support for Hobby-style tensions.

  • Participants
  • Parent commits 5ffa692

Comments (0)

Files changed (4)

 pure_DATA = fontdesign.pure
 
 fontdesigndir = $(puredir)/fontdesign
-fontdesign_DATA = $(addprefix fontdesign/, bezier.pure	\
-	piecewise.pure)
+fontdesign_DATA = $(addprefix fontdesign/, bezier.pure hobby.pure	\
+	line_intersection.pure piecewise.pure)

File fontdesign.pure

 using fontdesign::bezier;
+using fontdesign::hobby;
+using fontdesign::line_intersection;
 using fontdesign::piecewise;

File fontdesign/hobby.pure

+using line_intersection;
+using math;
+
+namespace fontdesign with
+
+  private root2;
+  private root5;
+  const root2 = sqrt 2.0;
+  const root5 = sqrt 5.0;
+
+  // A function (or factor) due to John Hobby. See the METAFONTbook.
+  f_hobby theta phi = top/bottom
+    when
+      sintheta = sin theta;
+      costheta = cos theta;
+      sinphi = sin phi;
+      cosphi = cos phi;
+      top = 2.0 + root2 * (sintheta - sinphi / 16.0) * (sinphi - sintheta / 16.0) * (costheta - cosphi);
+      bottom = 3.0 * (1.0 + 0.5 * (root5 - 1.0) * costheta + 0.5 * (3.0 - root5) * cosphi);
+    end;
+
+  // Find off-curve control points of a cubic bezier, given on-curve
+  // points, directions, and tensions.
+  tensions_to_controls p0 p3 dir0 dir1 tension0 tension1 = (p1,p2)
+    when
+      chord = p3 - p0;
+      theta = arg (dir0 / chord);
+      phi = arg (chord / dir1);
+      p1 = p0 + (1<:theta) * chord * ((f_hobby theta phi) / tension0);
+      p2 = p3 - (1<:(-phi)) * chord * ((f_hobby phi theta) / tension1);
+    end;
+
+  tensions_to_ctrlpoints p0 p3 dir0 dir1 tension0 tension1 = [p0,q!0,q!1,p3]
+    when
+      q = tensions_to_controls p0 p3 dir0 dir1 tension0 tension1;
+    end;
+
+  tensions_to_bezier p0 p3 dir0 dir1 tension0 tension1 =
+    Bezier (tensions_to_ctrlpoints p0 p3 dir0 dir1 tension0 tension1);
+
+  // Make a cubic bezier inflectionless (by increasing tensions if
+  // necessary).
+  remove_inflection (Bezier ctrlpoints) = Bezier (remove_inflection ctrlpoints);
+  remove_inflection [p0,p1,p2,p3] = [p0,q1,q2,p3]
+    when
+      new_points = find_intersection_of_lines true true p0 p1 p3 p2;
+      pa = new_points!0;
+      pb = new_points!1;
+      q1 = (if (abs pa) < inf then pa else p1);
+      q2 = (if (abs pb) < inf then pb else p2);
+    end;
+
+end;

File fontdesign/line_intersection.pure

+using math;
+
+namespace fontdesign with
+
+  // See http://paulbourke.net/geometry/lineline2d/ --
+  // "Intersection point of two lines (2 dimensions)"
+
+  find_intersection_of_lines first_is_segment second_is_segment p1 p2 p3 p4 =
+    (pa,pb)
+   when
+     x1 = re p1;
+     y1 = im p1;
+     x2 = re p2;
+     y2 = im p2;
+     x3 = re p3;
+     y3 = im p3;
+     x4 = re p4;
+     y4 = im p4;
+     denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
+     numer_a = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
+     numer_b = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3);
+     ua = numer_a / denom;
+     ub = numer_b / denom;
+     x = x1 + ua * (x2 - x1);
+     y = y1 + ua * (y2 - y1);
+     pa =
+       (if first_is_segment then
+          (if 0.0 <= ua && ua <= 1.0 then x+:y else nan+:nan)
+        else
+          x+:y);
+     pb =
+       (if second_is_segment then
+          (if 0.0 <= ub && ub <= 1.0 then x+:y else nan+:nan)
+        else
+          x+:y);
+   end;
+
+end;