Clone in a terminal:
Commit b18c4dd
Johan Mattsson - 2015-04-02T15:31:12+00:00
b18c4dda6d61f04d3a0caa5d7ae804baf307e3f4johan.mattsson.m@gmail.com

Merge branch 'master' of github.com:johanmattssonm/birdfont Conflicts: libbirdfont/StrokeTool.vala

Changed files (12)

Diff

diff --git a/libbirdfont/ClickMap.vala b/libbirdfont/ClickMap.valaindex 9683ef6..180dff8 100644--- a/libbirdfont/ClickMap.vala+++ b/libbirdfont/ClickMap.vala@@ -17,52 +17,102 @@ using Cairo; 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;+}+	 }diff --git a/libbirdfont/EditPoint.vala b/libbirdfont/EditPoint.valaindex bc05924..d8d674f 100644--- a/libbirdfont/EditPoint.vala+++ b/libbirdfont/EditPoint.vala@@ -44,8 +44,8 @@ public class EditPoint : GLib.Object { 	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; 	@@ -151,6 +151,8 @@ public class EditPoint : GLib.Object { 		} 	} 	+	public Color? color = null;+	 	public EditPoint (double nx = 0, double ny = 0, PointType nt = PointType.NONE) {	 		x = nx; 		y = ny;@@ -436,7 +438,9 @@ public class EditPoint : GLib.Object { 		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; 	} diff --git a/libbirdfont/Font.vala b/libbirdfont/Font.valaindex 61ec225..2716db9 100644--- a/libbirdfont/Font.vala+++ b/libbirdfont/Font.vala@@ -485,18 +485,7 @@ public class Font : GLib.Object { 	 	/** 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; 	} diff --git a/libbirdfont/OpenFontFormat/GlyfData.vala b/libbirdfont/OpenFontFormat/GlyfData.valaindex 9ba455a..a01e2e2 100644--- a/libbirdfont/OpenFontFormat/GlyfData.vala+++ b/libbirdfont/OpenFontFormat/GlyfData.vala@@ -36,6 +36,7 @@ public class CoordinateFlags { /** 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 @@ public class GlyfData : GLib.Object { 		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 @@ public class GlyfData : GLib.Object { 		uint16 last_end_point = 0; 		PointType type; 		+		end_points.clear (); 		end_point = 0; 		 		foreach (Path quadratic in paths) {@@ -187,6 +211,7 @@ public class GlyfData : GLib.Object { 		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,17 +230,31 @@ public class GlyfData : GLib.Object { 		} 	} 	-	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); 				 				type = e.get_right_handle ().type;@@ -223,10 +262,24 @@ public class GlyfData : GLib.Object { 				// 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,3 +336,4 @@ public class GlyfData : GLib.Object { }  }+diff --git a/libbirdfont/OpenFontFormat/GlyfTable.vala b/libbirdfont/OpenFontFormat/GlyfTable.valaindex 5ce2ddc..f1fb066 100644--- a/libbirdfont/OpenFontFormat/GlyfTable.vala+++ b/libbirdfont/OpenFontFormat/GlyfTable.vala@@ -319,20 +319,6 @@ public class GlyfTable : OtfTable { 		} 		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 { 		this.cmap_table = cmap_table;@@ -344,56 +330,6 @@ public class GlyfTable : OtfTable { 		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 { 		uint16 component_flags = 0; 		uint16 glyph_index;diff --git a/libbirdfont/OpenFontFormat/HmtxTable.vala b/libbirdfont/OpenFontFormat/HmtxTable.valaindex 2402b07..2a9abe8 100644--- a/libbirdfont/OpenFontFormat/HmtxTable.vala+++ b/libbirdfont/OpenFontFormat/HmtxTable.vala@@ -173,7 +173,9 @@ public class HmtxTable : OtfTable { 		 		font_data = fd; 		-		warn_if_fail (max_advance != 0);+		if (max_advance != 0) {+			warning ("max_advance is zero");+		} 	} 	 	public int16 get_average_width () {diff --git a/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala b/libbirdfont/OpenFontFormat/OpenFontFormatReader.valaindex 3cc0f8f..e4d81bd 100644--- a/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala+++ b/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala@@ -60,16 +60,6 @@ public class OpenFontFormatReader : Object { 		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 { 		file = File.new_for_path (file_name); 		if (!file.query_exists ()) {diff --git a/libbirdfont/Path.vala b/libbirdfont/Path.valaindex 375249c..1db159c 100644--- a/libbirdfont/Path.vala+++ b/libbirdfont/Path.vala@@ -410,8 +410,8 @@ public class Path { 	} 	 	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 @@ public class Path { 		 		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 class Path { 	}  	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 @@ public class Path { 		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 @@ public class Path { 		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 @@ public class Path { 		int i; 		int index = 0; 		-		if (!has_deleted_point ()) { +		if (!has_deleted_point ()) { 			return path_list; 		} 		@@ -1676,7 +1711,7 @@ public class Path { 			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 @@ public class Path { 		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); 	}diff --git a/libbirdfont/PenTool.vala b/libbirdfont/PenTool.valaindex 041dc45..65804d1 100644--- a/libbirdfont/PenTool.vala+++ b/libbirdfont/PenTool.vala@@ -1209,16 +1209,16 @@ public class PenTool : Tool { 		 		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")); 			} 		} 	}diff --git a/libbirdfont/PointConverter.vala b/libbirdfont/PointConverter.valaindex 9b56c89..858160e 100644--- a/libbirdfont/PointConverter.vala+++ b/libbirdfont/PointConverter.vala@@ -38,8 +38,6 @@ public class PointConverter { 			warning ("Too many points in segment."); 		} 	-		quadratic_path.remove_points_on_points ();- 		if (quadratic_path.points.size < 2) { 			return new Path (); 		}diff --git a/libbirdfont/StrokeTool.vala b/libbirdfont/StrokeTool.valaindex 5831a17..fe86f3d 100644--- a/libbirdfont/StrokeTool.vala+++ b/libbirdfont/StrokeTool.vala@@ -19,9 +19,16 @@ 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 class StrokeTool : Tool { 	} 	 	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 @@ public class StrokeTool : Tool { 				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 @@ public class StrokeTool : Tool { 			} 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 @@ public class StrokeTool : Tool { 	 	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 @@ public class StrokeTool : Tool { 			warning ("One point."); 			stroked = new Path (); 		}-		-		remove_self_intersections (stroked);-		+ 		return stroked; 	} @@ -166,7 +198,7 @@ public class StrokeTool : Tool { 		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 @@ public class StrokeTool : Tool { 			 			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 @@ public class StrokeTool : Tool { 		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 @@ public class StrokeTool : Tool {  			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,31 +526,114 @@ public class StrokeTool : Tool { 		} 		 		// 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;+		}+	} } +}diff --git a/scripts/version.py b/scripts/version.pyindex dc4df09..390f67e 100644--- a/scripts/version.py+++ b/scripts/version.py@@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112