The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

BezierTool.vala in libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdfont/BezierTool.vala.
Set point type in Beziér tool
1 /* 2 Copyright (C) 2015 Johan Mattsson 3 4 This library is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 3 of the 7 License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Lesser General Public License for more details. 13 */ 14 15 using Math; 16 using Cairo; 17 18 namespace BirdFont { 19 20 /** Create Beziér curves. */ 21 public class BezierTool : Tool { 22 23 public const uint NONE = 0; 24 public const uint MOVE_POINT = 1; 25 public const uint MOVE_HANDLES = 2; 26 public const uint MOVE_LAST_HANDLE = 3; 27 public const uint MOVE_FIRST_HANDLE = 4; 28 29 uint state = NONE; 30 31 bool move_right_handle = true; 32 int previous_point = 0; 33 34 Path current_path = new Path (); 35 EditPoint current_point = new EditPoint (); 36 37 int last_x = 0; 38 int last_y = 0; 39 40 double last_release_time = 0; 41 double last_press_time = 0; 42 43 public BezierTool (string name) { 44 base (name, t_ ("Create Beziér curves")); 45 46 select_action.connect ((self) => { 47 state = NONE; 48 MainWindow.set_cursor (NativeWindow.VISIBLE); 49 }); 50 51 deselect_action.connect ((self) => { 52 state = NONE; 53 MainWindow.set_cursor (NativeWindow.VISIBLE); 54 }); 55 56 press_action.connect ((self, b, x, y) => { 57 press (b, x, y); 58 }); 59 60 double_click_action.connect ((self, b, x, y) => { 61 }); 62 63 release_action.connect ((self, b, x, y) => { 64 release (b, x, y); 65 }); 66 67 move_action.connect ((self, x, y) => { 68 move (x, y); 69 }); 70 71 key_press_action.connect ((self, keyval) => { 72 }); 73 74 key_release_action.connect ((self, keyval) => { 75 }); 76 77 draw_action.connect ((tool, cairo_context, glyph) => { 78 if (PenTool.can_join (current_point)) { 79 PenTool.draw_join_icon (cairo_context, last_x, last_y); 80 } 81 }); 82 } 83 84 public void press (int b, int x, int y) { 85 Glyph g = MainWindow.get_current_glyph (); 86 double px, py; 87 Path? p; 88 Path path; 89 90 if (b == 2) { 91 if (g.is_open ()) { 92 stop_drawing (); 93 g.close_path (); 94 } else { 95 g.open_path (); 96 } 97 98 MainWindow.set_cursor (NativeWindow.VISIBLE); 99 state = NONE; 100 101 return; 102 } 103 104 // ignore double clicks 105 if ((GLib.get_real_time () - last_press_time) / 1000000.0 < 0.2) { 106 last_press_time = GLib.get_real_time (); 107 return; 108 } 109 last_press_time = GLib.get_real_time (); 110 111 g.store_undo_state (); 112 113 PenTool.update_orientation (); 114 115 MainWindow.set_cursor (NativeWindow.HIDDEN); 116 g.open_path (); 117 118 px = Glyph.path_coordinate_x (x); 119 py = Glyph.path_coordinate_y (y); 120 121 if (state == NONE) { 122 g.open_path (); 123 current_path = new Path (); 124 current_path.reopen (); 125 current_path.hide_end_handle = true; 126 current_point = current_path.add (px, py); 127 current_point.get_left_handle ().convert_to_line (); 128 current_point.recalculate_linear_handles (); 129 g.add_path (current_path); 130 131 if (StrokeTool.add_stroke) { 132 current_path.stroke = StrokeTool.stroke_width; 133 } 134 135 GlyphCanvas.redraw (); 136 state = MOVE_POINT; 137 } else if (state == MOVE_POINT) { 138 if (PenTool.can_join (current_point)) { 139 p = PenTool.join_paths (current_point); 140 141 return_if_fail (p != null); 142 path = (!) p; 143 144 if (current_path.points.size == 1) { 145 return_if_fail (path.is_open ()); 146 current_path = path; 147 current_point = path.get_last_point (); 148 state = MOVE_POINT; 149 } else { 150 g.open_path (); 151 current_path = path; 152 current_point = path.get_first_point (); 153 state = MOVE_LAST_HANDLE; 154 } 155 } else { 156 state = MOVE_HANDLES; 157 } 158 } 159 } 160 161 public void release (int b, int x, int y) { 162 double px, py; 163 Glyph g; 164 165 // ignore double clicks 166 if ((GLib.get_real_time () - last_release_time) / 1000000.0 < 0.2) { 167 last_release_time = GLib.get_real_time (); 168 return; 169 } 170 last_release_time = GLib.get_real_time (); 171 172 px = Glyph.path_coordinate_x (x); 173 py = Glyph.path_coordinate_y (y); 174 g = MainWindow.get_current_glyph (); 175 176 if (state == MOVE_HANDLES) { 177 current_point = current_path.add (px, py); 178 current_path.hide_end_handle = true; 179 current_point.get_left_handle ().convert_to_line (); 180 current_point.recalculate_linear_handles (); 181 g.clear_active_paths (); 182 g.add_active_path (current_path); 183 GlyphCanvas.redraw (); 184 state = MOVE_POINT; 185 } else if (state == MOVE_LAST_HANDLE) { 186 current_path.update_region_boundaries (); 187 g.close_path (); 188 MainWindow.set_cursor (NativeWindow.VISIBLE); 189 190 if (Path.is_counter (g.get_paths (), current_path)) { 191 current_path.force_direction (Direction.COUNTER_CLOCKWISE); 192 } else { 193 current_path.force_direction (Direction.CLOCKWISE); 194 } 195 196 state = NONE; 197 } 198 } 199 200 public void move (int x, int y) { 201 double px, py; 202 203 last_x = x; 204 last_y = y; 205 206 px = Glyph.path_coordinate_x (x); 207 py = Glyph.path_coordinate_y (y); 208 209 if (state == MOVE_POINT) { 210 current_point.x = px; 211 current_point.y = py; 212 current_path.hide_end_handle = true; 213 current_point.recalculate_linear_handles (); 214 current_path.reset_stroke (); 215 GlyphCanvas.redraw (); 216 } else if (state == MOVE_HANDLES || state == MOVE_LAST_HANDLE) { 217 current_path.hide_end_handle = false; 218 current_point.set_reflective_handles (true); 219 current_point.convert_to_curve (); 220 current_point.get_right_handle ().move_to_coordinate (px, py); 221 current_path.reset_stroke (); 222 GlyphCanvas.redraw (); 223 } 224 225 if (current_path.points.size > 0) { 226 current_path.get_first_point ().set_reflective_handles (false); 227 current_path.get_last_point ().set_reflective_handles (false); 228 } 229 } 230 231 public void switch_to_line_mode () { 232 int s = current_path.points.size; 233 EditPoint p; 234 235 if (s > 2) { 236 p = current_path.points.get (s - 2); 237 p.get_right_handle ().convert_to_line (); 238 current_point.get_left_handle ().convert_to_line (); 239 p.recalculate_linear_handles (); 240 current_point.recalculate_linear_handles (); 241 current_path.reset_stroke (); 242 GlyphCanvas.redraw (); 243 244 state = MOVE_POINT; 245 } 246 } 247 248 public void stop_drawing () { 249 if (state == MOVE_POINT && current_path.points.size > 0) { 250 current_path.delete_last_point (); 251 current_path.reset_stroke (); 252 } 253 254 state = NONE; 255 } 256 257 public override void before_undo () { 258 } 259 260 public override void after_undo () { 261 if (state != NONE) { 262 MainWindow.set_cursor (NativeWindow.VISIBLE); 263 state = NONE; 264 } 265 } 266 } 267 268 } 269