.
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 static int n_handles = 0;
82
83 public EditPointHandle.empty() {
84 this.parent = none;
85 this.angle = 0;
86 this.length = 10;
87 this.type = PointType.NONE;
88 this.active = false;
89 this.selected = false;
90
91 n_handles++;
92 }
93
94 public EditPointHandle (EditPoint parent, double angle, double length) {
95 this.parent = parent;
96 this.angle = angle;
97 this.length = length;
98 this.type = PointType.LINE_CUBIC;
99 this.active = false;
100 this.selected = false;
101
102 n_handles++;
103 }
104
105 ~EditPointHandle () {
106 n_handles--;
107 }
108
109 public EditPointHandle copy () {
110 EditPointHandle n = new EditPointHandle.empty ();
111 n.angle = angle;
112 n.length = length;
113 n.parent = parent;
114 n.type = type;
115 n.active = active;
116 n.selected = selected;
117 return n;
118 }
119
120 public EditPoint get_parent () {
121 return parent;
122 }
123
124 public void convert_to_line () {
125 switch (type) {
126 case PointType.QUADRATIC:
127 type = PointType.LINE_QUADRATIC;
128 break;
129 case PointType.DOUBLE_CURVE:
130 type = PointType.LINE_DOUBLE_CURVE;
131 break;
132 case PointType.CUBIC:
133 type = PointType.LINE_CUBIC;
134 break;
135 default:
136 break;
137 }
138 }
139
140 public bool is_curve () {
141 switch (type) {
142 case PointType.LINE_QUADRATIC:
143 return false;
144 case PointType.LINE_DOUBLE_CURVE:
145 return false;
146 case PointType.LINE_CUBIC:
147 return false;
148 }
149
150 return true;
151 }
152
153 public void convert_to_curve () {
154 switch (type) {
155 case PointType.LINE_QUADRATIC:
156 type = PointType.QUADRATIC;
157 break;
158 case PointType.LINE_DOUBLE_CURVE:
159 type = PointType.DOUBLE_CURVE;
160 break;
161 case PointType.LINE_CUBIC:
162 type = PointType.CUBIC;
163 break;
164 case PointType.QUADRATIC:
165 break;
166 case PointType.DOUBLE_CURVE:
167 break;
168 case PointType.CUBIC:
169 break;
170 default:
171 warning (@"Type $type");
172 break;
173 }
174 }
175
176 public void set_point_type (PointType point_type) {
177 type = point_type;
178 }
179
180 double px () {
181 assert ((EditPoint?) parent != null);
182 return cos (angle) * length + parent.x;
183 }
184
185 double py () {
186 assert ((EditPoint?) parent != null);
187 return sin (angle) * length + parent.y;
188 }
189
190 internal void print_position () {
191 warning (@"\nEdit point handle at position $(px ()),$(py ()) is not valid.\n"
192 + @"Type: $(parent.type), "
193 + @"Angle: $angle, Length: $length.");
194 }
195
196 public EditPoint get_point () {
197 EditPoint p;
198
199 if (visual_handle == null) {
200 visual_handle = new EditPoint (0, 0);
201 }
202
203 p = (!) visual_handle;
204 p.x = x;
205 p.y = y;
206
207 return p;
208 }
209
210 public bool is_left_handle () {
211 return parent.get_left_handle () == this;
212 }
213
214 public void move_to_coordinate_delta (double dx, double dy) {
215 move_to_coordinate_internal (px () + dx, py () + dy);
216 }
217
218 public void move_to_coordinate (double x, double y) {
219 move_to_coordinate_internal (x, y);
220
221 if (parent.tie_handles) {
222 tie_handle ();
223 }
224
225 if (parent.reflective_point) {
226 tie_handle ();
227 process_symmetrical_handle ();
228 }
229
230 process_connected_handle ();
231 }
232
233 public void move_to_coordinate_internal (double x, double y) {
234 double a, b, c;
235
236 a = parent.x - x;
237 b = parent.y - y;
238 c = a * a + b * b;
239
240 if (unlikely(c == 0)) {
241 angle = 0; // FIXME: this should be a different point type without line handles
242 length = 0;
243 return;
244 }
245
246 length = sqrt (fabs (c));
247
248 if (c < 0) length = -length;
249
250 if (y < parent.y) {
251 angle = acos (a / length) + PI;
252 } else {
253 angle = -acos (a / length) + PI;
254 }
255 }
256
257 public void process_connected_handle () {
258 EditPointHandle h;
259
260 if (unlikely (type == PointType.NONE)) {
261 warning ("Invalid type.");
262 }
263
264 if (type == PointType.QUADRATIC) {
265 if (!is_left_handle ()) {
266 if (parent.next != null) {
267 h = parent.get_next ().get_left_handle ();
268 h.parent.set_tie_handle (false);
269 h.type = PointType.QUADRATIC;
270 h.move_to_coordinate_internal (px (), py ());
271 }
272 } else {
273 if (parent.prev != null) {
274 h = parent.get_prev ().get_right_handle ();
275 h.parent.set_tie_handle (false);
276 h.type = PointType.QUADRATIC;
277 h.move_to_coordinate_internal (px (), py ());
278 }
279 }
280 }
281 }
282
283 public void process_symmetrical_handle () {
284 if (is_left_handle ()) {
285 parent.get_right_handle ().length = length;
286 parent.get_right_handle ().process_connected_handle ();
287 } else {
288 parent.get_left_handle ().length = length;
289 parent.get_left_handle ().process_connected_handle ();
290 }
291
292 process_connected_handle ();
293 }
294
295 public void tie_handle () {
296 if (is_left_handle ()) {
297 parent.get_right_handle ().angle = angle - PI;
298 parent.get_right_handle ().process_connected_handle ();
299 } else {
300 parent.get_left_handle ().angle = angle - PI;
301 parent.get_left_handle ().process_connected_handle ();
302 }
303
304 process_connected_handle ();
305 }
306
307 public void move_delta_coordinate (double dx, double dy) {
308 double px = px () + dx;
309 double py = py () + dy;
310 move_to_coordinate (px, py);
311 }
312
313 public void move_to (double x, double y) {
314 EditPoint.to_coordinate (ref x, ref y);
315 move_to_coordinate (x, y);
316 }
317
318 public bool is_line () {
319 return PenTool.is_line (type);
320 }
321 }
322
323 }
324