2006-12-03 Dimitris Glezos <dimitris@glezos.com>
[dia.git] / lib / beziershape.c
blobc07a68ace9d152cc31addc96175a0580e1cbd208
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
4 * beziershape.c - code to help implement bezier shapes
5 * Copyright (C) 2000 James Henstridge
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.
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.
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.
22 #include <config.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <string.h> /* memcpy() */
29 #include "beziershape.h"
30 #include "message.h"
31 #include "diarenderer.h"
33 #define HANDLE_BEZMAJOR (HANDLE_CUSTOM1)
34 #define HANDLE_LEFTCTRL (HANDLE_CUSTOM2)
35 #define HANDLE_RIGHTCTRL (HANDLE_CUSTOM3)
37 enum change_type {
38 TYPE_ADD_POINT,
39 TYPE_REMOVE_POINT
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.
47 struct PointChange {
48 ObjectChange obj_change;
50 enum change_type type;
51 int applied;
53 BezPoint point;
54 BezCornerType corner_type;
55 int pos;
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;
63 struct CornerChange {
64 ObjectChange obj_change;
65 /* Only one kind of corner_change */
66 int applied;
68 Handle *handle;
69 /* Old places when SET_CORNER_TYPE is applied */
70 Point point_left, point_right;
71 BezCornerType old_type, new_type;
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);
86 static void new_handles_and_connections(BezierShape *bezier, int num_points);
88 static void setup_handle(Handle *handle, int handle_id)
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;
100 static int get_handle_nr(BezierShape *bezier, Handle *handle)
102 int i = 0;
103 for (i = 0; i < bezier->object.num_handles; i++) {
104 if (bezier->object.handles[i] == handle)
105 return i;
107 return -1;
110 #define get_comp_nr(hnum) ((int)(hnum)/3+1)
111 #define get_major_nr(hnum) (((int)(hnum)+2)/3)
113 ObjectChange *
114 beziershape_move_handle(BezierShape *bezier, Handle *handle,
115 Point *to, ConnectionPoint *cp,
116 HandleMoveReason reason, ModifierKeys modifiers)
118 int handle_nr, comp_nr, next_nr, prev_nr;
119 Point delta, pt;
121 delta = *to;
122 point_sub(&delta, &handle->pos);
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;
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);
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;
158 pt = bezier->points[next_nr].p1;
159 point_sub(&pt, &bezier->points[comp_nr].p3);
160 len = point_len(&pt);
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;
169 point_scale(&pt, len);
170 point_add(&pt, &bezier->points[comp_nr].p3);
171 bezier->points[next_nr].p1 = pt;
172 break;
174 case BEZ_CORNER_CUSP:
175 /* no mirror point movement required */
176 break;
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;
191 pt = bezier->points[prev_nr].p2;
192 point_sub(&pt, &bezier->points[prev_nr].p3);
193 len = point_len(&pt);
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;
202 point_scale(&pt, len);
203 point_add(&pt, &bezier->points[prev_nr].p3);
204 bezier->points[prev_nr].p2 = pt;
205 break;
207 case BEZ_CORNER_CUSP:
208 /* no mirror point movement required */
209 break;
211 break;
212 default:
213 message_error("Internal error in beziershape_move_handle.");
214 break;
216 return NULL;
219 ObjectChange*
220 beziershape_move(BezierShape *bezier, Point *to)
222 Point p;
223 int i;
225 p = *to;
226 point_sub(&p, &bezier->points[0].p1);
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);
235 return NULL;
239 beziershape_closest_segment(BezierShape *bezier, Point *point, real line_width)
241 Point last;
242 int i;
243 real dist = G_MAXDOUBLE;
244 int closest;
246 closest = 0;
247 last = bezier->points[0].p1;
248 for (i = 1; i < bezier->numpoints; i++) {
249 real new_dist = distance_bez_seg_point(&last, &bezier->points[i].p1,
250 &bezier->points[i].p2, &bezier->points[i].p3,
251 line_width, point);
252 if (new_dist < dist) {
253 dist = new_dist;
254 closest = i;
256 last = bezier->points[i].p3;
258 return closest;
261 Handle *
262 beziershape_closest_handle(BezierShape *bezier, Point *point)
264 int i, hn;
265 real dist = G_MAXDOUBLE;
266 Handle *closest = NULL;
268 for (i = 1, hn = 0; i < bezier->numpoints; i++, hn++) {
269 real new_dist;
271 new_dist = distance_point_point( point, &bezier->points[i].p1);
272 if (new_dist < dist) {
273 dist = new_dist;
274 closest = bezier->object.handles[hn];
276 hn++;
278 new_dist = distance_point_point( point, &bezier->points[i].p2);
279 if (new_dist < dist) {
280 dist = new_dist;
281 closest = bezier->object.handles[hn];
284 hn++;
285 new_dist = distance_point_point( point, &bezier->points[i].p3);
286 if (new_dist < dist) {
287 dist = new_dist;
288 closest = bezier->object.handles[hn];
291 return closest;
294 Handle *
295 beziershape_closest_major_handle(BezierShape *bezier, Point *point)
297 Handle *closest = beziershape_closest_handle(bezier, point);
298 int pos = get_major_nr(get_handle_nr(bezier, closest));
300 if (pos == 0)
301 pos = bezier->numpoints - 1;
302 return bezier->object.handles[3*pos - 1];
305 real
306 beziershape_distance_from(BezierShape *bezier, Point *point, real line_width)
308 return distance_bez_shape_point(bezier->points, bezier->numpoints,
309 line_width, point);
312 static void
313 add_handles(BezierShape *bezier, int pos, BezPoint *point,
314 BezCornerType corner_type, Handle *handle1,
315 Handle *handle2, Handle *handle3,
316 ConnectionPoint *cp1, ConnectionPoint *cp2)
318 int i, next;
319 DiaObject *obj;
321 g_assert(pos >= 1);
322 g_assert(pos <= bezier->numpoints);
324 obj = (DiaObject *)bezier;
325 bezier->numpoints++;
326 next = pos + 1;
327 if (pos == bezier->numpoints - 1)
328 next = 1;
329 bezier->points = g_realloc(bezier->points,
330 bezier->numpoints * sizeof(BezPoint));
331 bezier->corner_types = g_realloc(bezier->corner_types,
332 bezier->numpoints * sizeof(BezCornerType));
334 for (i = bezier->numpoints - 1; i > pos; i--) {
335 bezier->points[i] = bezier->points[i-1];
336 bezier->corner_types[i] =bezier->corner_types[i-1];
338 bezier->points[pos] = *point;
339 bezier->points[pos].p1 = bezier->points[next].p1;
340 bezier->points[next].p1 = point->p1;
341 if (pos == bezier->numpoints - 1)
342 bezier->points[0].p1 = bezier->points[0].p3 = bezier->points[pos].p3;
343 bezier->corner_types[pos] = corner_type;
344 object_add_handle_at((DiaObject*)bezier, handle1, 3*pos-3);
345 object_add_handle_at((DiaObject*)bezier, handle2, 3*pos-2);
346 object_add_handle_at((DiaObject*)bezier, handle3, 3*pos-1);
347 object_add_connectionpoint_at((DiaObject *)bezier, cp1, 2*pos-2);
348 object_add_connectionpoint_at((DiaObject *)bezier, cp2, 2*pos-1);
351 static void
352 remove_handles(BezierShape *bezier, int pos)
354 int i;
355 DiaObject *obj;
356 Handle *old_handle1, *old_handle2, *old_handle3;
357 ConnectionPoint *old_cp1, *old_cp2;
358 Point tmppoint;
359 Point controlvector;
361 g_assert(pos > 0);
362 g_assert(pos < bezier->numpoints);
364 obj = (DiaObject *)bezier;
366 /* delete the points */
367 bezier->numpoints--;
368 tmppoint = bezier->points[pos].p1;
369 if (pos == bezier->numpoints) {
370 controlvector = bezier->points[pos-1].p3;
371 point_sub(&controlvector, &bezier->points[pos].p1);
373 for (i = pos; i < bezier->numpoints; i++) {
374 bezier->points[i] = bezier->points[i+1];
375 bezier->corner_types[i] = bezier->corner_types[i+1];
377 bezier->points[pos].p1 = tmppoint;
378 if (pos == bezier->numpoints) {
379 /* If this was the last point, we also need to move points[0] and
380 the control point in points[1]. */
381 bezier->points[0].p1 = bezier->points[bezier->numpoints-1].p3;
382 bezier->points[1].p1 = bezier->points[0].p1;
383 point_sub(&bezier->points[1].p1, &controlvector);
385 bezier->points = g_realloc(bezier->points,
386 bezier->numpoints * sizeof(BezPoint));
387 bezier->corner_types = g_realloc(bezier->corner_types,
388 bezier->numpoints * sizeof(BezCornerType));
390 old_handle1 = obj->handles[3*pos-3];
391 old_handle2 = obj->handles[3*pos-2];
392 old_handle3 = obj->handles[3*pos-1];
393 object_remove_handle(&bezier->object, old_handle1);
394 object_remove_handle(&bezier->object, old_handle2);
395 object_remove_handle(&bezier->object, old_handle3);
396 old_cp1 = obj->connections[2*pos-2];
397 old_cp2 = obj->connections[2*pos-1];
398 object_remove_connectionpoint(&bezier->object, old_cp1);
399 object_remove_connectionpoint(&bezier->object, old_cp2);
403 /* Add a point by splitting segment into two, putting the new point at
404 'point' or, if NULL, in the middle */
405 ObjectChange *
406 beziershape_add_segment(BezierShape *bezier, int segment, Point *point)
408 BezPoint realpoint;
409 BezCornerType corner_type = BEZ_CORNER_SYMMETRIC;
410 Handle *new_handle1, *new_handle2, *new_handle3;
411 ConnectionPoint *new_cp1, *new_cp2;
412 Point startpoint;
413 Point other;
415 if (segment != 1)
416 startpoint = bezier->points[segment-1].p3;
417 else
418 startpoint = bezier->points[0].p1;
419 other = bezier->points[segment].p3;
420 if (point == NULL) {
421 realpoint.p1.x = (startpoint.x + other.x)/6;
422 realpoint.p1.y = (startpoint.y + other.y)/6;
423 realpoint.p2.x = (startpoint.x + other.x)/3;
424 realpoint.p2.y = (startpoint.y + other.y)/3;
425 realpoint.p3.x = (startpoint.x + other.x)/2;
426 realpoint.p3.y = (startpoint.y + other.y)/2;
427 } else {
428 realpoint.p2.x = point->x+(startpoint.x-other.x)/6;
429 realpoint.p2.y = point->y+(startpoint.y-other.y)/6;
431 realpoint.p3 = *point;
432 /* this really goes into the next segment ... */
433 realpoint.p1.x = point->x-(startpoint.x-other.x)/6;
434 realpoint.p1.y = point->y-(startpoint.y-other.y)/6;
436 realpoint.type = BEZ_CURVE_TO;
438 new_handle1 = g_new(Handle, 1);
439 new_handle2 = g_new(Handle, 1);
440 new_handle3 = g_new(Handle, 1);
441 setup_handle(new_handle1, HANDLE_RIGHTCTRL);
442 setup_handle(new_handle2, HANDLE_LEFTCTRL);
443 setup_handle(new_handle3, HANDLE_BEZMAJOR);
444 new_cp1 = g_new0(ConnectionPoint, 1);
445 new_cp2 = g_new0(ConnectionPoint, 1);
446 new_cp1->object = &bezier->object;
447 new_cp2->object = &bezier->object;
448 add_handles(bezier, segment, &realpoint, corner_type,
449 new_handle1, new_handle2, new_handle3, new_cp1, new_cp2);
450 return beziershape_create_point_change(bezier, TYPE_ADD_POINT,
451 &realpoint, corner_type, segment,
452 new_handle1, new_handle2, new_handle3,
453 new_cp1, new_cp2);
456 ObjectChange *
457 beziershape_remove_segment(BezierShape *bezier, int pos)
459 Handle *old_handle1, *old_handle2, *old_handle3;
460 ConnectionPoint *old_cp1, *old_cp2;
461 BezPoint old_point;
462 BezCornerType old_ctype;
464 g_assert(pos > 0);
465 g_assert(bezier->numpoints > 2);
466 g_assert(pos < bezier->numpoints);
468 old_handle1 = bezier->object.handles[3*pos-3];
469 old_handle2 = bezier->object.handles[3*pos-2];
470 old_handle3 = bezier->object.handles[3*pos-1];
471 old_point = bezier->points[pos];
472 old_ctype = bezier->corner_types[pos];
474 old_cp1 = bezier->object.connections[2*pos-2];
475 old_cp2 = bezier->object.connections[2*pos-1];
477 object_unconnect((DiaObject *)bezier, old_handle1);
478 object_unconnect((DiaObject *)bezier, old_handle2);
479 object_unconnect((DiaObject *)bezier, old_handle3);
481 remove_handles(bezier, pos);
483 beziershape_update_data(bezier);
485 return beziershape_create_point_change(bezier, TYPE_REMOVE_POINT,
486 &old_point, old_ctype, pos,
487 old_handle1, old_handle2, old_handle3,
488 old_cp1, old_cp2);
491 static void
492 beziershape_straighten_corner(BezierShape *bez, int comp_nr) {
493 int next_nr;
495 if (comp_nr == 0) comp_nr = bez->numpoints - 1;
496 next_nr = comp_nr + 1;
497 if (comp_nr == bez->numpoints - 1)
498 next_nr = 1;
499 /* Neat thing would be to have the kind of straigthening depend on
500 which handle was chosen: Mid-handle does average, other leaves that
501 handle where it is. */
502 bez->points[0].p3 = bez->points[0].p1;
503 switch (bez->corner_types[comp_nr]) {
504 case BEZ_CORNER_SYMMETRIC: {
505 Point pt1, pt2;
507 pt1 = bez->points[comp_nr].p3;
508 point_sub(&pt1, &bez->points[comp_nr].p2);
509 pt2 = bez->points[comp_nr].p3;
510 point_sub(&pt2, &bez->points[next_nr].p1);
511 point_scale(&pt2, -1.0);
512 point_add(&pt1, &pt2);
513 point_scale(&pt1, 0.5);
514 pt2 = pt1;
515 point_scale(&pt1, -1.0);
516 point_add(&pt1, &bez->points[comp_nr].p3);
517 point_add(&pt2, &bez->points[comp_nr].p3);
518 bez->points[comp_nr].p2 = pt1;
519 bez->points[next_nr].p1 = pt2;
520 beziershape_update_data(bez);
522 break;
523 case BEZ_CORNER_SMOOTH: {
524 Point pt1, pt2;
525 real len1, len2;
526 pt1 = bez->points[comp_nr].p3;
527 point_sub(&pt1, &bez->points[comp_nr].p2);
528 pt2 = bez->points[comp_nr].p3;
529 point_sub(&pt2, &bez->points[next_nr].p1);
530 len1 = point_len(&pt1);
531 len2 = point_len(&pt2);
532 point_scale(&pt2, -1.0);
533 if (len1 > 0)
534 point_normalize(&pt1);
535 if (len2 > 0)
536 point_normalize(&pt2);
537 point_add(&pt1, &pt2);
538 point_scale(&pt1, 0.5);
539 pt2 = pt1;
540 point_scale(&pt1, -len1);
541 point_add(&pt1, &bez->points[comp_nr].p3);
542 point_scale(&pt2, len2);
543 point_add(&pt2, &bez->points[comp_nr].p3);
544 bez->points[comp_nr].p2 = pt1;
545 bez->points[next_nr].p1 = pt2;
546 beziershape_update_data(bez);
548 break;
549 case BEZ_CORNER_CUSP:
550 break;
552 bez->points[0].p1 = bez->points[0].p3;
555 ObjectChange *
556 beziershape_set_corner_type(BezierShape *bez, Handle *handle,
557 BezCornerType corner_type)
559 Handle *mid_handle = NULL;
560 Point old_left, old_right;
561 int old_type;
562 int handle_nr, comp_nr;
564 handle_nr = get_handle_nr(bez, handle);
566 switch (handle->id) {
567 case HANDLE_BEZMAJOR:
568 mid_handle = handle;
569 break;
570 case HANDLE_LEFTCTRL:
571 handle_nr++;
572 if (handle_nr == bez->object.num_handles) handle_nr = 0;
573 mid_handle = bez->object.handles[handle_nr];
574 break;
575 case HANDLE_RIGHTCTRL:
576 handle_nr--;
577 if (handle_nr < 0) handle_nr = bez->object.num_handles - 1;
578 mid_handle = bez->object.handles[handle_nr];
579 break;
580 default:
581 g_assert_not_reached();
582 break;
585 comp_nr = get_major_nr(handle_nr);
587 old_type = bez->corner_types[comp_nr];
588 old_left = bez->points[comp_nr].p2;
589 if (comp_nr == bez->numpoints - 1)
590 old_right = bez->points[1].p1;
591 else
592 old_right = bez->points[comp_nr+1].p1;
594 #if 0
595 g_message("Setting corner type on segment %d to %s", comp_nr,
596 corner_type == BEZ_CORNER_SYMMETRIC ? "symmetric" :
597 corner_type == BEZ_CORNER_SMOOTH ? "smooth" : "cusp");
598 #endif
599 bez->corner_types[comp_nr] = corner_type;
600 if (comp_nr == 0)
601 bez->corner_types[bez->numpoints-1] = corner_type;
602 else if (comp_nr == bez->numpoints - 1)
603 bez->corner_types[0] = corner_type;
605 beziershape_straighten_corner(bez, comp_nr);
607 return beziershape_create_corner_change(bez, mid_handle, &old_left,
608 &old_right, old_type, corner_type);
611 void
612 beziershape_update_data(BezierShape *bezier)
614 int i;
615 Point last;
616 Point minp, maxp;
618 DiaObject *obj = &bezier->object;
620 /* handle the case of whole points array update (via set_prop) */
621 if (3*(bezier->numpoints-1) != obj->num_handles ||
622 2*(bezier->numpoints-1) + 1 != obj->num_connections) {
623 object_unconnect_all(obj); /* too drastic ? */
625 /* delete the old ones */
626 for (i = 0; i < obj->num_handles; i++)
627 g_free(obj->handles[i]);
628 g_free(obj->handles);
629 for (i = 0; i < obj->num_connections; i++);
630 g_free(obj->connections[i]);
631 g_free(obj->connections);
633 obj->num_handles = 3*(bezier->numpoints-1);
634 obj->handles = g_new(Handle*, obj->num_handles);
635 obj->num_connections = 2*(bezier->numpoints-1) + 1;
636 obj->connections = g_new(ConnectionPoint *, obj->num_connections);
638 new_handles_and_connections(bezier, bezier->numpoints);
640 bezier->corner_types = g_realloc(bezier->corner_types, bezier->numpoints*sizeof(BezCornerType));
641 for (i = 0; i < bezier->numpoints; i++)
642 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
645 /* Update handles: */
646 bezier->points[0].p3 = bezier->points[0].p1;
647 for (i = 1; i < bezier->numpoints; i++) {
648 obj->handles[3*i-3]->pos = bezier->points[i].p1;
649 obj->handles[3*i-2]->pos = bezier->points[i].p2;
650 obj->handles[3*i-1]->pos = bezier->points[i].p3;
653 /* Update connection points: */
654 last = bezier->points[0].p1;
655 for (i = 1; i < bezier->numpoints; i++) {
656 Point slopepoint1, slopepoint2;
657 slopepoint1 = bezier->points[i].p1;
658 point_sub(&slopepoint1, &last);
659 point_scale(&slopepoint1, .5);
660 point_add(&slopepoint1, &last);
661 slopepoint2 = bezier->points[i].p2;
662 point_sub(&slopepoint2, &bezier->points[i].p3);
663 point_scale(&slopepoint2, .5);
664 point_add(&slopepoint2, &bezier->points[i].p3);
666 obj->connections[2*i-2]->pos = last;
667 obj->connections[2*i-2]->directions =
668 find_slope_directions(last, bezier->points[i].p1);
669 obj->connections[2*i-1]->pos.x =
670 (last.x + 3*bezier->points[i].p1.x + 3*bezier->points[i].p2.x +
671 bezier->points[i].p3.x)/8;
672 obj->connections[2*i-1]->pos.y =
673 (last.y + 3*bezier->points[i].p1.y + 3*bezier->points[i].p2.y +
674 bezier->points[i].p3.y)/8;
675 obj->connections[2*i-1]->directions =
676 find_slope_directions(slopepoint1, slopepoint2);
677 last = bezier->points[i].p3;
680 /* Find the middle of the object (or some approximation at least) */
681 minp = maxp = bezier->points[0].p1;
682 for (i = 1; i < bezier->numpoints; i++) {
683 Point p = bezier->points[i].p3;
684 if (p.x < minp.x) minp.x = p.x;
685 if (p.x > maxp.x) maxp.x = p.x;
686 if (p.y < minp.y) minp.y = p.y;
687 if (p.y > maxp.y) maxp.y = p.y;
689 obj->connections[obj->num_connections-1]->pos.x = (minp.x + maxp.x) / 2;
690 obj->connections[obj->num_connections-1]->pos.y = (minp.y + maxp.y) / 2;
691 obj->connections[obj->num_connections-1]->directions = DIR_ALL;
694 void
695 beziershape_update_boundingbox(BezierShape *bezier)
697 ElementBBExtras *extra;
698 PolyBBExtras pextra;
700 g_assert(bezier != NULL);
702 extra = &bezier->extra_spacing;
703 pextra.start_trans = pextra.end_trans =
704 pextra.start_long = pextra.end_long = 0;
705 pextra.middle_trans = extra->border_trans;
707 polybezier_bbox(&bezier->points[0],
708 bezier->numpoints,
709 &pextra, TRUE,
710 &bezier->object.bounding_box);
713 void
714 beziershape_simple_draw(BezierShape *bezier, DiaRenderer *renderer, real width)
716 BezPoint *points;
718 g_assert(bezier != NULL);
719 g_assert(renderer != NULL);
721 points = &bezier->points[0];
723 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
724 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
725 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_ROUND);
726 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
728 DIA_RENDERER_GET_CLASS(renderer)->fill_bezier(renderer, points, bezier->numpoints,&color_white);
729 DIA_RENDERER_GET_CLASS(renderer)->draw_bezier(renderer, points, bezier->numpoints,&color_black);
732 void
733 beziershape_draw_control_lines(BezierShape *bez, DiaRenderer *renderer)
735 Color line_colour = {0.0, 0.0, 0.6};
736 Point startpoint;
737 int i;
739 /* setup renderer ... */
740 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, 0);
741 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_DOTTED);
742 DIA_RENDERER_GET_CLASS(renderer)->set_dashlength(renderer, 1);
743 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_MITER);
744 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
746 startpoint = bez->points[0].p1;
747 for (i = 1; i < bez->numpoints; i++) {
748 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &startpoint, &bez->points[i].p1,
749 &line_colour);
750 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &bez->points[i].p2, &bez->points[i].p3,
751 &line_colour);
752 startpoint = bez->points[i].p3;
756 static void
757 new_handles_and_connections(BezierShape *bezier, int num_points)
759 DiaObject *obj;
760 int i;
762 obj = &bezier->object;
764 for (i = 0; i < num_points-1; i++) {
765 obj->handles[3*i] = g_new(Handle, 1);
766 obj->handles[3*i+1] = g_new(Handle, 1);
767 obj->handles[3*i+2] = g_new(Handle, 1);
769 obj->handles[3*i]->connect_type = HANDLE_NONCONNECTABLE;
770 obj->handles[3*i]->connected_to = NULL;
771 obj->handles[3*i]->type = HANDLE_MINOR_CONTROL;
772 obj->handles[3*i]->id = HANDLE_RIGHTCTRL;
774 obj->handles[3*i+1]->connect_type = HANDLE_NONCONNECTABLE;
775 obj->handles[3*i+1]->connected_to = NULL;
776 obj->handles[3*i+1]->type = HANDLE_MINOR_CONTROL;
777 obj->handles[3*i+1]->id = HANDLE_LEFTCTRL;
779 obj->handles[3*i+2]->connect_type = HANDLE_NONCONNECTABLE;
780 obj->handles[3*i+2]->connected_to = NULL;
781 obj->handles[3*i+2]->type = HANDLE_MAJOR_CONTROL;
782 obj->handles[3*i+2]->id = HANDLE_BEZMAJOR;
784 obj->connections[2*i] = g_new0(ConnectionPoint, 1);
785 obj->connections[2*i+1] = g_new0(ConnectionPoint, 1);
786 obj->connections[2*i]->object = obj;
787 obj->connections[2*i+1]->object = obj;
788 obj->connections[2*i]->flags = 0;
789 obj->connections[2*i+1]->flags = 0;
792 /** Main point */
793 obj->connections[obj->num_connections-1] = g_new0(ConnectionPoint, 1);
794 obj->connections[obj->num_connections-1]->object = obj;
795 obj->connections[obj->num_connections-1]->flags = CP_FLAGS_MAIN;
798 void
799 beziershape_init(BezierShape *bezier, int num_points)
801 DiaObject *obj;
802 int i;
804 obj = &bezier->object;
806 object_init(obj, 3*(num_points-1), 2*(num_points-1) + 1);
808 bezier->numpoints = num_points;
810 bezier->points = g_new(BezPoint, num_points);
811 bezier->points[0].type = BEZ_MOVE_TO;
812 bezier->corner_types = g_new(BezCornerType, num_points);
813 for (i = 1; i < num_points; i++) {
814 bezier->points[i].type = BEZ_CURVE_TO;
815 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
818 new_handles_and_connections(bezier, num_points);
820 /* The points are not assigned at this point, so don't try to use
821 them */
822 /* beziershape_update_data(bezier);*/
826 /** This function does *not* set up handles */
827 void
828 beziershape_set_points(BezierShape *bez, int num_points, BezPoint *points)
830 int i;
832 bez->numpoints = num_points;
834 if (bez->points)
835 g_free(bez->points);
837 bez->points = g_malloc((bez->numpoints)*sizeof(BezPoint));
839 for (i=0;i<bez->numpoints;i++) {
840 bez->points[i] = points[i];
844 void
845 beziershape_copy(BezierShape *from, BezierShape *to)
847 int i;
848 DiaObject *toobj, *fromobj;
850 toobj = &to->object;
851 fromobj = &from->object;
853 object_copy(fromobj, toobj);
855 to->numpoints = from->numpoints;
857 to->points = g_new(BezPoint, to->numpoints);
858 to->corner_types = g_new(BezCornerType, to->numpoints);
860 for (i = 0; i < to->numpoints; i++) {
861 to->points[i] = from->points[i];
862 to->corner_types[i] = from->corner_types[i];
865 for (i = 0; i < toobj->num_handles; i++) {
866 toobj->handles[i] = g_new(Handle, 1);
867 setup_handle(toobj->handles[i], fromobj->handles[i]->id);
869 for (i = 0; i < toobj->num_connections; i++) {
870 toobj->connections[i] = g_new0(ConnectionPoint, 1);
871 toobj->connections[i]->object = &to->object;
872 toobj->connections[i]->flags = fromobj->connections[i]->flags;
875 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
876 beziershape_update_data(to);
879 void
880 beziershape_destroy(BezierShape *bezier)
882 int i;
883 Handle **temp_handles;
884 ConnectionPoint **temp_cps;
886 /* Need to store these temporary since object.handles is
887 freed by object_destroy() */
888 temp_handles = g_new(Handle *, bezier->object.num_handles);
889 for (i = 0; i < bezier->object.num_handles; i++)
890 temp_handles[i] = bezier->object.handles[i];
892 temp_cps = g_new(ConnectionPoint *, bezier->object.num_connections);
893 for (i = 0; i < bezier->object.num_connections; i++)
894 temp_cps[i] = bezier->object.connections[i];
896 object_destroy(&bezier->object);
898 for (i = 0; i < bezier->object.num_handles; i++)
899 g_free(temp_handles[i]);
900 g_free(temp_handles);
902 for (i = 0; i < bezier->object.num_connections; i++)
903 g_free(temp_cps[i]);
904 g_free(temp_cps);
906 g_free(bezier->points);
907 g_free(bezier->corner_types);
911 void
912 beziershape_save(BezierShape *bezier, ObjectNode obj_node)
914 int i;
915 AttributeNode attr;
917 object_save(&bezier->object, obj_node);
919 attr = new_attribute(obj_node, "bez_points");
921 data_add_point(attr, &bezier->points[0].p1);
922 for (i = 1; i < bezier->numpoints; i++) {
923 data_add_point(attr, &bezier->points[i].p1);
924 data_add_point(attr, &bezier->points[i].p2);
925 if (i < bezier->numpoints - 1)
926 data_add_point(attr, &bezier->points[i].p3);
929 attr = new_attribute(obj_node, "corner_types");
930 for (i = 0; i < bezier->numpoints; i++)
931 data_add_enum(attr, bezier->corner_types[i]);
934 void
935 beziershape_load(BezierShape *bezier, ObjectNode obj_node)
937 int i;
938 AttributeNode attr;
939 DataNode data;
941 DiaObject *obj = &bezier->object;
943 object_load(obj, obj_node);
945 attr = object_find_attribute(obj_node, "bez_points");
947 if (attr != NULL)
948 bezier->numpoints = attribute_num_data(attr) / 3 + 1;
949 else
950 bezier->numpoints = 0;
952 object_init(obj, 3 * (bezier->numpoints - 1),
953 2 * (bezier->numpoints - 1) + 1);
955 data = attribute_first_data(attr);
956 if (bezier->numpoints != 0) {
957 bezier->points = g_new(BezPoint, bezier->numpoints);
958 bezier->points[0].type = BEZ_MOVE_TO;
959 data_point(data, &bezier->points[0].p1);
960 bezier->points[0].p3 = bezier->points[0].p1;
961 data = data_next(data);
963 for (i = 1; i < bezier->numpoints; i++) {
964 bezier->points[i].type = BEZ_CURVE_TO;
965 data_point(data, &bezier->points[i].p1);
966 data = data_next(data);
967 data_point(data, &bezier->points[i].p2);
968 data = data_next(data);
969 if (i < bezier->numpoints - 1) {
970 data_point(data, &bezier->points[i].p3);
971 data = data_next(data);
972 } else
973 bezier->points[i].p3 = bezier->points[0].p1;
977 bezier->corner_types = g_new(BezCornerType, bezier->numpoints);
978 attr = object_find_attribute(obj_node, "corner_types");
979 if (!attr || attribute_num_data(attr) != bezier->numpoints) {
980 for (i = 0; i < bezier->numpoints; i++)
981 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
982 } else {
983 data = attribute_first_data(attr);
984 for (i = 0; i < bezier->numpoints; i++) {
985 bezier->corner_types[i] = data_enum(data);
986 data = data_next(data);
990 for (i = 0; i < bezier->numpoints - 1; i++) {
991 obj->handles[3*i] = g_new(Handle, 1);
992 obj->handles[3*i+1] = g_new(Handle, 1);
993 obj->handles[3*i+2] = g_new(Handle, 1);
995 setup_handle(obj->handles[3*i], HANDLE_RIGHTCTRL);
996 setup_handle(obj->handles[3*i+1], HANDLE_LEFTCTRL);
997 setup_handle(obj->handles[3*i+2], HANDLE_BEZMAJOR);
999 for (i = 0; i < obj->num_connections; i++) {
1000 obj->connections[i] = g_new0(ConnectionPoint, 1);
1001 obj->connections[i]->object = obj;
1003 obj->connections[obj->num_connections-1]->flags = CP_FLAGS_MAIN;
1005 beziershape_update_data(bezier);
1008 static void
1009 beziershape_point_change_free(struct PointChange *change)
1011 if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
1012 (change->type==TYPE_REMOVE_POINT && change->applied) ){
1013 g_free(change->handle1);
1014 g_free(change->handle2);
1015 g_free(change->handle3);
1016 g_free(change->cp1);
1017 g_free(change->cp2);
1018 change->handle1 = NULL;
1019 change->handle2 = NULL;
1020 change->handle3 = NULL;
1021 change->cp1 = NULL;
1022 change->cp2 = NULL;
1026 static void
1027 beziershape_point_change_apply(struct PointChange *change, DiaObject *obj)
1029 change->applied = 1;
1030 switch (change->type) {
1031 case TYPE_ADD_POINT:
1032 add_handles((BezierShape *)obj, change->pos, &change->point,
1033 change->corner_type,
1034 change->handle1, change->handle2, change->handle3,
1035 change->cp1, change->cp2);
1036 break;
1037 case TYPE_REMOVE_POINT:
1038 object_unconnect(obj, change->handle1);
1039 object_unconnect(obj, change->handle2);
1040 object_unconnect(obj, change->handle3);
1041 remove_handles((BezierShape *)obj, change->pos);
1042 break;
1046 static void
1047 beziershape_point_change_revert(struct PointChange *change, DiaObject *obj)
1049 switch (change->type) {
1050 case TYPE_ADD_POINT:
1051 remove_handles((BezierShape *)obj, change->pos);
1052 break;
1053 case TYPE_REMOVE_POINT:
1054 add_handles((BezierShape *)obj, change->pos, &change->point,
1055 change->corner_type,
1056 change->handle1, change->handle2, change->handle3,
1057 change->cp1, change->cp2);
1058 break;
1060 change->applied = 0;
1063 static ObjectChange *
1064 beziershape_create_point_change(BezierShape *bezier, enum change_type type,
1065 BezPoint *point, BezCornerType corner_type,
1066 int pos,
1067 Handle *handle1, Handle *handle2,
1068 Handle *handle3,
1069 ConnectionPoint *cp1, ConnectionPoint *cp2)
1071 struct PointChange *change;
1073 change = g_new(struct PointChange, 1);
1075 change->obj_change.apply =
1076 (ObjectChangeApplyFunc)beziershape_point_change_apply;
1077 change->obj_change.revert =
1078 (ObjectChangeRevertFunc)beziershape_point_change_revert;
1079 change->obj_change.free =
1080 (ObjectChangeFreeFunc)beziershape_point_change_free;
1082 change->type = type;
1083 change->applied = 1;
1084 change->point = *point;
1085 change->corner_type = corner_type;
1086 change->pos = pos;
1087 change->handle1 = handle1;
1088 change->handle2 = handle2;
1089 change->handle3 = handle3;
1090 change->cp1 = cp1;
1091 change->cp2 = cp2;
1093 return (ObjectChange *)change;
1096 static void
1097 beziershape_corner_change_apply(struct CornerChange *change, DiaObject *obj)
1099 BezierShape *bez = (BezierShape *)obj;
1100 int handle_nr = get_handle_nr(bez, change->handle);
1101 int comp_nr = get_major_nr(handle_nr);
1103 beziershape_straighten_corner(bez, comp_nr);
1105 bez->corner_types[comp_nr] = change->new_type;
1106 if (comp_nr == 0)
1107 bez->corner_types[bez->numpoints-1] = change->new_type;
1108 if (comp_nr == bez->numpoints - 1)
1109 bez->corner_types[0] = change->new_type;
1111 change->applied = 1;
1114 static void
1115 beziershape_corner_change_revert(struct CornerChange *change, DiaObject *obj)
1117 BezierShape *bez = (BezierShape *)obj;
1118 int handle_nr = get_handle_nr(bez, change->handle);
1119 int comp_nr = get_major_nr(handle_nr);
1121 bez->points[comp_nr].p2 = change->point_left;
1122 if (comp_nr == bez->numpoints - 1)
1123 bez->points[1].p1 = change->point_right;
1124 else
1125 bez->points[comp_nr+1].p1 = change->point_right;
1126 bez->corner_types[comp_nr] = change->old_type;
1127 if (comp_nr == 0)
1128 bez->corner_types[bez->numpoints-1] = change->new_type;
1129 if (comp_nr == bez->numpoints - 1)
1130 bez->corner_types[0] = change->new_type;
1132 change->applied = 0;
1135 static ObjectChange *
1136 beziershape_create_corner_change(BezierShape *bez, Handle *handle,
1137 Point *point_left, Point *point_right,
1138 BezCornerType old_corner_type,
1139 BezCornerType new_corner_type)
1141 struct CornerChange *change;
1143 change = g_new(struct CornerChange, 1);
1145 change->obj_change.apply =
1146 (ObjectChangeApplyFunc)beziershape_corner_change_apply;
1147 change->obj_change.revert =
1148 (ObjectChangeRevertFunc)beziershape_corner_change_revert;
1149 change->obj_change.free = (ObjectChangeFreeFunc)NULL;
1151 change->old_type = old_corner_type;
1152 change->new_type = new_corner_type;
1153 change->applied = 1;
1155 change->handle = handle;
1156 change->point_left = *point_left;
1157 change->point_right = *point_right;
1159 return (ObjectChange *)change;