Clone in a terminal:
Commit da8a53e
Johan Mattsson - 2014-03-19T02:24:41+00:00
da8a53eaef9454acca5c8986112fc909eb7c9370johan.mattsson.m@gmail.com

Save stroke width in bf files

Changed files (12)

Diff

diff --git a/libbirdfont/BirdFontFile.vala b/libbirdfont/BirdFontFile.valaindex 98589be..0c7090d 100644--- a/libbirdfont/BirdFontFile.vala+++ b/libbirdfont/BirdFontFile.vala@@ -56,9 +56,6 @@ class BirdFontFile { 		 		font.font_file = "typeface.bf"; 		-		printd ("Load data:\n");-		printd (xml_data);-		 		tr = new TextReader.for_doc (xml_data, ""); 		ok = load_xml (tr); 		@@ -304,6 +301,7 @@ class BirdFontFile { 			if (pl.is_open ()) { 				data.append (" O"); 			}+			 			return data.str; 		} 		@@ -317,6 +315,7 @@ class BirdFontFile { 			if (pl.is_open ()) { 				data.append (" O"); 			}+			 			return data.str; 		} 		@@ -435,7 +434,7 @@ class BirdFontFile { 		foreach (Path p in g.path_list) { 			data = get_point_data (p); 			if (data != "") {-				os.put_string (@"\t\t<path data=\"$(data)\" />\n");+				os.put_string (@"\t\t<path stroke=\"$(p.stroke)\" data=\"$(data)\" />\n"); 			} 		} 		@@ -955,18 +954,30 @@ class BirdFontFile { 	private Path parse_path (Xml.Node* node) {	 		string attr_name; 		string attr_content;+		Path path = new Path (); 		 		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) { 			attr_name = prop->name; 			attr_content = prop->children->content; 			 			if (attr_name == "data") {-				return parse_path_data (attr_content);+				path = parse_path_data (attr_content);+			}+		}++		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+			attr_name = prop->name;+			attr_content = prop->children->content;++			if (attr_name == "stroke") {+				path.set_stroke (double.parse (attr_content)); 			}+		}		+		if (path.points.length () == 0) {+			warning ("Empty path"); 		} 		-		warning ("No path");-		return new Path ();	+		return path;	 	} 	 	private static void line (Path path, string px, string py) {@@ -1230,6 +1241,10 @@ class BirdFontFile { 			close (path); 		} else { 			path.points.remove_link (path.points.last ());+			+			if (!path.is_open ()) {+				warning ("Closed path.");+			} 		} 		 		path.update_region_boundaries ();@@ -1308,7 +1323,9 @@ class BirdFontFile { 		} 	} 	-	/** Parse visual objects and paths */+	/** Parse visual objects and paths +	 * @deprecated ffi format use bf+	 */ 	private void parse_content (Glyph g, Xml.Node* node) { 		Xml.Node* i; 		return_if_fail (node != null);diff --git a/libbirdfont/DrawingTools.vala b/libbirdfont/DrawingTools.valaindex 3f54ffc..b21eb2c 100644--- a/libbirdfont/DrawingTools.vala+++ b/libbirdfont/DrawingTools.vala@@ -37,6 +37,7 @@ public class DrawingTools : ToolCollection  { 	public static SpinButton object_stroke; 	 	public static Tool move_tool;+	public static PenTool pen_tool; 	 	public DrawingTools (GlyphCanvas main_glyph_canvas) { 		glyph_canvas = main_glyph_canvas;@@ -59,7 +60,7 @@ public class DrawingTools : ToolCollection  { 		grid_expander = grid;  		// Draw tools-		PenTool pen_tool = new PenTool ("pen_tool");+		pen_tool = new PenTool ("pen_tool"); 		draw_tools.add_tool (pen_tool);  		PointTool point_tool = new PointTool ("point_tool");diff --git a/libbirdfont/EditPoint.vala b/libbirdfont/EditPoint.valaindex 334719a..faf5ed3 100644--- a/libbirdfont/EditPoint.vala+++ b/libbirdfont/EditPoint.vala@@ -89,27 +89,6 @@ public class EditPoint { 		 		return a; 	}--	public double get_angle () {-		double a = fabs ((right_handle.angle - PI) - left_handle.angle);-		while (a >= 2 * PI - 0.01) {-			a -= 2 * PI;-		}-		-		return fabs (a);-	}--	public bool is_corner () {-		return !(get_angle ()  <= 0.01);-	}-		-	public bool is_narrow () {-		return !(get_angle () < PI / 2 - 0.01);-	}--	public bool is_wide () {-		return !(get_angle () > PI / 2 - 0.01);-	} 		 	public bool equals (EditPoint e) { 		return e.x == x diff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valaindex 05fbe10..e1bbee2 100644--- a/libbirdfont/Glyph.vala+++ b/libbirdfont/Glyph.vala@@ -962,8 +962,12 @@ public class Glyph : FontDisplay { 		} else { 			np = new Path (); 			path_list.append (np);-			np.add (xt, yt);			-		}	+			np.add (xt, yt);+			+			if (DrawingTools.pen_tool.is_selected ()) {+				np.set_stroke (PenTool.path_stroke_width);+			}+		}  		clear_active_paths (); 		add_active_path (np);@@ -1034,9 +1038,11 @@ public class Glyph : FontDisplay { 		} 		 		foreach (Path path in active_paths) {-			EditPoint? pt = path.get_second_last_point ();-			if (pt != null) {-				EditPoint p = (!) pt;+			EditPoint p;+			EditPoint pl = path.get_last_point ();+			+			if (pl.prev != null) {+				p = pl.get_prev ().data; 				 				px = p.x + xc; 				py = p.y - xc;@@ -1076,15 +1082,13 @@ public class Glyph : FontDisplay { 	/** Close all editable paths and return false if no path have been closed. */ 	public bool close_path () { 		bool r = false;-		+ 		foreach (var p in path_list) { 			if (p.is_editable ()) { 				r = true;-				p.close ();+				p.set_editable (false); 			} 		}--		print ("CLOSE\n"); // FIXME: DELETE 		 		redraw_area (0, 0, allocation.width, allocation.height); 		open = false;@@ -1327,7 +1331,6 @@ public class Glyph : FontDisplay { 	 	public void draw_path (Context cr) { 		double left, baseline;-		Path stroke; 		 		baseline = get_line ("baseline").pos; 		left = get_line ("left").pos;@@ -1335,7 +1338,9 @@ public class Glyph : FontDisplay { 		if (!is_open ()) { 			cr.save (); 			cr.set_source_rgba (0, 0, 0, 1);+			 			Svg.draw_svg_path (cr, get_svg_data (), Glyph.xc () + left, Glyph.yc () + baseline);+			 			cr.restore (); 		} @@ -1351,27 +1356,37 @@ public class Glyph : FontDisplay { 		if (is_open ()) { 			foreach (unowned Path p in path_list) { 				p.draw_outline (cr, allocation, view_zoom);-				p.draw_edit_points (cr, allocation, view_zoom);+				p.draw_edit_points (cr, allocation, view_zoom);	++				if (p.stroke > 0) {			+					draw_outline_for_paths (StrokeTool.get_stroke (p, p.stroke), cr);+				} 			} 		}  		if (!is_open ()) { 			foreach (unowned Path p in active_paths) {-				if (!p.is_editable ()) {+				if (p.stroke == 0) { 					p.fill_path (cr, allocation, view_zoom);+				} else {+					fill_paths (StrokeTool.get_stroke (p, p.stroke), cr); 				} 			} 		}-		-		// draw stroke-		foreach (unowned Path p in path_list) {-			if (p.stroke > 0) {			-				stroke = StrokeTool.get_stroke (p, p.stroke);-				stroke.draw_outline (cr, allocation, view_zoom);-			}-		} 	} 	+	private void draw_outline_for_paths (PathList pl, Context cr) {+		foreach (Path p in pl.paths) {+			p.draw_outline (cr, allocation, view_zoom);+		}+	} +	+	private void fill_paths (PathList pl, Context cr) {+		foreach (Path p in pl.paths) {+			p.fill_path (cr, allocation, view_zoom);+		}+	} +	 	private void draw_zoom_area(Context cr) { 		cr.save (); 		cr.set_line_width (2.0);@@ -1663,7 +1678,6 @@ public class Glyph : FontDisplay { 			merge_path (p0); 		} 		-		//open_path (); 		close_path ();  		foreach (Path p in path_list) {diff --git a/libbirdfont/MoveTool.vala b/libbirdfont/MoveTool.valaindex 35eb95e..490696d 100644--- a/libbirdfont/MoveTool.vala+++ b/libbirdfont/MoveTool.vala@@ -37,6 +37,8 @@ class MoveTool : Tool { 		base (n, t_("Move paths"), 'm', CTRL);  		select_action.connect((self) => {+			Glyph glyph = MainWindow.get_current_glyph ();+			glyph.close_path (); 		});  		deselect_action.connect((self) => {diff --git a/libbirdfont/OverView.vala b/libbirdfont/OverView.valaindex 078a8d8..9e94b1d 100644--- a/libbirdfont/OverView.vala+++ b/libbirdfont/OverView.vala@@ -66,7 +66,6 @@ public class OverView : FontDisplay { 				tabs.add_tab (g); 				canvas.set_current_glyph (g); 				set_initial_zoom ();-				g.close_path (); 			} 		}); @@ -96,7 +95,6 @@ public class OverView : FontDisplay { 				canvas = MainWindow.get_glyph_canvas (); 				canvas.set_current_glyph (glyph); 				-				glyph.close_path (); 				set_initial_zoom (); 			} 		});diff --git a/libbirdfont/Path.vala b/libbirdfont/Path.valaindex 72745fe..7716171 100644--- a/libbirdfont/Path.vala+++ b/libbirdfont/Path.vala@@ -35,8 +35,11 @@ public class Path { 	public double ymax = double.MIN; 	public double ymin = double.MAX; -	/** The width of a stroked outline. */+	/** Stroke width */ 	public double stroke = 0;+	+	/** Fill property for closed paths with stroke. */+	public bool fill = false;  	bool edit = true; 	bool open = true;@@ -94,7 +97,7 @@ public class Path { 	public static double stroke_width = 1; 	public static bool show_all_line_handles = true; 	public static bool fill_open_path = false;-		+	 	public Path () { 		string width; 		points = new List<EditPoint> ();@@ -135,6 +138,14 @@ public class Path { 		} 	} +	public bool is_filled () {+		return fill;+	}++	public void set_fill (bool f) {+		fill = f;+	}+ 	public EditPoint get_first_point () { 		if (unlikely (points.length () == 0)) { 			warning ("No point");@@ -162,7 +173,7 @@ public class Path { 	}  	public void set_stroke (double width) {-		stroke = width;+		stroke = width;	 	}  	public void draw_boundaries  (Context cr, WidgetAllocation allocation, double view_zoom) {@@ -714,6 +725,8 @@ public class Path { 		 		new_path.edit = edit; 		new_path.open = open;+		new_path.stroke = stroke;+		new_path.fill = fill; 		new_path.direction_is_set = direction_is_set; 		new_path.create_list (); 		@@ -979,11 +992,6 @@ public class Path { 		open = true; 		edit = true; 	}--	// FIXME: delete this:-	public EditPoint? get_second_last_point () {-		return second_last_point;-	} 	 	/** Move path. */ 	public void move (double delta_x, double delta_y) {@@ -1513,6 +1521,7 @@ public class Path { 		previous = ((!) previous_point).data; 		next = ((!) next_point).data; +		// FIXME: delete 		bezier_vector (step, previous.x, previous.get_right_handle ().x (), next.get_left_handle ().x (), next.x, out handle_x0, out handle_x1); 		bezier_vector (step, previous.y, previous.get_right_handle ().y (), next.get_left_handle ().y (), next.y, out handle_y0, out handle_y1); @@ -1650,46 +1659,6 @@ public class Path { 			return true; 		}); 	}--	// FIXME: DELETE-	public void all_vectors (VectorIterator iter, int steps = -1) {-		all_segments ((start, stop) => {-			all_of (start, stop, (x, y, s) => {-				double handle_x0, handle_x1, handle_y0, handle_y1;-				PointType pt;-		-				handle_x0 = 0;-				handle_x1 = 0;-				handle_y0 = 0;-				handle_y1 = 0;-		-				pt = PenTool.to_curve (start.type);-				-				if (unlikely (PenTool.is_line (pt))) {-					warning (@"Wrong type: $(start.type) ($pt)");-				}-				-				switch (pt) {-					case PointType.QUADRATIC: -						handle_x0 = quadratic_bezier_vector (s, start.x, start.get_right_handle ().x (), stop.x);-						handle_y0 = quadratic_bezier_vector (s, start.y, start.get_right_handle ().y (), stop.y);-						break;-					case PointType.DOUBLE_CURVE:				-						double_bezier_vector (s, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x, out handle_x0, out handle_x1);-						double_bezier_vector (s, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y, out handle_y0, out handle_y1);-						break;-					default:-						bezier_vector (s, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x, out handle_x0, out handle_x1);-						bezier_vector (s, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y, out handle_y0, out handle_y1);-						break;-				}-				-				iter (start, stop, x, y, handle_x0, handle_x1, handle_y0, handle_y1, s);-				return true;-			}, steps);-			return true;-		}); -	} 	 	public static double bezier_path (double step, double p0, double p1, double p2, double p3) { 		double q0, q1, q2;diff --git a/libbirdfont/PathList.vala b/libbirdfont/PathList.valaindex 3382470..424fe2e 100644--- a/libbirdfont/PathList.vala+++ b/libbirdfont/PathList.vala@@ -21,6 +21,10 @@ public class PathList : GLib.Object { 		 paths = new List<Path> (); 	} 	+	public void add (Path p) {+		paths.append (p);+	}+	 	public void append (PathList pl) { 		foreach (Path p in pl.paths) { 			paths.append (p);diff --git a/libbirdfont/PenTool.vala b/libbirdfont/PenTool.valaindex 603e324..77c240d 100644--- a/libbirdfont/PenTool.vala+++ b/libbirdfont/PenTool.vala@@ -71,7 +71,7 @@ public class PenTool : Tool { 	static List<Path> clockwise; 	static List<Path> counter_clockwise; 	-	static double path_stroke_width = 0;+	public static double path_stroke_width = 0; 			 	public PenTool (string name) { 		string click_to_add_points;@@ -104,11 +104,7 @@ public class PenTool : Tool { 		});  		deselect_action.connect ((self) => {-			Glyph glyph = MainWindow.get_current_glyph ();-			-			force_direction ();-			glyph.close_path ();-			+			force_direction ();		 			move_point_on_path = false; 		}); 		@@ -1107,8 +1103,6 @@ public class PenTool : Tool { 		set_point_type (selected_point); 		set_default_handle_positions (); 		-		new_point.path.set_stroke (path_stroke_width);-		 		return new_point; 	} diff --git a/libbirdfont/StrokeTool.vala b/libbirdfont/StrokeTool.valaindex 14be4f6..40e0314 100644--- a/libbirdfont/StrokeTool.vala+++ b/libbirdfont/StrokeTool.vala@@ -27,7 +27,8 @@ public class StrokeTool : Tool { 	 	public static void set_stroke_for_selected_paths (double width) { 		Glyph g = MainWindow.get_current_glyph ();-			+		+		warning ("set_stroke_for_selected_paths"); 		foreach (Path p in g.active_paths) { 			p.set_stroke (width); 		}@@ -38,57 +39,92 @@ public class StrokeTool : Tool { 	/** Create strokes for the selected outlines. */ 	void stroke_selected_paths () { 		Glyph g = MainWindow.get_current_glyph ();-		List<Path> paths = new List<Path> ();+		PathList paths = new PathList (); 		 		foreach (Path p in g.active_paths) { 			paths.append (get_stroke (p, p.stroke));-			paths.append (get_stroke (p, -1 * p.stroke)); 		} 		-		foreach (Path np in paths) {+		foreach (Path np in paths.paths) { 			g.add_path (np); 		} 	} 	-	public static Path get_stroke (Path p, double thickness) {+	public static PathList get_stroke (Path p, double thickness) { 		Path counter, outline, merged;+		PathList paths = new PathList (); 		-		if (!p.is_open ()) {-			merged = create_stroke (p, thickness);-		} else {+		if (!p.is_open () && p.is_filled ()) {+			outline = create_stroke (p, thickness);+			outline.close ();+			paths.add (outline);+		} 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 {+				outline.force_direction (Direction.COUNTER_CLOCKWISE);+			}+			+			if (outline.is_clockwise ()) {+				counter.force_direction (Direction.COUNTER_CLOCKWISE);+			} else {+				counter.force_direction (Direction.CLOCKWISE);+			}+			+		} else if (p.is_open ()) { 			outline = create_stroke (p, thickness); 			counter = create_stroke (p, -1 * thickness); 			merged = merge_strokes (outline, counter);+			+			if (p.is_clockwise ()) {+				merged.force_direction (Direction.CLOCKWISE);+			} else {+				merged.force_direction (Direction.COUNTER_CLOCKWISE);+			}+			+			paths.add (merged);+		} else {+			warning ("Can not create stroke.");+			paths.add (p); 		} 		-		return merged;+		return paths; 	} 	 	/** 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 ();+		Path merged;  		if (stroke.points.length () < 3) { 			warning ("Missing points.");-			return merged;+			return stroke; 		}  		if (counter.points.length () < 3) { 			warning ("Missing points.");-			return merged;+			return stroke; 		} 						 		counter.reverse (); 		+		counter.points.remove_link (counter.points.first ());+		counter.points.remove_link (counter.points.last ());++		merged = stroke.copy ();+ 		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 ();@@ -99,8 +135,8 @@ public class StrokeTool : Tool { 	static Path create_stroke (Path p, double thickness) { 		Path new_path; 		Path stroked;--		new_path = add_tangent_points (p);+		+		new_path = tangents (p); 		 		if (new_path.points.length () >= 2) { 			stroked = change_stroke_width (new_path, thickness);@@ -113,6 +149,7 @@ public class StrokeTool : Tool { 			} 		} else { 			// TODO: create stroke for path with one point+			warning ("One point."); 			stroked = new Path (); 		} 		@@ -128,13 +165,21 @@ public class StrokeTool : Tool { 		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 ());+		stroked.points.remove_link (stroked.points.first ());+	}++	static Path tangents (Path p) {+		Path pn = p;+		for (int i = 0; i < 3; i++) {+			pn = add_tangent_points (pn);+		}+		return pn; 	}  	static Path add_tangent_points (Path p) { 		Path path = p.copy ();-		int steps = 4;+		int steps = 2; 		List<EditPoint> new_points = new List<EditPoint> (); 		 		EditPoint nep;@@ -142,16 +187,23 @@ public class StrokeTool : Tool { 		 		path.all_segments ((start, stop) => { 			EditPoint? prev = null;+			+			if (start.type == PointType.DOUBLE_CURVE) {+				steps = 1;+			} else {+				steps = 2;+			}+			 			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;+							// FIXME: Rename tracker points+							return true; 						} 						 						return_val_if_fail (prev != null, false);@@ -159,6 +211,8 @@ public class StrokeTool : Tool { 						ep.prev = ((!) prev).get_link_item (); 						ep.next = start.get_next (); 						+						ep.type = PenTool.to_curve (start.type);+						 						if (unlikely (ep.next == null || ep.prev == null)) { 							warning ("No points on segment."); 							return false;@@ -190,7 +244,7 @@ public class StrokeTool : Tool { 				nep.prev = ((!) previous).get_link_item (); 			} 			-			if (!path.has_point (nep)) {+			if (!path.has_point (nep)) {				 				path.insert_new_point_on_path (nep); 				return_if_fail (nep.prev != null); 			}@@ -212,19 +266,15 @@ public class StrokeTool : Tool {  	/** Shrink or expand the outline. */ 	static Path change_stroke_width (Path new_path, double thickness) {-		double nx, ny;	-		double stroke_thickness;+		double px, py, nx, ny, dnx, dny, counter_dnx, counter_dny;	 		double space_left, space_right; 		Path stroked = new Path ();		-		EditPoint ep, corner1, corner2, corner3, swap_corner;+		EditPoint ep; 		bool clockwise; 		bool end_point; // first and last point 		uint k; 		uint npoints;--		double middle_x, middle_y;-		double corner_thickness_left, corner_thickness_right;-		double middle_point_position;+		double distance_to_counter, distance_to_path; 								 		clockwise = new_path.is_clockwise (); 		@@ -240,77 +290,48 @@ public class StrokeTool : Tool { 		foreach (EditPoint e in new_path.points) { 			ep = e.copy (); 			-			get_new_position (e, clockwise, thickness, out nx, out ny);-+			//get_new_position (e, clockwise, thickness, out nx, out ny);+			get_new_position_delta (e, clockwise, thickness, out dnx, out dny);+			get_new_position_delta (e, clockwise, thickness, out counter_dnx, out counter_dny);+				 			end_point = (k == 0 || k == npoints - 1); 			k++; 			+			px = ep.x;+			py = ep.y;+			 			if (ep.type != PointType.HIDDEN) {-				ep.x = nx;-				ep.y = ny;+				ep.x += dnx;+				ep.y += dny; 			} else { 				break; 			} 			-			if (e.is_corner ()) {-				// add new points in order to preserve the stroke-				corner1 = e.copy ();-				stroked.add_point (corner1);-				-				corner2 = e.copy ();-				stroked.get_closest_point_on_path (corner2, nx, ny); // FIXME: optimize--				if (end_point) {-					swap_corner = corner1;-					corner1 = corner2;-					corner2 = swap_corner;-				}--				if (end_point) {-					stroked.insert_new_point_on_path (corner1);-				} else {-					stroked.insert_new_point_on_path (corner2);-				}-				-				corner1.x += cos (e.get_right_handle ().angle + PI / 2) * 2 * thickness;-				corner1.y += sin (e.get_right_handle ().angle + PI / 2) * 2 * thickness;--				corner2.x += cos (e.get_left_handle ().angle - PI / 2) * 2 * thickness;-				corner2.y += sin (e.get_left_handle ().angle - PI / 2) * 2 * thickness;--				// fill the gap--				corner3 = e.copy ();-				middle_x = corner1.x + (corner2.x - corner1.x) / 2;-				middle_y = corner1.y + (corner2.y - corner1.y) / 2;-				stroked.get_closest_point_on_path (corner3, middle_x, middle_y);-				stroked.insert_new_point_on_path (corner3);-				-				corner_thickness_right = thickness * ((e.get_right_handle ().angle - PI) / (2 * PI));-				corner_thickness_left = thickness * ((e.get_left_handle ().angle) / (2 * PI));--				middle_point_position = 2 * thickness;-				middle_point_position *= 1 / sin (e.get_corner_angle ());-				-				corner3.x = e.x;-				corner3.y = e.y;-				-				corner3.x += middle_point_position * cos (e.get_right_handle ().angle - PI);-				corner3.y += middle_point_position * sin (e.get_right_handle ().angle - PI);--				corner3.x += middle_point_position * cos (e.get_left_handle ().angle - PI);-				corner3.y += middle_point_position * sin (e.get_left_handle ().angle - PI);+			nx = ep.x;+			ny = ep.y;+			+			space_left = get_space_difference (e, e.get_prev ().data, thickness, clockwise);+			space_right = get_space_difference (e, e.get_next ().data, thickness, clockwise);+		+			distance_to_counter = Path.distance (nx, px + counter_dnx, ny, py + counter_dny);+			distance_to_path = Path.distance (px, nx, py, ny); 			+			//print (@"$k:  $distance_to_path < $thickness\n");+			//print (@"$k  space_left + space_right  $(space_left + space_right)  $space_left + $space_right\n");+			+			if (distance_to_path >= fabs (2 * thickness) - 0.1) {+				ep.get_left_handle ().length += space_left / 3; +				ep.get_right_handle ().length += space_right / 3;+				stroked.add_point (ep);+			} else if (distance_to_path < fabs (2 * thickness) && (space_left + space_right) > 0) {+				add_corner_nodes (stroked, e, clockwise, thickness, end_point, nx, ny);+			} else if (distance_to_path < fabs (2 * thickness) && (space_left + space_right) < 0) {+				corner_position (e, clockwise, thickness, stroked); 			} else {-				stroke_thickness = fabs (thickness) * 0.04; // FIXME: 0.04--				space_left = get_space_difference (e, e.get_prev ().data, thickness, clockwise);-				space_right = get_space_difference (e, e.get_next ().data, thickness, clockwise);--				ep.get_left_handle ().length += space_left / 3;+				ep.get_left_handle ().length += space_left / 3;  				ep.get_right_handle ().length += space_right / 3;-				 				stroked.add_point (ep);+				warning ("Stroke might become distorted"); 			} 		} 	@@ -321,6 +342,105 @@ public class StrokeTool : Tool { 		return stroked; 	} +	/** Find the point where two lines intersect. */+	public static void find_intersection (double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4,+		out double point_x, out double point_y) {+		point_x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));+		point_y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));+	}+		+	/** Add a corner node for sharp corners. */+	static void corner_position (EditPoint ep, bool clockwise, double thickness, Path p) {+		EditPoint corner = ep.copy ();+		double x1, x3;+		double y1, y3;+		double lx1, lx2, lx3, lx4;+		double ly1, ly2, ly3, ly4;+		double k1, k2;+		double n1, n2;+		double posx, posy;+		EditPointHandle h1, h2;++		h1 = ep.get_right_handle ();+		h2 = ep.get_left_handle ();+		+		x1 = ep.x + cos (h1.angle + PI / 2) * 2 * thickness;+		y1 = ep.y + sin (h1.angle + PI / 2) * 2 * thickness;++		x3 = ep.x + cos (h2.angle - PI / 2) * 2 * thickness;+		y3 = ep.y + sin (h2.angle - PI / 2) * 2 * thickness;++		k1 = sin (h1.angle) / cos (h1.angle);+		n1 = y1;++		k2 = sin (h2.angle) / cos (h2.angle);+		n2 = y3;+		+		lx1 = -50 + x1;+		ly1 = k1 * -50 + n1;+		lx2 = 100 + x1;+		ly2 = k1 * 100 + n1;+		lx3 = -50 + x3;+		ly3 = k2 * -50 + n2;+		lx4 = 100 + x3;+		ly4 = k2 * 100 + n2;++		find_intersection (lx1, ly1, lx2, ly2, lx3, ly3, lx4, ly4, out posx, out posy);+		+		corner.x = posx;+		corner.y = posy;++		if (Path.distance (ep.x, posx, ep.y, posy) > fabs (10 * thickness)) {+			add_corner_end (ep, p, x3, y3);+		} else {+			p.add_point (corner);+		}+	}++	// TODO: elaborated stroke endings+	static void add_corner_end (EditPoint e, Path path, double next_x, double next_y) {+		double px = e.x + (e.x - next_x) / 2;+		double py = e.y + (e.y - next_y) / 2;+		path.add (px, py);+	}++	/** Sharp corner that need two extra points to be built. */+	public static void add_corner_nodes (Path stroked, EditPoint e, bool clockwise, double thickness, bool end_point, double nx, double ny) {+		double middle_x, middle_y;+		double corner_thickness_left, corner_thickness_right;+		double middle_point_position;+		EditPoint corner1, corner2, corner3, swap_corner;+		+		// add new points in order to preserve the stroke+		corner1 = e.copy ();+		stroked.add_point (corner1);+	+		corner2 = e.copy ();+		stroked.get_closest_point_on_path (corner2, nx, ny); // FIXME: optimize++		if (end_point) {+			swap_corner = corner1;+			corner1 = corner2;+			corner2 = swap_corner;+		}++		if (end_point) {+			stroked.insert_new_point_on_path (corner1);+		} else {+			stroked.insert_new_point_on_path (corner2);+		}+		+		corner1.x += cos (e.get_right_handle ().angle + PI / 2) * 2 * thickness;+		corner1.y += sin (e.get_right_handle ().angle + PI / 2) * 2 * thickness;++		corner2.x += cos (e.get_left_handle ().angle - PI / 2) * 2 * thickness;+		corner2.y += sin (e.get_left_handle ().angle - PI / 2) * 2 * thickness;++		// fill the gap+		// FIXME: this should move the point not create a new point+		corner_position (e, clockwise, thickness, stroked);+	}+ 	public static void get_new_position (EditPoint e, bool clockwise, double thickness, out double strokex, out double strokey) { 		double sx, sy; 		get_new_position_delta (e, clockwise, thickness, out sx, out sy);@@ -328,7 +448,7 @@ public class StrokeTool : Tool { 		strokey = sy + e.y;	 	} -	public static void get_new_position_delta (EditPoint e, bool clockwise, double thickness, out double strokex, out double strokey) {+	static void get_new_position_delta (EditPoint e, bool clockwise, double thickness, out double strokex, out double strokey) { 			double ra, la; 			double avg_angle; 			double angle;diff --git a/libbirdfont/Svg.vala b/libbirdfont/Svg.valaindex 376b8a0..bedeff6 100644--- a/libbirdfont/Svg.vala+++ b/libbirdfont/Svg.vala@@ -21,9 +21,15 @@ class Svg { 	/** Export to svg glyph data. */ 	public static string to_svg_glyph (Glyph g) {	 		StringBuilder svg = new StringBuilder ();-+		PathList stroke_list;+		 		foreach (Path pl in g.path_list) {-			write_path_as_glyph (pl, svg, g);+			if (pl.stroke == 0) {+				write_path_as_glyph (pl, svg, g);+			} else {+				stroke_list = StrokeTool.get_stroke (pl, pl.stroke);+				write_paths_as_glyph (stroke_list, svg, g);+			} 		} 		 		return svg.str;@@ -37,22 +43,28 @@ class Svg { 		return svg.str; 	} +	private static void write_paths_as_glyph (PathList pl, StringBuilder svg, Glyph g) {+		foreach (Path p in pl.paths) {+			write_path_as_glyph (p, svg, g);+		}+	}+ 	private static void write_path_as_glyph (Path pl, StringBuilder svg, Glyph g) { 		write_path (pl, svg, g, true); 	} -	private static void write_path (Path pl, StringBuilder svg, Glyph g, bool do_glyph) {+	private static void write_path (Path p, StringBuilder svg, Glyph g, bool do_glyph) { 		int i = 0; 		EditPoint? n = null; 		EditPoint m; 		-		if (pl.points.length () < 2) {+		if (p.points.length () < 2) { 			return; 		}-		-		pl.create_list ();++		p.create_list (); 			-		foreach (var e in pl.points) {+		foreach (var e in p.points) { 			if (i == 0) { 				add_abs_start (e, svg, g, do_glyph); 				i++;@@ -68,7 +80,7 @@ class Svg { 			i++; 		} -		m = pl.points.first ().data;	+		m = p.points.first ().data;	 		add_abs_next ((!) n, m, svg, g, do_glyph); 		 		close_path (svg);diff --git a/libbirdfont/TrackTool.vala b/libbirdfont/TrackTool.valaindex 372ba49..ce10911 100644--- a/libbirdfont/TrackTool.vala+++ b/libbirdfont/TrackTool.vala@@ -53,13 +53,21 @@ public class TrackTool : Tool { 			Toolbox.set_object_stroke (stroke_width); 		}); 		-		press_action.connect ((self, b, x, y) => {+		press_action.connect ((self, button, x, y) => { 			Glyph glyph = MainWindow.get_current_glyph (); 			Path p; 			PointSelection? ps; 			PointSelection end_point;++			if (button == 3) {+				glyph.clear_active_paths ();+			}+						+			if (button == 2) {+				glyph.close_path ();+			} 			-			if (b == 1) {+			if (button == 1) { 				draw_free_hand = true; 				 				last_x = x;@@ -71,6 +79,7 @@ public class TrackTool : Tool { 					ps = get_path_with_end_point (x, y); 					if (unlikely (ps == null)) { 						warning ("No end point.");+						return; 					} 					end_point = (!) ps; 					if (end_point.is_first ()) {@@ -78,6 +87,8 @@ public class TrackTool : Tool { 					} 					glyph.set_active_path (end_point.path); 					add_corner (x, y);+					+					set_stroke_width (end_point.path.stroke); 				} else { 					p = new Path (); 					glyph.add_path (p);@@ -98,7 +109,7 @@ public class TrackTool : Tool { 		});  		release_action.connect ((self, button, x, y) => {-			if (button == 1) {+			if (button == 1 && draw_free_hand) { 				add_endpoint_and_merge (x, y); 			} 		});@@ -142,6 +153,7 @@ public class TrackTool : Tool { 		string w = SpinButton.convert_to_string (width); 		Preferences.set ("free_hand_stroke_width", w); 		stroke_width = width;+		warning (@"W: $width\n"); 	}  	void add_endpoint_and_merge (int x, int y) {@@ -167,8 +179,8 @@ public class TrackTool : Tool { 				  				if (joined_path.path == p) { 					delete_last_points_at (x, y);-					p.points.remove_link (p.points.first ()); 					glyph.close_path ();+					p.close (); 				} else { 					p = merge_paths (p, joined_path); 					if (!p.is_open ()) {@@ -190,8 +202,6 @@ public class TrackTool : Tool { 			ep.set_tie_handle (false); 			ep.recalculate_linear_handles (); 			-			//p.delete_last_point ();-			 			p.create_list (); 			 			if (glyph.path_list.length () < 2) {@@ -358,7 +368,7 @@ public class TrackTool : Tool { 			update_cycles = 0; 		} 		-		if (update_cycles > 5) { // cycles of 100 ms+		if (update_cycles > 4) { // cycles of 100 ms 			convert_points_to_line (); 			last_update  = get_current_time (); 			add_corner (last_x, last_y);

Contact

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