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