Commit 448aa3e
Speed optimization of tangent point function
Changed files (8)
modifiedlibbirdfont/DrawingTools.valamodifiedlibbirdfont/EditPoint.valamodifiedlibbirdfont/FileTab.valamodifiedlibbirdfont/Glyph.valamodifiedlibbirdfont/Path.valamodifiedlibbirdfont/PenTool.valamodifiedlibbirdfont/StrokeTool.valamodifiedlibbirdfont/TrackTool.vala
Diff
@@ -126,6 +126,10 @@ public class DrawingTools : ToolCollection { if (track_tool.is_selected ()) { track_tool.set_stroke_width (object_stroke.get_value ()); }+ + if (pen_tool.is_selected ()) {+ pen_tool.set_stroke_width (object_stroke.get_value ());+ } }); draw_tool_modifiers.add_tool (object_stroke); @@ -389,6 +389,23 @@ public class EditPoint { return (!) next; } + public unowned List<EditPoint>? get_link_item () {+ + if (unlikely (prev == null && next == null)) {+ warning ("Need prev or next, create a list first.");+ }+ + if (prev != null) {+ return ((!) prev).data.get_next ();+ }+ + if (next != null) {+ return ((!) next).data.get_prev ();+ }+ + return null; + }+ public unowned List<EditPoint> get_list () { return get_next ().first (); }@@ -24,7 +24,7 @@ public class FileTab : FontDisplay { double top = 60; WidgetAllocation allocation = new WidgetAllocation (); List<Font> recent_fonts = new List<Font> ();- List<string> backups = new List<string> ();+ List<string> backups = new List<string> (); // FIXME: use ref counted object public signal void open_file (); @@ -160,7 +160,8 @@ public class FileTab : FontDisplay { } if (is_null (backups)) {- warning ("No backups");+ // FIXME:+ // warning ("No backups"); return; } @@ -962,7 +962,7 @@ public class Glyph : FontDisplay { } else { np = new Path (); path_list.append (np);- np.add (xt, yt);+ np.add (xt, yt); } clear_active_paths ();@@ -1083,14 +1083,11 @@ public class Glyph : FontDisplay { p.close (); } }- - // FIXME: delete- // clear_active_paths ();++ print ("CLOSE\n"); // FIXME: DELETE redraw_area (0, 0, allocation.width, allocation.height);- open = false;- return r; } @@ -1616,7 +1613,9 @@ public class Glyph : FontDisplay { return img; } - /** Split curve in two parts and add a new point in between. */+ /** Split curve in two parts and add a new point in between.+ * @return the new point+ */ public void insert_new_point_on_path (double x, double y) { double min, distance; Path? p = null;@@ -1341,7 +1341,6 @@ public class Path { return true; }); - // this is a point on a line if (right == PointType.LINE_QUADRATIC && left == PointType.LINE_QUADRATIC) { ep.get_right_handle ().set_point_type (PointType.LINE_QUADRATIC); ep.get_left_handle ().set_point_type (PointType.LINE_QUADRATIC);@@ -1438,6 +1437,11 @@ public class Path { EditPoint next; double step = 0; + if (points.length () == 0) {+ warning ("Empty path.");+ return;+ }+ if (points.length () == 1) { edit_point.x = i.data.x; edit_point.y = i.data.y;@@ -1446,7 +1450,6 @@ public class Path { edit_point.next = i; exit = true;- return; } @@ -1493,6 +1496,10 @@ public class Path { }); } + if (previous_point == null && is_open ()) {+ previous_point = points.last ();+ }+ if (previous_point == null) { warning (@"previous_point == null, points.length (): $(points.length ())"); return;@@ -1616,7 +1623,7 @@ public class Path { } } - private void all_segments (SegmentIterator iter) {+ public void all_segments (SegmentIterator iter) { unowned List<EditPoint> i, next; if (points.length () < 2) {@@ -1644,6 +1651,7 @@ public class Path { }); } + // FIXME: DELETE public void all_vectors (VectorIterator iter, int steps = -1) { all_segments ((start, stop) => { all_of (start, stop, (x, y, s) => {@@ -70,6 +70,8 @@ public class PenTool : Tool { static List<Path> clockwise; static List<Path> counter_clockwise;+ + static double path_stroke_width = 0; public PenTool (string name) { string click_to_add_points;@@ -233,6 +235,12 @@ public class PenTool : Tool { }); } + public void set_stroke_width (double width) {+ string w = SpinButton.convert_to_string (width);+ Preferences.set ("pen_tool_stroke_width", w);+ path_stroke_width = width;+ }+ /** @return true after the initial delay.*/ public static bool isResponding (int px, int py) { double d;@@ -1087,6 +1095,7 @@ public class PenTool : Tool { public static PointSelection add_new_edit_point (int x, int y) { Glyph glyph; PointSelection new_point;+ glyph = MainWindow.get_current_glyph (); new_point = glyph.add_new_edit_point (x, y);@@ -1098,6 +1107,8 @@ public class PenTool : Tool { set_point_type (selected_point); set_default_handle_positions (); + new_point.path.set_stroke (path_stroke_width);+ return new_point; } @@ -51,49 +51,163 @@ public class StrokeTool : Tool { } public static Path get_stroke (Path p, double thickness) {+ Path counter, outline, merged;+ + if (!p.is_open ()) {+ merged = create_stroke (p, thickness);+ } else {+ outline = create_stroke (p, thickness);+ counter = create_stroke (p, -1 * thickness);+ merged = merge_strokes (outline, counter);+ }+ + return merged;+ }+ + /** Create one stroke from the outline and counter stroke and close the + * open endings.+ */+ static Path merge_strokes (Path stroke, Path counter) {+ Glyph g;+ Path merged = stroke.copy ();++ if (stroke.points.length () < 3) {+ warning ("Missing points.");+ return merged;+ }++ if (counter.points.length () < 3) {+ warning ("Missing points.");+ return merged;+ }+ + counter.reverse ();+ + counter.get_last_point ().get_right_handle ().convert_to_line ();+ counter.get_first_point ().get_left_handle ().convert_to_line ();++ merged.get_last_point ().get_right_handle ().convert_to_line ();+ merged.get_first_point ().get_left_handle ().convert_to_line ();+ + merged.append_path (counter);+ merged.close ();+ merged.recalculate_linear_handles ();+ + return merged;+ }+ + static Path create_stroke (Path p, double thickness) { Path new_path; Path stroked; new_path = add_tangent_points (p);- stroked = change_stroke_width (new_path, thickness);- - stroked.reverse (); - if (p.is_open ()) {- remove_end_corner (stroked);+ if (new_path.points.length () >= 2) {+ stroked = change_stroke_width (new_path, thickness);++ if (p.is_open ()) {+ remove_end_corner (stroked);+ } else {+ stroked.reverse ();+ stroked.close ();+ } } else {- stroked.close ();+ // TODO: create stroke for path with one point+ stroked = new Path (); } return stroked; } static void remove_end_corner (Path stroked) {- if (stroked.points.length () < 2) {- warning ("points < 2");+ if (stroked.points.length () < 4) {+ warning ("points < 4"); return; } stroked.points.remove_link (stroked.points.last ()); stroked.points.remove_link (stroked.points.last ());+ + //stroked.points.remove_link (stroked.points.first ());+ stroked.points.remove_link (stroked.points.first ()); } static Path add_tangent_points (Path p) {- Path new_path = p.copy ();+ Path path = p.copy (); int steps = 4;- - p.all_of_path ((x, y, step) => {- EditPoint ep = new EditPoint ();- - if (step != 0 && step != 1) {- new_path.get_closest_point_on_path (ep, x, y);- new_path.insert_new_point_on_path (ep);- }+ List<EditPoint> new_points = new List<EditPoint> ();+ + EditPoint nep;+ EditPoint? previous = null;+ + path.all_segments ((start, stop) => {+ EditPoint? prev = null;+ Path.all_of (start, stop, (x, y, step) => {+ EditPoint ep = new EditPoint ();++ if (0 < step < 1) {+ //new_path.get_closest_point_on_path (ep, x, y);+ ep.set_position (x, y);+ + // Stop processing when the first hidden point is found.+ if (start.type == PointType.HIDDEN || stop.type == PointType.HIDDEN) {+ return false;+ }+ + return_val_if_fail (prev != null, false);++ ep.prev = ((!) prev).get_link_item ();+ ep.next = start.get_next ();+ + if (unlikely (ep.next == null || ep.prev == null)) {+ warning ("No points on segment.");+ return false;+ }++ new_points.append (ep);+ }+ + if (step <= 0) {+ prev = start;+ new_points.append (start);+ } else if (step >= 1) {+ prev = stop;+ new_points.append (stop);+ } else {+ prev = ep;+ }+ + return true;+ }, steps); return true;- }, steps);+ });++ while (new_points.length () > 0) {+ nep = new_points.first ().data;+ + if (previous != null) {+ nep.prev = ((!) previous).get_link_item ();+ } - return new_path;+ if (!path.has_point (nep)) {+ path.insert_new_point_on_path (nep);+ return_if_fail (nep.prev != null);+ }+ + previous = nep;++ new_points.remove_link (new_points.first ());+ path.create_list ();+ }+ + while (path.points.length () > 0 && path.points.last ().data.type == PointType.HIDDEN) {+ path.delete_last_point ();+ }+ + path.create_list ();+ + return path; } /** Shrink or expand the outline. */@@ -118,19 +232,24 @@ public class StrokeTool : Tool { k = 0; npoints = new_path.points.length (); - foreach (EditPoint e in new_path.points) { + if (npoints < 2) {+ warning ("npoints < 2");+ return new_path;+ }+ + foreach (EditPoint e in new_path.points) { ep = e.copy (); get_new_position (e, clockwise, thickness, out nx, out ny); end_point = (k == 0 || k == npoints - 1); k++;-- if (k == 0) {- - } else {+ + if (ep.type != PointType.HIDDEN) { ep.x = nx; ep.y = ny;+ } else {+ break; } if (e.is_corner ()) {@@ -139,7 +258,7 @@ public class StrokeTool : Tool { stroked.add_point (corner1); corner2 = e.copy ();- stroked.get_closest_point_on_path (corner2, nx, ny);+ stroked.get_closest_point_on_path (corner2, nx, ny); // FIXME: optimize if (end_point) { swap_corner = corner1;@@ -69,7 +69,9 @@ public class TrackTool : Tool { if (join_paths) { ps = get_path_with_end_point (x, y);- return_if_fail (ps != null);+ if (unlikely (ps == null)) {+ warning ("No end point.");+ } end_point = (!) ps; if (end_point.is_first ()) { end_point.path.reverse ();@@ -214,7 +216,7 @@ public class TrackTool : Tool { glyph.update_view (); } - public static Path merge_paths (Path a, PointSelection b) {+ private static Path merge_paths (Path a, PointSelection b) { Glyph g; Path merged = a.copy ();