The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

MoveTool.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/MoveTool.vala.
Tip for deleting points
1 /* 2 Copyright (C) 2012 2013 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 public class MoveTool : Tool { 21 22 static bool move_path = false; 23 static bool moved = false; 24 static double last_x = 0; 25 static double last_y = 0; 26 27 static double selection_x = 0; 28 static double selection_y = 0; 29 static bool group_selection= false; 30 31 public static static double selection_box_width = 0; 32 public static static double selection_box_height = 0; 33 public static static double selection_box_center_x = 0; 34 public static static double selection_box_center_y = 0; 35 36 public signal void selection_changed (); 37 public signal void objects_moved (); 38 public signal void objects_deselected (); 39 40 public MoveTool (string name) { 41 base (name, t_("Move paths")); 42 43 selection_changed.connect (() => { 44 update_selection_boundaries (); 45 redraw(); 46 }); 47 48 objects_deselected.connect (() => { 49 update_selection_boundaries (); 50 redraw(); 51 }); 52 53 select_action.connect((self) => { 54 MainWindow.get_current_glyph ().close_path (); 55 }); 56 57 deselect_action.connect((self) => { 58 }); 59 60 press_action.connect((self, b, x, y) => { 61 press (b, x, y); 62 }); 63 64 release_action.connect((self, b, x, y) => { 65 release (b, x, y); 66 }); 67 68 move_action.connect ((self, x, y) => { 69 move (x, y); 70 }); 71 72 key_press_action.connect ((self, keyval) => { 73 key_down (keyval); 74 }); 75 76 draw_action.connect ((self, cr, glyph) => { 77 draw_actions (cr); 78 }); 79 } 80 81 public static void draw_actions (Context cr) { 82 if (group_selection) { 83 draw_selection_box (cr); 84 } 85 } 86 87 public void key_down (uint32 keyval) { 88 Glyph g = MainWindow.get_current_glyph (); 89 90 // delete selected paths 91 if (keyval == Key.DEL || keyval == Key.BACK_SPACE) { 92 93 if (g.active_paths.size > 0) { 94 g.store_undo_state (); 95 } 96 97 foreach (Path p in g.active_paths) { 98 g.layers.remove_path (p); 99 g.update_view (); 100 } 101 102 g.active_paths.clear (); 103 } 104 105 if (is_arrow_key (keyval)) { 106 move_selected_paths (keyval); 107 } 108 } 109 110 public void move (int x, int y) { 111 Glyph glyph = MainWindow.get_current_glyph (); 112 double dx = last_x - x; 113 double dy = last_y - y; 114 double p = PenTool.precision; 115 double delta_x, delta_y; 116 117 if (!move_path) { 118 return; 119 } 120 121 if (move_path && (fabs(dx) > 0 || fabs (dy) > 0)) { 122 moved = true; 123 124 delta_x = Glyph.ivz () * -dx * p; 125 delta_y = Glyph.ivz () * dy * p; 126 127 foreach (Layer group in glyph.selected_groups) { 128 if (group.gradient != null) { 129 Gradient g = (!) group.gradient; 130 g.x1 += delta_x; 131 g.x2 += delta_x; 132 g.y1 += delta_y; 133 g.y2 += delta_y; 134 } 135 } 136 137 foreach (Path path in glyph.active_paths) { 138 path.move (delta_x, delta_y); 139 } 140 } 141 142 last_x = x; 143 last_y = y; 144 145 update_selection_boundaries (); 146 147 if (glyph.active_paths.size > 0) { 148 objects_moved (); 149 } 150 151 BirdFont.get_current_font ().touch (); 152 153 GlyphCanvas.redraw (); 154 PenTool.reset_stroke (); 155 } 156 157 public void release (int b, int x, int y) { 158 Glyph glyph = MainWindow.get_current_glyph (); 159 160 move_path = false; 161 162 if (GridTool.is_visible () && moved) { 163 tie_paths_to_grid (glyph); 164 } else if (GridTool.has_ttf_grid ()) { 165 foreach (Path p in glyph.active_paths) { 166 tie_path_to_ttf_grid (p); 167 } 168 } 169 170 if (group_selection) { 171 select_group (); 172 } 173 174 group_selection = false; 175 moved = false; 176 177 if (glyph.active_paths.size > 0) { 178 selection_changed (); 179 objects_moved (); 180 DrawingTools.resize_tool.signal_objects_rotated (); 181 182 foreach (Path p in glyph.active_paths) { 183 p.create_full_stroke (); 184 } 185 } else { 186 objects_deselected (); 187 } 188 } 189 190 public void press (int b, int x, int y) { 191 Glyph glyph = MainWindow.get_current_glyph (); 192 Path p; 193 bool selected = false; 194 Layer? group; 195 Layer g; 196 197 glyph.store_undo_state (); 198 group_selection = false; 199 200 group = glyph.get_path_at (x, y); 201 202 if (group != null) { 203 g = (!) group; 204 return_if_fail (g.paths.paths.size > 0); 205 p = g.paths.paths.get (0); 206 selected = glyph.active_paths.contains (p); 207 208 if (!selected && !KeyBindings.has_shift ()) { 209 glyph.clear_active_paths (); 210 } 211 212 foreach (Path lp in g.paths.paths) { 213 if (selected && KeyBindings.has_shift ()) { 214 glyph.selected_groups.remove ((!) group); 215 glyph.active_paths.remove (lp); 216 } else { 217 glyph.add_active_path ((!) group, lp); 218 } 219 } 220 } else if (!KeyBindings.has_shift ()) { 221 glyph.clear_active_paths (); 222 } 223 224 move_path = true; 225 226 update_selection_boundaries (); 227 228 last_x = x; 229 last_y = y; 230 231 if (glyph.active_paths.size == 0) { 232 group_selection = true; 233 selection_x = x; 234 selection_y = y; 235 } 236 237 update_boundaries_for_selection (); 238 selection_changed (); 239 GlyphCanvas.redraw (); 240 } 241 242 243 void select_group () { 244 double x1 = Glyph.path_coordinate_x (Math.fmin (selection_x, last_x)); 245 double y1 = Glyph.path_coordinate_y (Math.fmin (selection_y, last_y)); 246 double x2 = Glyph.path_coordinate_x (Math.fmax (selection_x, last_x)); 247 double y2 = Glyph.path_coordinate_y (Math.fmax (selection_y, last_y)); 248 Glyph glyph = MainWindow.get_current_glyph (); 249 250 glyph.clear_active_paths (); 251 252 foreach (Path p in glyph.get_paths_in_current_layer ()) { 253 if (p.xmin > x1 && p.xmax < x2 && p.ymin < y1 && p.ymax > y2) { 254 if (p.points.size > 0) { 255 glyph.add_active_path (null, p); 256 } 257 } 258 } 259 260 selection_changed (); 261 } 262 263 public static void update_selection_boundaries () { 264 get_selection_box_boundaries (out selection_box_center_x, 265 out selection_box_center_y, out selection_box_width, 266 out selection_box_height); 267 } 268 269 public void move_to_baseline () { 270 Glyph glyph = MainWindow.get_current_glyph (); 271 Font font = BirdFont.get_current_font (); 272 double x, y, w, h; 273 274 get_selection_box_boundaries (out x, out y, out w, out h); 275 276 foreach (Path path in glyph.active_paths) { 277 path.move (glyph.left_limit - x + w / 2, font.base_line - y + h / 2); 278 } 279 280 update_selection_boundaries (); 281 objects_moved (); 282 GlyphCanvas.redraw (); 283 } 284 285 static void draw_selection_box (Context cr) { 286 double x = Math.fmin (selection_x, last_x); 287 double y = Math.fmin (selection_y, last_y); 288 289 double w = Math.fabs (selection_x - last_x); 290 double h = Math.fabs (selection_y - last_y); 291 292 cr.save (); 293 294 Theme.color (cr, "Foreground 1"); 295 cr.set_line_width (2); 296 cr.rectangle (x, y, w, h); 297 cr.stroke (); 298 299 cr.restore (); 300 } 301 302 public static void get_selection_box_boundaries (out double x, out double y, out double w, out double h) { 303 double px, py, px2, py2; 304 Glyph glyph = MainWindow.get_current_glyph (); 305 306 px = 10000; 307 py = 10000; 308 px2 = -10000; 309 py2 = -10000; 310 311 foreach (Path p in glyph.active_paths) { 312 p.update_region_boundaries (); 313 314 if (px > p.xmin) { 315 px = p.xmin; 316 } 317 318 if (py > p.ymin) { 319 py = p.ymin; 320 } 321 322 if (px2 < p.xmax) { 323 px2 = p.xmax; 324 } 325 326 if (py2 < p.ymax) { 327 py2 = p.ymax; 328 } 329 } 330 331 w = px2 - px; 332 h = py2 - py; 333 x = px + (w / 2); 334 y = py + (h / 2); 335 } 336 337 void move_selected_paths (uint key) { 338 Glyph glyph = MainWindow.get_current_glyph (); 339 double x, y; 340 341 x = 0; 342 y = 0; 343 344 switch (key) { 345 case Key.UP: 346 y = 1; 347 break; 348 case Key.DOWN: 349 y = -1; 350 break; 351 case Key.LEFT: 352 x = -1; 353 break; 354 case Key.RIGHT: 355 x = 1; 356 break; 357 default: 358 break; 359 } 360 361 foreach (Path path in glyph.active_paths) { 362 path.move (x * Glyph.ivz (), y * Glyph.ivz ()); 363 } 364 365 BirdFont.get_current_font ().touch (); 366 PenTool.reset_stroke (); 367 update_selection_boundaries (); 368 objects_moved (); 369 GlyphCanvas.redraw (); 370 } 371 372 static void tie_path_to_ttf_grid (Path p) { 373 double sx, sy, qx, qy; 374 375 sx = p.xmax; 376 sy = p.ymax; 377 qx = p.xmin; 378 qy = p.ymin; 379 380 GridTool.ttf_grid_coordinate (ref sx, ref sy); 381 GridTool.ttf_grid_coordinate (ref qx, ref qy); 382 383 if (Math.fabs (qy - p.ymin) < Math.fabs (sy - p.ymax)) { 384 p.move (0, qy - p.ymin); 385 } else { 386 p.move (0, sy - p.ymax); 387 } 388 389 if (Math.fabs (qx - p.xmin) < Math.fabs (sx - p.xmax)) { 390 p.move (qx - p.xmin, 0); 391 } else { 392 p.move (sx - p.xmax, 0); 393 } 394 } 395 396 static void tie_paths_to_grid (Glyph g) { 397 double sx, sy, qx, qy; 398 double dx_min, dx_max, dy_min, dy_max;; 399 double maxx, maxy, minx, miny; 400 401 update_selection_boundaries (); 402 403 // tie to grid 404 maxx = selection_box_center_x + selection_box_width / 2; 405 maxy = selection_box_center_y + selection_box_height / 2; 406 minx = selection_box_center_x - selection_box_width / 2; 407 miny = selection_box_center_y - selection_box_height / 2; 408 409 sx = maxx; 410 sy = maxy; 411 qx = minx; 412 qy = miny; 413 414 GridTool.tie_coordinate (ref sx, ref sy); 415 GridTool.tie_coordinate (ref qx, ref qy); 416 417 dy_min = Math.fabs (qy - miny); 418 dy_max = Math.fabs (sy - maxy); 419 dx_min = Math.fabs (qx - minx); 420 dx_max = Math.fabs (sx - maxx); 421 422 foreach (Path p in g.active_paths) { 423 if (dy_min < dy_max) { 424 p.move (0, qy - miny); 425 } else { 426 p.move (0, sy - maxy); 427 } 428 429 if (dx_min < dx_max) { 430 p.move (qx - minx, 0); 431 } else { 432 p.move (sx - maxx, 0); 433 } 434 } 435 436 update_selection_boundaries (); 437 } 438 439 public static void update_boundaries_for_selection () { 440 Glyph glyph = MainWindow.get_current_glyph (); 441 foreach (Path p in glyph.active_paths) { 442 p.update_region_boundaries (); 443 } 444 } 445 446 public static void flip_vertical () { 447 flip (true); 448 } 449 450 public static void flip_horizontal () { 451 flip (false); 452 } 453 454 public static void flip (bool vertical) { 455 double xc, yc, xc2, yc2, w, h; 456 double dx, dy; 457 Glyph glyph = MainWindow.get_current_glyph (); 458 459 update_selection_boundaries (); 460 461 xc = selection_box_center_x; 462 yc = selection_box_center_y; 463 464 foreach (Path p in glyph.active_paths) { 465 if (vertical) { 466 p.flip_vertical (); 467 } else { 468 p.flip_horizontal (); 469 } 470 471 p.reverse (); 472 } 473 474 get_selection_box_boundaries (out xc2, out yc2, out w, out h); 475 476 dx = -(xc2 - xc); 477 dy = -(yc2 - yc); 478 479 foreach (Path p in glyph.active_paths) { 480 p.move (dx, dy); 481 } 482 483 update_selection_boundaries (); 484 PenTool.reset_stroke (); 485 486 BirdFont.get_current_font ().touch (); 487 } 488 489 public void select_all_paths () { 490 Glyph g = MainWindow.get_current_glyph (); 491 492 g.clear_active_paths (); 493 foreach (Path p in g.get_paths_in_current_layer ()) { 494 if (p.points.size > 0) { 495 g.add_active_path (null, p); 496 } 497 } 498 499 g.update_view (); 500 501 update_selection_boundaries (); 502 objects_moved (); 503 } 504 } 505 506 } 507