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