The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

ForesightTool.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/ForesightTool.vala.
Fix point selection in Beziér tool
1 /* 2 Copyright (C) 2014 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 and preview the path. */ 21 public class ForesightTool : 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 bool move_right_handle = true; 31 int previous_point = 0; 32 33 Path current_path = new Path (); 34 35 int last_move_x = 0; 36 int last_move_y = 0; 37 38 public bool skip_deselect = false; 39 40 public ForesightTool (string name) { 41 base (name, t_ ("Create Beziér curves")); 42 43 select_action.connect ((self) => { 44 PenTool p = (PenTool) PointTool.pen (); 45 46 if (state != NONE) { 47 p.release_action (p, 1, last_move_x, last_move_y); 48 } 49 50 MainWindow.get_current_glyph ().clear_active_paths (); 51 MainWindow.set_cursor (NativeWindow.VISIBLE); 52 state = NONE; 53 }); 54 55 deselect_action.connect ((self) => { 56 PenTool p = (PenTool) PointTool.pen (); 57 58 if (state != NONE) { 59 p.release_action (p, 1, last_move_x, last_move_y); 60 } 61 62 MainWindow.set_cursor (NativeWindow.VISIBLE); 63 state = NONE; 64 }); 65 66 press_action.connect ((self, b, x, y) => { 67 PenTool p = (PenTool) PointTool.pen (); 68 PointSelection ps; 69 EditPoint first_point; 70 bool clockwise; 71 Path? path = null; 72 bool one_point = false; 73 74 MainWindow.set_cursor (NativeWindow.HIDDEN); 75 76 if (b == 2) { 77 stop_drawing (); 78 return; 79 } 80 81 BirdFont.get_current_font ().touch (); 82 MainWindow.get_current_glyph ().store_undo_state (); 83 84 last_move_x = x; 85 last_move_y = y; 86 87 if (previous_point > 0) { 88 previous_point = 0; 89 state = MOVE_POINT; 90 } else { 91 if (state == MOVE_POINT) { 92 state = MOVE_HANDLES; 93 94 path = PenTool.find_path_to_join (); 95 96 if (path != null) { 97 one_point = (((!) path).points.size == 1); 98 } 99 100 if (p.has_join_icon ()) { 101 // FIXME: last activa path in list 102 ps = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - 1), PenTool.active_path); 103 ps.point.set_tie_handle (false); 104 105 ps = new PointSelection (PenTool.active_path.points.get (0), PenTool.active_path); 106 ps.point.set_tie_handle (false); 107 108 first_point = PenTool.active_path.points.get (0); 109 clockwise = PenTool.active_path.is_clockwise (); 110 111 PenTool.move_selected = false; 112 p.release_action (p, 2, x, y); 113 114 PenTool.move_selected = false; 115 p.press_action (p, 2, x, y); 116 117 if (ps.path.has_point (first_point)) { 118 ps.path.reverse (); 119 } 120 121 if (clockwise && PenTool.active_path.is_clockwise ()) { 122 ps = new PointSelection (PenTool.active_path.points.get (0), PenTool.active_path); 123 } else { 124 ps = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - 1), PenTool.active_path); 125 } 126 127 PenTool.selected_points.clear (); 128 PenTool.selected_points.add (ps); 129 PenTool.selected_point = ps.point; 130 131 state = MOVE_LAST_HANDLE; 132 133 if (one_point) { 134 p.press_action (p, 2, x, y); 135 state = NONE; 136 } 137 } 138 } 139 } 140 141 if (state == NONE) { 142 state = MOVE_POINT; 143 PenTool.active_path = get_active_path (); 144 145 add_new_point (x, y); 146 147 PenTool.last_point_x = Glyph.path_coordinate_x (x); 148 PenTool.last_point_y = Glyph.path_coordinate_y (y); 149 150 move_action (this, x, y); 151 state = MOVE_FIRST_HANDLE; 152 release_action(this, b, x, y); 153 } 154 }); 155 156 release_action.connect ((self, b, x, y) => { 157 PenTool p = (PenTool) PointTool.pen (); 158 PointSelection last; 159 160 if (state == MOVE_HANDLES || state == MOVE_FIRST_HANDLE) { 161 if (state != MOVE_FIRST_HANDLE) { 162 last = add_new_point (x, y); 163 } 164 165 state = MOVE_POINT; 166 move_action (this, x, y); 167 } else if (state == MOVE_LAST_HANDLE) { 168 previous_point = 0; 169 170 if (unlikely (PenTool.selected_points.size == 0)) { 171 warning ("No point in move last handle."); 172 return; 173 } 174 175 last = PenTool.selected_points.get (PenTool.selected_points.size - 1); 176 177 p.release_action (p, 2, x, y); 178 179 PenTool.selected_points.add (last); 180 PenTool.active_path.highlight_last_segment = false; 181 182 last.path.direction_is_set = false; 183 PenTool.force_direction (); 184 185 state = NONE; 186 MainWindow.set_cursor (NativeWindow.VISIBLE); 187 } else if (state == MOVE_POINT) { 188 } else if (state == NONE) { 189 } else { 190 warning (@"Unknown state $state."); 191 } 192 193 current_path.hide_end_handle = true; 194 }); 195 196 move_action.connect ((self, x, y) => { 197 Tool p = PointTool.pen (); 198 PointSelection last; 199 bool lh; 200 EditPointHandle h; 201 202 last_move_x = x; 203 last_move_y = y; 204 205 if (MainWindow.dialog.visible && state != NONE) { 206 state = NONE; 207 p.release_action (p, 1, last_move_x, last_move_y); 208 } 209 210 if (state == NONE) { 211 MainWindow.set_cursor (NativeWindow.VISIBLE); 212 } 213 214 PenTool.active_path = current_path; 215 PenTool.active_path.hide_end_handle = (state == MOVE_POINT); 216 217 if (state == MOVE_HANDLES || state == MOVE_LAST_HANDLE) { 218 if (previous_point > 0) { 219 return_if_fail (PenTool.active_path.points.size >= previous_point + 1); 220 return_if_fail (PenTool.active_path.points.size > 0); 221 last = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - (previous_point + 1)), PenTool.active_path); 222 } else { 223 if (unlikely (PenTool.selected_points.size == 0)) { 224 warning ("No point to move in state %u", state); 225 return; 226 } 227 last = PenTool.selected_points.get (PenTool.selected_points.size - 1); 228 229 if (last.point.get_right_handle ().is_line () || last.point.get_left_handle ().is_line ()) { 230 last.point.convert_to_curve (); 231 last.point.get_right_handle ().length = 0.1; 232 last.point.get_left_handle ().length = 0.1; 233 } 234 } 235 236 PenTool.move_selected_handle = true; 237 PenTool.move_selected = false; 238 239 lh = (state == MOVE_LAST_HANDLE); 240 241 if (!move_right_handle) { 242 lh = !lh; 243 } 244 245 if (previous_point > 0) { 246 lh = false; 247 } 248 249 h = (lh) ? last.point.get_left_handle () : last.point.get_right_handle (); 250 PenTool.selected_handle = h; 251 PenTool.active_handle = h; 252 253 if (previous_point == 0 254 && !last.point.reflective_point 255 && !last.point.tie_handles) { 256 257 last.point.convert_to_curve (); 258 last.point.set_reflective_handles (true); 259 last.point.get_right_handle ().length = 0.1; 260 last.point.get_left_handle ().length = 0.1; 261 } 262 263 if (previous_point == 0) { 264 last.point.set_reflective_handles (true); 265 } 266 267 if (previous_point > 0) { 268 PenTool.retain_angle = last.point.tie_handles; 269 } 270 271 if (h.is_line ()) { 272 last.point.convert_to_curve (); 273 last.point.get_right_handle ().length = 0.1; 274 last.point.get_left_handle ().length = 0.1; 275 } 276 277 p.move_action (p, x, y); 278 last.point.set_reflective_handles (false); 279 280 PenTool.selected_handle = h; 281 PenTool.active_handle = h; 282 283 PenTool.move_selected_handle = false; 284 last.path.highlight_last_segment = true; 285 286 if (previous_point == 0) { 287 last.point.set_tie_handle (true); 288 p.move_action (p, x, y); 289 } 290 291 PenTool.retain_angle = false; 292 293 MainWindow.set_cursor (NativeWindow.HIDDEN); 294 } else { 295 if (DrawingTools.get_selected_point_type () != PointType.QUADRATIC) { 296 p.move_action (p, x, y); 297 } else { 298 PenTool.move_point_independent_of_handle = true; 299 p.move_action (p, x, y); 300 PenTool.move_point_independent_of_handle = false; 301 } 302 } 303 304 if (PenTool.active_path.points.size < 3) { 305 PenTool.active_edit_point = null; 306 } 307 }); 308 309 key_press_action.connect ((self, keyval) => { 310 PenTool p = (PenTool) PointTool.pen (); 311 p.key_press_action (p, keyval); 312 }); 313 314 key_release_action.connect ((self, keyval) => { 315 Tool p = PointTool.pen (); 316 p.key_release_action (p, keyval); 317 MainWindow.set_cursor (NativeWindow.VISIBLE); 318 }); 319 320 draw_action.connect ((tool, cairo_context, glyph) => { 321 Tool p = PointTool.pen (); 322 p.draw_action (p, cairo_context, glyph); 323 }); 324 } 325 326 public void stop_drawing () { 327 PenTool p = (PenTool) PointTool.pen (); 328 329 p.release_action (p, 1, 0, 0); 330 331 if (state != NONE) { 332 if (get_active_path ().is_open () && state != NONE) { 333 get_active_path ().delete_last_point (); 334 } 335 } 336 337 p.press_action (p, 2, 0, 0); 338 p.release_action (p, 2, 0, 0); 339 current_path.hide_end_handle = true; 340 341 MainWindow.set_cursor (NativeWindow.VISIBLE); 342 MainWindow.get_current_glyph ().clear_active_paths (); 343 344 state = NONE; 345 MainWindow.set_cursor (NativeWindow.VISIBLE); 346 } 347 348 public Path get_active_path () { 349 Glyph g = MainWindow.get_current_glyph (); 350 return_val_if_fail (g.active_paths.size > 0, new Path ()); 351 return g.active_paths.get (g.active_paths.size -1); 352 } 353 354 public void switch_to_line_mode () { 355 EditPoint ep; 356 EditPoint last; 357 358 if (PenTool.active_path.points.size > 2) { 359 ep = PenTool.active_path.points.get (PenTool.active_path.points.size - 2); 360 ep.get_right_handle ().convert_to_line (); 361 ep.set_tie_handle (false); 362 363 last = PenTool.active_path.points.get (PenTool.active_path.points.size - 1); 364 last.convert_to_line (); 365 366 move_action (this, last_move_x, last_move_y); 367 } 368 } 369 370 PointSelection add_new_point (int x, int y) { 371 PointSelection last; 372 double handle_x, handle_y; 373 374 PenTool p = (PenTool) PointTool.pen (); 375 376 if (PenTool.active_path.points.size == 0) { 377 last = p.new_point_action (x, y); 378 } else { 379 if (PenTool.selected_points.size == 0) { 380 warning ("No selected points."); 381 return new PointSelection.empty (); 382 } 383 384 last = PenTool.selected_points.get (PenTool.selected_points.size - 1); 385 386 PenTool.selected_points.clear (); 387 PenTool.selected_handle = new EditPointHandle.empty (); 388 389 if (DrawingTools.get_selected_point_type () != PointType.QUADRATIC) { 390 last = p.new_point_action (x, y); 391 } else { 392 last.point.get_right_handle ().length *= 0.999999; 393 handle_x = last.point.get_right_handle ().x; 394 handle_y = last.point.get_right_handle ().y; 395 396 last = p.new_point_action (x, y); 397 398 last.point.get_left_handle ().x = handle_x; 399 last.point.get_left_handle ().y = handle_y; 400 } 401 402 PenTool.move_selected = true; 403 p.move_action (p, x, y); 404 } 405 406 PenTool.selected_points.clear (); 407 PenTool.selected_points.add (last); 408 PenTool.selected_point = last.point; 409 PenTool.active_edit_point = null; 410 PenTool.show_selection_box = false; 411 412 PenTool.move_selected_handle = false; 413 PenTool.move_selected = true; 414 415 PenTool.active_path.hide_end_handle = (state == MOVE_POINT); 416 417 current_path = last.path; 418 419 return last; 420 } 421 422 // FIXME: solve the straight line issue in undo 423 public override void before_undo () { 424 EditPoint last; 425 426 if (PenTool.active_path.points.size > 1) { 427 last = PenTool.active_path.points.get (PenTool.active_path.points.size - 2); 428 last.convert_to_curve (); 429 } 430 } 431 432 public override void after_undo () { 433 PenTool.selected_points.clear (); 434 PenTool.active_edit_point = null; 435 state = NONE; 436 437 EditPoint last; 438 439 if (PenTool.active_path.points.size > 0) { 440 last = PenTool.active_path.points.get (PenTool.active_path.points.size - 1); 441 last.convert_to_curve (); 442 } 443 } 444 445 } 446 447 } 448