The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

EditPointHandle.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/EditPointHandle.vala.
Merge ../birdfont-2.x
1 /* 2 Copyright (C) 2012 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 17 namespace BirdFont { 18 19 public class EditPointHandle : GLib.Object { 20 21 public double length; 22 public unowned EditPoint parent; 23 public PointType type; 24 EditPoint? visual_handle = null; 25 static EditPoint none = new EditPoint (); 26 public bool active; 27 public bool selected; 28 29 public double angle; 30 31 public double x { 32 get { 33 double r = px (); 34 35 if (unlikely (r <= -100000)) { 36 print_position (); 37 move_to (0, 0); 38 } 39 40 return r; 41 } 42 43 set { 44 move_to_coordinate_internal (value, py ()); 45 46 if (parent.tie_handles) { 47 parent.process_tied_handle (); 48 } 49 50 if (parent.reflective_point) { 51 parent.process_symmetrical_handles (); 52 } 53 } 54 } 55 56 public double y { 57 get { 58 double r = py (); 59 60 if (unlikely (r <= -100000)) { 61 print_position (); 62 move_to (0, 0); 63 } 64 65 return r; 66 } 67 68 set { 69 move_to_coordinate_internal (px (), value); 70 71 if (parent.tie_handles) { 72 parent.process_tied_handle (); 73 } 74 75 if (parent.reflective_point) { 76 parent.process_symmetrical_handles (); 77 } 78 } 79 } 80 81 public double independent_x { 82 get { 83 double r = px (); 84 85 if (unlikely (r <= -100000)) { 86 print_position (); 87 move_to (0, 0); 88 } 89 90 return r; 91 } 92 93 set { 94 move_to_coordinate_internal (value, py ()); 95 } 96 } 97 98 public double independent_y { 99 get { 100 double r = py (); 101 102 if (unlikely (r <= -100000)) { 103 print_position (); 104 move_to (0, 0); 105 } 106 107 return r; 108 } 109 110 set { 111 move_to_coordinate_internal (px (), value); 112 } 113 } 114 115 public EditPointHandle.empty() { 116 this.parent = none; 117 this.angle = 0; 118 this.length = 10; 119 this.type = PointType.NONE; 120 this.active = false; 121 this.selected = false; 122 } 123 124 public EditPointHandle (EditPoint parent, double angle, double length) { 125 this.parent = parent; 126 this.angle = angle; 127 this.length = length; 128 this.type = PointType.LINE_CUBIC; 129 this.active = false; 130 this.selected = false; 131 } 132 133 public EditPointHandle copy () { 134 EditPointHandle n = new EditPointHandle.empty (); 135 n.angle = angle; 136 n.length = length; 137 n.parent = parent; 138 n.type = type; 139 n.active = active; 140 n.selected = selected; 141 return n; 142 } 143 144 public EditPoint get_parent () { 145 return parent; 146 } 147 148 public void convert_to_line () { 149 switch (type) { 150 case PointType.QUADRATIC: 151 type = PointType.LINE_QUADRATIC; 152 break; 153 case PointType.DOUBLE_CURVE: 154 type = PointType.LINE_DOUBLE_CURVE; 155 break; 156 case PointType.CUBIC: 157 type = PointType.LINE_CUBIC; 158 break; 159 default: 160 break; 161 } 162 } 163 164 public bool is_curve () { 165 switch (type) { 166 case PointType.LINE_QUADRATIC: 167 return false; 168 case PointType.LINE_DOUBLE_CURVE: 169 return false; 170 case PointType.LINE_CUBIC: 171 return false; 172 } 173 174 return true; 175 } 176 177 public void convert_to_curve () { 178 switch (type) { 179 case PointType.LINE_QUADRATIC: 180 type = PointType.QUADRATIC; 181 break; 182 case PointType.LINE_DOUBLE_CURVE: 183 type = PointType.DOUBLE_CURVE; 184 break; 185 case PointType.LINE_CUBIC: 186 type = PointType.CUBIC; 187 break; 188 case PointType.QUADRATIC: 189 break; 190 case PointType.DOUBLE_CURVE: 191 break; 192 case PointType.CUBIC: 193 break; 194 default: 195 warning (@"Type $type"); 196 break; 197 } 198 } 199 200 public void set_point_type (PointType point_type) { 201 type = point_type; 202 } 203 204 double px () { 205 assert ((EditPoint?) parent != null); 206 return cos (angle) * length + parent.x; 207 } 208 209 double py () { 210 assert ((EditPoint?) parent != null); 211 return sin (angle) * length + parent.y; 212 } 213 214 internal void print_position () { 215 warning (@"\nEdit point handle at position $(px ()),$(py ()) is not valid.\n" 216 + @"Type: $(parent.type), " 217 + @"Angle: $angle, Length: $length."); 218 } 219 220 public EditPoint get_point () { 221 EditPoint p; 222 223 if (visual_handle == null) { 224 visual_handle = new EditPoint (0, 0); 225 } 226 227 p = (!) visual_handle; 228 p.x = x; 229 p.y = y; 230 231 return p; 232 } 233 234 public bool is_left_handle () { 235 return parent.get_left_handle () == this; 236 } 237 238 public void move_to_coordinate_delta (double dx, double dy) { 239 move_to_coordinate_internal (px () + dx, py () + dy); 240 } 241 242 public void move_to_coordinate (double x, double y) { 243 move_to_coordinate_internal (x, y); 244 245 if (parent.tie_handles) { 246 tie_handle (); 247 } 248 249 if (parent.reflective_point) { 250 tie_handle (); 251 process_symmetrical_handle (); 252 } 253 254 process_connected_handle (); 255 } 256 257 public void move_to_coordinate_internal (double x, double y) { 258 double a, b, c; 259 260 a = parent.x - x; 261 b = parent.y - y; 262 c = a * a + b * b; 263 264 if (unlikely(c == 0)) { 265 angle = 0; // FIXME: this should be a different point type without line handles 266 length = 0; 267 return; 268 } 269 270 length = sqrt (fabs (c)); 271 272 if (c < 0) length = -length; 273 274 if (y < parent.y) { 275 angle = acos (a / length) + PI; 276 } else { 277 angle = -acos (a / length) + PI; 278 } 279 } 280 281 public void process_connected_handle () { 282 EditPointHandle h; 283 284 if (unlikely (type == PointType.NONE)) { 285 warning ("Invalid type."); 286 } 287 288 if (type == PointType.QUADRATIC) { 289 if (!is_left_handle ()) { 290 if (parent.next != null) { 291 h = parent.get_next ().get_left_handle (); 292 h.parent.set_tie_handle (false); 293 h.type = PointType.QUADRATIC; 294 h.move_to_coordinate_internal (px (), py ()); 295 } 296 } else { 297 if (parent.prev != null) { 298 h = parent.get_prev ().get_right_handle (); 299 h.parent.set_tie_handle (false); 300 h.type = PointType.QUADRATIC; 301 h.move_to_coordinate_internal (px (), py ()); 302 } 303 } 304 } 305 } 306 307 public void process_symmetrical_handle () { 308 if (is_left_handle ()) { 309 parent.get_right_handle ().length = length; 310 parent.get_right_handle ().process_connected_handle (); 311 } else { 312 parent.get_left_handle ().length = length; 313 parent.get_left_handle ().process_connected_handle (); 314 } 315 316 process_connected_handle (); 317 } 318 319 public void tie_handle () { 320 if (is_left_handle ()) { 321 parent.get_right_handle ().angle = angle - PI; 322 parent.get_right_handle ().process_connected_handle (); 323 } else { 324 parent.get_left_handle ().angle = angle - PI; 325 parent.get_left_handle ().process_connected_handle (); 326 } 327 328 process_connected_handle (); 329 } 330 331 public void move_delta_coordinate (double dx, double dy) { 332 double px = px () + dx; 333 double py = py () + dy; 334 move_to_coordinate (px, py); 335 } 336 337 public void move_to (double x, double y) { 338 EditPoint.to_coordinate (ref x, ref y); 339 move_to_coordinate (x, y); 340 } 341 342 public bool is_line () { 343 return PenTool.is_line (type); 344 } 345 346 public static double average_angle (double angle1, double angle2) { 347 EditPointHandle handle = new EditPointHandle (new EditPoint (0, 0, PointType.CUBIC), 0, 1); 348 double x = (cos (angle1) + cos (angle2)) / 2; 349 double y = (sin (angle1) + sin (angle2)) / 2; 350 handle.move_to_coordinate (x, y); 351 352 if (fabs (x) < 0.001 && fabs (y) < 0.001) { 353 return (angle1 + PI / 2) % (2 * PI); 354 } 355 356 return handle.angle; 357 } 358 359 } 360 361 } 362