1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
3 *
4 * beziershape.c - code to help implement bezier shapes
5 * Copyright (C) 2000 James Henstridge
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include <config.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <string.h> /* memcpy() */
28
29 #include "beziershape.h"
30 #include "message.h"
31 #include "diarenderer.h"
32
33 #define HANDLE_BEZMAJOR (HANDLE_CUSTOM1)
34 #define HANDLE_LEFTCTRL (HANDLE_CUSTOM2)
35 #define HANDLE_RIGHTCTRL (HANDLE_CUSTOM3)
36
37 enum change_type {
38 TYPE_ADD_POINT,
39 TYPE_REMOVE_POINT
40 };
41
42 /* Invariant:
43 # of handles = 3*(numpoints-1)
44 # of connections = 2*(numpoints-1) + 1 (main point)
45 For historical reasons, the main point is the last cp.
46 */
47 struct BezPointChange {
48 ObjectChange obj_change;
49
50 enum change_type type;
51 int applied;
52
53 BezPoint point;
54 BezCornerType corner_type;
55 int pos;
56
57 /* owning ref when not applied for ADD_POINT
58 * owning ref when applied for REMOVE_POINT */
59 Handle *handle1, *handle2, *handle3;
60 ConnectionPoint *cp1, *cp2;
61 };
62
63 struct CornerChange {
64 ObjectChange obj_change;
65 /* Only one kind of corner_change */
66 int applied;
67
68 Handle *handle;
69 /* Old places when SET_CORNER_TYPE is applied */
70 Point point_left, point_right;
71 BezCornerType old_type, new_type;
72 };
73
74 static ObjectChange *
75 beziershape_create_point_change(BezierShape *bezier, enum change_type type,
76 BezPoint *point, BezCornerType corner_type,
77 int segment,
78 Handle *handle1, Handle *handle2, Handle *handle3,
79 ConnectionPoint *cp1, ConnectionPoint *cp2);
80 static ObjectChange *
81 beziershape_create_corner_change(BezierShape *bezier, Handle *handle,
82 Point *point_left, Point *point_right,
83 BezCornerType old_corner_type,
84 BezCornerType new_corner_type);
85
86 static void new_handles_and_connections(BezierShape *bezier, int num_points);
87
setup_handle(Handle * handle,int handle_id)88 static void setup_handle(Handle *handle, int handle_id)
89 {
90 handle->id = handle_id;
91 handle->type =
92 (handle_id == HANDLE_BEZMAJOR) ?
93 HANDLE_MAJOR_CONTROL :
94 HANDLE_MINOR_CONTROL;
95 handle->connect_type = HANDLE_NONCONNECTABLE;
96 handle->connected_to = NULL;
97 }
98
99
get_handle_nr(BezierShape * bezier,Handle * handle)100 static int get_handle_nr(BezierShape *bezier, Handle *handle)
101 {
102 int i = 0;
103 for (i = 0; i < bezier->object.num_handles; i++) {
104 if (bezier->object.handles[i] == handle)
105 return i;
106 }
107 return -1;
108 }
109
110 #define get_comp_nr(hnum) ((int)(hnum)/3+1)
111 #define get_major_nr(hnum) (((int)(hnum)+2)/3)
112
113 ObjectChange *
beziershape_move_handle(BezierShape * bezier,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)114 beziershape_move_handle(BezierShape *bezier, Handle *handle,
115 Point *to, ConnectionPoint *cp,
116 HandleMoveReason reason, ModifierKeys modifiers)
117 {
118 int handle_nr, comp_nr, next_nr, prev_nr;
119 Point delta, pt;
120
121 delta = *to;
122 point_sub(&delta, &handle->pos);
123
124 handle_nr = get_handle_nr(bezier, handle);
125 comp_nr = get_comp_nr(handle_nr);
126 next_nr = comp_nr + 1;
127 prev_nr = comp_nr - 1;
128 if (comp_nr == bezier->numpoints - 1)
129 next_nr = 1;
130 if (comp_nr == 1)
131 prev_nr = bezier->numpoints - 1;
132
133 switch(handle->id) {
134 case HANDLE_BEZMAJOR:
135 if (comp_nr == bezier->numpoints - 1) {
136 bezier->points[comp_nr].p3 = *to;
137 bezier->points[0].p1 = bezier->points[0].p3 = *to;
138 point_add(&bezier->points[comp_nr].p2, &delta);
139 point_add(&bezier->points[1].p1, &delta);
140 } else {
141 bezier->points[comp_nr].p3 = *to;
142 point_add(&bezier->points[comp_nr].p2, &delta);
143 point_add(&bezier->points[comp_nr+1].p1, &delta);
144 }
145 break;
146 case HANDLE_LEFTCTRL:
147 bezier->points[comp_nr].p2 = *to;
148 switch (bezier->corner_types[comp_nr]) {
149 case BEZ_CORNER_SYMMETRIC:
150 pt = bezier->points[comp_nr].p3;
151 point_sub(&pt, &bezier->points[comp_nr].p2);
152 point_add(&pt, &bezier->points[comp_nr].p3);
153 bezier->points[next_nr].p1 = pt;
154 break;
155 case BEZ_CORNER_SMOOTH: {
156 real len;
157
158 pt = bezier->points[next_nr].p1;
159 point_sub(&pt, &bezier->points[comp_nr].p3);
160 len = point_len(&pt);
161
162 pt = bezier->points[comp_nr].p3;
163 point_sub(&pt, &bezier->points[comp_nr].p2);
164 if (point_len(&pt) > 0)
165 point_normalize(&pt);
166 else {
167 pt.x = 1.0; pt.y = 0.0;
168 }
169 point_scale(&pt, len);
170 point_add(&pt, &bezier->points[comp_nr].p3);
171 bezier->points[next_nr].p1 = pt;
172 break;
173 }
174 case BEZ_CORNER_CUSP:
175 /* no mirror point movement required */
176 break;
177 }
178 break;
179 case HANDLE_RIGHTCTRL:
180 bezier->points[comp_nr].p1 = *to;
181 switch (bezier->corner_types[prev_nr]) {
182 case BEZ_CORNER_SYMMETRIC:
183 pt = bezier->points[prev_nr].p3;
184 point_sub(&pt, &bezier->points[comp_nr].p1);
185 point_add(&pt, &bezier->points[prev_nr].p3);
186 bezier->points[prev_nr].p2 = pt;
187 break;
188 case BEZ_CORNER_SMOOTH: {
189 real len;
190
191 pt = bezier->points[prev_nr].p2;
192 point_sub(&pt, &bezier->points[prev_nr].p3);
193 len = point_len(&pt);
194
195 pt = bezier->points[prev_nr].p3;
196 point_sub(&pt, &bezier->points[comp_nr].p1);
197 if (point_len(&pt) > 0)
198 point_normalize(&pt);
199 else {
200 pt.x = 1.0; pt.y = 0.0;
201 }
202 point_scale(&pt, len);
203 point_add(&pt, &bezier->points[prev_nr].p3);
204 bezier->points[prev_nr].p2 = pt;
205 break;
206 }
207 case BEZ_CORNER_CUSP:
208 /* no mirror point movement required */
209 break;
210 }
211 break;
212 default:
213 message_error("Internal error in beziershape_move_handle.");
214 break;
215 }
216 return NULL;
217 }
218
219 ObjectChange*
beziershape_move(BezierShape * bezier,Point * to)220 beziershape_move(BezierShape *bezier, Point *to)
221 {
222 Point p;
223 int i;
224
225 p = *to;
226 point_sub(&p, &bezier->points[0].p1);
227
228 bezier->points[0].p1 = bezier->points[0].p3 = *to;
229 for (i = 1; i < bezier->numpoints; i++) {
230 point_add(&bezier->points[i].p1, &p);
231 point_add(&bezier->points[i].p2, &p);
232 point_add(&bezier->points[i].p3, &p);
233 }
234
235 return NULL;
236 }
237
238 int
beziershape_closest_segment(BezierShape * bezier,Point * point,real line_width)239 beziershape_closest_segment(BezierShape *bezier, Point *point, real line_width)
240 {
241 Point last;
242 int i;
243 real dist = G_MAXDOUBLE;
244 int closest;
245
246 closest = 0;
247 last = bezier->points[0].p1;
248 /* the first point is just move-to so there is no need to consider p2,p3 of it */
249 for (i = 1; i < bezier->numpoints; i++) {
250 real new_dist = distance_bez_seg_point(&last, &bezier->points[i].p1,
251 &bezier->points[i].p2, &bezier->points[i].p3,
252 line_width, point);
253 if (new_dist < dist) {
254 dist = new_dist;
255 closest = i;
256 }
257 last = bezier->points[i].p3;
258 }
259 return closest;
260 }
261
262 Handle *
beziershape_closest_handle(BezierShape * bezier,Point * point)263 beziershape_closest_handle(BezierShape *bezier, Point *point)
264 {
265 int i, hn;
266 real dist = G_MAXDOUBLE;
267 Handle *closest = NULL;
268
269 for (i = 1, hn = 0; i < bezier->numpoints; i++, hn++) {
270 real new_dist;
271
272 new_dist = distance_point_point( point, &bezier->points[i].p1);
273 if (new_dist < dist) {
274 dist = new_dist;
275 closest = bezier->object.handles[hn];
276 }
277 hn++;
278
279 new_dist = distance_point_point( point, &bezier->points[i].p2);
280 if (new_dist < dist) {
281 dist = new_dist;
282 closest = bezier->object.handles[hn];
283 }
284
285 hn++;
286 new_dist = distance_point_point( point, &bezier->points[i].p3);
287 if (new_dist < dist) {
288 dist = new_dist;
289 closest = bezier->object.handles[hn];
290 }
291 }
292 return closest;
293 }
294
295 Handle *
beziershape_closest_major_handle(BezierShape * bezier,Point * point)296 beziershape_closest_major_handle(BezierShape *bezier, Point *point)
297 {
298 Handle *closest = beziershape_closest_handle(bezier, point);
299 int pos = get_major_nr(get_handle_nr(bezier, closest));
300
301 if (pos == 0)
302 pos = bezier->numpoints - 1;
303 return bezier->object.handles[3*pos - 1];
304 }
305
306 real
beziershape_distance_from(BezierShape * bezier,Point * point,real line_width)307 beziershape_distance_from(BezierShape *bezier, Point *point, real line_width)
308 {
309 return distance_bez_shape_point(bezier->points, bezier->numpoints,
310 line_width, point);
311 }
312
313 static void
add_handles(BezierShape * bezier,int pos,BezPoint * point,BezCornerType corner_type,Handle * handle1,Handle * handle2,Handle * handle3,ConnectionPoint * cp1,ConnectionPoint * cp2)314 add_handles(BezierShape *bezier, int pos, BezPoint *point,
315 BezCornerType corner_type, Handle *handle1,
316 Handle *handle2, Handle *handle3,
317 ConnectionPoint *cp1, ConnectionPoint *cp2)
318 {
319 int i, next;
320 DiaObject *obj;
321
322 g_assert(pos >= 1);
323 g_assert(pos <= bezier->numpoints);
324
325 obj = (DiaObject *)bezier;
326 bezier->numpoints++;
327 next = pos + 1;
328 if (pos == bezier->numpoints - 1)
329 next = 1;
330 bezier->points = g_realloc(bezier->points,
331 bezier->numpoints * sizeof(BezPoint));
332 bezier->corner_types = g_realloc(bezier->corner_types,
333 bezier->numpoints * sizeof(BezCornerType));
334
335 for (i = bezier->numpoints - 1; i > pos; i--) {
336 bezier->points[i] = bezier->points[i-1];
337 bezier->corner_types[i] =bezier->corner_types[i-1];
338 }
339 bezier->points[pos] = *point;
340 bezier->points[pos].p1 = bezier->points[next].p1;
341 bezier->points[next].p1 = point->p1;
342 if (pos == bezier->numpoints - 1)
343 bezier->points[0].p1 = bezier->points[0].p3 = bezier->points[pos].p3;
344 bezier->corner_types[pos] = corner_type;
345 object_add_handle_at((DiaObject*)bezier, handle1, 3*pos-3);
346 object_add_handle_at((DiaObject*)bezier, handle2, 3*pos-2);
347 object_add_handle_at((DiaObject*)bezier, handle3, 3*pos-1);
348 object_add_connectionpoint_at((DiaObject *)bezier, cp1, 2*pos-2);
349 object_add_connectionpoint_at((DiaObject *)bezier, cp2, 2*pos-1);
350 }
351
352 static void
remove_handles(BezierShape * bezier,int pos)353 remove_handles(BezierShape *bezier, int pos)
354 {
355 int i;
356 DiaObject *obj;
357 Handle *old_handle1, *old_handle2, *old_handle3;
358 ConnectionPoint *old_cp1, *old_cp2;
359 Point tmppoint;
360 Point controlvector;
361 controlvector.x=0;
362 controlvector.y=0;
363
364 g_assert(pos > 0);
365 g_assert(pos < bezier->numpoints);
366
367 obj = (DiaObject *)bezier;
368
369 /* delete the points */
370 bezier->numpoints--;
371 tmppoint = bezier->points[pos].p1;
372 if (pos == bezier->numpoints) {
373 controlvector = bezier->points[pos-1].p3;
374 point_sub(&controlvector, &bezier->points[pos].p1);
375 }
376 for (i = pos; i < bezier->numpoints; i++) {
377 bezier->points[i] = bezier->points[i+1];
378 bezier->corner_types[i] = bezier->corner_types[i+1];
379 }
380 bezier->points[pos].p1 = tmppoint;
381 if (pos == bezier->numpoints) {
382 /* If this was the last point, we also need to move points[0] and
383 the control point in points[1]. */
384 bezier->points[0].p1 = bezier->points[bezier->numpoints-1].p3;
385 bezier->points[1].p1 = bezier->points[0].p1;
386 point_sub(&bezier->points[1].p1, &controlvector);
387 }
388 bezier->points = g_realloc(bezier->points,
389 bezier->numpoints * sizeof(BezPoint));
390 bezier->corner_types = g_realloc(bezier->corner_types,
391 bezier->numpoints * sizeof(BezCornerType));
392
393 old_handle1 = obj->handles[3*pos-3];
394 old_handle2 = obj->handles[3*pos-2];
395 old_handle3 = obj->handles[3*pos-1];
396 object_remove_handle(&bezier->object, old_handle1);
397 object_remove_handle(&bezier->object, old_handle2);
398 object_remove_handle(&bezier->object, old_handle3);
399 old_cp1 = obj->connections[2*pos-2];
400 old_cp2 = obj->connections[2*pos-1];
401 object_remove_connectionpoint(&bezier->object, old_cp1);
402 object_remove_connectionpoint(&bezier->object, old_cp2);
403 }
404
405
406 /* Add a point by splitting segment into two, putting the new point at
407 'point' or, if NULL, in the middle */
408 ObjectChange *
beziershape_add_segment(BezierShape * bezier,int segment,Point * point)409 beziershape_add_segment(BezierShape *bezier, int segment, Point *point)
410 {
411 BezPoint realpoint;
412 BezCornerType corner_type = BEZ_CORNER_SYMMETRIC;
413 Handle *new_handle1, *new_handle2, *new_handle3;
414 ConnectionPoint *new_cp1, *new_cp2;
415 Point startpoint;
416 Point other;
417
418 if (segment != 1)
419 startpoint = bezier->points[segment-1].p3;
420 else
421 startpoint = bezier->points[0].p1;
422 other = bezier->points[segment].p3;
423 if (point == NULL) {
424 realpoint.p1.x = (startpoint.x + other.x)/6;
425 realpoint.p1.y = (startpoint.y + other.y)/6;
426 realpoint.p2.x = (startpoint.x + other.x)/3;
427 realpoint.p2.y = (startpoint.y + other.y)/3;
428 realpoint.p3.x = (startpoint.x + other.x)/2;
429 realpoint.p3.y = (startpoint.y + other.y)/2;
430 } else {
431 realpoint.p2.x = point->x+(startpoint.x-other.x)/6;
432 realpoint.p2.y = point->y+(startpoint.y-other.y)/6;
433
434 realpoint.p3 = *point;
435 /* this really goes into the next segment ... */
436 realpoint.p1.x = point->x-(startpoint.x-other.x)/6;
437 realpoint.p1.y = point->y-(startpoint.y-other.y)/6;
438 }
439 realpoint.type = BEZ_CURVE_TO;
440
441 new_handle1 = g_new0(Handle,1);
442 new_handle2 = g_new0(Handle,1);
443 new_handle3 = g_new0(Handle,1);
444 setup_handle(new_handle1, HANDLE_RIGHTCTRL);
445 setup_handle(new_handle2, HANDLE_LEFTCTRL);
446 setup_handle(new_handle3, HANDLE_BEZMAJOR);
447 new_cp1 = g_new0(ConnectionPoint, 1);
448 new_cp2 = g_new0(ConnectionPoint, 1);
449 new_cp1->object = &bezier->object;
450 new_cp2->object = &bezier->object;
451 add_handles(bezier, segment, &realpoint, corner_type,
452 new_handle1, new_handle2, new_handle3, new_cp1, new_cp2);
453 return beziershape_create_point_change(bezier, TYPE_ADD_POINT,
454 &realpoint, corner_type, segment,
455 new_handle1, new_handle2, new_handle3,
456 new_cp1, new_cp2);
457 }
458
459 ObjectChange *
beziershape_remove_segment(BezierShape * bezier,int pos)460 beziershape_remove_segment(BezierShape *bezier, int pos)
461 {
462 Handle *old_handle1, *old_handle2, *old_handle3;
463 ConnectionPoint *old_cp1, *old_cp2;
464 BezPoint old_point;
465 BezCornerType old_ctype;
466 int next = pos+1;
467
468 g_assert(pos > 0);
469 g_assert(bezier->numpoints > 2);
470 g_assert(pos < bezier->numpoints);
471
472 if (pos == bezier->numpoints - 1)
473 next = 1;
474
475 old_handle1 = bezier->object.handles[3*pos-3];
476 old_handle2 = bezier->object.handles[3*pos-2];
477 old_handle3 = bezier->object.handles[3*pos-1];
478 old_point = bezier->points[pos];
479 /* remember the old contro point of following bezpoint */
480 old_point.p1 = bezier->points[next].p1;
481 old_ctype = bezier->corner_types[pos];
482
483 old_cp1 = bezier->object.connections[2*pos-2];
484 old_cp2 = bezier->object.connections[2*pos-1];
485
486 object_unconnect((DiaObject *)bezier, old_handle1);
487 object_unconnect((DiaObject *)bezier, old_handle2);
488 object_unconnect((DiaObject *)bezier, old_handle3);
489
490 remove_handles(bezier, pos);
491
492 beziershape_update_data(bezier);
493
494 return beziershape_create_point_change(bezier, TYPE_REMOVE_POINT,
495 &old_point, old_ctype, pos,
496 old_handle1, old_handle2, old_handle3,
497 old_cp1, old_cp2);
498 }
499
500 static void
beziershape_straighten_corner(BezierShape * bez,int comp_nr)501 beziershape_straighten_corner(BezierShape *bez, int comp_nr) {
502 int next_nr;
503
504 if (comp_nr == 0) comp_nr = bez->numpoints - 1;
505 next_nr = comp_nr + 1;
506 if (comp_nr == bez->numpoints - 1)
507 next_nr = 1;
508 /* Neat thing would be to have the kind of straigthening depend on
509 which handle was chosen: Mid-handle does average, other leaves that
510 handle where it is. */
511 bez->points[0].p3 = bez->points[0].p1;
512 switch (bez->corner_types[comp_nr]) {
513 case BEZ_CORNER_SYMMETRIC: {
514 Point pt1, pt2;
515
516 pt1 = bez->points[comp_nr].p3;
517 point_sub(&pt1, &bez->points[comp_nr].p2);
518 pt2 = bez->points[comp_nr].p3;
519 point_sub(&pt2, &bez->points[next_nr].p1);
520 point_scale(&pt2, -1.0);
521 point_add(&pt1, &pt2);
522 point_scale(&pt1, 0.5);
523 pt2 = pt1;
524 point_scale(&pt1, -1.0);
525 point_add(&pt1, &bez->points[comp_nr].p3);
526 point_add(&pt2, &bez->points[comp_nr].p3);
527 bez->points[comp_nr].p2 = pt1;
528 bez->points[next_nr].p1 = pt2;
529 beziershape_update_data(bez);
530 }
531 break;
532 case BEZ_CORNER_SMOOTH: {
533 Point pt1, pt2;
534 real len1, len2;
535 pt1 = bez->points[comp_nr].p3;
536 point_sub(&pt1, &bez->points[comp_nr].p2);
537 pt2 = bez->points[comp_nr].p3;
538 point_sub(&pt2, &bez->points[next_nr].p1);
539 len1 = point_len(&pt1);
540 len2 = point_len(&pt2);
541 point_scale(&pt2, -1.0);
542 if (len1 > 0)
543 point_normalize(&pt1);
544 if (len2 > 0)
545 point_normalize(&pt2);
546 point_add(&pt1, &pt2);
547 point_scale(&pt1, 0.5);
548 pt2 = pt1;
549 point_scale(&pt1, -len1);
550 point_add(&pt1, &bez->points[comp_nr].p3);
551 point_scale(&pt2, len2);
552 point_add(&pt2, &bez->points[comp_nr].p3);
553 bez->points[comp_nr].p2 = pt1;
554 bez->points[next_nr].p1 = pt2;
555 beziershape_update_data(bez);
556 }
557 break;
558 case BEZ_CORNER_CUSP:
559 break;
560 }
561 bez->points[0].p1 = bez->points[0].p3;
562 }
563
564 ObjectChange *
beziershape_set_corner_type(BezierShape * bez,Handle * handle,BezCornerType corner_type)565 beziershape_set_corner_type(BezierShape *bez, Handle *handle,
566 BezCornerType corner_type)
567 {
568 Handle *mid_handle = NULL;
569 Point old_left, old_right;
570 int old_type;
571 int handle_nr, comp_nr;
572
573 handle_nr = get_handle_nr(bez, handle);
574
575 switch (handle->id) {
576 case HANDLE_BEZMAJOR:
577 mid_handle = handle;
578 break;
579 case HANDLE_LEFTCTRL:
580 handle_nr++;
581 if (handle_nr == bez->object.num_handles) handle_nr = 0;
582 mid_handle = bez->object.handles[handle_nr];
583 break;
584 case HANDLE_RIGHTCTRL:
585 handle_nr--;
586 if (handle_nr < 0) handle_nr = bez->object.num_handles - 1;
587 mid_handle = bez->object.handles[handle_nr];
588 break;
589 default:
590 g_assert_not_reached();
591 break;
592 }
593
594 comp_nr = get_major_nr(handle_nr);
595
596 old_type = bez->corner_types[comp_nr];
597 old_left = bez->points[comp_nr].p2;
598 if (comp_nr == bez->numpoints - 1)
599 old_right = bez->points[1].p1;
600 else
601 old_right = bez->points[comp_nr+1].p1;
602
603 #if 0
604 g_message("Setting corner type on segment %d to %s", comp_nr,
605 corner_type == BEZ_CORNER_SYMMETRIC ? "symmetric" :
606 corner_type == BEZ_CORNER_SMOOTH ? "smooth" : "cusp");
607 #endif
608 bez->corner_types[comp_nr] = corner_type;
609 if (comp_nr == 0)
610 bez->corner_types[bez->numpoints-1] = corner_type;
611 else if (comp_nr == bez->numpoints - 1)
612 bez->corner_types[0] = corner_type;
613
614 beziershape_straighten_corner(bez, comp_nr);
615
616 return beziershape_create_corner_change(bez, mid_handle, &old_left,
617 &old_right, old_type, corner_type);
618 }
619
620 void
beziershape_update_data(BezierShape * bezier)621 beziershape_update_data(BezierShape *bezier)
622 {
623 int i;
624 Point last;
625 Point minp, maxp;
626
627 DiaObject *obj = &bezier->object;
628
629 /* handle the case of whole points array update (via set_prop) */
630 if (3*(bezier->numpoints-1) != obj->num_handles ||
631 2*(bezier->numpoints-1) + 1 != obj->num_connections) {
632 object_unconnect_all(obj); /* too drastic ? */
633
634 /* delete the old ones */
635 for (i = 0; i < obj->num_handles; i++)
636 g_free(obj->handles[i]);
637 g_free(obj->handles);
638 for (i = 0; i < obj->num_connections; i++)
639 g_free(obj->connections[i]);
640 g_free(obj->connections);
641
642 obj->num_handles = 3*(bezier->numpoints-1);
643 obj->handles = g_new(Handle*, obj->num_handles);
644 obj->num_connections = 2*(bezier->numpoints-1) + 1;
645 obj->connections = g_new(ConnectionPoint *, obj->num_connections);
646
647 new_handles_and_connections(bezier, bezier->numpoints);
648
649 bezier->corner_types = g_realloc(bezier->corner_types, bezier->numpoints*sizeof(BezCornerType));
650 for (i = 0; i < bezier->numpoints; i++)
651 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
652 }
653
654 /* Update handles: */
655 bezier->points[0].p3 = bezier->points[0].p1;
656 for (i = 1; i < bezier->numpoints; i++) {
657 obj->handles[3*i-3]->pos = bezier->points[i].p1;
658 obj->handles[3*i-2]->pos = bezier->points[i].p2;
659 obj->handles[3*i-1]->pos = bezier->points[i].p3;
660 }
661
662 /* Update connection points: */
663 last = bezier->points[0].p1;
664 for (i = 1; i < bezier->numpoints; i++) {
665 Point slopepoint1, slopepoint2;
666 slopepoint1 = bezier->points[i].p1;
667 point_sub(&slopepoint1, &last);
668 point_scale(&slopepoint1, .5);
669 point_add(&slopepoint1, &last);
670 slopepoint2 = bezier->points[i].p2;
671 point_sub(&slopepoint2, &bezier->points[i].p3);
672 point_scale(&slopepoint2, .5);
673 point_add(&slopepoint2, &bezier->points[i].p3);
674
675 obj->connections[2*i-2]->pos = last;
676 obj->connections[2*i-2]->directions =
677 find_slope_directions(last, bezier->points[i].p1);
678 obj->connections[2*i-1]->pos.x =
679 (last.x + 3*bezier->points[i].p1.x + 3*bezier->points[i].p2.x +
680 bezier->points[i].p3.x)/8;
681 obj->connections[2*i-1]->pos.y =
682 (last.y + 3*bezier->points[i].p1.y + 3*bezier->points[i].p2.y +
683 bezier->points[i].p3.y)/8;
684 obj->connections[2*i-1]->directions =
685 find_slope_directions(slopepoint1, slopepoint2);
686 last = bezier->points[i].p3;
687 }
688
689 /* Find the middle of the object (or some approximation at least) */
690 minp = maxp = bezier->points[0].p1;
691 for (i = 1; i < bezier->numpoints; i++) {
692 Point p = bezier->points[i].p3;
693 if (p.x < minp.x) minp.x = p.x;
694 if (p.x > maxp.x) maxp.x = p.x;
695 if (p.y < minp.y) minp.y = p.y;
696 if (p.y > maxp.y) maxp.y = p.y;
697 }
698 obj->connections[obj->num_connections-1]->pos.x = (minp.x + maxp.x) / 2;
699 obj->connections[obj->num_connections-1]->pos.y = (minp.y + maxp.y) / 2;
700 obj->connections[obj->num_connections-1]->directions = DIR_ALL;
701 }
702
703 void
beziershape_update_boundingbox(BezierShape * bezier)704 beziershape_update_boundingbox(BezierShape *bezier)
705 {
706 ElementBBExtras *extra;
707 PolyBBExtras pextra;
708
709 g_assert(bezier != NULL);
710
711 extra = &bezier->extra_spacing;
712 pextra.start_trans = pextra.end_trans =
713 pextra.start_long = pextra.end_long = 0;
714 pextra.middle_trans = extra->border_trans;
715
716 polybezier_bbox(&bezier->points[0],
717 bezier->numpoints,
718 &pextra, TRUE,
719 &bezier->object.bounding_box);
720 }
721
722 void
beziershape_simple_draw(BezierShape * bezier,DiaRenderer * renderer,real width)723 beziershape_simple_draw(BezierShape *bezier, DiaRenderer *renderer, real width)
724 {
725 BezPoint *points;
726
727 g_assert(bezier != NULL);
728 g_assert(renderer != NULL);
729
730 points = &bezier->points[0];
731
732 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
733 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
734 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_ROUND);
735 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
736
737 DIA_RENDERER_GET_CLASS(renderer)->fill_bezier(renderer, points, bezier->numpoints,&color_white);
738 DIA_RENDERER_GET_CLASS(renderer)->draw_bezier(renderer, points, bezier->numpoints,&color_black);
739 }
740
741 void
beziershape_draw_control_lines(BezierShape * bez,DiaRenderer * renderer)742 beziershape_draw_control_lines(BezierShape *bez, DiaRenderer *renderer)
743 {
744 Color line_colour = {0.0, 0.0, 0.6};
745 Point startpoint;
746 int i;
747
748 /* setup renderer ... */
749 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, 0);
750 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_DOTTED);
751 DIA_RENDERER_GET_CLASS(renderer)->set_dashlength(renderer, 1);
752 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_MITER);
753 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
754
755 startpoint = bez->points[0].p1;
756 for (i = 1; i < bez->numpoints; i++) {
757 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &startpoint, &bez->points[i].p1,
758 &line_colour);
759 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &bez->points[i].p2, &bez->points[i].p3,
760 &line_colour);
761 startpoint = bez->points[i].p3;
762 }
763 }
764
765 static void
new_handles_and_connections(BezierShape * bezier,int num_points)766 new_handles_and_connections(BezierShape *bezier, int num_points)
767 {
768 DiaObject *obj;
769 int i;
770
771 obj = &bezier->object;
772
773 for (i = 0; i < num_points-1; i++) {
774 obj->handles[3*i] = g_new0(Handle,1);
775 obj->handles[3*i+1] = g_new0(Handle,1);
776 obj->handles[3*i+2] = g_new0(Handle,1);
777
778 obj->handles[3*i]->connect_type = HANDLE_NONCONNECTABLE;
779 obj->handles[3*i]->connected_to = NULL;
780 obj->handles[3*i]->type = HANDLE_MINOR_CONTROL;
781 obj->handles[3*i]->id = HANDLE_RIGHTCTRL;
782
783 obj->handles[3*i+1]->connect_type = HANDLE_NONCONNECTABLE;
784 obj->handles[3*i+1]->connected_to = NULL;
785 obj->handles[3*i+1]->type = HANDLE_MINOR_CONTROL;
786 obj->handles[3*i+1]->id = HANDLE_LEFTCTRL;
787
788 obj->handles[3*i+2]->connect_type = HANDLE_NONCONNECTABLE;
789 obj->handles[3*i+2]->connected_to = NULL;
790 obj->handles[3*i+2]->type = HANDLE_MAJOR_CONTROL;
791 obj->handles[3*i+2]->id = HANDLE_BEZMAJOR;
792
793 obj->connections[2*i] = g_new0(ConnectionPoint, 1);
794 obj->connections[2*i+1] = g_new0(ConnectionPoint, 1);
795 obj->connections[2*i]->object = obj;
796 obj->connections[2*i+1]->object = obj;
797 obj->connections[2*i]->flags = 0;
798 obj->connections[2*i+1]->flags = 0;
799 }
800
801 /** Main point */
802 obj->connections[obj->num_connections-1] = g_new0(ConnectionPoint, 1);
803 obj->connections[obj->num_connections-1]->object = obj;
804 obj->connections[obj->num_connections-1]->flags = CP_FLAGS_MAIN;
805 }
806
807 void
beziershape_init(BezierShape * bezier,int num_points)808 beziershape_init(BezierShape *bezier, int num_points)
809 {
810 DiaObject *obj;
811 int i;
812
813 obj = &bezier->object;
814
815 object_init(obj, 3*(num_points-1), 2*(num_points-1) + 1);
816
817 bezier->numpoints = num_points;
818
819 bezier->points = g_new(BezPoint, num_points);
820 bezier->points[0].type = BEZ_MOVE_TO;
821 bezier->corner_types = g_new(BezCornerType, num_points);
822 for (i = 1; i < num_points; i++) {
823 bezier->points[i].type = BEZ_CURVE_TO;
824 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
825 }
826
827 new_handles_and_connections(bezier, num_points);
828
829 /* The points are not assigned at this point, so don't try to use
830 them */
831 /* beziershape_update_data(bezier);*/
832 }
833
834
835 /** This function does *not* set up handles */
836 void
beziershape_set_points(BezierShape * bez,int num_points,BezPoint * points)837 beziershape_set_points(BezierShape *bez, int num_points, BezPoint *points)
838 {
839 int i;
840
841 bez->numpoints = num_points;
842
843 if (bez->points)
844 g_free(bez->points);
845
846 bez->points = g_malloc((bez->numpoints)*sizeof(BezPoint));
847
848 for (i=0;i<bez->numpoints;i++) {
849 bez->points[i] = points[i];
850 }
851 }
852
853 void
beziershape_copy(BezierShape * from,BezierShape * to)854 beziershape_copy(BezierShape *from, BezierShape *to)
855 {
856 int i;
857 DiaObject *toobj, *fromobj;
858
859 toobj = &to->object;
860 fromobj = &from->object;
861
862 object_copy(fromobj, toobj);
863
864 to->numpoints = from->numpoints;
865
866 to->points = g_new(BezPoint, to->numpoints);
867 to->corner_types = g_new(BezCornerType, to->numpoints);
868
869 for (i = 0; i < to->numpoints; i++) {
870 to->points[i] = from->points[i];
871 to->corner_types[i] = from->corner_types[i];
872 }
873
874 for (i = 0; i < toobj->num_handles; i++) {
875 toobj->handles[i] = g_new0(Handle,1);
876 setup_handle(toobj->handles[i], fromobj->handles[i]->id);
877 }
878 for (i = 0; i < toobj->num_connections; i++) {
879 toobj->connections[i] = g_new0(ConnectionPoint, 1);
880 toobj->connections[i]->object = &to->object;
881 toobj->connections[i]->flags = fromobj->connections[i]->flags;
882 }
883
884 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
885 beziershape_update_data(to);
886 }
887
888 void
beziershape_destroy(BezierShape * bezier)889 beziershape_destroy(BezierShape *bezier)
890 {
891 int i;
892 Handle **temp_handles;
893 ConnectionPoint **temp_cps;
894
895 /* Need to store these temporary since object.handles is
896 freed by object_destroy() */
897 temp_handles = g_new(Handle *, bezier->object.num_handles);
898 for (i = 0; i < bezier->object.num_handles; i++)
899 temp_handles[i] = bezier->object.handles[i];
900
901 temp_cps = g_new(ConnectionPoint *, bezier->object.num_connections);
902 for (i = 0; i < bezier->object.num_connections; i++)
903 temp_cps[i] = bezier->object.connections[i];
904
905 object_destroy(&bezier->object);
906
907 for (i = 0; i < bezier->object.num_handles; i++)
908 g_free(temp_handles[i]);
909 g_free(temp_handles);
910
911 for (i = 0; i < bezier->object.num_connections; i++)
912 g_free(temp_cps[i]);
913 g_free(temp_cps);
914
915 g_free(bezier->points);
916 g_free(bezier->corner_types);
917 }
918
919
920 void
beziershape_save(BezierShape * bezier,ObjectNode obj_node)921 beziershape_save(BezierShape *bezier, ObjectNode obj_node)
922 {
923 int i;
924 AttributeNode attr;
925
926 object_save(&bezier->object, obj_node);
927
928 attr = new_attribute(obj_node, "bez_points");
929
930 data_add_point(attr, &bezier->points[0].p1);
931 for (i = 1; i < bezier->numpoints; i++) {
932 data_add_point(attr, &bezier->points[i].p1);
933 data_add_point(attr, &bezier->points[i].p2);
934 if (i < bezier->numpoints - 1)
935 data_add_point(attr, &bezier->points[i].p3);
936 }
937
938 attr = new_attribute(obj_node, "corner_types");
939 for (i = 0; i < bezier->numpoints; i++)
940 data_add_enum(attr, bezier->corner_types[i]);
941 }
942
943 void
beziershape_load(BezierShape * bezier,ObjectNode obj_node)944 beziershape_load(BezierShape *bezier, ObjectNode obj_node)
945 {
946 int i;
947 AttributeNode attr;
948 DataNode data;
949
950 DiaObject *obj = &bezier->object;
951
952 object_load(obj, obj_node);
953
954 attr = object_find_attribute(obj_node, "bez_points");
955
956 if (attr != NULL)
957 bezier->numpoints = attribute_num_data(attr) / 3 + 1;
958 else
959 bezier->numpoints = 0;
960
961 object_init(obj, 3 * (bezier->numpoints - 1),
962 2 * (bezier->numpoints - 1) + 1);
963
964 data = attribute_first_data(attr);
965 if (bezier->numpoints != 0) {
966 bezier->points = g_new(BezPoint, bezier->numpoints);
967 bezier->points[0].type = BEZ_MOVE_TO;
968 data_point(data, &bezier->points[0].p1);
969 bezier->points[0].p3 = bezier->points[0].p1;
970 data = data_next(data);
971
972 for (i = 1; i < bezier->numpoints; i++) {
973 bezier->points[i].type = BEZ_CURVE_TO;
974 data_point(data, &bezier->points[i].p1);
975 data = data_next(data);
976 data_point(data, &bezier->points[i].p2);
977 data = data_next(data);
978 if (i < bezier->numpoints - 1) {
979 data_point(data, &bezier->points[i].p3);
980 data = data_next(data);
981 } else
982 bezier->points[i].p3 = bezier->points[0].p1;
983 }
984 }
985
986 bezier->corner_types = g_new(BezCornerType, bezier->numpoints);
987 attr = object_find_attribute(obj_node, "corner_types");
988 if (!attr || attribute_num_data(attr) != bezier->numpoints) {
989 for (i = 0; i < bezier->numpoints; i++)
990 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
991 } else {
992 data = attribute_first_data(attr);
993 for (i = 0; i < bezier->numpoints; i++) {
994 bezier->corner_types[i] = data_enum(data);
995 data = data_next(data);
996 }
997 }
998
999 for (i = 0; i < bezier->numpoints - 1; i++) {
1000 obj->handles[3*i] = g_new0(Handle,1);
1001 obj->handles[3*i+1] = g_new0(Handle,1);
1002 obj->handles[3*i+2] = g_new0(Handle,1);
1003
1004 setup_handle(obj->handles[3*i], HANDLE_RIGHTCTRL);
1005 setup_handle(obj->handles[3*i+1], HANDLE_LEFTCTRL);
1006 setup_handle(obj->handles[3*i+2], HANDLE_BEZMAJOR);
1007 }
1008 for (i = 0; i < obj->num_connections; i++) {
1009 obj->connections[i] = g_new0(ConnectionPoint, 1);
1010 obj->connections[i]->object = obj;
1011 }
1012 obj->connections[obj->num_connections-1]->flags = CP_FLAGS_MAIN;
1013
1014 beziershape_update_data(bezier);
1015 }
1016
1017 static void
beziershape_point_change_free(struct BezPointChange * change)1018 beziershape_point_change_free(struct BezPointChange *change)
1019 {
1020 if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
1021 (change->type==TYPE_REMOVE_POINT && change->applied) ){
1022 g_free(change->handle1);
1023 g_free(change->handle2);
1024 g_free(change->handle3);
1025 g_free(change->cp1);
1026 g_free(change->cp2);
1027 change->handle1 = NULL;
1028 change->handle2 = NULL;
1029 change->handle3 = NULL;
1030 change->cp1 = NULL;
1031 change->cp2 = NULL;
1032 }
1033 }
1034
1035 static void
beziershape_point_change_apply(struct BezPointChange * change,DiaObject * obj)1036 beziershape_point_change_apply(struct BezPointChange *change, DiaObject *obj)
1037 {
1038 change->applied = 1;
1039 switch (change->type) {
1040 case TYPE_ADD_POINT:
1041 add_handles((BezierShape *)obj, change->pos, &change->point,
1042 change->corner_type,
1043 change->handle1, change->handle2, change->handle3,
1044 change->cp1, change->cp2);
1045 break;
1046 case TYPE_REMOVE_POINT:
1047 object_unconnect(obj, change->handle1);
1048 object_unconnect(obj, change->handle2);
1049 object_unconnect(obj, change->handle3);
1050 remove_handles((BezierShape *)obj, change->pos);
1051 break;
1052 }
1053 }
1054
1055 static void
beziershape_point_change_revert(struct BezPointChange * change,DiaObject * obj)1056 beziershape_point_change_revert(struct BezPointChange *change, DiaObject *obj)
1057 {
1058 switch (change->type) {
1059 case TYPE_ADD_POINT:
1060 remove_handles((BezierShape *)obj, change->pos);
1061 break;
1062 case TYPE_REMOVE_POINT:
1063 add_handles((BezierShape *)obj, change->pos, &change->point,
1064 change->corner_type,
1065 change->handle1, change->handle2, change->handle3,
1066 change->cp1, change->cp2);
1067 break;
1068 }
1069 change->applied = 0;
1070 }
1071
1072 static ObjectChange *
beziershape_create_point_change(BezierShape * bezier,enum change_type type,BezPoint * point,BezCornerType corner_type,int pos,Handle * handle1,Handle * handle2,Handle * handle3,ConnectionPoint * cp1,ConnectionPoint * cp2)1073 beziershape_create_point_change(BezierShape *bezier, enum change_type type,
1074 BezPoint *point, BezCornerType corner_type,
1075 int pos,
1076 Handle *handle1, Handle *handle2,
1077 Handle *handle3,
1078 ConnectionPoint *cp1, ConnectionPoint *cp2)
1079 {
1080 struct BezPointChange *change;
1081
1082 change = g_new(struct BezPointChange, 1);
1083
1084 change->obj_change.apply =
1085 (ObjectChangeApplyFunc)beziershape_point_change_apply;
1086 change->obj_change.revert =
1087 (ObjectChangeRevertFunc)beziershape_point_change_revert;
1088 change->obj_change.free =
1089 (ObjectChangeFreeFunc)beziershape_point_change_free;
1090
1091 change->type = type;
1092 change->applied = 1;
1093 change->point = *point;
1094 change->corner_type = corner_type;
1095 change->pos = pos;
1096 change->handle1 = handle1;
1097 change->handle2 = handle2;
1098 change->handle3 = handle3;
1099 change->cp1 = cp1;
1100 change->cp2 = cp2;
1101
1102 return (ObjectChange *)change;
1103 }
1104
1105 static void
beziershape_corner_change_apply(struct CornerChange * change,DiaObject * obj)1106 beziershape_corner_change_apply(struct CornerChange *change, DiaObject *obj)
1107 {
1108 BezierShape *bez = (BezierShape *)obj;
1109 int handle_nr = get_handle_nr(bez, change->handle);
1110 int comp_nr = get_major_nr(handle_nr);
1111
1112 beziershape_straighten_corner(bez, comp_nr);
1113
1114 bez->corner_types[comp_nr] = change->new_type;
1115 if (comp_nr == 0)
1116 bez->corner_types[bez->numpoints-1] = change->new_type;
1117 if (comp_nr == bez->numpoints - 1)
1118 bez->corner_types[0] = change->new_type;
1119
1120 change->applied = 1;
1121 }
1122
1123 static void
beziershape_corner_change_revert(struct CornerChange * change,DiaObject * obj)1124 beziershape_corner_change_revert(struct CornerChange *change, DiaObject *obj)
1125 {
1126 BezierShape *bez = (BezierShape *)obj;
1127 int handle_nr = get_handle_nr(bez, change->handle);
1128 int comp_nr = get_major_nr(handle_nr);
1129
1130 bez->points[comp_nr].p2 = change->point_left;
1131 if (comp_nr == bez->numpoints - 1)
1132 bez->points[1].p1 = change->point_right;
1133 else
1134 bez->points[comp_nr+1].p1 = change->point_right;
1135 bez->corner_types[comp_nr] = change->old_type;
1136 if (comp_nr == 0)
1137 bez->corner_types[bez->numpoints-1] = change->new_type;
1138 if (comp_nr == bez->numpoints - 1)
1139 bez->corner_types[0] = change->new_type;
1140
1141 change->applied = 0;
1142 }
1143
1144 static ObjectChange *
beziershape_create_corner_change(BezierShape * bez,Handle * handle,Point * point_left,Point * point_right,BezCornerType old_corner_type,BezCornerType new_corner_type)1145 beziershape_create_corner_change(BezierShape *bez, Handle *handle,
1146 Point *point_left, Point *point_right,
1147 BezCornerType old_corner_type,
1148 BezCornerType new_corner_type)
1149 {
1150 struct CornerChange *change;
1151
1152 change = g_new(struct CornerChange, 1);
1153
1154 change->obj_change.apply =
1155 (ObjectChangeApplyFunc)beziershape_corner_change_apply;
1156 change->obj_change.revert =
1157 (ObjectChangeRevertFunc)beziershape_corner_change_revert;
1158 change->obj_change.free = (ObjectChangeFreeFunc)NULL;
1159
1160 change->old_type = old_corner_type;
1161 change->new_type = new_corner_type;
1162 change->applied = 1;
1163
1164 change->handle = handle;
1165 change->point_left = *point_left;
1166 change->point_right = *point_right;
1167
1168 return (ObjectChange *)change;
1169 }
1170