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