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