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