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.
Copy and paste strokes
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 Path a; 202 203 last_move_x = x; 204 last_move_y = y; 205 206 a = get_active_path (); 207 if (a.is_open ()) { 208 a.get_first_point ().set_reflective_handles (false); 209 a.get_first_point ().set_tie_handle (false); 210 } 211 212 if (MainWindow.dialog.visible && state != NONE) { 213 state = NONE; 214 p.release_action (p, 1, last_move_x, last_move_y); 215 } 216 217 if (state == NONE) { 218 MainWindow.set_cursor (NativeWindow.VISIBLE); 219 } 220 221 PenTool.active_path = current_path; 222 PenTool.active_path.hide_end_handle = (state == MOVE_POINT); 223 224 if (state == MOVE_HANDLES || state == MOVE_LAST_HANDLE) { 225 if (previous_point > 0) { 226 return_if_fail (PenTool.active_path.points.size >= previous_point + 1); 227 return_if_fail (PenTool.active_path.points.size > 0); 228 last = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - (previous_point + 1)), PenTool.active_path); 229 } else { 230 if (unlikely (PenTool.selected_points.size == 0)) { 231 warning ("No point to move in state %u", state); 232 return; 233 } 234 last = PenTool.selected_points.get (PenTool.selected_points.size - 1); 235 236 if (last.point.get_right_handle ().is_line () || last.point.get_left_handle ().is_line ()) { 237 last.point.convert_to_curve (); 238 last.point.get_right_handle ().length = 0.1; 239 last.point.get_left_handle ().length = 0.1; 240 } 241 } 242 243 PenTool.move_selected_handle = true; 244 PenTool.move_selected = false; 245 246 lh = (state == MOVE_LAST_HANDLE); 247 248 if (!move_right_handle) { 249 lh = !lh; 250 } 251 252 if (previous_point > 0) { 253 lh = false; 254 } 255 256 h = (lh) ? last.point.get_left_handle () : last.point.get_right_handle (); 257 PenTool.selected_handle = h; 258 PenTool.active_handle = h; 259 260 if (previous_point == 0 261 && !last.point.reflective_point 262 && !last.point.tie_handles) { 263 264 last.point.convert_to_curve (); 265 last.point.set_reflective_handles (true); 266 last.point.get_right_handle ().length = 0.1; 267 last.point.get_left_handle ().length = 0.1; 268 } 269 270 if (previous_point == 0) { 271 last.point.set_reflective_handles (true); 272 } 273 274 if (previous_point > 0) { 275 PenTool.retain_angle = last.point.tie_handles; 276 } 277 278 if (h.is_line ()) { 279 last.point.convert_to_curve (); 280 last.point.get_right_handle ().length = 0.1; 281 last.point.get_left_handle ().length = 0.1; 282 } 283 284 p.move_action (p, x, y); 285 last.point.set_reflective_handles (false); 286 287 PenTool.selected_handle = h; 288 PenTool.active_handle = h; 289 290 PenTool.move_selected_handle = false; 291 last.path.highlight_last_segment = true; 292 293 if (previous_point == 0) { 294 last.point.set_tie_handle (true); 295 p.move_action (p, x, y); 296 } 297 298 PenTool.retain_angle = false; 299 300 MainWindow.set_cursor (NativeWindow.HIDDEN); 301 } else { 302 if (DrawingTools.get_selected_point_type () != PointType.QUADRATIC) { 303 p.move_action (p, x, y); 304 } else { 305 PenTool.move_point_independent_of_handle = true; 306 p.move_action (p, x, y); 307 PenTool.move_point_independent_of_handle = false; 308 } 309 } 310 311 if (PenTool.active_path.points.size < 3) { 312 PenTool.active_edit_point = null; 313 } 314 }); 315 316 key_press_action.connect ((self, keyval) => { 317 PenTool p = (PenTool) PointTool.pen (); 318 p.key_press_action (p, keyval); 319 }); 320 321 key_release_action.connect ((self, keyval) => { 322 Tool p = PointTool.pen (); 323 p.key_release_action (p, keyval); 324 MainWindow.set_cursor (NativeWindow.VISIBLE); 325 }); 326 327 draw_action.connect ((tool, cairo_context, glyph) => { 328 Tool p = PointTool.pen (); 329 p.draw_action (p, cairo_context, glyph); 330 }); 331 } 332 333 public void stop_drawing () { 334 PenTool p = (PenTool) PointTool.pen (); 335 Path a; 336 337 p.release_action (p, 1, 0, 0); 338 339 if (state != NONE) { 340 a = get_active_path (); 341 if (a.is_open () && state != NONE) { 342 a.delete_last_point (); 343 } 344 } 345 346 p.press_action (p, 2, 0, 0); 347 p.release_action (p, 2, 0, 0); 348 current_path.hide_end_handle = true; 349 350 MainWindow.set_cursor (NativeWindow.VISIBLE); 351 MainWindow.get_current_glyph ().clear_active_paths (); 352 353 state = NONE; 354 MainWindow.set_cursor (NativeWindow.VISIBLE); 355 } 356 357 public Path get_active_path () { 358 Glyph g = MainWindow.get_current_glyph (); 359 return_val_if_fail (g.active_paths.size > 0, new Path ()); 360 return g.active_paths.get (g.active_paths.size -1); 361 } 362 363 public void switch_to_line_mode () { 364 EditPoint ep; 365 EditPoint last; 366 367 if (PenTool.active_path.points.size > 2) { 368 ep = PenTool.active_path.points.get (PenTool.active_path.points.size - 2); 369 ep.get_right_handle ().convert_to_line (); 370 ep.set_tie_handle (false); 371 372 last = PenTool.active_path.points.get (PenTool.active_path.points.size - 1); 373 last.convert_to_line (); 374 375 move_action (this, last_move_x, last_move_y); 376 } 377 } 378 379 PointSelection add_new_point (int x, int y) { 380 PointSelection last; 381 double handle_x, handle_y; 382 383 PenTool p = (PenTool) PointTool.pen (); 384 385 if (PenTool.active_path.points.size == 0) { 386 last = p.new_point_action (x, y); 387 } else { 388 if (PenTool.selected_points.size == 0) { 389 warning ("No selected points."); 390 return new PointSelection.empty (); 391 } 392 393 last = PenTool.selected_points.get (PenTool.selected_points.size - 1); 394 395 PenTool.selected_points.clear (); 396 PenTool.selected_handle = new EditPointHandle.empty (); 397 398 if (DrawingTools.get_selected_point_type () != PointType.QUADRATIC) { 399 last = p.new_point_action (x, y); 400 } else { 401 last.point.get_right_handle ().length *= 0.999999; 402 handle_x = last.point.get_right_handle ().x; 403 handle_y = last.point.get_right_handle ().y; 404 405 last = p.new_point_action (x, y); 406 407 last.point.get_left_handle ().x = handle_x; 408 last.point.get_left_handle ().y = handle_y; 409 } 410 411 PenTool.move_selected = true; 412 p.move_action (p, x, y); 413 } 414 415 PenTool.selected_points.clear (); 416 PenTool.selected_points.add (last); 417 PenTool.selected_point = last.point; 418 PenTool.active_edit_point = null; 419 PenTool.show_selection_box = false; 420 421 PenTool.move_selected_handle = false; 422 PenTool.move_selected = true; 423 424 PenTool.active_path.hide_end_handle = (state == MOVE_POINT); 425 426 current_path = last.path; 427 428 return last; 429 } 430 431 // FIXME: solve the straight line issue in undo 432 public override void before_undo () { 433 EditPoint last; 434 435 if (PenTool.active_path.points.size > 1) { 436 last = PenTool.active_path.points.get (PenTool.active_path.points.size - 2); 437 last.convert_to_curve (); 438 } 439 } 440 441 public override void after_undo () { 442 PenTool.selected_points.clear (); 443 PenTool.active_edit_point = null; 444 state = NONE; 445 446 EditPoint last; 447 448 if (PenTool.active_path.points.size > 0) { 449 last = PenTool.active_path.points.get (PenTool.active_path.points.size - 1); 450 last.convert_to_curve (); 451 } 452 } 453 454 } 455 456 } 457