Clone in a terminal:
Commit ef2b95c
Johan Mattsson - 2015-05-05T10:58:07+00:00
ef2b95c4a29d3dac0e0e6db1054142ac4a1f574bjohan.mattsson.m@gmail.com

New bezier drawing tool

Changed files (7)

Diff

diff --git a/libbirdfont/BezierTool.vala b/libbirdfont/BezierTool.valanew file mode 100644index 0000000..5a6c2d3--- /dev/null+++ b/libbirdfont/BezierTool.vala@@ -0,0 +1,228 @@+/*+    Copyright (C) 2015 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 Math;+using Cairo;++namespace BirdFont {++/** Create Beziér curves. */+public class BezierTool : Tool {+	+	public const uint NONE = 0;+	public const uint MOVE_POINT = 1;+	public const uint MOVE_HANDLES = 2;+	public const uint MOVE_LAST_HANDLE = 3;+	public const uint MOVE_FIRST_HANDLE = 4;+	+	uint state = NONE;+	bool move_right_handle = true;+	int previous_point = 0;+	+	Path current_path = new Path ();+	EditPoint current_point = new EditPoint ();++	int last_x = 0;+	int last_y = 0;++	double last_release_time = 0;+	+	public BezierTool (string name) {+		base (name, t_ ("Create Beziér curves"));++		select_action.connect ((self) => {+			state = NONE;+			MainWindow.set_cursor (NativeWindow.VISIBLE);+		});++		deselect_action.connect ((self) => {+			state = NONE;+			MainWindow.set_cursor (NativeWindow.VISIBLE);+		});+		+		press_action.connect ((self, b, x, y) => {+			press (b, x, y);+		});++		double_click_action.connect ((self, b, x, y) => {+		});++		release_action.connect ((self, b, x, y) => {+			release (b, x, y);+		});++		move_action.connect ((self, x, y) => {+			move (x, y);+		});+		+		key_press_action.connect ((self, keyval) => {+		});+		+		key_release_action.connect ((self, keyval) => {+		});+		+		draw_action.connect ((tool, cairo_context, glyph) => {+			if (PenTool.can_join (current_point)) {+				PenTool.draw_join_icon (cairo_context, last_x, last_y);+			}+		});+	}+	+	public void press (int b, int x, int y) {+		Glyph g = MainWindow.get_current_glyph ();+		double px, py;+		Path? p;+		Path path;++		if (b == 2) {+			if (g.is_open ()) {+				g.close_path ();+			} else {+				g.open_path ();+			}+			+			MainWindow.set_cursor (NativeWindow.VISIBLE);+			state = NONE;++			return;+		}+		+		PenTool.update_orientation ();+		+		MainWindow.set_cursor (NativeWindow.HIDDEN);+		g.open_path ();+		+		px = Glyph.path_coordinate_x (x);+		py = Glyph.path_coordinate_y (y);		++		if (state == NONE) {+			g.open_path ();+			current_path = new Path ();+			current_path.reopen ();+			current_path.hide_end_handle = true;+			current_point = current_path.add (px, py);+			current_point.get_left_handle ().convert_to_line ();+			current_point.recalculate_linear_handles ();+			g.add_path (current_path);+			GlyphCanvas.redraw ();+			state = MOVE_POINT;+		} else if (state == MOVE_POINT) {+			if (PenTool.can_join (current_point)) {+				p = PenTool.join_paths (current_point);+				+				return_if_fail (p != null);+				path = (!) p;+				+				if (current_path.points.size == 1) {+					return_if_fail (path.is_open ());+					current_path = path;+					current_point = path.get_last_point ();+					state = MOVE_POINT;+				} else {+					g.open_path ();+					current_path = path;+					current_point = path.get_first_point ();+					state = MOVE_LAST_HANDLE;	+				}+			} else {+				state = MOVE_HANDLES;+			}+		}+	}++	public void release (int b, int x, int y) {+		double px, py;+		Glyph g;+		+		// ignore double clicks+		if ((GLib.get_real_time () - last_release_time) / 1000000.0 < 0.2) {+			last_release_time = GLib.get_real_time ();+			return;+		}+		last_release_time = GLib.get_real_time ();+			+		px = Glyph.path_coordinate_x (x);+		py = Glyph.path_coordinate_y (y);+		+		if (state == MOVE_HANDLES) {+			current_point = current_path.add (px, py);+			current_path.hide_end_handle = true;+			current_point.get_left_handle ().convert_to_line ();+			current_point.recalculate_linear_handles ();+			GlyphCanvas.redraw ();+			state = MOVE_POINT;+		} else if (state == MOVE_LAST_HANDLE) {+			current_path.update_region_boundaries ();+			g = MainWindow.get_current_glyph ();+			g.close_path ();+			MainWindow.set_cursor (NativeWindow.VISIBLE);+			+			if (Path.is_counter (g.get_paths (), current_path)) {+				current_path.force_direction (Direction.COUNTER_CLOCKWISE);+			} else {+				current_path.force_direction (Direction.CLOCKWISE);+			}+			+			state = NONE;+		}+	}+	+	public void move (int x, int y) {+		double px, py;+		+		last_x = x;+		last_y = y;+		+		px = Glyph.path_coordinate_x (x);+		py = Glyph.path_coordinate_y (y);	+		+		if (state == MOVE_POINT) {+			current_point.x = px;+			current_point.y = py;+			current_path.hide_end_handle = true;+			current_point.recalculate_linear_handles ();+			GlyphCanvas.redraw ();+		} else if (state == MOVE_HANDLES || state == MOVE_LAST_HANDLE) {+			current_path.hide_end_handle = false;+			current_point.set_reflective_handles (true);+			current_point.convert_to_curve ();+			current_point.get_right_handle ().move_to_coordinate (px, py);+			GlyphCanvas.redraw ();+		}+		+		if (current_path.points.size > 0) {+			current_path.get_first_point ().set_reflective_handles (false);+			current_path.get_last_point ().set_reflective_handles (false);+		}+	}+	+	public void switch_to_line_mode () {+		int s = current_path.points.size;+		if (s > 2) {+			current_path.points.get (s - 2).get_right_handle ().convert_to_line ();+			current_point.get_left_handle ().convert_to_line ();+		}+	}+	+	public void stop_drawing () {+		if (state == MOVE_POINT && current_path.points.size > 0) {+			current_path.delete_last_point ();+		}+		+		state = NONE;+	}+	+}++}diff --git a/libbirdfont/DrawingTools.vala b/libbirdfont/DrawingTools.valaindex ef5aaad..0746422 100644--- a/libbirdfont/DrawingTools.vala+++ b/libbirdfont/DrawingTools.vala@@ -40,7 +40,7 @@ public class DrawingTools : ToolCollection  { 	public static MoveTool move_tool; 	public static PenTool pen_tool; -	public static ForesightTool foresight_tool;+	public static BezierTool bezier_tool; 	PointTool point_tool; 	public static ZoomTool zoom_tool; 	public static ResizeTool resize_tool;@@ -121,15 +121,15 @@ public class DrawingTools : ToolCollection  { 		font_name.add_tool (new FontName ());  		// Draw tools-		foresight_tool = new ForesightTool ("foresight");-		foresight_tool.select_action.connect ((self) => {+		bezier_tool = new BezierTool ("bezier_tool");+		bezier_tool.select_action.connect ((self) => { 			update_drawing_and_background_tools (self); 		});-		draw_tools.add_tool (foresight_tool);+		draw_tools.add_tool (bezier_tool);  		Tool bezier_line = new Tool ("bezier_line", t_("Convert the last segment to a straight line")); 		bezier_line.select_action.connect ((self) => {-			foresight_tool.switch_to_line_mode ();+			bezier_tool.switch_to_line_mode (); 		}); 		bezier_line.is_tool_modifier = true; 		draw_tools.add_tool (bezier_line);@@ -589,8 +589,8 @@ public class DrawingTools : ToolCollection  { 			 			current = MainWindow.get_toolbox ().get_current_tool (); 			-			if (current is ForesightTool) {-				((ForesightTool) current).stop_drawing ();+			if (current is BezierTool) {+				((BezierTool) current).stop_drawing (); 			} 			 			PenTool.reset_stroke ();@@ -821,7 +821,7 @@ public class DrawingTools : ToolCollection  { 				|| pen_tool.is_selected () 				|| track_tool.is_selected () 				|| point_tool.is_selected ()-				|| foresight_tool.is_selected ();+				|| bezier_tool.is_selected (); 			 			StrokeTool.stroke_width = object_stroke.get_value (); 			@@ -1097,9 +1097,9 @@ public class DrawingTools : ToolCollection  { 			Toolbox tb = MainWindow.get_toolbox (); 			 			tb.reset_active_tool ();-			update_drawing_and_background_tools (foresight_tool);-			tb.select_tool (foresight_tool);-			tb.set_current_tool (foresight_tool);+			update_drawing_and_background_tools (bezier_tool);+			tb.select_tool (bezier_tool);+			tb.set_current_tool (bezier_tool); 					 			set_point_type_from_preferences (); 			@@ -1269,7 +1269,7 @@ public class DrawingTools : ToolCollection  { 			move_background.set_selected (false); 			cut_background.set_selected (false); 			-			foresight_tool.set_selected (false);+			bezier_tool.set_selected (false); 			pen_tool.set_selected (false); 			point_tool.set_selected (false); 			zoom_tool.set_selected (false);@@ -1296,7 +1296,7 @@ public class DrawingTools : ToolCollection  { 		 			if (resize_tool.is_selected () || move_tool.is_selected ()) { 				show_object_tool_modifiers ();-			} else if (foresight_tool.is_selected ()+			} else if (bezier_tool.is_selected () 					|| pen_tool.is_selected ()  					|| point_tool.is_selected () 					|| track_tool.is_selected ()) {diff --git a/libbirdfont/ForesightTool.vala b/libbirdfont/ForesightTool.valadeleted file mode 100644index b7eefb9..0000000--- a/libbirdfont/ForesightTool.vala+++ /dev/null@@ -1,515 +0,0 @@-/*-    Copyright (C) 2014 2015 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 Math;-using Cairo;--namespace BirdFont {--/** Create Beziér curves and preview the path. */-public class ForesightTool : Tool {-	-	public const uint NONE = 0;-	public const uint MOVE_POINT = 1;-	public const uint MOVE_HANDLES = 2;-	public const uint MOVE_LAST_HANDLE = 3;-	public const uint MOVE_FIRST_HANDLE = 4;-	-	uint state = NONE;-	bool move_right_handle = true;-	int previous_point = 0;-	-	Path current_path = new Path ();--	int last_move_x = 0;-	int last_move_y = 0;--	public bool skip_deselect = false;--	static int press_counter = 0;-	double last_release_time = 0;-	-	public ForesightTool (string name) {-		base (name, t_ ("Create Beziér curves"));--		select_action.connect ((self) => {-			PenTool p = (PenTool) PointTool.pen ();-		-			if (state != NONE) {-				p.release_action (p, 1, last_move_x, last_move_y);-			}-			-			MainWindow.get_current_glyph ().clear_active_paths ();-			MainWindow.set_cursor (NativeWindow.VISIBLE);-			state = NONE;-		});--		deselect_action.connect ((self) => {-			PenTool p = (PenTool) PointTool.pen ();-			-			if (state != NONE) {-				p.release_action (p, 1, last_move_x, last_move_y);-			}-			-			MainWindow.set_cursor (NativeWindow.VISIBLE);-			state = NONE;-		});-		-		press_action.connect ((self, b, x, y) => {-			// ignore double clicks-			if ((GLib.get_real_time () - last_release_time) / 1000000.0 < 0.2) {-				print ("Double click.");-				last_release_time = GLib.get_real_time ();-				MainWindow.set_cursor (NativeWindow.VISIBLE);-				return;-			}-			last_release_time = GLib.get_real_time ();--			press (b, x, y);-		});--		double_click_action.connect ((self, b, x, y) => {-			EditPoint last;-			get_active_path ().delete_last_point ();-			-			last = get_active_path ().get_last_point ();-			-			PenTool.selected_points.clear ();-			PenTool.selected_points.add (new PointSelection (last, get_active_path ()));	-			PenTool.selected_point = last; -			PenTool.active_edit_point = null;-		});--		release_action.connect ((self, b, x, y) => {-			PenTool p = (PenTool) PointTool.pen ();-			PointSelection last;-			-			last_move_x = x;-			last_move_y = y;-				-			if (b == 2) {-				return;-			}-			-			if (state == MOVE_HANDLES || state == MOVE_FIRST_HANDLE) {-				if (state != MOVE_FIRST_HANDLE) {-					last = add_new_point (x, y);	-				}-				-				state = MOVE_POINT;-				move_action (this, x, y);-			} else if (state == MOVE_LAST_HANDLE) {-				previous_point = 0;-				-				if (unlikely (PenTool.selected_points.size == 0)) {-					warning ("No point in move last handle.");-					return;-				}-				-				last = PenTool.selected_points.get (PenTool.selected_points.size - 1);-				-				p.release_action (p, 2, x, y);-				-				PenTool.selected_points.add (last);-				PenTool.active_path.highlight_last_segment = false;-				-				last.path.direction_is_set = false;-				PenTool.force_direction ();-				-				state = NONE;-				MainWindow.set_cursor (NativeWindow.VISIBLE);-			} else if (state == MOVE_POINT) {-			} else if (state == NONE) {-			} else {-				warning (@"Unknown state $state.");-			}-			-			current_path.hide_end_handle = true;-		});--		move_action.connect ((self, x, y) => {-			Tool p =  PointTool.pen ();-			PointSelection last;-			bool lh;-			EditPointHandle h;-			Path a;-			-			last_move_x = x;-			last_move_y = y;-			-			a = get_active_path ();-			if (a.is_open () && a.points.size > 1) {-				a.get_first_point ().set_reflective_handles (false);-				a.get_first_point ().set_tie_handle (false);-			}-							-			if (MainWindow.dialog.visible && state != NONE) {-				state = NONE;-				p.release_action (p, 1, last_move_x, last_move_y);-			}-			-			if (state == NONE) {-				MainWindow.set_cursor (NativeWindow.VISIBLE);-			}-			-			PenTool.active_path = current_path;-			PenTool.active_path.hide_end_handle = (state == MOVE_POINT);-			-			if (state == MOVE_HANDLES || state == MOVE_LAST_HANDLE) {			-				if (previous_point > 0) {-					return_if_fail (PenTool.active_path.points.size >= previous_point + 1);-					return_if_fail (PenTool.active_path.points.size > 0);-					last = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - (previous_point + 1)), PenTool.active_path);				-				} else {-					if (unlikely (PenTool.selected_points.size == 0)) {-						warning ("No point to move in state %u", state);-						return;-					}-					last = PenTool.selected_points.get (PenTool.selected_points.size - 1);-					-					if (last.point.get_right_handle ().is_line () || last.point.get_left_handle ().is_line ()) {-						last.point.convert_to_curve ();-						last.point.get_right_handle ().length = 0.1;-						last.point.get_left_handle ().length = 0.1;-					}-				}-				-				PenTool.move_selected_handle = true;-				PenTool.move_selected = false;-				-				lh = (state == MOVE_LAST_HANDLE);-				-				if (!move_right_handle) {-					lh = !lh;-				}-				-				if (previous_point > 0) {-					lh = false;-				}-				-				h = (lh) ? last.point.get_left_handle () : last.point.get_right_handle ();-				PenTool.selected_handle = h;-				PenTool.active_handle = h;-				-				if (previous_point == 0 -					&& !last.point.reflective_point-					&& !last.point.tie_handles) {-					-					last.point.convert_to_curve ();-					last.point.set_reflective_handles (true);-					last.point.get_right_handle ().length = 0.1;-					last.point.get_left_handle ().length = 0.1;-				}-				-				if (previous_point == 0) {-					last.point.set_reflective_handles (true);-				}-				-				if (previous_point > 0) {-					PenTool.retain_angle = last.point.tie_handles;-				}-				-				if (h.is_line ()) {-					last.point.convert_to_curve ();-					last.point.get_right_handle ().length = 0.1;-					last.point.get_left_handle ().length = 0.1;-				}-				-				p.move_action (p, x, y);-				last.point.set_reflective_handles (false);-				-				PenTool.selected_handle = h;-				PenTool.active_handle = h;-				-				PenTool.move_selected_handle = false;-				last.path.highlight_last_segment = true;-				-				if (previous_point == 0) {-					last.point.set_tie_handle (true);-					p.move_action (p, x, y);-				}-				-				PenTool.retain_angle = false;-				-				MainWindow.set_cursor (NativeWindow.HIDDEN);-			} else {-				if (DrawingTools.get_selected_point_type () != PointType.QUADRATIC) {-					p.move_action (p, x, y);-				} else {-					PenTool.move_point_independent_of_handle = true;-					p.move_action (p, x, y);-					PenTool.move_point_independent_of_handle = false;-				}-			}-			-			if (PenTool.active_path.points.size < 3) {-				PenTool.active_edit_point = null;-			}-		});-		-		key_press_action.connect ((self, keyval) => {-			PenTool p = (PenTool) PointTool.pen ();-			p.key_press_action (p, keyval);-		});-		-		key_release_action.connect ((self, keyval) => {-			Tool p = PointTool.pen ();-			p.key_release_action (p, keyval);-			MainWindow.set_cursor (NativeWindow.VISIBLE);-		});-		-		draw_action.connect ((tool, cairo_context, glyph) => {-			Tool p = PointTool.pen ();-			p.draw_action (p, cairo_context, glyph);-		});-	}-	-	public void press (int b, int x, int y) {-		PenTool p = (PenTool) PointTool.pen ();-		PointSelection ps;-		EditPoint first_point;-		bool clockwise;-		Path? path = null;-		bool one_point = false;-		Glyph g;-		-		MainWindow.set_cursor (NativeWindow.HIDDEN);--		BirdFont.get_current_font ().touch ();-		g = MainWindow.get_current_glyph ();-		g.store_undo_state ();--		if (b == 2) {-			last_move_x = x;-			last_move_y = y;-			-			stop_drawing ();-			-			move_action (this, x, y);-			return;-		} -		-		if (state == NONE) {-			state = MOVE_POINT;-			-			PenTool.active_path = get_active_path ();-			-			add_new_point (x, y);-			-			PenTool.last_point_x = Glyph.path_coordinate_x (x);-			PenTool.last_point_y = Glyph.path_coordinate_y (y);-			-			move_action (this, x, y);-			state = MOVE_FIRST_HANDLE;-			-			press_action(this, b, x, y);-			release_action(this, b, x, y);-		}-		-		if (previous_point > 0) {-			previous_point = 0;-			state = MOVE_POINT;-		}-		-		if (previous_point == 0) {-			if (state == MOVE_POINT) {			-				state = MOVE_HANDLES;-				-				path = PenTool.find_path_to_join ();-				-				if (path != null) {-					one_point = (((!) path).points.size == 1);-				}-				-				if (p.has_join_icon ()) {-					// FIXME: last activa path in list-					ps = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - 1), PenTool.active_path);-					ps.point.set_tie_handle (false);-					-					ps = new PointSelection (PenTool.active_path.points.get (0), PenTool.active_path);-					ps.point.set_tie_handle (false);-					-					first_point = PenTool.active_path.points.get (0);-					clockwise = PenTool.active_path.is_clockwise ();--					PenTool.move_selected = false;-					p.release_action (p, 2, x, y);-					-					PenTool.move_selected = false;-					p.press_action (p, 2, x, y);-					-					if (ps.path.has_point (first_point)) {-						ps.path.reverse ();-					}-					-					if (clockwise && PenTool.active_path.is_clockwise ()) {-						ps = new PointSelection (PenTool.active_path.points.get (0), PenTool.active_path);-					} else {-						ps = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - 1), PenTool.active_path);-					}-					-					PenTool.selected_points.clear ();-					PenTool.selected_points.add (ps);-					PenTool.selected_point = ps.point; -					-					state = MOVE_LAST_HANDLE;-					-					if (one_point) {-						p.press_action (p, 2, x, y);-						state = NONE;-					}-				}-			} -		}--		last_move_x = x;-		last_move_y = y;-	}-	-	public void stop_drawing () {-		PenTool p = (PenTool) PointTool.pen ();-		Path a;-		-		p.release_action (p, 1, last_move_x, last_move_y);--		if (state != NONE) {-			a = get_active_path ();-			if (a.is_open () && a.points.size > 0) {-				a.delete_last_point ();--				a.get_first_point ().set_reflective_handles (false);-				a.get_first_point ().set_tie_handle (false);--				a.get_last_point ().set_reflective_handles (false);-				a.get_last_point ().set_tie_handle (false);-			}-		}--		p.press_action (p, 2, last_move_x, last_move_y);-		p.release_action (p, 2, last_move_x, last_move_y);-		current_path.hide_end_handle = true;-		-		previous_point = 0;-		state = NONE;-		-		MainWindow.get_current_glyph ().clear_active_paths ();-		PenTool.active_path = new Path ();-	}-	-	public Path get_active_path () {-		Glyph g = MainWindow.get_current_glyph ();-		if (g.active_paths.size == 0) {-			return new Path ();-		}-		return g.active_paths.get (g.active_paths.size -1);-	}-	-	public void switch_to_line_mode () {-		EditPoint ep;-		EditPoint last;-		-		if (PenTool.active_path.points.size > 2) {-			ep = PenTool.active_path.points.get (PenTool.active_path.points.size - 2);-			ep.get_right_handle ().convert_to_line ();-			ep.set_tie_handle (false);-			-			last = PenTool.active_path.points.get (PenTool.active_path.points.size - 1);-			last.convert_to_line ();-			-			move_action (this, last_move_x, last_move_y);-		}-	}-	-	PointSelection add_new_point (int x, int y) {-		PointSelection last;-		double handle_x, handle_y;-		-		PenTool p = (PenTool) PointTool.pen ();-		-		if (PenTool.active_path.points.size == 0) {-			last = p.new_point_action (x, y);-		} else {			-			if (PenTool.selected_points.size == 0) {-				last = p.new_point_action (x, y);-			}-			-			if (PenTool.selected_points.size == 0) {-				warning ("No selected points.");-				return new PointSelection.empty ();-			}-			-			last = PenTool.selected_points.get (PenTool.selected_points.size - 1);-			-			PenTool.selected_points.clear ();-			PenTool.selected_handle = new EditPointHandle.empty ();-						-			if (DrawingTools.get_selected_point_type () != PointType.QUADRATIC) {-				last = p.new_point_action (x, y);-			} else {-				last.point.get_right_handle ().length *= 0.999999;-				handle_x = last.point.get_right_handle ().x;-				handle_y = last.point.get_right_handle ().y;-				-				last = p.new_point_action (x, y);-				-				last.point.get_left_handle ().x = handle_x;-				last.point.get_left_handle ().y = handle_y;-			}-			-			PenTool.move_selected = true;-			p.move_action (p, x, y);-		}-		-		PenTool.selected_points.clear ();-		PenTool.selected_points.add (last);	-		PenTool.selected_point = last.point; -		PenTool.active_edit_point = null;-		PenTool.show_selection_box = false;-		-		PenTool.move_selected_handle = false;-		PenTool.move_selected = true;--		PenTool.active_path.hide_end_handle = (state == MOVE_POINT);-		-		current_path = last.path;-		-		return last;-	}-	-	// FIXME: solve the straight line issue in undo-	public override void before_undo () {-		EditPoint last;-		-		if (PenTool.active_path.points.size > 1) {-			last = PenTool.active_path.points.get (PenTool.active_path.points.size - 2);-			last.convert_to_curve ();-		}-	}-	-	public override void after_undo () {-		PenTool.selected_points.clear ();-		PenTool.active_edit_point = null;-		state = NONE;--		EditPoint last;-		-		if (PenTool.active_path.points.size > 0) {-			last = PenTool.active_path.points.get (PenTool.active_path.points.size - 1);-			last.convert_to_curve ();-		}-	}--}--}diff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valaindex fea0c9f..33e248c 100644--- a/libbirdfont/Glyph.vala+++ b/libbirdfont/Glyph.vala@@ -1559,7 +1559,7 @@ public class Glyph : FontDisplay { 		if (!(MainWindow.get_toolbox ().get_current_tool () is PenTool) 			&& !(MainWindow.get_toolbox ().get_current_tool () is PointTool) 			&& !(MainWindow.get_toolbox ().get_current_tool () is TrackTool)-			&& !(MainWindow.get_toolbox ().get_current_tool () is ForesightTool)) {+			&& !(MainWindow.get_toolbox ().get_current_tool () is BezierTool)) { 			cr.save (); 			cr.new_path (); 			foreach (Path p in active_paths) {@@ -2278,6 +2278,16 @@ public class Glyph : FontDisplay { 		 		TabContent.show_text_input (listener); 	}+	+	public PathList get_paths () {+		PathList pl = new PathList ();+		+		foreach (Path p in path_list) {+			pl.add (p);+		}+		+		return pl;+	} }  }diff --git a/libbirdfont/MainWindow.vala b/libbirdfont/MainWindow.valaindex c7cd8c5..69ca86a 100644--- a/libbirdfont/MainWindow.vala+++ b/libbirdfont/MainWindow.vala@@ -52,7 +52,7 @@ public class MainWindow : GLib.Object { 		dialog = new Dialog ();
 		spacing_tab = new SpacingTab ();
 		
-		tools.select_tool (DrawingTools.foresight_tool);
+		tools.select_tool (DrawingTools.bezier_tool);
 	}
 
 	public static SpacingTab get_spacing_tab () {
diff --git a/libbirdfont/PenTool.vala b/libbirdfont/PenTool.valaindex 4918c8e..83a0d2c 100644--- a/libbirdfont/PenTool.vala+++ b/libbirdfont/PenTool.vala@@ -161,8 +161,8 @@ public class PenTool : Tool { 			x = Glyph.path_coordinate_x (ix); 			y = Glyph.path_coordinate_y (iy); -			if (has_join_icon ()) {-				join_paths (x, y);+			if (has_join_icon () && active_edit_point != null) {+				join_paths ((!) active_edit_point); 			}  			active_handle = new EditPointHandle.empty ();@@ -176,20 +176,7 @@ public class PenTool : Tool { 			edit_active_corner = false; 			show_selection_box = false; 			-			// update path direction if it has changed-			foreach (Path p in clockwise) {-				if (!p.is_open () && !p.is_clockwise ()) {-					p.reverse ();-					update_selection ();-				}-			}--			foreach (Path p in counter_clockwise) {-				if (!p.is_open () &&  p.is_clockwise ()) {-					p.reverse ();-					update_selection ();-				}-			}+			set_orientation (); 			 			MainWindow.set_cursor (NativeWindow.VISIBLE); @@ -252,10 +239,10 @@ public class PenTool : Tool { 		key_release_action.connect ((self, keyval) => { 			double x, y; 			if (is_arrow_key (keyval)) {-				if (KeyBindings.modifier != CTRL) {+				if (KeyBindings.modifier != CTRL && active_edit_point != null) { 					x = Glyph.reverse_path_coordinate_x (selected_point.x); 					y = Glyph.reverse_path_coordinate_y (selected_point.y);-					join_paths (x, y);+					join_paths ((!) active_edit_point); 					reset_stroke (); 				} 			}	@@ -280,9 +267,34 @@ public class PenTool : Tool { 		} 	} 	+	public static void set_orientation () {+		// update path direction if it has changed+		foreach (Path p in clockwise) {+			if (!p.is_open () && !p.is_clockwise ()) {+				p.reverse ();+				update_selection ();+			}+		}++		foreach (Path p in counter_clockwise) {+			if (!p.is_open () &&  p.is_clockwise ()) {+				p.reverse ();+				update_selection ();+			}+		}	+	}+	 	public bool has_join_icon () {+		if (active_edit_point != null) {+			return can_join ((!) active_edit_point);+		}+		+		return false;+	}++	public static bool can_join (EditPoint ep) { 		double mx, my;-		get_tie_position (out mx, out my);+		get_tie_position (ep, out mx, out my); 		return (mx > -10 * MainWindow.units && my > -10 * MainWindow.units); 	} @@ -1055,7 +1067,7 @@ public class PenTool : Tool { 		} 	} 	-	public static Path? find_path_to_join () {+	public static Path? find_path_to_join (EditPoint end_point) { 		Path? m = null; 		Glyph glyph = MainWindow.get_current_glyph (); 		EditPoint ep_last, ep_first;@@ -1068,12 +1080,12 @@ public class PenTool : Tool { 			ep_last = path.points.get (path.points.size - 1); 			ep_first = path.points.get (0);	 			-			if (active_edit_point == ep_last) {+			if (end_point == ep_last) { 				m = path; 				break; 			} 			-			if (active_edit_point == ep_first) {+			if (end_point == ep_first) { 				m = path; 				break;				 			}@@ -1162,7 +1174,8 @@ public class PenTool : Tool { 		} 	}	 	-	private static void join_paths (double x, double y) {+	/** @return the new path or null if no path could be merged with the end point. */+	public static Path? join_paths (EditPoint end_point) { 		Glyph glyph = MainWindow.get_current_glyph (); 		Path? p; 		Path path;@@ -1171,25 +1184,26 @@ public class PenTool : Tool { 		int px, py;  		if (glyph.path_list.size == 0) {-			return;+			warning ("No paths.");+			return null; 		} -		p = find_path_to_join ();+		p = find_path_to_join (end_point); 		if (p == null) { 			warning ("No path to join.");-			return;+			return null; 		} 		 		path = (!) p; 		if (!path.is_open ()) { 			warning ("Path is closed.");-			return;+			return null; 		} 		 		return_if_fail (path.points.size > 0); 		-		px = Glyph.reverse_path_coordinate_x (((!) active_edit_point).x);-		py = Glyph.reverse_path_coordinate_y (((!) active_edit_point).y);+		px = Glyph.reverse_path_coordinate_x (end_point.x);+		py = Glyph.reverse_path_coordinate_y (end_point.y); 		 		if (path.points.size == 1) { 			glyph.delete_path (path);@@ -1202,10 +1216,7 @@ public class PenTool : Tool { 						active_path = merge; 						merge.reopen (); 						glyph.open_path ();-						-						print ("CONTINUE FIRST\n");-						-						return;+						return merge; 					} 					 					if (is_close_to_point (merge.points.get (0), px, py)) {@@ -1215,15 +1226,12 @@ public class PenTool : Tool { 						merge.reopen (); 						glyph.open_path (); 						merge.reverse ();-						-						print ("CONTINUE\n");-						-						return;+						return merge; 					} 				} 			} 			-			return;+			return null; 		} 		 		if (active_edit_point == path.points.get (0)) {@@ -1237,11 +1245,11 @@ public class PenTool : Tool { 		 		if (path.points.get (0) == active_edit_point) { 			warning ("Wrong direction.");-			return;+			return null; 		} 		 		// join path with it self-		if (is_endpoint ((!) active_edit_point)+		if (is_endpoint (end_point) 			&& is_close_to_point (path.points.get (0), px, py)) { 			 			close_path (path);@@ -1258,7 +1266,7 @@ public class PenTool : Tool { 			 			remove_all_selected_points (); 			-			return;+			return path; 		} 		 		foreach (Path merge in glyph.path_list) {@@ -1306,7 +1314,7 @@ public class PenTool : Tool { 					 					union.update_region_boundaries (); 					-					return;+					return union; 				} 			} 		}@@ -1315,6 +1323,8 @@ public class PenTool : Tool { 			path.reverse (); 			update_selection (); 		}+		+		return null; 	} 	 	/** Merge paths if ends are close. */@@ -1390,29 +1400,27 @@ public class PenTool : Tool {  	void draw_merge_icon (Context cr) { 		double x, y;-		get_tie_position (out x, out y);-		draw_join_icon (cr, x, y);+		if (active_edit_point != null) {+			get_tie_position ((!) active_edit_point, out x, out y);+			draw_join_icon (cr, x, y);+		} 	} 	 	/** Obtain the position where to ends meet. */-	void get_tie_position (out double x, out double y) {+	static void get_tie_position (EditPoint current_point, out double x, out double y) { 		Glyph glyph; 		EditPoint active; 		double px, py;  		x = -100; 		y = -100;-				-		if (active_edit_point == null) {-			return;-		} 		-		if (!is_endpoint ((!) active_edit_point)) {+		if (!is_endpoint (current_point)) { 			return; 		} 		 		glyph = MainWindow.get_current_glyph ();-		active = (!) active_edit_point;+		active = current_point; 		 		return_if_fail (!is_null (glyph)); 		diff --git a/resources/icons.bf b/resources/icons.bfindex adaae92..3f43855 100644--- a/resources/icons.bf+++ b/resources/icons.bf@@ -199,6 +199,17 @@ GNU Lesser General Public License</copyright> 		<path stroke="0" skew="0" data="B 28.7823504083,25.8680965676 M 26.2950154895,23.2677009707 M 9.6750958051,39.5484386207 M 12.1624307239,42.1488342176 M 28.7823504083,25.8680965676" /> 	</glyph> </collection>+<collection name="bezier_tool">+	<selected id="2"/>+	<glyph id="1" left="-28" right="28">+	</glyph>+	<glyph id="2" left="-28" right="44.054140127388528">+		<path stroke="0" skew="0" data="B 2.5292812784,44.7926187215 M 3.8367822764,44.0794363589 C 3.8367822764,44.0794363589 5.6698701483,42.9721208955 7.9970127245,41.8210255443 C 10.3241553008,40.6699301929 13.1430703979,39.5057503226 14.8911088956,39.0871598212 C 19.3725947163,38.0139630021 26.1831629693,36.8287490065 26.1831629693,36.8287490065 M 27.3718002401,36.5910215523 M 27.3718002401,35.4023842815 M 27.2527463310,22.0896468474 M 27.1336924219,21.2576007579 M 26.4205100595,20.9011997585 C 14.7034373343,14.4397295176 4.6763976082,5.9023851103 -3.6520128941,-5.3676839283 M -4.1274678025,-5.9620025637 M -4.8406501651,-5.9620025637 M -12.2102012447,-5.6056015645 M -13.8742934239,-5.4865476553 M -13.5178924246,-3.8224554761 C -11.9078879957,3.0714124768 -9.7659065790,13.9251351612 -4.7219766199,31.0046165611 M -4.7219766199,31.2423440153 M -4.6029227109,31.3613979244 M 1.9345822790,43.4854980875 M 2.5289009145,44.7929990855 M 2.5292812784,44.7926187215" />+		<path stroke="0" skew="0" data="B -6.0814353303,7.6780380292 C -3.5720984332,13.3816711826 -0.0943930151,18.9058105991 -0.0943930151,18.9058105991 C -0.1213608175,21.2315838652 1.2735097744,23.8255517361 3.7141149100,24.8070428125 C 3.9112575332,22.5672698301 3.4487349984,20.0078770403 1.1973228799,18.3312708879 C 1.2685270069,18.4826557306 -1.6727511654,13.8106836556 -4.5064243827,7.6525916825 C -6.0859768756,4.2199593898 -7.8261540635,-0.4207775145 -8.7011999798,-2.8886939252 M -5.4349688005,-3.1090829315 C 2.8294264529,7.8699316302 12.8614869829,16.3623550577 24.2809629719,22.8032095738 M 24.5186904260,34.2141273744 C 23.3475879321,34.4153018553 18.4726536657,35.2347198626 14.2964098964,36.2348107351 C 12.0221759422,36.7794158052 9.0907872321,38.1373150235 6.6891313626,39.3252676394 C 5.0266747482,40.1476144488 4.4331929135,40.4779605192 3.7175381854,40.8704960915 C 3.7176015794,40.8705594855 3.7176649733,40.8706228794 3.7177283673,40.8706862734 C 1.8159087339,37.3443957031 -0.0859108995,33.8181051328 -1.9877305330,30.2918145624 C -2.0242454699,30.1693373781 -2.0725516886,30.1761839288 -2.1067844420,30.0540871083 M -2.1067844420,29.9350331993 C -6.7668891626,14.0819211717 -8.7232149468,4.0716935311 -10.3083816112,-2.8713554774 C -9.4782492743,-0.6638813021 -7.6385524745,4.1387053056 -6.0814353303,7.6780380292" />+		<path stroke="0" skew="0" data="B 27.6008011519,43.5614898312 C 27.5611165156,43.5771398250 27.6856077556,43.5107018807 27.7280110575,43.4853079055 M 27.3716100582,35.1648470092 M 24.5188806080,35.2839009183 M 24.8752816073,42.4157245437 C 21.8079888660,42.7833462789 18.5182973370,42.7399847911 15.2473197131,43.8420892688 C 12.5363138619,44.7555332387 9.9518170529,46.4453760558 7.8777686335,47.8834559898 M 4.3118568209,42.0591333624 M 1.9345822790,43.6043618145 C 3.3609470041,45.8627726292 4.7873117292,48.1211834440 6.2136764543,50.3795942587 C 6.4910251508,50.8550491670 6.7683738473,51.3305040754 7.0457225439,51.8059589838 C 7.0457859379,51.8058955898 7.0458493318,51.8058321958 7.0459127258,51.8057688018 M 9.4229970857,57.7491453382 C 9.6211032975,58.1453577619 9.8192095093,58.5415701855 10.0173157211,58.9377826091 C 10.4135281448,58.7792976396 10.8097405684,58.6208126702 11.2059529921,58.4623277007 C 17.3076243160,56.3227806132 23.4092956399,54.1832335256 29.5109669638,52.0436864380 C 29.9864218721,51.8852014685 30.4618767805,51.7267164990 30.9373316888,51.5682315295 C 30.7392254770,51.1323978636 30.5411192652,50.6965641976 30.3430130534,50.2607305316 C 29.4317244791,48.0023197169 28.5148717858,45.7945700646 27.6008011519,43.5614898312" />+		<path stroke="0" skew="0" data="B 25.0586929962,45.1476024612 C 25.7093155024,46.7741587267 26.4420259467,48.3186270539 27.1336924219,49.9041393503 M 11.4436804462,55.3718707964 C 10.6555237791,53.6766937351 9.8673671120,51.9815166738 9.0792104449,50.2863396124 C 9.2940350824,50.2199310451 9.4702169488,50.0445714056 9.6005524501,49.9639820033 C 11.4474465869,48.7280947041 14.0174099645,47.3106270615 16.1984197118,46.5757648098 C 18.7674832545,45.7101735478 21.9871036230,45.4530142351 25.0586929962,45.1476024612" />+	</glyph>+</collection> <collection unicode="U+63"> 	<selected id="1"/> 	<glyph id="1" left="-28" right="28">@@ -350,15 +361,6 @@ GNU Lesser General Public License</copyright> 		<path stroke="0" skew="0" data="B -23.5540421546,0.0711646889 M 30.3522079454,20.7274646889 M -23.5540421546,20.7274646889 M -23.5540421546,0.0711646889" /> 	</glyph> </collection>-<collection name="foresight">-	<selected id="1"/>-	<glyph id="1" left="-28" right="44.054140127388528">-		<path stroke="0" skew="0" data="B 2.5292812784,44.7926187215 M 3.8367822764,44.0794363589 C 3.8367822764,44.0794363589 5.6698701483,42.9721208955 7.9970127245,41.8210255443 C 10.3241553008,40.6699301929 13.1430703979,39.5057503226 14.8911088956,39.0871598212 C 19.3725947163,38.0139630021 26.1831629693,36.8287490065 26.1831629693,36.8287490065 M 27.3718002401,36.5910215523 M 27.3718002401,35.4023842815 M 27.2527463310,22.0896468474 M 27.1336924219,21.2576007579 M 26.4205100595,20.9011997585 C 14.7034373343,14.4397295176 4.6763976082,5.9023851103 -3.6520128941,-5.3676839283 M -4.1274678025,-5.9620025637 M -4.8406501651,-5.9620025637 M -12.2102012447,-5.6056015645 M -13.8742934239,-5.4865476553 M -13.5178924246,-3.8224554761 C -11.9078879957,3.0714124768 -9.7659065790,13.9251351612 -4.7219766199,31.0046165611 M -4.7219766199,31.2423440153 M -4.6029227109,31.3613979244 M 1.9345822790,43.4854980875 M 2.5289009145,44.7929990855 M 2.5292812784,44.7926187215" />-		<path stroke="0" skew="0" data="B -6.0814353303,7.6780380292 C -3.5720984332,13.3816711826 -0.0943930151,18.9058105991 -0.0943930151,18.9058105991 C -0.1213608175,21.2315838652 1.2735097744,23.8255517361 3.7141149100,24.8070428125 C 3.9112575332,22.5672698301 3.4487349984,20.0078770403 1.1973228799,18.3312708879 C 1.2685270069,18.4826557306 -1.6727511654,13.8106836556 -4.5064243827,7.6525916825 C -6.0859768756,4.2199593898 -7.8261540635,-0.4207775145 -8.7011999798,-2.8886939252 M -5.4349688005,-3.1090829315 C 2.8294264529,7.8699316302 12.8614869829,16.3623550577 24.2809629719,22.8032095738 M 24.5186904260,34.2141273744 C 23.3475879321,34.4153018553 18.4726536657,35.2347198626 14.2964098964,36.2348107351 C 12.0221759422,36.7794158052 9.0907872321,38.1373150235 6.6891313626,39.3252676394 C 5.0266747482,40.1476144488 4.4331929135,40.4779605192 3.7175381854,40.8704960915 C 3.7176015794,40.8705594855 3.7176649733,40.8706228794 3.7177283673,40.8706862734 C 1.8159087339,37.3443957031 -0.0859108995,33.8181051328 -1.9877305330,30.2918145624 C -2.0242454699,30.1693373781 -2.0725516886,30.1761839288 -2.1067844420,30.0540871083 M -2.1067844420,29.9350331993 C -6.7668891626,14.0819211717 -8.7232149468,4.0716935311 -10.3083816112,-2.8713554774 C -9.4782492743,-0.6638813021 -7.6385524745,4.1387053056 -6.0814353303,7.6780380292" />-		<path stroke="0" skew="0" data="B 27.6008011519,43.5614898312 C 27.5611165156,43.5771398250 27.6856077556,43.5107018807 27.7280110575,43.4853079055 M 27.3716100582,35.1648470092 M 24.5188806080,35.2839009183 M 24.8752816073,42.4157245437 C 21.8079888660,42.7833462789 18.5182973370,42.7399847911 15.2473197131,43.8420892688 C 12.5363138619,44.7555332387 9.9518170529,46.4453760558 7.8777686335,47.8834559898 M 4.3118568209,42.0591333624 M 1.9345822790,43.6043618145 C 3.3609470041,45.8627726292 4.7873117292,48.1211834440 6.2136764543,50.3795942587 C 6.4910251508,50.8550491670 6.7683738473,51.3305040754 7.0457225439,51.8059589838 C 7.0457859379,51.8058955898 7.0458493318,51.8058321958 7.0459127258,51.8057688018 M 9.4229970857,57.7491453382 C 9.6211032975,58.1453577619 9.8192095093,58.5415701855 10.0173157211,58.9377826091 C 10.4135281448,58.7792976396 10.8097405684,58.6208126702 11.2059529921,58.4623277007 C 17.3076243160,56.3227806132 23.4092956399,54.1832335256 29.5109669638,52.0436864380 C 29.9864218721,51.8852014685 30.4618767805,51.7267164990 30.9373316888,51.5682315295 C 30.7392254770,51.1323978636 30.5411192652,50.6965641976 30.3430130534,50.2607305316 C 29.4317244791,48.0023197169 28.5148717858,45.7945700646 27.6008011519,43.5614898312" />-		<path stroke="0" skew="0" data="B 25.0586929962,45.1476024612 C 25.7093155024,46.7741587267 26.4420259467,48.3186270539 27.1336924219,49.9041393503 M 11.4436804462,55.3718707964 C 10.6555237791,53.6766937351 9.8673671120,51.9815166738 9.0792104449,50.2863396124 C 9.2940350824,50.2199310451 9.4702169488,50.0445714056 9.6005524501,49.9639820033 C 11.4474465869,48.7280947041 14.0174099645,47.3106270615 16.1984197118,46.5757648098 C 18.7674832545,45.7101735478 21.9871036230,45.4530142351 25.0586929962,45.1476024612" />-	</glyph>-</collection> <collection name="full_glyph"> 	<selected id="1"/> 	<glyph id="1" left="-28" right="28">@@ -1103,6 +1105,7 @@ GNU Lesser General Public License</copyright> <ligature sequence="r e v e r s e _ p a t h" replacement="reverse_path"/> <ligature sequence="e x p o r t _ f o n t s" replacement="export_fonts"/> <ligature sequence="a p p l y _ s t r o k e" replacement="apply_stroke"/>+<ligature sequence="b e z i e r _ t o o l" replacement="bezier_tool"/> <ligature sequence="r i g h t _ a r r o w" replacement="right_arrow"/> <ligature sequence="b e z i e r _ l i n e" replacement="bezier_line"/> <ligature sequence="c r e a t e _ l i n e" replacement="create_line"/>

Contact

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