The Birdfont Source Code


All Repositories / birdfont.git / commitdiff – RSS feed

Merge simple paths

These changes was commited to the Birdfont repository Wed, 08 Apr 2015 08:56:26 +0000.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
[Wed, 08 Apr 2015 08:56:26 +0000]

Updated Files

libbirdfont/EditPoint.vala
libbirdfont/Path.vala
libbirdfont/StrokeTool.vala
libbirdfont/SvgParser.vala
--- a/libbirdfont/EditPoint.vala +++ b/libbirdfont/EditPoint.vala @@ -49,6 +49,7 @@ public static uint NEW_CORNER = 1 << 6; public static uint STROKE_OFFSET = 1 << 7; public static uint COUNTER_TO_OUTLINE = 1 << 8; + public static uint COPIED = 1 << 9; public bool counter_to_outline = false;
--- a/libbirdfont/Path.vala +++ b/libbirdfont/Path.vala @@ -2094,7 +2094,9 @@ bool inside; foreach (Path p in pl.paths) { - if (p.points.size > 1 && p != path) { + if (p.points.size > 1 && p != path + && path.boundaries_intersecting (p)) { + inside = true; foreach (EditPoint ep in path.points) { if (!SvgParser.is_inside (ep, p)) { @@ -2110,6 +2112,32 @@ return inside_count; } + + public bool boundaries_intersecting (Path p) { + bool xm1 = in_boundaries (p.xmin, p.ymin); + bool xm2 = in_boundaries (p.xmax, p.ymax); + bool xm3 = in_boundaries (p.xmin, p.ymax); + bool xm4 = in_boundaries (p.xmax, p.ymin); + + if (xm1 || xm2 || xm3 || xm3) { + return true; + } + + xm1 = p.in_boundaries (xmin, ymin); + xm2 = p.in_boundaries (xmax, ymax); + xm3 = p.in_boundaries (xmin, ymax); + xm4 = p.in_boundaries (xmax, ymin); + + if (xm1 || xm2 || xm3 || xm3) { + return true; + } + + return false; + } + + public bool in_boundaries (double x, double y) { + return xmin <= x <= xmax && ymin <= y <= ymax; + } /** @param t smallest distance to other points. */ public void remove_points_on_points (double t = 0.00001) {
--- a/libbirdfont/StrokeTool.vala +++ b/libbirdfont/StrokeTool.vala @@ -48,9 +48,30 @@ PathList paths = new PathList (); foreach (Path p in g.active_paths) { - paths.append (get_stroke (p, p.stroke)); + if (p.stroke > 0) { + paths.append (get_stroke (p, p.stroke)); + } } - + + // FIXME: delete + Path? last = null; + foreach (Path p in g.active_paths) { + if (p.stroke == 0) { + if (last != null) { + PathList pl; + if (merge_path (p, (!) last, out pl)) { + paths.paths.remove (p); + paths.paths.remove ((!) last); + paths.append (pl); + g.path_list.remove (p); + g.path_list.remove ((!) last); + } + } + + last = p; + } + } + foreach (Path np in paths.paths) { g.add_path (np); } @@ -65,17 +86,21 @@ n = get_stroke_outline (original, thickness); - o = split_corners (n); - remove_self_intersecting_corners (o); + o = n; + //o = split_corners (n); + //remove_self_intersecting_corners (o); o = merge (o); - + /* PathList parts = new PathList (); foreach (Path p in o.paths) { parts.append (split (p)); } return parts; + */ + + return o; } static bool is_corner_self_intersection (Path p) { @@ -98,9 +123,8 @@ r = ep.get_right_handle (); if (fabs (l.angle - r.angle) < 0.005) { - ep.color = new Color (1,0,0,1); remove = true; - } else ep.color = new Color (0,0,1,1); + } } i = corner && p.points.size == 4; @@ -452,8 +476,6 @@ if ((p1.flags & EditPoint.STROKE_OFFSET) > 0 && (a1.flags & EditPoint.STROKE_OFFSET) > 0) { - p1.color = new Color (1,0,1,0.7); - a1.color = new Color (1,0,1,0.7); p1.flags = EditPoint.COUNTER_TO_OUTLINE; a1.flags = EditPoint.COUNTER_TO_OUTLINE; @@ -941,9 +963,6 @@ if ((end_ep.flags & EditPoint.COUNTER_TO_OUTLINE) > 0) { start_ep.flags = EditPoint.NONE; end_ep.flags = EditPoint.NONE; - - //start_ep.color = new Color (0,0,1,1); - //end_ep.color = new Color (0,0,1,1); if (merge_segments (pl, p, start_prev, start_ep, end_ep, end_next, out result)) { return true; @@ -998,12 +1017,7 @@ pl.paths.remove (path2); // FIXME: find criteria - - start1.color = new Color (1,0,0,1); - stop1.color = new Color (1,0,0,1); - start2.color = new Color (1,0,0,1); - stop2.color = new Color (1,0,0,1); - + double d1 = Path.point_distance (r1.paths.get (0).get_first_point (), r2.paths.get (0).get_first_point ()); @@ -1132,19 +1146,76 @@ public EditPoint point; public EditPoint other_point; public Path path; + public Path other_path; - public Intersection (EditPoint point, Path path, EditPoint other_point) { + public Intersection (EditPoint point, Path path, + EditPoint other_point, Path other_path) { + this.point = point; this.path = path; this.other_point = other_point; + this.other_path = other_path; + } + + public Intersection.empty () { + this.point = new EditPoint (); + this.path = new Path (); + this.other_point = new EditPoint (); + this.other_path = new Path (); + } + + public Path get_other_path (Path p) { + if (p == path) { + return other_path; + } + + if (p == other_path) { + return path; + } + + warning ("Wrong intersection."); + return new Path (); + } + + public EditPoint get_point (Path p) { + if (p == path) { + return point; + } + + if (p == other_path) { + return other_point; + } + + warning ("Wrong intersection."); + return new EditPoint (); } } - static bool merge_path (Path path1, Path path2, out PathList result) { - Gee.ArrayList<Intersection> intersections; + public class IntersectionList : GLib.Object { + public Gee.ArrayList<Intersection> points = new Gee.ArrayList<Intersection> (); + + public IntersectionList () { + } + + public Intersection get_point (EditPoint ep, out bool other) { + other = false; + foreach (Intersection i in points) { + if (i.other_point == ep || i.point == ep) { + other = (i.other_point == ep); + return i; + } + } + + warning ("No intersection found for point."); + return new Intersection.empty (); + } + } + + static bool merge_path (Path path1, Path path2, out PathList merged_paths) { + IntersectionList intersections; EditPoint ep1, next, p1, p2, pp1, pp2; Path path, other, merged; - PathList pl1, pl2, r, other_paths; + PathList pl1, pl2, r, other_paths, result; bool intersects; int s = 0; int i; @@ -1152,8 +1223,9 @@ bool merge = false; EditPoint intersection_point, other_intersection_point; Intersection intersection; - - intersections = new Gee.ArrayList<Intersection> (); + + merged_paths = new PathList (); + intersections = new IntersectionList (); iix = 0; iiy = 0; @@ -1161,6 +1233,10 @@ result = new PathList (); if (path1.points.size == 0 || path2.points.size == 0) { + return false; + } + + if (!path1.boundaries_intersecting (path2)) { return false; } @@ -1177,10 +1253,10 @@ other_paths = new PathList (); r = new PathList (); - path = path1; - other = path2; - other_paths.add (path1); - other_paths.add (path2); + path = path1.copy (); + other = path2.copy (); + other_paths.add (path); + other_paths.add (other); intersects = false; p1 = new EditPoint (); p2 = new EditPoint (); @@ -1191,116 +1267,270 @@ iy = 0; i = s; merged = new Path (); - - path.points.get (i % path.points.size).color = new Color (0,1,0,1); while (true) { + print ("Next point\n"); ep1 = path.points.get (i % path.points.size); next = path.points.get ((i + 1) % path.points.size); - - if (merged.points.index_of (ep1) != -1) { + + if ((ep1.flags & EditPoint.COPIED) > 0) { print ("Done. Point already added.\n"); if (merge) { - reset_intersections (merged); merged.close (); - result.add (merged); + result.add (merged.copy ()); } if (stroke_selected) { // FIXME: DELETE - ((!) BirdFont.get_current_font ().get_glyph ("a")).add_path (merged.copy ()); + Path mm = merged.copy (); + Path mo = path1.copy (); + Path mn = path2.copy (); + mm.move (0, 0); + mo.move (0, -150); + mn.move (0, 150); + ((!) BirdFont.get_current_font ().get_glyph ("a")).add_path (mm); + ((!) BirdFont.get_current_font ().get_glyph ("a")).add_path (mo); + ((!) BirdFont.get_current_font ().get_glyph ("a")).add_path (mn); } merged = new Path (); - if (intersections.size == 0) { - break; - } - - int j = 0; - intersection = intersections.get (j); - while (intersection.done) { - if (j >= intersections.size) { + print ("Find next part\n"); + 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; break; } - intersection = intersections.get (j); - intersection.done = true; - j++; } - if (intersection.done) { - break; + print (@"part? $find_parts\n"); + if (!find_parts) { + break; // done, no more parts + } else { + // set start point for next part + path = new_start.other_path; + + if (path.points.size == 0) { + warning ("No points."); + return false; + } + + i = index_of (path, new_start.get_point (path)); + + print (@"news start: $i\n"); + if (i < 0) { + warning ("Start point not found."); + return false; + } + + ep1 = path.points.get (i % path.points.size); + next = path.points.get ((i + 1) % path.points.size); + + if ((ep1.flags & EditPoint.INTERSECTION) == 0) { + warning ("Not starting on an intersection point."); + return false; + } + + new_start.done = true; } - - intersection.done = true; - path = intersection.path; - i = index_of (path, intersection.point); - continue; } intersects = false; - double dm = double.MAX; + double dm; double d; - foreach (Path o in other_paths.paths) { - bool inter = segment_intersects (o, ep1, next, out iix, out iiy, - out pp1, out pp2, true); - d = Path.distance (ep1.x, iix, ep1.y, iiy); - if (d < dm && inter) { - print ("Found one at $iix, $iiy d: $d\n"); - other = o; - dm = d; - intersects = true; - p1 = pp1; - p2 = pp2; - ix = iix; - iy = iiy; - } - } - if (intersects) { - print ("Swap\n"); - if (ep1.x == 0 && ep1.y == 0) warning ("Add zero -1"); - print (@"Add $(ep1.x) $(ep1.y)\n"); - merged.add_point (ep1); - - intersection_point = add_intersection (path, ep1, next, ix, iy); - other_intersection_point = add_intersection (other, p1, p2, ix, iy); + if ((ep1.flags & EditPoint.INTERSECTION) > 0) { + print ("Inter.\n"); + Intersection current_intersection; + bool continue_on_other_path; + current_intersection = intersections.get_point (ep1, out continue_on_other_path); + current_intersection.done = true; - bool g = false; - foreach (Intersection old_intersection in intersections) { - if (old_intersection.point == intersection_point - || old_intersection.other_point == other_intersection_point) { - old_intersection.done = true; - g = true; + // take the other part of an intersection + if ((next.flags & EditPoint.COPIED) != 0) { + bool next_is_intersection = false; + bool next_continue_on_other_path; + + next_is_intersection = ((next.flags & EditPoint.INTERSECTION) > 0); + + if (next_is_intersection) { + Intersection next_intersection = intersections.get_point (next, out next_continue_on_other_path); + + print (@"Add $(ep1.x) $(ep1.y)\n"); + ep1.flags |= EditPoint.COPIED; + merged.add_point (ep1.copy ()); + + if (!next_intersection.done) { + EditPoint new_start_point; + + path = next_continue_on_other_path + ? next_intersection.other_path : next_intersection.path; + + new_start_point = next_continue_on_other_path + ? next_intersection.other_point : next_intersection.point; + + i = index_of (path, new_start_point); + + if (i < 0) { + warning ("Point not found in path.\n"); + return false; + } + + ep1 = path.points.get (i % path.points.size); + next = path.points.get ((i + 1) % path.points.size); + } else { + warning ("Part is already created.\n"); + return false; + } + } else { + print ("Next is not inter.\n"); + + print (@"Add $(ep1.x) $(ep1.y)\n"); + ep1.flags |= EditPoint.COPIED; + merged.add_point (ep1.copy ()); + + EditPoint new_start_point; + + if ((next.flags & EditPoint.COPIED) > 0) { + print ("next is copied swap."); + + path = current_intersection.get_other_path (path); + new_start_point = current_intersection.get_point (path); + i = index_of (path, new_start_point); + + if (i < 0) { + warning ("Point not found in path."); + return false; + } + + ep1 = path.points.get (i % path.points.size); + next = path.points.get ((i + 1) % path.points.size); + + if ((next.flags & EditPoint.INTERSECTION) > 0) { + warning ("Wrong type."); + } + + if ((next.flags & EditPoint.COPIED) > 0) { + print ("Segment already addeed"); + + ep1.color = new Color (0, 0.5, 0, 0.5); + next.color = new Color (0.5, 0, 0.5, 0.5); + + + merged.add_point (ep1.copy ()); + //merged.add_point (next.copy ()); + + continue; + } + } else { + print (@"Add $(ep1.x) $(ep1.y)\n"); + ep1.flags |= EditPoint.COPIED; + merged.add_point (ep1.copy ()); + } } + } else { + print (@"Add $(ep1.x) $(ep1.y)\n"); + ep1.flags |= EditPoint.COPIED; + merged.add_point (ep1.copy ()); } - if (!g) { - Intersection ip = new Intersection (intersection_point, path, other_intersection_point); - intersections.add (ip); + print ("Mark inter as done.\n"); + current_intersection.done = true; + } else { + // find new intersection + dm = double.MAX; + foreach (Path o in other_paths.paths) { + bool inter = segment_intersects (o, ep1, next, out iix, out iiy, + out pp1, out pp2, true); + d = Path.distance (ep1.x, iix, ep1.y, iiy); + if (d < dm && inter) { + print (@"Found one at $iix, $iiy d: $d\n"); + other = o; + dm = d; + intersects = true; + p1 = pp1; + p2 = pp2; + ix = iix; + iy = iiy; + } } + + if (intersects) { + print ("Swap\n"); + print (@"Add $(ep1.x) $(ep1.y)\n"); + merged.add_point (ep1.copy ()); + ep1.flags |= EditPoint.COPIED; + + intersection_point = add_intersection (path, ep1, next, ix, iy); + other_intersection_point = add_intersection (other, p1, p2, ix, iy); + + bool g = false; + bool stay = false; + foreach (Intersection old_intersection in intersections.points) { + if (old_intersection.point == intersection_point + || old_intersection.other_point == other_intersection_point) { + old_intersection.done = true; + g = true; + } + } + + if (!g) { + Intersection ip = new Intersection (intersection_point, path, other_intersection_point, other); + intersections.points.add (ip); + } - merged.add_point (intersection_point); - - i = index_of (other, other_intersection_point); - - if (i < 0) { - warning (@"Point not found ($i)."); - break; + merged.add_point (intersection_point.copy ()); + intersection_point.flags |= EditPoint.COPIED; + + i = index_of (other, other_intersection_point); + + if (i < 0) { + warning (@"Point not found ($i)."); + break; + } + + path = other; + merge = true; + } else { + print (@"Add $(ep1.x) $(ep1.y)\n"); + ep1.flags |= EditPoint.COPIED; + merged.add_point (ep1.copy ()); } - - i++; - - path = other; - merge = true; - } else { - print (@"Add $(ep1.x) $(ep1.y)\n"); - merged.add_point (ep1); - i++; } + + i++; } + + int counter; + foreach (Path p in result.paths) { + counter = Path.counters (result, p); + if (counter == 0 || counter == 3) { + merged.force_direction (Direction.CLOCKWISE); + reset_intersections (p); + merged_paths.add (p); + p.update_region_boundaries (); + } else { + if (path1.is_clockwise () != path2.is_clockwise ()) { + merged.force_direction (Direction.COUNTER_CLOCKWISE); + reset_intersections (p); + merged_paths.add (p); + p.update_region_boundaries (); + } else { + merged.force_direction (Direction.CLOCKWISE); + reset_intersections (p); + merged_paths.add (p); + p.update_region_boundaries (); + } + } + + p.recalculate_linear_handles (); + } + return merge; } @@ -1366,42 +1596,6 @@ return i; } - /** @return true if the two paths can be merged. */ - static bool merge_path_delete (Path path1, Path path2, out Path merged) { - PathList pl1, pl2; - Path p1, p2; - int i; - - merged = new Path (); - - if (path1 == path2) { - return false; - } - - if (add_intersection_points (path1, path2)) { - i = mark_intersection_as_deleted (path1); - return_if_fail (i == 1); - - i = mark_intersection_as_deleted (path2); - return_if_fail (i == 1); - - pl1 = get_remaining_points (path1.copy ()); - pl2 = get_remaining_points (path2.copy ()); - - return_if_fail (pl1.paths.size == 1); - return_if_fail (pl2.paths.size == 1); - - p1 = pl1.paths.get (0); - p2 = pl2.paths.get (0); - - merged = PenTool.merge_open_paths (p1, p2); - - return true; - } - - return false; - } - /** @param n number of interrsections to find per path. */ static bool add_intersection_points (Path path1, Path path2, int n = 1) { bool intersection = false;
--- a/libbirdfont/SvgParser.vala +++ b/libbirdfont/SvgParser.vala @@ -480,7 +480,10 @@ prev = path.get_last_point (); foreach (EditPoint p in path.points) { - if ((p.y > point.y) != (prev.y > point.y) + if (p.x == point.x && point.y == p.y) { + inside = true; + break; + } else if ((p.y > point.y) != (prev.y > point.y) && point.x < (prev.x - p.x) * (point.y - p.y) / (prev.y - p.y) + p.x) { inside = !inside; }