The Birdfont Source Code


All Repositories / birdfont.git / commit – RSS feed

Merge branch 'master' of github.com:johanmattssonm/birdfont

These changes was commited to the Birdfont repository Thu, 02 Apr 2015 15:31:12 +0000.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
author Johan Mattsson <johan.mattsson.m@gmail.com>
Thu, 02 Apr 2015 15:31:12 +0000 (17:31 +0200)
committer Johan Mattsson <johan.mattsson.m@gmail.com>
Thu, 02 Apr 2015 15:31:12 +0000 (17:31 +0200)
commit b18c4dda6d61f04d3a0caa5d7ae804baf307e3f4
tree 1b2ee0c5fc9d7fd7bedc32c4a849c581b7e1437a
parent e759aaa49562b08b8201ac76ad8c32dbf1136597
parent 2343663e14b0467f532bc41a13aa4775ebef4bb5
Merge branch 'master' of github.com:johanmattssonm/birdfont

Conflicts:
libbirdfont/StrokeTool.vala

13 files changed:
libbirdfont/ClickMap.vala
libbirdfont/EditPoint.vala
libbirdfont/Font.vala
libbirdfont/OpenFontFormat/GlyfData.vala
libbirdfont/OpenFontFormat/GlyfTable.vala
libbirdfont/OpenFontFormat/HmtxTable.vala
libbirdfont/OpenFontFormat/OpenFontFormatReader.vala
libbirdfont/Path.vala
libbirdfont/PenTool.vala
libbirdfont/PointConverter.vala
libbirdfont/StrokeTool.vala
scripts/version.py
--- a/libbirdfont/ClickMap.vala +++ b/libbirdfont/ClickMap.vala @@ -17,53 +17,103 @@ namespace BirdFont { public class ClickMap : GLib.Object { - ImageSurface map; - int width; + public ImageSurface map; + public int width; + public double xmax; + public double ymax; + public double xmin; + public double ymin; + public ClickMap (int width) { this.width = width; map = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, width); + xmax = 0; + ymax = 0; + xmin = 0; + ymin = 0; } public bool get_value (int x, int y) { unowned uchar[] d = map.get_data (); bool transparent; + + Context c; + + c = new Context (map); + c.set_fill_rule (Cairo.FillRule.WINDING); if (unlikely (!(0 <= x < width && 0 <= y < width))) { - warning ("Array index out of bounds."); + warning (@"Array index out of bounds. x: $x y: $y size: $width"); return true; } transparent = d[y * map.get_stride () + 4 * x + 3] == 0; - + return !transparent; } - - public void create_click_map (Path path) { + + bool add_point (Context c, double cx, double cy) { + int px = (int) (width * ((cx - xmin) / (xmax - xmin))); + int py = (int) (width * ((cy - ymin) / (ymax - ymin))); + c.line_to (px, py); + return true; + } + + public void create_click_map (Path path, Path? counter = null) { // FIXME: clean up + Path p; Context c; - + c = new Context (map); - c.save (); + if (counter == null) { + xmax = path.xmax; + ymax = path.ymax; + xmin = path.xmin; + ymin = path.ymin; + } else { + p = (!) counter; + + xmax = fmax (p.xmax, path.xmax); + ymax = fmax (p.ymax, path.ymax); + xmin = fmin (p.xmin, path.xmin); + ymin = fmin (p.ymin, path.ymin); + } + c.save (); + c.set_fill_rule (FillRule.EVEN_ODD); c.set_source_rgba (0, 0, 0, 1); c.new_path (); - - path.all_of_path ((cx, cy, ct) => { - int px = (int) (width * ((cx - path.xmin) / (path.xmax - path.xmin))); - int py = (int) (width * ((cy - path.ymin) / (path.ymax - path.ymin))); - - c.line_to (px, py); - + path.all_of_path ((cx, cy) => { + add_point (c, cx, cy); return true; }, 2 * width); + c.close_path (); - c.close_path (); - c.fill (); + if (counter != null) { + c.new_path (); + p = (!) counter; + p.all_of_path ((cx, cy) => { + add_point (c, cx, cy); + return true; + }, 2 * width); + + c.close_path (); + } + + c.fill (); c.restore (); - } + } } + static double fmin (double a, double b) { + return a < b ? a : b; + } + + static double fmax (double a, double b) { + return a > b ? a : b; + } + }
--- a/libbirdfont/EditPoint.vala +++ b/libbirdfont/EditPoint.vala @@ -44,8 +44,8 @@ public static uint DELETED = 1 << 2; public static uint TIE = 1 << 3; public static uint REFLECTIVE = 1 << 4; - public static uint CORNER = 1 << 5; - public static uint INTERSECTION = 1 << 6; + public static uint INTERSECTION = 1 << 5; + public static uint NEW_CORNER = 1 << 6; public uint flags = NONE; @@ -150,6 +150,8 @@ left_handle.y -= d; } } + + public Color? color = null; public EditPoint (double nx = 0, double ny = 0, PointType nt = PointType.NONE) { x = nx; @@ -436,7 +438,9 @@ new_point.left_handle.angle = left_handle.angle; new_point.left_handle.length = left_handle.length; new_point.left_handle.type = left_handle.type; - + + new_point.color = color; + return new_point; }
--- a/libbirdfont/Font.vala +++ b/libbirdfont/Font.vala @@ -485,18 +485,7 @@ /** Obtain all versions and alterntes for this glyph. */ public GlyphCollection? get_glyph_collection (string glyph) { - GlyphCollection? gc = get_cached_glyph_collection (glyph); - Glyph? g; - - if (gc == null && otf_font) { - // load it from otf file if we need to - g = otf.read_glyph (glyph); - - if (g != null) { - return get_cached_glyph_collection (glyph); - } - } - + GlyphCollection? gc = get_cached_glyph_collection (glyph); return gc; }
--- a/libbirdfont/OpenFontFormat/GlyfData.vala +++ b/libbirdfont/OpenFontFormat/GlyfData.vala @@ -36,6 +36,7 @@ /** Data for one entry in the glyf table. */ public class GlyfData : GLib.Object { public Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> (); + public Gee.ArrayList<EditPoint> points = new Gee.ArrayList<EditPoint> (); public Gee.ArrayList<uint16> end_points = new Gee.ArrayList<uint16> (); public Gee.ArrayList<uint8> flags = new Gee.ArrayList<uint8> (); public Gee.ArrayList<int16> coordinate_x = new Gee.ArrayList<int16> (); @@ -55,26 +56,48 @@ get { return HeadTable.UNITS; } } - public GlyfData (Glyph g) { + public GlyfData (Glyph g) { + bool process; + PathList qp = g.get_quadratic_paths (); + glyph = g; - foreach (Path p in g.get_quadratic_paths ().paths) { + foreach (Path p in qp.paths) { if (p.points.size > 0) { if (!is_empty (p)) { // Add points at extrema p.add_extrema (); - paths.add (p); } } } - if (paths.size > 0) { + process = true; + + while (process) { + points.clear (); + paths.clear (); + foreach (Path p in qp.paths) { + if (!is_empty (p)) { + paths.add (p); + foreach (EditPoint ep in p.points) { + points.add (ep); + } + } + } + + if (paths.size == 0) { + break; + } + process_end_points (); process_flags (); process_x (); - process_y (); + + // error checking is done here + process = !process_y (); + process_bounding_box (); - } + } } bool is_empty (Path p) { @@ -112,6 +135,7 @@ uint16 last_end_point = 0; PointType type; + end_points.clear (); end_point = 0; foreach (Path quadratic in paths) { @@ -187,6 +211,7 @@ double x; PointType type; + coordinate_x.clear (); foreach (Path p in paths) { foreach (EditPoint e in p.points) { x = rint (e.x * UNITS - prev - glyph.left_limit * UNITS); @@ -205,16 +230,30 @@ } } - void process_y () { + bool process_y () { double prev = 0; double y; Font font = OpenFontFormatWriter.get_current_font (); PointType type; + int epi = 0; + + coordinate_y.clear (); foreach (Path p in paths) { foreach (EditPoint e in p.points) { y = rint (e.y * UNITS - prev - font.base_line * UNITS); coordinate_y.add ((int16) y); + + if ((int16) y == 0 && (int16) coordinate_x.get (coordinate_y.size - 1) == 0) { + warning (@"Point on point in TTF. Index $(coordinate_y.size - 1)"); + + if (BirdFont.has_argument ("--test")) { + print (glyph.get_name () + "\n"); + print (points.get (epi).to_string ()); + PenTool.remove_point_simplify (new PointSelection (points.get (epi), p)); + return false; + } + } prev = rint (e.y * UNITS - font.base_line * UNITS); @@ -223,10 +262,24 @@ // off curve y = rint (e.get_right_handle ().y * UNITS - prev - font.base_line * UNITS); coordinate_y.add ((int16) y); - + + if ((int16) y == 0 && (int16) coordinate_x.get (coordinate_y.size - 1) == 0) { + warning (@"Point on point in TTF (off curve) Index: $(coordinate_y.size - 1) "); + if (BirdFont.has_argument ("--test")) { + print (glyph.get_name () + "\n"); + print (points.get (epi).to_string ()); + + PenTool.remove_point_simplify (new PointSelection (points.get (epi), p)); + return false; + } + } + prev = rint (e.get_right_handle ().y * UNITS - font.base_line * UNITS); + epi++; } } + + return true; } void process_bounding_box () { @@ -283,4 +336,5 @@ } } +
--- a/libbirdfont/OpenFontFormat/GlyfTable.vala +++ b/libbirdfont/OpenFontFormat/GlyfTable.vala @@ -318,20 +318,6 @@ fd.add (0); } printd (@"length after padding: $(fd.length ())\n"); - } - - public Glyph? read_glyph (string name) throws GLib.Error { - Glyph? glyph; - int i; - - i = post_table.get_gid (name); - - if (i == -1) { - return null; - } - - glyph = parse_index (i, dis, loca_table, hmtx_table, head_table, post_table); - return glyph; } public new void parse (FontData dis, CmapTable cmap_table, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table, KernTable kern_table) throws GLib.Error { @@ -342,56 +328,6 @@ this.head_table = head_table; this.kern_table = kern_table; this.dis = dis; - } - - Glyph parse_index (int index, FontData dis, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table) throws GLib.Error { - - // FIXME: DELETE parse with freetype - Glyph glyph = new Glyph (""); - /* - double xmin, xmax; - double units_per_em = head_table.get_units_per_em (); - unichar character = 0; - string name; - - character = cmap_table.get_char (index); - name = post_table.get_name (index); - - if (name == "") { - StringBuilder name_c = new StringBuilder (); - name_c.append_unichar (character); - name = name_c.str; - } - - printd (@"name: $(name)\n"); - - if (!loca.is_empty (index)) { - glyph = parse_next_glyf (dis, character, index, out xmin, out xmax, units_per_em); - - glyph.left_limit = xmin - hmtx_table.get_lsb (index); - glyph.left_limit = 0; - glyph.right_limit = glyph.left_limit + hmtx_table.get_advance (index); - } else { - // add empty glyph - glyph = new Glyph (name, character); - glyph.left_limit = -hmtx_table.get_lsb (index); - glyph.right_limit = hmtx_table.get_advance (index) - hmtx_table.get_lsb (index); - } - - glyph.name = name; - - if (character == 0) { - glyph.set_unassigned (true); - } - - if (character == 0 && name != "") { - stderr.printf (@"Got null character\n"); - stderr.printf (@"gid: $index\n"); - stderr.printf (@"char: $((uint) character)\n"); - stderr.printf (@"name: $(name)\n"); - } - */ - return glyph; } Glyph parse_next_composite_glyf (FontData dis, unichar character, int pgid) throws Error {
--- a/libbirdfont/OpenFontFormat/HmtxTable.vala +++ b/libbirdfont/OpenFontFormat/HmtxTable.vala @@ -173,7 +173,9 @@ font_data = fd; - warn_if_fail (max_advance != 0); + if (max_advance != 0) { + warning ("max_advance is zero"); + } } public int16 get_average_width () {
--- a/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala +++ b/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala @@ -58,16 +58,6 @@ public void close () { dis.close (); - } - - public Glyph? read_glyph (string name) { - try { - return directory_table.glyf_table.read_glyph (name); - } catch (GLib.Error e) { - warning (e.message); - } - - return null; } public void parse_index (string file_name) throws Error {
--- a/libbirdfont/Path.vala +++ b/libbirdfont/Path.vala @@ -410,8 +410,8 @@ } public void draw_edit_point_handles (EditPoint e, Context cr) { - string color_left = "Control Point Handle"; - string color_right = "Control Point Handle"; + Color color_left = Theme.get_color ("Control Point Handle"); + Color color_right = Theme.get_color ("Control Point Handle"); EditPoint handle_right = e.get_right_handle ().get_point (); EditPoint handle_left = e.get_left_handle ().get_point (); @@ -419,19 +419,19 @@ if (e.type != PointType.HIDDEN) { if (e.get_right_handle ().selected) { - color_right = "Selected Control Point Handle"; + color_right = Theme.get_color ("Selected Control Point Handle"); } else if (e.get_right_handle ().active) { - color_right = "Active Control Point Handle"; + color_right = Theme.get_color ("Active Control Point Handle"); } else { - color_right = "Control Point Handle"; + color_right = Theme.get_color ("Control Point Handle"); } if (e.get_left_handle ().selected) { - color_left = "Selected Control Point Handle"; + color_left = Theme.get_color ("Selected Control Point Handle"); } else if (e.get_left_handle ().active) { - color_left = "Active Control Point Handle"; + color_left = Theme.get_color ("Active Control Point Handle"); } else { - color_left = "Control Point Handle"; + color_left = Theme.get_color ("Control Point Handle"); } if (!hide_end_handle || !(is_open () && e == points.get (points.size - 1))) { @@ -447,40 +447,76 @@ } public static void draw_edit_point_center (EditPoint e, Context cr) { + Color c; + if (e.type != PointType.HIDDEN) { if (e.type == PointType.CUBIC || e.type == PointType.LINE_CUBIC) { if (e.is_selected ()) { if (e.active_point) { - draw_control_point (cr, e.x, e.y, "Selected Active Cubic Control Point"); + if (e.color != null) { + c = (!) e.color; + } else { + c = Theme.get_color ("Selected Active Cubic Control Point"); + } } else { - draw_control_point (cr, e.x, e.y, "Selected Cubic Control Point"); + if (e.color != null) { + c = (!) e.color; + } else { + c = Theme.get_color ("Selected Cubic Control Point"); + } } } else { if (e.active_point) { - draw_control_point (cr, e.x, e.y, "Active Cubic Control Point"); + if (e.color != null) { + c = (!) e.color; + } else { + c = Theme.get_color ("Active Cubic Control Point"); + } } else { - draw_control_point (cr, e.x, e.y, "Cubic Control Point"); + if (e.color != null) { + c = (!) e.color; + } else { + c = Theme.get_color ("Cubic Control Point"); + } } } } else { if (e.is_selected ()) { if (e.active_point) { - draw_control_point (cr, e.x, e.y, "Selected Active Quadratic Control Point"); + if (e.color != null) { + c = (!) e.color; + } else { + c = Theme.get_color ("Selected Active Quadratic Control Point"); + } } else { - draw_control_point (cr, e.x, e.y, "Selected Quadratic Control Point"); + if (e.color != null) { + c = (!) e.color; + } else { + c = Theme.get_color ("Selected Quadratic Control Point"); + } } } else { if (e.active_point) { - draw_control_point (cr, e.x, e.y, "Active Quadratic Control Point"); + if (e.color != null) { + c = (!) e.color; + } else { + c = Theme.get_color ("Active Quadratic Control Point"); + } } else { - draw_control_point (cr, e.x, e.y, "Quadratic Control Point"); + if (e.color != null) { + c = (!) e.color; + } else { + c = Theme.get_color ("Quadratic Control Point"); + } } } } + + draw_control_point (cr, e.x, e.y, c); } } - public static void draw_control_point (Context cr, double x, double y, string color, double size = 3.5) { + public static void draw_control_point (Context cr, double x, double y, Color color, double size = 3.5) { Glyph g = MainWindow.get_current_glyph (); double ivz = 1 / g.view_zoom; double width = size * Math.sqrt (stroke_width) * ivz; @@ -492,7 +528,7 @@ x = xc + x - (width / 2.0) * ivz; y = yc - y - (width / 2.0) * ivz; - Theme.color (cr, color); + cr.set_source_rgba (color.r, color.g, color.b, color.a); cr.move_to (x, y); cr.arc (x, y, width, 0, 2 * Math.PI); @@ -764,8 +800,7 @@ if (!is_over_boundry (x, y)) { return false; } - - + // generate a rasterized image of the object width = 160; click_map = new ClickMap (width); @@ -1667,7 +1702,7 @@ int i; int index = 0; - if (!has_deleted_point ()) { + if (!has_deleted_point ()) { return path_list; } @@ -1676,7 +1711,7 @@ return path_list; } - // set start position to the point that will be removed + // set start position to a point that will be removed for (i = 0; i < points.size; i++) { p = points.get (i); @@ -1959,6 +1994,7 @@ find_intersection (h1.parent.x, h1.parent.y, h1.x, h1.y, h2.parent.x, h2.parent.y, h2.x, h2.y, out point_x, out point_y); } + /** Finx intersection point for two straight lines. */ public static void find_intersection_point (EditPoint p1, EditPoint p2, EditPoint q1, EditPoint q2, out double point_x, out double point_y) { find_intersection (p1.x, p1.y, p2.x, p2.y, q1.x, q1.y, q2.x, q2.y, out point_x, out point_y); }
--- a/libbirdfont/PenTool.vala +++ b/libbirdfont/PenTool.vala @@ -1209,16 +1209,16 @@ if (active_handle.active) { Path.draw_control_point (cr, Glyph.path_coordinate_x (begin_action_x), - Glyph.path_coordinate_y (begin_action_y), "Control Point Handle"); + Glyph.path_coordinate_y (begin_action_y), Theme.get_color ("Control Point Handle")); } else if (selected_points.size > 0) { ps = selected_points.get (selected_points.size - 1); if (ps.point.type == PointType.CUBIC) { Path.draw_control_point (cr, Glyph.path_coordinate_x (begin_action_x), - Glyph.path_coordinate_y (begin_action_y), "Selected Cubic Control Point"); + Glyph.path_coordinate_y (begin_action_y), Theme.get_color ("Selected Cubic Control Point")); } else { Path.draw_control_point (cr, Glyph.path_coordinate_x (begin_action_x), - Glyph.path_coordinate_y (begin_action_y), "Selected Quadratic Control Point"); + Glyph.path_coordinate_y (begin_action_y), Theme.get_color ("Selected Quadratic Control Point")); } } }
--- a/libbirdfont/PointConverter.vala +++ b/libbirdfont/PointConverter.vala @@ -38,8 +38,6 @@ warning ("Too many points in segment."); } - quadratic_path.remove_points_on_points (); - if (quadratic_path.points.size < 2) { return new Path (); }
--- a/libbirdfont/StrokeTool.vala +++ b/libbirdfont/StrokeTool.vala @@ -18,10 +18,17 @@ namespace BirdFont { public class StrokeTool : Tool { + + static bool stroke_selected = false; + static int iterations = 0; public StrokeTool (string tooltip) { + iterations = 10; select_action.connect((self) => { + stroke_selected = true; + iterations++; stroke_selected_paths (); + stroke_selected = false; }); } @@ -50,29 +57,39 @@ } public static PathList get_stroke (Path path, double thickness) { - Path p = path.copy (); - PathList pl; + PathList pl = new PathList (); + StrokeParts parts; + + parts = get_parts (path.copy (), thickness, path); - pl = get_stroke_outline (p, thickness); + foreach (Path p in parts.get_all ()) { + p.get_first_point ().color = new Color (0, 1, 0, 1); + pl.append (get_stroke_outline (p, thickness)); + } return pl; } public static PathList get_stroke_outline (Path p, double thickness) { - Path counter, outline, merged; + Path counter, outline; + Path merged; PathList paths = new PathList (); - + StrokeParts parts; + if (!p.is_open () && p.is_filled ()) { outline = create_stroke (p, thickness); outline.close (); - paths.add (outline); - outline.update_region_boundaries (); + + parts = remove_intersections (outline, thickness, p); + + foreach (Path sp in parts.get_all ()) { + paths.add (sp); + sp.update_region_boundaries (); + } } else if (!p.is_open () && !p.is_filled ()) { outline = create_stroke (p, thickness); counter = create_stroke (p, -1 * thickness); - paths.add (outline); - paths.add (counter); - + if (p.is_clockwise ()) { outline.force_direction (Direction.CLOCKWISE); } else { @@ -85,8 +102,19 @@ counter.force_direction (Direction.CLOCKWISE); } - outline.update_region_boundaries (); - counter.update_region_boundaries (); + parts = remove_intersections (outline, thickness, p); + + foreach (Path sp in parts.get_all ()) { + paths.add (sp); + sp.update_region_boundaries (); + } + + parts = remove_intersections (counter, thickness, p); + + foreach (Path sp in parts.get_all ()) { + paths.add (sp); + sp.update_region_boundaries (); + } } else if (p.is_open ()) { outline = create_stroke (p, thickness); counter = create_stroke (p, -1 * thickness); @@ -97,9 +125,13 @@ } else { merged.force_direction (Direction.COUNTER_CLOCKWISE); } + + parts = remove_intersections (merged, thickness, p); - merged.update_region_boundaries (); - paths.add (merged); + foreach (Path sp in parts.get_all ()) { + paths.add (sp); + sp.update_region_boundaries (); + } } else { warning ("Can not create stroke."); paths.add (p); @@ -139,10 +171,12 @@ static Path create_stroke (Path p, double thickness) { Path stroked; + Path path; if (p.points.size >= 2) { - stroked = p.copy (); - stroked = generate_stroke (stroked, thickness); + path = p.copy (); + path.remove_points_on_points (); + stroked = generate_stroke (path, thickness); if (!p.is_open ()) { stroked.reverse (); @@ -153,9 +187,7 @@ warning ("One point."); stroked = new Path (); } - - remove_self_intersections (stroked); - + return stroked; } @@ -166,7 +198,7 @@ EditPoint previous; int i; - previous = p.get_last_point ().copy (); + previous = p.get_first_point ().copy (); move_segment (start, previous, thickness); i = 0; @@ -176,10 +208,12 @@ move_segment (start, end, thickness); - if (end.get_left_handle ().length > 0 && end.get_right_handle ().length > 0) { - if (!p.is_open () || (i != 0 && i != p.points.size - 1)) { // FIXME: first point i=0 - add_corner (stroked, previous, start, ep.copy (), thickness); - } + if (start == p.get_last_point ()) { + end = p.get_first_point (); + } + + if (!p.is_open () || (i != 0 && i != p.points.size - 1)) { + add_corner (stroked, previous, start, ep.copy (), thickness); } stroked.add_point (start); @@ -253,10 +287,16 @@ corner = new EditPoint (corner_x, corner_y, previous.type); corner.convert_to_line (); + previous_handle.angle -= PI; + next_handle.angle -= PI; + distance = Path.distance_to_point (corner, original); + ratio = fabs (stroke_width) / distance; // FIXME: cutoff parameter - ratio = fabs (stroke_width) / distance; - + double r = original.get_right_handle ().angle; + double l = original.get_left_handle ().angle; + double angle = atan2 (sin (r - l), cos (r - l)); + if (ratio > 1) { stroked.add_point (corner); } else { @@ -273,74 +313,200 @@ cutoff2.x = next.x + (corner.x - next.x) * ratio; cutoff2.y = next.y + (corner.y - next.y) * ratio; + + cutoff1 = stroked.add_point (cutoff1); + cutoff2 = stroked.add_point (cutoff2); - stroked.add_point (cutoff1); - stroked.add_point (cutoff2); - } + cutoff1.recalculate_linear_handles (); + cutoff2.recalculate_linear_handles (); + } + } - previous_handle.angle -= PI; - next_handle.angle -= PI; + static StrokeParts remove_intersections (Path path, double thickness, Path original) { + StrokeParts parts; + + parts = get_parts (path, thickness, original); + delete_intersection_parts (original, parts.parts, thickness); + + return parts; + } + + static StrokeParts get_parts (Path path, double thickness, Path original) { + StrokeParts sp; + PathList pl = new PathList (); + StrokeParts parts = new StrokeParts (); + Path remaining_points = path; + + while (add_self_intersection_points (remaining_points)) { + foreach (EditPoint p in remaining_points.points) { + if ((p.flags & EditPoint.INTERSECTION) > 0) { + p.deleted = true; + // FIXME: delete p.color = new Color (1, 1, 0, 1); + } + } + + remaining_points = get_remaining_points (remaining_points, parts); + } + + foreach (Path p in parts.parts.paths) { + p.close (); + sp = get_parts (p, thickness, original); + pl.add (sp.path); + pl.append (sp.parts); + } + + parts.parts.clear (); + + foreach (Path p in pl.paths) { + parts.parts.add (p); + } + + parts.path = remaining_points; + + return parts; } - static void remove_self_intersections (Path p) { - bool keep = true; + static PathList process_deleted_control_points (Path path) { + PathList paths, nl, pl, rl; - add_self_intersection_points (p); - - // FIXME: set start on non intersecting point - foreach (EditPoint ep in p.points) { - if ((ep.flags & EditPoint.INTERSECTION) > 0) { - print (@"Inter $(ep)"); - keep = !keep; + paths = new PathList (); + rl = new PathList (); + pl = new PathList (); + nl = new PathList (); + + if (!path.has_deleted_point ()) { + return pl; + } + + pl.add (path); + + foreach (Path p in pl.paths) { + nl = p.process_deleted_points (); + + if (nl.paths.size > 0) { + rl.append (nl); + rl.paths.remove (p); } + } + + foreach (Path p in rl.paths) { + pl = process_deleted_control_points (p); - if (!keep && (ep.flags & EditPoint.INTERSECTION) == 0) { - ep.deleted = true; - ep.type = PointType.CUBIC; + if (pl.paths.size > 0) { + paths.append (pl); + } else { + paths.add (p); } } - p.remove_deleted_points (); + return paths; } - static void add_self_intersection_points (Path path) { - Gee.ArrayList<EditPoint> n = new Gee.ArrayList<EditPoint> (); + static Path get_remaining_points (Path old_path, StrokeParts parts) { + Path new_path; + PathList pl; + + old_path.close (); + pl = process_deleted_control_points (old_path); + + if (pl.paths.size == 0) { + return old_path; + } + + new_path = new Path (); + foreach (Path pn in pl.paths) { + if (pn.points.size > new_path.points.size) { + new_path = pn; + } + + if (stroke_selected) // FIXME: DELETE + ((!) BirdFont.get_current_font ().get_glyph ("a")).add_path (pn); + } + new_path.reopen (); + + foreach (Path pn in pl.paths) { + if (pn != new_path) { + parts.parts.add (pn); + } + } + + if (new_path.has_deleted_point ()) { + warning ("Points left."); + } + + return get_remaining_points (new_path, parts); + } + + static bool add_self_intersection_points (Path path) { + bool intersection = false; + + path.get_first_point ().color = new Color (0, 1, 0, 1); + path.all_segments ((ep1, ep2) => { double ix, iy; - EditPoint nep; - EditPoint nep2; + EditPoint p1; EditPoint p2; - if (segment_intersects (path, ep1, ep2, out ix, out iy, out p1, out p2)) { - nep = new EditPoint (); - nep.prev = ep1; - nep.next = ep2; - - nep.x = ix; - nep.y = iy; - - n.add (nep); - - nep2 = new EditPoint (); - nep2.prev = p1; - nep2.next = p2; - - nep2.x = ix; - nep2.y = iy; - - n.add (nep2); + if (segment_intersects (path, ep1, ep2, out ix, out iy, out p1, out p2)) { + add_intersection (path, ep1, ep2, ix, iy); + add_intersection (path, p1, p2, ix, iy); + intersection = true; + return false; } return true; }); + return intersection; + } + + static void add_intersection (Path path, EditPoint prev, EditPoint next, double px, double py, Color? c = null) { + Gee.ArrayList<EditPoint> n = new Gee.ArrayList<EditPoint> (); + EditPoint ep1 = new EditPoint (); + EditPoint ep2 = new EditPoint (); + EditPoint ep3 = new EditPoint (); + + if (prev == path.get_last_point ()) { // FIXME: double check + ep1.prev = null; + } else { + ep1.prev = prev; + } + + ep1.prev = prev; + ep1.next = ep2; + ep1.flags |= EditPoint.NEW_CORNER; + ep1.type = PointType.CUBIC; + ep1.x = px; + ep1.y = py; + ep1.color = c; + n.add (ep1); + + ep2.prev = ep1; + ep2.next = ep3; + ep2.flags |= EditPoint.INTERSECTION; + ep2.type = PointType.QUADRATIC; + ep2.x = px; + ep2.y = py; + ep2.color = c; + n.add (ep2); + + ep3.prev = ep2; + ep3.next = next; + ep3.flags |= EditPoint.NEW_CORNER; + ep3.type = PointType.CUBIC; + ep3.x = px; + ep3.y = py; + ep3.color = c; + n.add (ep3); + foreach (EditPoint np in n) { - path.insert_new_point_on_path (np, -1, true); - np.type = PointType.QUADRATIC; - np.flags |= EditPoint.INTERSECTION; + np = path.add_point_after (np, np.prev); + path.create_list (); } + + path.recalculate_linear_handles (); } static bool segment_intersects (Path path, EditPoint ep, EditPoint next, @@ -360,32 +526,115 @@ } // FIXME: last to first - for (int i = 1; i < path.points.size - 2; i++) { + for (int i = 2; i < path.points.size - 2; i++) { p1 = path.points.get (i - 1); p2 = path.points.get (i); Path.find_intersection_point (ep, next, p1, p2, out cross_x, out cross_y); - - if ((p1.x < cross_x < p2.x || p1.x > cross_x > p2.x) - && (p1.y < cross_y < p2.y || p1.y > cross_y > p2.y) - && (ep.x < cross_x < next.x || ep.x > cross_x > next.x) - && (ep.y < cross_y < next.y || ep.y > cross_y > next.y)) { + + if (Glyph.CANVAS_MIN < cross_x < Glyph.CANVAS_MAX + && Glyph.CANVAS_MIN < cross_y < Glyph.CANVAS_MAX) { + // iterate to find intersection. + + if (!((ep.x == cross_x && ep.y == cross_y) + || (next.x == cross_x && next.y == cross_y) + || (p1.x == cross_x && p1.y == cross_y) + || (p2.x == cross_x && p2.y == cross_y))) { + + if (is_line (ep.x, ep.y, cross_x, cross_y, next.x, next.y) + && is_line (p1.x, p1.y, cross_x, cross_y, p2.x, p2.y)) { - // iterate to find intersection. - ix = cross_x; - iy = cross_y; - - ia = p1; - ib = p2; - - return true; - } + // FIXME: delete ep.color = new Color (1, 0, 0, 1); + next.color = new Color (0.5, 0, 0, 1); + + // FIXME: delete p1.color = new Color (0, 0, 1, 1); + // FIXME: delete p2.color = new Color (0, 0, 0.5, 1); + + ix = cross_x; + iy = cross_y; + + ia = p1; + ib = p2; + + return true; + } + } + } } return false; } - } + + /** @return true if p2 is on the line p1 to p3 */ + static bool is_line (double x1, double y1, double x2, double y2, double x3, double y3) { + double ds = Path.distance (x1, x3, y1, y3); + double d1 = Path.distance (x1, x2, y1, y2); + double d2 = Path.distance (x2, x3, y2, y3); + double p = d1 / ds; + double x = fabs ((x3 - x1) * p - (x2 - x1)); + double y = fabs ((y3 - y1) * p - (y2 - y1)); + double d = fabs (ds - (d1 + d2)); + + return ds > 0.001 && d1 > 0.001 && d2 > 0.001 + && d < 0.001 && x < 0.001 && y < 0.001 + && fmin (x1, x3) < x2 && x2 < fmax (x1, x3) + && fmin (y1, y3) < y2 && y2 < fmax (y1, y3); + } + static void delete_intersection_parts (Path original, PathList parts, double stroke_width) { + PathList remove = new PathList (); + + foreach (Path p in parts.paths) { + if (is_stroke (original, p, stroke_width)) { + remove.add (p); + } + } + + foreach (Path p in remove.paths) { + parts.paths.remove (p); + } + } + + static bool is_stroke (Path original, Path part, double stroke_width) { + double stroke_size = fabs (stroke_width); + bool stroke = false; + + original.all_of_path ((cx, cy, ct) => { + foreach (EditPoint p in part.points) { + if (Path.distance (cx, p.x, cy, p.y) < stroke_size - 0.1) { + if (48 < p.x < 50) print (@"D: $(Path.distance (cx, p.x, cy, p.y)) < $(stroke_size) \n"); + + p.color = new Color (1, 0, 1, 1); // FIXME: DELETE + stroke = true; + return false; + } else { + p.color = new Color (1, 1, 1, 1); // FIXME: DELETE + } + } + + return true; + }, 12); + + return stroke; + } + + class StrokeParts : GLib.Object { + public PathList parts = new PathList (); + public Path path = new Path (); + + public Gee.ArrayList<Path> get_all () { + Gee.ArrayList<Path> pl = new Gee.ArrayList<Path> (); + + pl.add (path); + + foreach (Path p in parts.paths) { + pl.add (p); + } + + return pl; + } + } } + }
--- a/scripts/version.py +++ b/scripts/version.py @@ -13,7 +13,7 @@ Lesser General Public License for more details. """ - VERSION = '2.4.3' + VERSION = '2.4.4' SO_VERSION_MAJOR = '36' SO_VERSION_MINOR = '0' SO_VERSION = SO_VERSION_MAJOR + '.' + SO_VERSION_MINOR