Clone in a terminal:
Commit 4f7c8c8
Johan Mattsson - 2014-03-10T18:44:22+00:00
4f7c8c89d4c42abb3cd8ce5c789347a0dec6fffejohan.mattsson.m@gmail.com

Stroke This commit adds code for generating a stroke from a path.

Changed files (9)

Diff

diff --git a/libbirdfont/BirdFontFile.vala b/libbirdfont/BirdFontFile.valaindex c7fc64f..0350a06 100644--- a/libbirdfont/BirdFontFile.vala+++ b/libbirdfont/BirdFontFile.vala@@ -69,7 +69,6 @@ class BirdFontFile {  	private bool load_xml (TextReader tr) { 		bool ok = true;-		int parsed; 		Xml.Node* root; 		 		tr.read ();diff --git a/libbirdfont/DrawingTools.vala b/libbirdfont/DrawingTools.valaindex a272ed0..61b1b05 100644--- a/libbirdfont/DrawingTools.vala+++ b/libbirdfont/DrawingTools.vala@@ -69,6 +69,9 @@ public class DrawingTools : ToolCollection  { 		 		Tool resize_tool = new ResizeTool ("resize"); 		draw_tools.add_tool (resize_tool);++		Tool stroke_tool = new StrokeTool ("stroke");+		draw_tools.add_tool (stroke_tool);	 		 		// quadratic Bézier points 		Tool quadratic_points = new Tool ("quadratic_points", t_("Create quadratic Bézier curves"));diff --git a/libbirdfont/EditPoint.vala b/libbirdfont/EditPoint.valaindex 4130581..f04a6c7 100644--- a/libbirdfont/EditPoint.vala+++ b/libbirdfont/EditPoint.vala@@ -71,6 +71,41 @@ public class EditPoint { 		} 	} +	public double get_corner_angle () {+		double l, r, a;+		+		l = get_left_handle ().angle;+		r = get_right_handle ().angle;+		a = fabs (l - r);++		if (l > r) {+			return 2 * PI - a;+		}+		+		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  			&& e.y == y @@ -216,6 +251,18 @@ public class EditPoint { 		} 	} 	+	public bool is_clockwise () {+		return get_direction () >= 0;+	}+	+	public double get_direction () {+		if (is_null (prev)) {+			return 0;+		}+		+		return (x - ((!) prev).data.x) * (y + ((!) prev).data.y);+	}+	 	public void set_tie_handle (bool t) { 		tie_handles = t; 	}diff --git a/libbirdfont/EditPointHandle.vala b/libbirdfont/EditPointHandle.valaindex 9dccf11..e9eebd5 100644--- a/libbirdfont/EditPointHandle.vala+++ b/libbirdfont/EditPointHandle.vala@@ -93,7 +93,7 @@ public class EditPointHandle  { 		return sin (angle) * length + parent.y; 	} 		-	void print_position () {+	internal void print_position () { 			warning (@"\nEdit point handle at position $(px ()),$(py ()) is not valid.\n" 				+ @"Type: $(parent.type), " 				+ @"Index: $(parent.get_index ()) of $(parent.get_list ().length ())\n"diff --git a/libbirdfont/FileTab.vala b/libbirdfont/FileTab.valaindex 57cfcc5..39f6541 100644--- a/libbirdfont/FileTab.vala+++ b/libbirdfont/FileTab.vala@@ -235,7 +235,6 @@ public class FileTab : FontDisplay { 	}  	private void draw_file_row (WidgetAllocation allocation, Context cr, Font font, bool color, double y) {-		File thumbnail; 		string fn = (!) font.font_file; 		 		fn = fn.substring (fn.replace ("\\", "/").last_index_of ("/") + 1);diff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valaindex 2ecf506..04ee953 100644--- a/libbirdfont/Glyph.vala+++ b/libbirdfont/Glyph.vala@@ -1321,6 +1321,7 @@ 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;@@ -1355,6 +1356,13 @@ public class Glyph : FontDisplay { 				} 			} 		}+		+		// draw stroke+		foreach (unowned Path p in path_list) {+			stroke = StrokeTool.get_stroke (p, 3);+			stroke.close ();+			stroke.draw_outline (cr, allocation, view_zoom);+		} 	} 	 	private void draw_zoom_area(Context cr) {diff --git a/libbirdfont/Path.vala b/libbirdfont/Path.valaindex f038206..922ed91 100644--- a/libbirdfont/Path.vala+++ b/libbirdfont/Path.vala@@ -42,7 +42,16 @@ public class Path { 	bool no_derived_direction = false; 	bool clockwise_direction = true; -	public delegate bool RasterIterator (double x, double y, double step); // iterate over each pixel at a given zoom level+	// Iterate over each pixel in a path+	public delegate bool RasterIterator (double x, double y, double step);+	+	// Iterate over all points in a rasterized path and compute a vector +	// for each point+	public delegate bool VectorIterator (EditPoint start, EditPoint stop, +		double x, double y, double hx0, double hx1, double dy0, double dy1, +		double step);+	+	public delegate bool SegmentIterator (EditPoint start, EditPoint stop); 	 	private static ImageSurface? edit_point_image = null; 	private static ImageSurface? active_edit_point_image = null;@@ -531,13 +540,12 @@ public class Path { 	} 	 	private double clockwise_sum () {+		double sum = 0;+		 		return_val_if_fail (points.length () >= 3, 0); 		-		double sum = 0;-		EditPoint prev = points.last ().data; 		foreach (EditPoint e in points) {-			sum += (e.x - prev.x) * (e.y + prev.y);-			prev = e;+			sum += e.get_direction (); 		} 		 		return sum;@@ -1471,6 +1479,12 @@ public class Path { 		} 	} +	public static void get_point_for_step (EditPoint start, EditPoint stop, double step, out double x, out double y) {+		// FIXME: Types+		x = bezier_path (step, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x);+		y = bezier_path (step, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y);	+	}+ 	private static void all_of_double (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, RasterIterator iter, double steps = 400) { 		double px = x1; 		double py = y1;@@ -1549,7 +1563,7 @@ public class Path { 		}	 	} -	private void all_of_path (RasterIterator iter) {+	private void all_segments (SegmentIterator iter) { 		unowned List<EditPoint> i, next; 		 		if (points.length () < 2) {@@ -1560,16 +1574,49 @@ public class Path { 		next = i.next;  		while (i != points.last ()) {-			all_of (i.data, next.data, iter);+			iter (i.data, next.data); 			i = i.next; 			next = i.next; 		} 		 		if (!is_open ()) {-			all_of (points.last ().data, points.first ().data, iter);-		}+			iter (points.last ().data, points.first ().data);+		}		 	} +	private void all_of_path (RasterIterator iter, int steps = -1) {+		all_segments ((start, stop) => {+			all_of (start, stop, iter, steps);+			return true;+		});+	}++	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;+		+				handle_x0 = 0;+				handle_x1 = 0;+				handle_y0 = 0;+				handle_y1 = 0;+		+				//FIXME:+				//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);++				print (@"s: $s\n");++				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);+				+				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; 		double r0, r1;@@ -1616,7 +1663,7 @@ public class Path { 		return quadratic_bezier_path (2 * (step - 0.5), middle, p2, p3); 	} 	-	private void double_bezier_vector (double step, double p0, double p1, double p2, double p3, out double a0, out double a1) {+	public static void double_bezier_vector (double step, double p0, double p1, double p2, double p3, out double a0, out double a1) { 		double b0, b1, c0, c1, d0, d1; 	 		// set angle@@ -1633,7 +1680,7 @@ public class Path { 		a0 = d0; 		a1 = d1; 	}-		+	 	public void plot (Context cr, WidgetAllocation allocation, double view_zoom) { 			double px = 0, py = 0; 			double xc = allocation.width / 2.0;diff --git a/libbirdfont/StrokeTool.vala b/libbirdfont/StrokeTool.valanew file mode 100644index 0000000..5fe6773--- /dev/null+++ b/libbirdfont/StrokeTool.vala@@ -0,0 +1,312 @@+/*+    Copyright (C) 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library is distributed in the hope that it will be useful, but +    WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +    Lesser General Public License for more details.+*/++using Cairo;+using Math;++namespace BirdFont {++public class StrokeTool : Tool {+	+	public StrokeTool (string tool_tip) {+		select_action.connect((self) => {+			stroke_selected_paths ();+		});+	}++	void stroke_selected_paths () {+		Glyph g = MainWindow.get_current_glyph ();+		List<Path> paths = new List<Path> ();+		+		foreach (Path p in g.path_list) {+			paths.append (get_stroke (p, 4));+			paths.append (get_stroke (p, -4));+		}+		+		foreach (Path np in paths) {+			g.add_path (np);+		}+	}+	+	public static Path get_stroke (Path p, double thickness) {+		Path new_path = new Path ();+		Path stroked = new Path ();+		+		bool clockwise = p.is_clockwise ();+		+		p.create_list ();+		p.all_vectors ((start, stop, px, py, handle_x0, handle_x1, handle_y0, handle_y1, position) => {+			EditPoint ep;+			PointType right, left;+			+			ep = new EditPoint (px, py);+			+			right = start.get_right_handle ().type;+			left = stop.get_right_handle ().type;+			+			if (right == PointType.LINE_QUADRATIC && left == PointType.LINE_QUADRATIC) {+				ep.get_right_handle ().set_point_type (PointType.LINE_QUADRATIC);+				ep.get_left_handle ().set_point_type (PointType.LINE_QUADRATIC);+				ep.type = PointType.QUADRATIC;+			} else if (right == PointType.LINE_CUBIC && left == PointType.LINE_CUBIC) {+				ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);+				ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);+				ep.type = PointType.LINE_CUBIC;+			} else if (right == PointType.LINE_DOUBLE_CURVE && left == PointType.LINE_DOUBLE_CURVE) {+				ep.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);+				ep.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);+				ep.type = PointType.DOUBLE_CURVE;+				+				if (position == 0) {+					ep.left_handle = start.left_handle.copy ();+					ep.right_handle = start.right_handle.copy ();+				+					ep.get_left_handle ().length *= 0.833;+					ep.get_right_handle ().length *= 0.833;++				} else {+					ep.get_left_handle ().set_point_type (PointType.CUBIC);+					ep.get_right_handle ().set_point_type (PointType.CUBIC);+						+					ep.get_left_handle ().parent = ep;+					ep.get_right_handle ().parent = ep;+					+					ep.get_left_handle ().move_to_coordinate (handle_x0, handle_y0);+					ep.get_right_handle ().move_to_coordinate (handle_x1, handle_y1);+				}+				+			} else if (right == PointType.DOUBLE_CURVE || left == PointType.DOUBLE_CURVE) {+				ep.get_left_handle ().move_to_coordinate (handle_x1, handle_y1);+				ep.get_right_handle ().move_to_coordinate (handle_x0, handle_y0);++				ep.get_left_handle ().set_point_type (PointType.DOUBLE_CURVE);	+				ep.get_right_handle ().set_point_type (PointType.DOUBLE_CURVE);+				+				ep.type = PointType.DOUBLE_CURVE;+			} else if (right == PointType.QUADRATIC) {		+				ep.get_right_handle ().move_to_coordinate (handle_x0, handle_y0);+				+				ep.get_left_handle ().set_point_type (PointType.QUADRATIC);	+				ep.get_right_handle ().set_point_type (PointType.QUADRATIC);+				+				ep.get_left_handle ().move_to_coordinate_internal (0, 0);+				+				ep.type = PointType.QUADRATIC;				+			} else {+				+				if (position == 0) {+					ep.left_handle = start.left_handle.copy ();+					ep.right_handle = start.right_handle.copy ();+				+					ep.get_left_handle ().length *= 0.833;+					ep.get_right_handle ().length *= 0.833;++				} else {+					ep.get_left_handle ().set_point_type (PointType.CUBIC);+					ep.get_right_handle ().set_point_type (PointType.CUBIC);+						+					ep.get_left_handle ().parent = ep;+					ep.get_right_handle ().parent = ep;+					+					ep.get_left_handle ().move_to_coordinate (handle_x0, handle_y0);+					ep.get_right_handle ().move_to_coordinate (handle_x1, handle_y1);+				}++				ep.type = PointType.LINE_CUBIC;+			}+			ep.get_left_handle ().parent = ep;+			ep.get_right_handle ().parent = ep;+			+			new_path.add_point (ep);+			+			return true;+		}, 3);+		+		new_path.create_list ();++		foreach (EditPoint e in new_path.points) {			+			e.get_left_handle ().length /= 2;	+			e.get_right_handle ().length /= 2;+		}+		+		bool end_point; // first and last point+		uint k;+		uint npoints;+		+		end_point = true;+		k = 0;+		npoints = new_path.points.length ();+		new_path.create_list ();+		foreach (EditPoint e in new_path.points) {	+			double nx, ny;	+			double stroke_thickness;+			double space_left, space_right;+							+			get_new_position (e, clockwise, thickness, out nx, out ny);+		+			EditPoint ep = e.copy ();++			end_point = (k == 0 || k == npoints - 1);+			k++;++			ep.x = nx;+			ep.y = ny;++			if (e.is_corner ()) {+				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);++				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+				double middle_x, middle_y;+				double corner_thickness_left, corner_thickness_right;+				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));+								+				double ncf;++				ncf = 2 * thickness;+				ncf *= 1 / sin (e.get_corner_angle ());+				+				corner3.x = e.x;+				corner3.y = e.y;+				+				corner3.x += ncf * cos (e.get_right_handle ().angle - PI);+				corner3.y += ncf * sin (e.get_right_handle ().angle - PI);++				corner3.x += ncf * cos (e.get_left_handle ().angle - PI);+				corner3.y += ncf * sin (e.get_left_handle ().angle - PI);+			+			} else {+				stroke_thickness = fabs (thickness) * 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_right_handle ().length += space_right / 3;+				+				stroked.add_point (ep);+			}+		}+	+		foreach (EditPoint ep in stroked.points) {+			ep.recalculate_linear_handles ();+		}+		+		stroked.reverse ();+		return 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);+		strokex = sx + e.x;+		strokey = sy + e.y;	+	}++	public static void get_new_position_delta (EditPoint e, bool clockwise, double thickness, out double strokex, out double strokey) {+			double ra, la;+			double len_tot;+			double len_r, len_l;+			+			double avg_angle;+			double angle;+			+			e.recalculate_linear_handles ();+			+			ra = e.get_right_handle ().angle;+			la = e.get_left_handle ().angle;+			+			len_r = e.get_right_handle ().length;+			len_l = e.get_left_handle ().length;+			+			len_tot = len_r + len_l;+			+			avg_angle = (la + ra + PI) / 2.0;++			while (avg_angle > 2 * PI) {+				avg_angle -= 2 * PI;+			}+									+			angle = avg_angle - PI / 2;+			+			ldnx = 0;+			ldny = 0;+			+			double m, n, o, p;++			m = cos (ra + PI / 2) * thickness;+			n = sin (ra + PI / 2) * thickness;+			+			o = cos (la - PI / 2) * thickness;+			p = sin (la - PI / 2) * thickness;++			ldnx = m + o;+			ldny = n + p;+			+			strokex = ldnx;+			strokey = ldny;+	}+	+	static double get_space_difference (EditPoint ep0, EditPoint ep1, double stroke, bool clockwise) {+		double p0x, p0y, p1x, p1y;+		double d1, d2;+		+		get_new_position (ep0, clockwise, stroke, out p0x, out p0y);+		get_new_position (ep1, clockwise, stroke, out p1x, out p1y);+		+		d1 = Path.distance (ep0.x, ep1.x, ep0.y, ep1.y);+		d2 = Path.distance (p0x, p1x, p0y, p1y);+		+		return d2 - d1;+	}++	public static double length_adjustment (double lx, double ldx, double ly, double ldy, double l) {+		double a = sqrt (pow (ldx, 2) + pow (ldy, 2)) / fabs (l);	+		return a;+	}+}++}diff --git a/scripts/bavala.py b/scripts/bavala.pyindex f2cbad3..016787d 100644--- a/scripts/bavala.py+++ b/scripts/bavala.py@@ -85,11 +85,8 @@ class Vala(object):             self.so_link = join(build, src) + '.so'             self.so_link_name = src + '.so'             self.so_version = so_version-<<<<<<< HEAD-            self.so_name = 'lib' + library + '.so.' + so_version;-=======-            self.so_name = library + '.so.' + so_version;->>>>>>> e8a3b18d3b7fd2193801a61a9d8b1d1c306dfd0c+            self.so_name = 'lib' + library + '.so.' + so_version+      def gen_c(self, opts):         """translate code from vala to C and create .vapi"""

Contact

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