The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

BackgroundTool.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
Circle boundaries heads/master
1 /* 2 Copyright (C) 2012 2014 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 Cairo; 16 using Math; 17 18 namespace BirdFont { 19 20 public class BackgroundTool : Tool { 21 22 double begin_x = 0; 23 double begin_y = 0; 24 25 int last_x; 26 int last_y; 27 28 public double img_offset_x = 0; 29 public double img_offset_y = 0; 30 31 public double img_width = 0; 32 public double img_height = 0; 33 34 public double img_scale_x = 0; 35 36 public static double top_limit; 37 public static double bottom_limit; 38 39 bool move_bg; 40 41 static BackgroundImage imported_background; 42 static ImageSurface imported_surface; 43 44 static bool on_axis = false; 45 double rotation_position_x = 0; 46 double rotation_position_y = 0; 47 48 public BackgroundTool (string name) { 49 base (name, ""); 50 51 top_limit = 0; 52 bottom_limit = 0; 53 54 imported_background = new BackgroundImage (""); 55 56 select_action.connect((self) => { 57 }); 58 59 deselect_action.connect ((self) => { 60 }); 61 62 press_action.connect((self, b, x, y) => { 63 Glyph g = MainWindow.get_current_glyph (); 64 BackgroundImage? bg = g.get_background_image (); 65 BackgroundImage background; 66 67 if (bg == null) { 68 return; 69 } 70 71 background = (!) bg; 72 73 g.store_undo_state (); 74 75 background.handler_press (x, y); 76 77 begin_x = x; 78 begin_y = y; 79 80 img_offset_x = background.img_offset_x; 81 img_offset_y = background.img_offset_y; 82 83 img_scale_x = background.img_scale_x; 84 85 img_width = background.get_img ().get_width () * background.img_scale_x; 86 img_height = background.get_img ().get_height () * background.img_scale_y; 87 88 background.start_rotation_preview (); 89 90 move_bg = true; 91 92 last_x = x; 93 last_y = y; 94 }); 95 96 release_action.connect((self, b, x, y) => { 97 Glyph g = MainWindow.get_current_glyph (); 98 BackgroundImage? bg = g.get_background_image (); 99 double coordinate_x, coordinate_y; 100 101 if (bg == null) { 102 return; 103 } 104 105 BackgroundImage background = (!) bg; 106 107 coordinate_x = Glyph.path_coordinate_x (x); 108 coordinate_y = Glyph.path_coordinate_y (y); 109 110 if (background.selected_handle == 2) { 111 background.set_img_rotation_from_coordinate (rotation_position_x, rotation_position_y); 112 } 113 114 img_offset_x = background.img_offset_x; 115 img_offset_y = background.img_offset_y; 116 117 background.handler_release (x, y); 118 119 on_axis = false; 120 move_bg = false; 121 }); 122 123 move_action.connect((self, x, y) => { 124 move (x, y); 125 }); 126 127 key_press_action.connect ((self, keyval) => { 128 move_bg = true; 129 begin_x = 0; 130 begin_y = 0; 131 132 switch (keyval) { 133 case Key.UP: 134 move (0, -1); 135 break; 136 case Key.DOWN: 137 move (0, 1); 138 break; 139 case Key.LEFT: 140 move (-1, 0); 141 break; 142 case Key.RIGHT: 143 move (1, 0); 144 break; 145 default: 146 break; 147 } 148 149 move_bg = false; 150 }); 151 152 153 draw_action.connect ((self, cairo_context, glyph) => { 154 Glyph g = MainWindow.get_current_glyph (); 155 BackgroundImage? background_image = g.get_background_image (); 156 if (background_image == null) return; 157 158 ((!) background_image).draw_handle (cairo_context, glyph); 159 }); 160 } 161 162 public override string get_tip () { 163 string tip = t_("Move, resize and rotate background image") + "\n"; 164 tip += HiddenTools.move_along_axis.get_key_binding (); 165 tip += " - "; 166 tip += t_ ("on axis") + "\n"; 167 return tip; 168 } 169 170 public static void move_handle_on_axis () { 171 on_axis = true; 172 } 173 174 void move (int x, int y) { 175 Glyph g = MainWindow.get_current_glyph (); 176 BackgroundImage? background_image = g.get_background_image (); 177 BackgroundImage bg = (!) background_image; 178 double xscale, yscale, dx, dy, xc, yc; 179 double coordinate_x, coordinate_y; 180 double view_zoom; 181 182 if (background_image == null) { 183 return; 184 } 185 186 bg.handler_move (x, y); 187 188 dx = x - begin_x; 189 dy = y - begin_y; 190 191 dx *= 1 / g.view_zoom; 192 dy *= 1 / g.view_zoom; 193 194 dx *= PenTool.precision; 195 dy *= PenTool.precision; 196 197 // rotation handle 198 if (bg.selected_handle == 2) { 199 coordinate_x = Glyph.path_coordinate_x (x); 200 coordinate_y = Glyph.path_coordinate_y (y); 201 view_zoom = MainWindow.get_current_glyph ().view_zoom; 202 203 rotation_position_x = coordinate_x; 204 rotation_position_y = coordinate_y; 205 206 if (on_axis) { 207 double length = fabs (Path.distance (bg.img_middle_x, coordinate_x, 208 bg.img_middle_y, coordinate_y)); 209 210 double min = double.MAX; 211 double circle_edge; 212 double circle_x; 213 double circle_y; 214 215 for (double circle_angle = 0; circle_angle < 2 * PI; circle_angle += PI / 4) { 216 circle_x = bg.img_middle_x + cos (circle_angle) * length; 217 circle_y = bg.img_middle_y + sin (circle_angle) * length; 218 219 circle_edge = fabs (Path.distance (coordinate_x, circle_x, 220 coordinate_y, circle_y)); 221 222 if (circle_edge < min) { 223 rotation_position_x = circle_x; 224 rotation_position_y = circle_y; 225 min = circle_edge; 226 } 227 } 228 } 229 230 bg.preview_img_rotation_from_coordinate (rotation_position_x, rotation_position_y, view_zoom); 231 } 232 233 // resize handle 234 if (bg.selected_handle == 1) { 235 xscale = img_scale_x * ((img_width - dx) / img_width); 236 yscale = xscale; 237 238 xc = bg.img_middle_x; 239 yc = bg.img_middle_y; 240 241 bg.set_img_scale (xscale, yscale); 242 243 bg.img_middle_x = xc; 244 bg.img_middle_y = yc; 245 } 246 247 if (move_bg && bg.selected_handle <= 0) { 248 bg.set_img_offset (this.img_offset_x + dx, this.img_offset_y + dy); 249 } 250 251 GlyphCanvas.redraw (); 252 253 last_x = x; 254 last_y = y; 255 } 256 257 public static void load_background_image () { 258 // generate png file if needed and load the image with cairo 259 imported_surface = imported_background.get_img (); 260 261 IdleSource idle = new IdleSource (); 262 idle.set_callback (() => { 263 TabBar tb = MainWindow.get_tab_bar (); 264 Glyph g = MainWindow.get_current_glyph (); 265 266 g.set_background_image (imported_background); 267 imported_background.center_in_glyph (); 268 269 Toolbox.select_tool_by_name ("zoom_background_image"); 270 tb.select_tab_name (g.get_name ()); 271 Toolbox.select_tool_by_name ("cut_background"); 272 273 GlyphCanvas.redraw (); 274 return false; 275 }); 276 idle.attach (null); 277 } 278 279 internal static void import_background_image () { 280 FileChooser fc = new FileChooser (); 281 282 fc.file_selected.connect ((fn) => { 283 BackgroundImage bg; 284 string path; 285 286 if (fn != null) { 287 path = (!) fn; 288 bg = new BackgroundImage (path); 289 imported_background = bg; 290 MainWindow.native_window.load_background_image (); 291 } 292 }); 293 294 MainWindow.file_chooser (t_("Select background image"), fc, FileChooser.LOAD); 295 } 296 } 297 298 } 299