The Birdfont Source Code
Merge curves
These changes was commited to the Birdfont repository Mon, 29 Jun 2015 17:47:46 +0000.
Contributing
Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
Merge curves
--- a/libbirdfont/EditPoint.vala
+++ b/libbirdfont/EditPoint.vala
@@ -55,6 +55,7 @@
public static uint CURVE = 1 << 12;
public static uint CURVE_KEEP = 1 << 13;
public static uint SEGMENT_END = 1 << 14;
+ public static uint SPLIT_POINT = 1 << 15;
public static uint ALL = 0xFFFFFF;
--- a/libbirdfont/Intersection.vala
+++ b/libbirdfont/Intersection.vala
@@ -64,6 +64,23 @@
warning ("Wrong intersection.");
return new EditPoint ();
+ }
+
+ public EditPoint get_other_point (Path p) {
+ if (p == path) {
+ return other_point;
+ }
+
+ if (p == other_path) {
+ return point;
+ }
+
+ warning ("Wrong intersection.");
+ return new EditPoint ();
+ }
+
+ public string to_string () {
+ return @"$(point.x), $(point.y) & $(other_point.x), $(other_point.y)";
}
}
--- a/libbirdfont/StrokeTool.vala
+++ b/libbirdfont/StrokeTool.vala
@@ -103,17 +103,51 @@
public static PathList merge_selected_paths () {
PathList n = new PathList ();
+ PathList o = new PathList ();
Glyph g = MainWindow.get_current_glyph ();
g.store_undo_state ();
foreach (Path p in g.active_paths) {
if (p.stroke == 0) {
+ o.add (p.copy ());
n.add (p.copy ().flatten ());
}
}
n = merge (n);
+
+ foreach (Path p in n.paths) {
+ foreach (EditPoint ep in p.points) {
+ if ((ep.flags & EditPoint.SPLIT_POINT) > 0) {
+ foreach (Path pp in o.paths) {
+ EditPoint lep = new EditPoint ();
+ pp.get_closest_point_on_path (lep, ep.x, ep.y);
+
+ if (Path.distance_to_point (ep, lep) < 1) {
+ pp.insert_new_point_on_path (lep);
+ EditPoint lep2 = lep.copy ();
+ EditPoint lep3 = lep.copy ();
+
+ lep.get_right_handle ().convert_to_line ();
+ lep2.convert_to_line ();
+ lep3.get_left_handle ().convert_to_line ();
+
+ lep.flags |= EditPoint.NEW_CORNER;
+ lep2.flags |= EditPoint.INTERSECTION;
+ lep3.flags |= EditPoint.NEW_CORNER;
+
+ pp.add_point_after (lep2, lep);
+ pp.add_point_after (lep3, lep2);
+ }
+ }
+ }
+ }
+ }
+
+ o = merge_curves (o);
+
+ // FIXME: remove split points
foreach (Path p in g.active_paths) {
g.delete_path (p);
@@ -121,7 +155,7 @@
g.clear_active_paths ();
- foreach (Path p in n.paths) {
+ foreach (Path p in o.paths) {
g.add_path (p);
g.add_active_path (null, p);
}
@@ -130,7 +164,147 @@
return n;
}
+
+ static PathList merge_curves (PathList pl) {
+ PathList r = new PathList ();
+ if (pl.paths.size != 2) {
+ warning ("FIXME: more than two paths");
+ return pl;
+ }
+
+ r = merge_paths_with_curves (pl.paths.get (0), pl.paths.get (1));
+
+ return r;
+ }
+
+ static PathList merge_paths_with_curves (Path path1, Path path2) {
+ PathList r = new PathList ();
+ IntersectionList intersections = new IntersectionList ();
+ EditPoint ep1, ep2, found;
+ double d;
+ double min_d;
+ Path current;
+ bool found_intersection;
+
+ // reset copied points
+ foreach (EditPoint n in path2.points) {
+ n.flags &= uint.MAX ^ EditPoint.COPIED;
+ }
+
+ // build list of intersection points
+ for (int i = 0; i < path1.points.size; i++) {
+ ep1 = path1.points.get (i);
+ found = new EditPoint ();
+ min_d = double.MAX;
+ found_intersection = false;
+ if ((ep1.flags & EditPoint.INTERSECTION) > 0) {
+ for (int j = 0; j < path2.points.size; j++) {
+ ep2 = path2.points.get (j);
+ d = Path.distance_to_point (ep1, ep2);
+ if ((ep2.flags & EditPoint.COPIED) == 0
+ && (ep2.flags & EditPoint.INTERSECTION) > 0) {
+ if (d < min_d) {
+ min_d = d;
+ found_intersection = true;
+ found = ep2;
+ }
+ }
+ }
+
+ if (!found_intersection) {
+ warning ("No intersection");
+ return r;
+ }
+
+ found.flags |= EditPoint.COPIED;
+
+ Intersection intersection = new Intersection (ep1, path1, found, path2);
+ intersections.points.add (intersection);
+ }
+ }
+
+ foreach (Intersection inter in intersections.points) {
+ print (@"inter: $inter\n");
+ }
+
+ // reset copied points
+ foreach (EditPoint n in path2.points) {
+ n.flags &= uint.MAX ^ EditPoint.COPIED;
+ }
+
+ return_val_if_fail (intersections.points.size > 0, r);
+
+ Path new_path = new Path ();
+ current = path1;
+ while (true) {
+ // find a beginning of a new part
+ bool find_parts = false;
+ Intersection new_start = new Intersection.empty ();
+ foreach (Intersection inter in intersections.points) {
+ if (!inter.done && !find_parts) {
+ find_parts = true;
+ new_start = inter;
+
+ print (@"new_start: $new_start\n");
+ break;
+ }
+ }
+
+ if (new_path.points.size > 0) {
+ new_path.get_first_point ().color = Color.green ();
+ new_path.get_last_point ().color = Color.yellow ();
+ new_path.get_last_point ().get_prev ().color = Color.blue ();
+ r.add (new_path);
+ }
+
+ if (!find_parts) { // no more parts
+ break;
+ }
+
+ current = new_start.get_other_path (current);
+ int i = index_of (current, new_start.get_point (current));
+
+ if (i < 0) {
+ warning ("i < 0");
+ return r;
+ }
+
+ new_path = new Path ();
+ ep1 = current.points.get (i);
+ current = new_start.get_other_path (current); // swap at first iteration
+ while (true) {
+ if ((ep1.flags & EditPoint.INTERSECTION) > 0) {
+ bool other;
+ new_start = intersections.get_point (ep1, out other);
+ current = new_start.get_other_path (current);
+ i = index_of (current, new_start.get_point (current));
+
+ if (i < 0) {
+ warning ("i < 0");
+ return r;
+ }
+
+ ep1 = current.points.get (i);
+ ep2 = current.points.get ((i + 1) % current.points.size);
+ }
+
+ if ((ep1.flags & EditPoint.COPIED) > 0) {
+ break;
+ }
+
+ ep1.flags |= EditPoint.COPIED;
+ new_path.add_point (ep1.copy ());
+ i++;
+ ep1 = current.points.get (i % current.points.size);
+ }
+
+ new_start.done = (new_start.get_other_point (current).flags & EditPoint.COPIED) > 0;
+ }
+
+ return r;
+ }
+
static Path simplify_stroke (Path p) {
Path simplified = new Path ();
Path segment, added_segment;
@@ -842,6 +1016,7 @@
nl = new PathList ();
if (!path.has_deleted_point ()) {
+ print ("No deleted points\n");
return pl;
}
@@ -883,6 +1058,7 @@
pl = process_deleted_control_points (old_path);
if (pl.paths.size == 0) {
+ print ("No paths.\n");
pl.add (old_path);
}
@@ -947,7 +1123,7 @@
ep1.prev = prev;
ep1.next = ep2;
- ep1.flags |= EditPoint.NEW_CORNER;
+ ep1.flags |= EditPoint.NEW_CORNER | EditPoint.SPLIT_POINT;
ep1.type = prev.type;
ep1.x = px;
ep1.y = py;
@@ -956,7 +1132,7 @@
ep2.prev = ep1;
ep2.next = ep3;
- ep2.flags |= EditPoint.INTERSECTION;
+ ep2.flags |= EditPoint.INTERSECTION | EditPoint.SPLIT_POINT;
ep2.type = prev.type;
ep2.x = px;
ep2.y = py;
@@ -965,7 +1141,7 @@
ep3.prev = ep2;
ep3.next = next;
- ep3.flags |= EditPoint.NEW_CORNER;
+ ep3.flags |= EditPoint.NEW_CORNER | EditPoint.SPLIT_POINT;
ep3.type = prev.type;
ep3.x = px;
ep3.y = py;
@@ -979,8 +1155,6 @@
path.create_list ();
}
- // FIXME: set handle position
- /*
PenTool.convert_point_to_line (ep1, true);
PenTool.convert_point_to_line (ep2, true);
PenTool.convert_point_to_line (ep3, true);
@@ -988,7 +1162,6 @@
ep1.recalculate_linear_handles ();
ep2.recalculate_linear_handles ();
ep3.recalculate_linear_handles ();
- */
d = Path.distance_to_point (prev, next);
prev.get_right_handle ().length *= Path.distance_to_point (prev, ep1) / d;
@@ -1043,8 +1216,9 @@
iy = cross_y;
return true;
- }
- }
+ }
+ }
+
return false;
}
@@ -1089,7 +1263,7 @@
if (p1 != ep && p2 != next) {
inter = segments_intersects (p1, p2, ep, next, out iix, out iiy,
skip_points_on_points);
-
+
if (inter) {
distance = Path.distance (ep.x, iix, ep.y, iiy);
if (distance < min_distance) {
@@ -1349,7 +1523,7 @@
r.add (np);
}
- r = get_all_parts (r);
+ r = get_all_parts (r);
} else {
warning ("Not merged.");
error = true;