2004-01-17 Hans Breuer <hans@breuer.org>
[dia.git] / lib / beziershape.c
blobf3ca4e5848b133044ab4b7cf2775fca515cc3ebd
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)
46 struct PointChange {
47 ObjectChange obj_change;
49 enum change_type type;
50 int applied;
52 BezPoint point;
53 BezCornerType corner_type;
54 int pos;
56 /* owning ref when not applied for ADD_POINT
57 * owning ref when applied for REMOVE_POINT */
58 Handle *handle1, *handle2, *handle3;
59 ConnectionPoint *cp1, *cp2;
62 struct CornerChange {
63 ObjectChange obj_change;
64 /* Only one kind of corner_change */
65 int applied;
67 Handle *handle;
68 /* Old places when SET_CORNER_TYPE is applied */
69 Point point_left, point_right;
70 BezCornerType old_type, new_type;
73 static ObjectChange *
74 beziershape_create_point_change(BezierShape *bezier, enum change_type type,
75 BezPoint *point, BezCornerType corner_type,
76 int segment,
77 Handle *handle1, Handle *handle2, Handle *handle3,
78 ConnectionPoint *cp1, ConnectionPoint *cp2);
79 static ObjectChange *
80 beziershape_create_corner_change(BezierShape *bezier, Handle *handle,
81 Point *point_left, Point *point_right,
82 BezCornerType old_corner_type,
83 BezCornerType new_corner_type);
85 static void new_handles_and_connections(BezierShape *bezier, int num_points);
87 static void setup_handle(Handle *handle, int handle_id)
89 handle->id = handle_id;
90 handle->type =
91 (handle_id == HANDLE_BEZMAJOR) ?
92 HANDLE_MAJOR_CONTROL :
93 HANDLE_MINOR_CONTROL;
94 handle->connect_type = HANDLE_NONCONNECTABLE;
95 handle->connected_to = NULL;
99 static int get_handle_nr(BezierShape *bezier, Handle *handle)
101 int i = 0;
102 for (i = 0; i < bezier->object.num_handles; i++) {
103 if (bezier->object.handles[i] == handle)
104 return i;
106 return -1;
109 #define get_comp_nr(hnum) ((int)(hnum)/3+1)
110 #define get_major_nr(hnum) (((int)(hnum)+2)/3)
112 ObjectChange *
113 beziershape_move_handle(BezierShape *bezier, Handle *handle,
114 Point *to, ConnectionPoint *cp,
115 HandleMoveReason reason, ModifierKeys modifiers)
117 int handle_nr, comp_nr, next_nr, prev_nr;
118 Point delta, pt;
120 delta = *to;
121 point_sub(&delta, &handle->pos);
123 handle_nr = get_handle_nr(bezier, handle);
124 comp_nr = get_comp_nr(handle_nr);
125 next_nr = comp_nr + 1;
126 prev_nr = comp_nr - 1;
127 if (comp_nr == bezier->numpoints - 1)
128 next_nr = 1;
129 if (comp_nr == 1)
130 prev_nr = bezier->numpoints - 1;
132 switch(handle->id) {
133 case HANDLE_BEZMAJOR:
134 if (comp_nr == bezier->numpoints - 1) {
135 bezier->points[comp_nr].p3 = *to;
136 bezier->points[0].p1 = bezier->points[0].p3 = *to;
137 point_add(&bezier->points[comp_nr].p2, &delta);
138 point_add(&bezier->points[1].p1, &delta);
139 } else {
140 bezier->points[comp_nr].p3 = *to;
141 point_add(&bezier->points[comp_nr].p2, &delta);
142 point_add(&bezier->points[comp_nr+1].p1, &delta);
144 break;
145 case HANDLE_LEFTCTRL:
146 bezier->points[comp_nr].p2 = *to;
147 switch (bezier->corner_types[comp_nr]) {
148 case BEZ_CORNER_SYMMETRIC:
149 pt = bezier->points[comp_nr].p3;
150 point_sub(&pt, &bezier->points[comp_nr].p2);
151 point_add(&pt, &bezier->points[comp_nr].p3);
152 bezier->points[next_nr].p1 = pt;
153 break;
154 case BEZ_CORNER_SMOOTH: {
155 real len;
157 pt = bezier->points[next_nr].p1;
158 point_sub(&pt, &bezier->points[comp_nr].p3);
159 len = point_len(&pt);
161 pt = bezier->points[comp_nr].p3;
162 point_sub(&pt, &bezier->points[comp_nr].p2);
163 if (point_len(&pt) > 0)
164 point_normalize(&pt);
165 else {
166 pt.x = 1.0; pt.y = 0.0;
168 point_scale(&pt, len);
169 point_add(&pt, &bezier->points[comp_nr].p3);
170 bezier->points[next_nr].p1 = pt;
171 break;
173 case BEZ_CORNER_CUSP:
174 /* no mirror point movement required */
175 break;
177 break;
178 case HANDLE_RIGHTCTRL:
179 bezier->points[comp_nr].p1 = *to;
180 switch (bezier->corner_types[prev_nr]) {
181 case BEZ_CORNER_SYMMETRIC:
182 pt = bezier->points[prev_nr].p3;
183 point_sub(&pt, &bezier->points[comp_nr].p1);
184 point_add(&pt, &bezier->points[prev_nr].p3);
185 bezier->points[prev_nr].p2 = pt;
186 break;
187 case BEZ_CORNER_SMOOTH: {
188 real len;
190 pt = bezier->points[prev_nr].p2;
191 point_sub(&pt, &bezier->points[prev_nr].p3);
192 len = point_len(&pt);
194 pt = bezier->points[prev_nr].p3;
195 point_sub(&pt, &bezier->points[comp_nr].p1);
196 if (point_len(&pt) > 0)
197 point_normalize(&pt);
198 else {
199 pt.x = 1.0; pt.y = 0.0;
201 point_scale(&pt, len);
202 point_add(&pt, &bezier->points[prev_nr].p3);
203 bezier->points[prev_nr].p2 = pt;
204 break;
206 case BEZ_CORNER_CUSP:
207 /* no mirror point movement required */
208 break;
210 break;
211 default:
212 message_error("Internal error in beziershape_move_handle.");
213 break;
215 return NULL;
218 ObjectChange*
219 beziershape_move(BezierShape *bezier, Point *to)
221 Point p;
222 int i;
224 p = *to;
225 point_sub(&p, &bezier->points[0].p1);
227 bezier->points[0].p1 = bezier->points[0].p3 = *to;
228 for (i = 1; i < bezier->numpoints; i++) {
229 point_add(&bezier->points[i].p1, &p);
230 point_add(&bezier->points[i].p2, &p);
231 point_add(&bezier->points[i].p3, &p);
234 return NULL;
238 beziershape_closest_segment(BezierShape *bezier, Point *point, real line_width)
240 Point last;
241 int i;
242 real dist = G_MAXDOUBLE;
243 int closest;
245 closest = 0;
246 last = bezier->points[0].p1;
247 for (i = 1; i < bezier->numpoints; i++) {
248 real new_dist = distance_bez_seg_point(&last, &bezier->points[i].p1,
249 &bezier->points[i].p2, &bezier->points[i].p3,
250 line_width, point);
251 if (new_dist < dist) {
252 dist = new_dist;
253 closest = i;
255 last = bezier->points[i].p3;
257 return closest;
260 Handle *
261 beziershape_closest_handle(BezierShape *bezier, Point *point)
263 int i, hn;
264 real dist = G_MAXDOUBLE;
265 Handle *closest = NULL;
267 for (i = 1, hn = 0; i < bezier->numpoints; i++, hn++) {
268 real new_dist;
270 new_dist = distance_point_point( point, &bezier->points[i].p1);
271 if (new_dist < dist) {
272 dist = new_dist;
273 closest = bezier->object.handles[hn];
275 hn++;
277 new_dist = distance_point_point( point, &bezier->points[i].p2);
278 if (new_dist < dist) {
279 dist = new_dist;
280 closest = bezier->object.handles[hn];
283 hn++;
284 new_dist = distance_point_point( point, &bezier->points[i].p3);
285 if (new_dist < dist) {
286 dist = new_dist;
287 closest = bezier->object.handles[hn];
290 return closest;
293 Handle *
294 beziershape_closest_major_handle(BezierShape *bezier, Point *point)
296 Handle *closest = beziershape_closest_handle(bezier, point);
297 int pos = get_major_nr(get_handle_nr(bezier, closest));
299 if (pos == 0)
300 pos = bezier->numpoints - 1;
301 return bezier->object.handles[3*pos - 1];
304 real
305 beziershape_distance_from(BezierShape *bezier, Point *point, real line_width)
307 return distance_bez_shape_point(bezier->points, bezier->numpoints,
308 line_width, point);
311 static void
312 add_handles(BezierShape *bezier, int pos, BezPoint *point,
313 BezCornerType corner_type, Handle *handle1,
314 Handle *handle2, Handle *handle3,
315 ConnectionPoint *cp1, ConnectionPoint *cp2)
317 int i, next;
318 Object *obj;
320 g_assert(pos >= 1);
321 g_assert(pos <= bezier->numpoints);
323 obj = (Object *)bezier;
324 bezier->numpoints++;
325 next = pos + 1;
326 if (pos == bezier->numpoints - 1)
327 next = 1;
328 bezier->points = g_realloc(bezier->points,
329 bezier->numpoints * sizeof(BezPoint));
330 bezier->corner_types = g_realloc(bezier->corner_types,
331 bezier->numpoints * sizeof(BezCornerType));
333 for (i = bezier->numpoints - 1; i > pos; i--) {
334 bezier->points[i] = bezier->points[i-1];
335 bezier->corner_types[i] =bezier->corner_types[i-1];
337 bezier->points[pos] = *point;
338 bezier->points[pos].p1 = bezier->points[next].p1;
339 bezier->points[next].p1 = point->p1;
340 if (pos == bezier->numpoints - 1)
341 bezier->points[0].p1 = bezier->points[0].p3 = bezier->points[pos].p3;
342 bezier->corner_types[pos] = corner_type;
343 object_add_handle_at((Object*)bezier, handle1, 3*pos-3);
344 object_add_handle_at((Object*)bezier, handle2, 3*pos-2);
345 object_add_handle_at((Object*)bezier, handle3, 3*pos-1);
346 object_add_connectionpoint_at((Object *)bezier, cp1, 2*pos-1);
347 object_add_connectionpoint_at((Object *)bezier, cp2, 2*pos);
350 static void
351 remove_handles(BezierShape *bezier, int pos)
353 int i;
354 Object *obj;
355 Handle *old_handle1, *old_handle2, *old_handle3;
356 ConnectionPoint *old_cp1, *old_cp2;
357 Point tmppoint;
358 Point controlvector;
360 g_assert(pos > 0);
361 g_assert(pos < bezier->numpoints);
363 obj = (Object *)bezier;
365 /* delete the points */
366 bezier->numpoints--;
367 tmppoint = bezier->points[pos].p1;
368 if (pos == bezier->numpoints) {
369 controlvector = bezier->points[pos-1].p3;
370 point_sub(&controlvector, &bezier->points[pos].p1);
372 for (i = pos; i < bezier->numpoints; i++) {
373 bezier->points[i] = bezier->points[i+1];
374 bezier->corner_types[i] = bezier->corner_types[i+1];
376 bezier->points[pos].p1 = tmppoint;
377 if (pos == bezier->numpoints) {
378 /* If this was the last point, we also need to move points[0] and
379 the control point in points[1]. */
380 bezier->points[0].p1 = bezier->points[bezier->numpoints-1].p3;
381 bezier->points[1].p1 = bezier->points[0].p1;
382 point_sub(&bezier->points[1].p1, &controlvector);
384 bezier->points = g_realloc(bezier->points,
385 bezier->numpoints * sizeof(BezPoint));
386 bezier->corner_types = g_realloc(bezier->corner_types,
387 bezier->numpoints * sizeof(BezCornerType));
389 old_handle1 = obj->handles[3*pos-3];
390 old_handle2 = obj->handles[3*pos-2];
391 old_handle3 = obj->handles[3*pos-1];
392 object_remove_handle(&bezier->object, old_handle1);
393 object_remove_handle(&bezier->object, old_handle2);
394 object_remove_handle(&bezier->object, old_handle3);
395 old_cp1 = obj->connections[2*pos-2];
396 old_cp2 = obj->connections[2*pos-1];
397 object_remove_connectionpoint(&bezier->object, old_cp1);
398 object_remove_connectionpoint(&bezier->object, old_cp2);
402 /* Add a point by splitting segment into two, putting the new point at
403 'point' or, if NULL, in the middle */
404 ObjectChange *
405 beziershape_add_segment(BezierShape *bezier, int segment, Point *point)
407 BezPoint realpoint;
408 BezCornerType corner_type = BEZ_CORNER_SYMMETRIC;
409 Handle *new_handle1, *new_handle2, *new_handle3;
410 ConnectionPoint *new_cp1, *new_cp2;
411 Point startpoint;
412 Point other;
414 if (segment != 1)
415 startpoint = bezier->points[segment-1].p3;
416 else
417 startpoint = bezier->points[0].p1;
418 other = bezier->points[segment].p3;
419 if (point == NULL) {
420 realpoint.p1.x = (startpoint.x + other.x)/6;
421 realpoint.p1.y = (startpoint.y + other.y)/6;
422 realpoint.p2.x = (startpoint.x + other.x)/3;
423 realpoint.p2.y = (startpoint.y + other.y)/3;
424 realpoint.p3.x = (startpoint.x + other.x)/2;
425 realpoint.p3.y = (startpoint.y + other.y)/2;
426 } else {
427 realpoint.p2.x = point->x+(startpoint.x-other.x)/6;
428 realpoint.p2.y = point->y+(startpoint.y-other.y)/6;
430 realpoint.p3 = *point;
431 /* this really goes into the next segment ... */
432 realpoint.p1.x = point->x-(startpoint.x-other.x)/6;
433 realpoint.p1.y = point->y-(startpoint.y-other.y)/6;
435 realpoint.type = BEZ_CURVE_TO;
437 new_handle1 = g_new(Handle, 1);
438 new_handle2 = g_new(Handle, 1);
439 new_handle3 = g_new(Handle, 1);
440 setup_handle(new_handle1, HANDLE_RIGHTCTRL);
441 setup_handle(new_handle2, HANDLE_LEFTCTRL);
442 setup_handle(new_handle3, HANDLE_BEZMAJOR);
443 new_cp1 = g_new0(ConnectionPoint, 1);
444 new_cp2 = g_new0(ConnectionPoint, 1);
445 new_cp1->object = &bezier->object;
446 new_cp2->object = &bezier->object;
447 add_handles(bezier, segment, &realpoint, corner_type,
448 new_handle1, new_handle2, new_handle3, new_cp1, new_cp2);
449 return beziershape_create_point_change(bezier, TYPE_ADD_POINT,
450 &realpoint, corner_type, segment,
451 new_handle1, new_handle2, new_handle3,
452 new_cp1, new_cp2);
455 ObjectChange *
456 beziershape_remove_segment(BezierShape *bezier, int pos)
458 Handle *old_handle1, *old_handle2, *old_handle3;
459 ConnectionPoint *old_cp1, *old_cp2;
460 BezPoint old_point;
461 BezCornerType old_ctype;
463 g_assert(pos > 0);
464 g_assert(bezier->numpoints > 2);
465 g_assert(pos < bezier->numpoints);
467 old_handle1 = bezier->object.handles[3*pos-3];
468 old_handle2 = bezier->object.handles[3*pos-2];
469 old_handle3 = bezier->object.handles[3*pos-1];
470 old_point = bezier->points[pos];
471 old_ctype = bezier->corner_types[pos];
473 old_cp1 = bezier->object.connections[2*pos-2];
474 old_cp2 = bezier->object.connections[2*pos-1];
476 object_unconnect((Object *)bezier, old_handle1);
477 object_unconnect((Object *)bezier, old_handle2);
478 object_unconnect((Object *)bezier, old_handle3);
480 remove_handles(bezier, pos);
482 beziershape_update_data(bezier);
484 return beziershape_create_point_change(bezier, TYPE_REMOVE_POINT,
485 &old_point, old_ctype, pos,
486 old_handle1, old_handle2, old_handle3,
487 old_cp1, old_cp2);
490 static void
491 beziershape_straighten_corner(BezierShape *bez, int comp_nr) {
492 int next_nr;
494 if (comp_nr == 0) comp_nr = bez->numpoints - 1;
495 next_nr = comp_nr + 1;
496 if (comp_nr == bez->numpoints - 1)
497 next_nr = 1;
498 /* Neat thing would be to have the kind of straigthening depend on
499 which handle was chosen: Mid-handle does average, other leaves that
500 handle where it is. */
501 bez->points[0].p3 = bez->points[0].p1;
502 switch (bez->corner_types[comp_nr]) {
503 case BEZ_CORNER_SYMMETRIC: {
504 Point pt1, pt2;
506 pt1 = bez->points[comp_nr].p3;
507 point_sub(&pt1, &bez->points[comp_nr].p2);
508 pt2 = bez->points[comp_nr].p3;
509 point_sub(&pt2, &bez->points[next_nr].p1);
510 point_scale(&pt2, -1.0);
511 point_add(&pt1, &pt2);
512 point_scale(&pt1, 0.5);
513 pt2 = pt1;
514 point_scale(&pt1, -1.0);
515 point_add(&pt1, &bez->points[comp_nr].p3);
516 point_add(&pt2, &bez->points[comp_nr].p3);
517 bez->points[comp_nr].p2 = pt1;
518 bez->points[next_nr].p1 = pt2;
519 beziershape_update_data(bez);
521 break;
522 case BEZ_CORNER_SMOOTH: {
523 Point pt1, pt2;
524 real len1, len2;
525 pt1 = bez->points[comp_nr].p3;
526 point_sub(&pt1, &bez->points[comp_nr].p2);
527 pt2 = bez->points[comp_nr].p3;
528 point_sub(&pt2, &bez->points[next_nr].p1);
529 len1 = point_len(&pt1);
530 len2 = point_len(&pt2);
531 point_scale(&pt2, -1.0);
532 if (len1 > 0)
533 point_normalize(&pt1);
534 if (len2 > 0)
535 point_normalize(&pt2);
536 point_add(&pt1, &pt2);
537 point_scale(&pt1, 0.5);
538 pt2 = pt1;
539 point_scale(&pt1, -len1);
540 point_add(&pt1, &bez->points[comp_nr].p3);
541 point_scale(&pt2, len2);
542 point_add(&pt2, &bez->points[comp_nr].p3);
543 bez->points[comp_nr].p2 = pt1;
544 bez->points[next_nr].p1 = pt2;
545 beziershape_update_data(bez);
547 break;
548 case BEZ_CORNER_CUSP:
549 break;
551 bez->points[0].p1 = bez->points[0].p3;
554 ObjectChange *
555 beziershape_set_corner_type(BezierShape *bez, Handle *handle,
556 BezCornerType corner_type)
558 Handle *mid_handle = NULL;
559 Point old_left, old_right;
560 int old_type;
561 int handle_nr, comp_nr;
563 handle_nr = get_handle_nr(bez, handle);
565 switch (handle->id) {
566 case HANDLE_BEZMAJOR:
567 mid_handle = handle;
568 break;
569 case HANDLE_LEFTCTRL:
570 handle_nr++;
571 if (handle_nr == bez->object.num_handles) handle_nr = 0;
572 mid_handle = bez->object.handles[handle_nr];
573 break;
574 case HANDLE_RIGHTCTRL:
575 handle_nr--;
576 if (handle_nr < 0) handle_nr = bez->object.num_handles - 1;
577 mid_handle = bez->object.handles[handle_nr];
578 break;
579 default:
580 g_assert_not_reached();
581 break;
584 comp_nr = get_major_nr(handle_nr);
586 old_type = bez->corner_types[comp_nr];
587 old_left = bez->points[comp_nr].p2;
588 if (comp_nr == bez->numpoints - 1)
589 old_right = bez->points[1].p1;
590 else
591 old_right = bez->points[comp_nr+1].p1;
593 #if 0
594 g_message("Setting corner type on segment %d to %s", comp_nr,
595 corner_type == BEZ_CORNER_SYMMETRIC ? "symmetric" :
596 corner_type == BEZ_CORNER_SMOOTH ? "smooth" : "cusp");
597 #endif
598 bez->corner_types[comp_nr] = corner_type;
599 if (comp_nr == 0)
600 bez->corner_types[bez->numpoints-1] = corner_type;
601 else if (comp_nr == bez->numpoints - 1)
602 bez->corner_types[0] = corner_type;
604 beziershape_straighten_corner(bez, comp_nr);
606 return beziershape_create_corner_change(bez, mid_handle, &old_left,
607 &old_right, old_type, corner_type);
610 void
611 beziershape_update_data(BezierShape *bezier)
613 int i;
614 Point last;
616 Object *obj = &bezier->object;
618 /* handle the case of whole points array update (via set_prop) */
619 if (3*(bezier->numpoints-1) != obj->num_handles ||
620 2*(bezier->numpoints-1) != obj->num_connections) {
621 object_unconnect_all(obj); /* too drastic ? */
623 /* delete the old ones */
624 for (i = 0; i < obj->num_handles; i++)
625 g_free(obj->handles[i]);
626 g_free(obj->handles);
627 for (i = 0; i < obj->num_connections; i++);
628 g_free(obj->connections[i]);
629 g_free(obj->connections);
631 obj->num_handles = 3*(bezier->numpoints-1);
632 obj->handles = g_new(Handle*, obj->num_handles);
633 obj->num_connections = 2*(bezier->numpoints-1);
634 obj->connections = g_new(ConnectionPoint *, obj->num_connections);
636 new_handles_and_connections(bezier, bezier->numpoints);
638 bezier->corner_types = g_realloc(bezier->corner_types, bezier->numpoints*sizeof(BezCornerType));
639 for (i = 0; i < bezier->numpoints; i++)
640 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
643 /* Update handles: */
644 bezier->points[0].p3 = bezier->points[0].p1;
645 for (i = 1; i < bezier->numpoints; i++) {
646 bezier->object.handles[3*i-3]->pos = bezier->points[i].p1;
647 bezier->object.handles[3*i-2]->pos = bezier->points[i].p2;
648 bezier->object.handles[3*i-1]->pos = bezier->points[i].p3;
651 /* Update connection points: */
652 last = bezier->points[0].p1;
653 for (i = 1; i < bezier->numpoints; i++) {
654 Point slopepoint1, slopepoint2;
655 slopepoint1 = bezier->points[i].p1;
656 point_sub(&slopepoint1, &last);
657 point_scale(&slopepoint1, .5);
658 point_add(&slopepoint1, &last);
659 slopepoint2 = bezier->points[i].p2;
660 point_sub(&slopepoint2, &bezier->points[i].p3);
661 point_scale(&slopepoint2, .5);
662 point_add(&slopepoint2, &bezier->points[i].p3);
664 bezier->object.connections[2*i-2]->pos = last;
665 bezier->object.connections[2*i-2]->directions =
666 find_slope_directions(last, bezier->points[i].p1);
667 bezier->object.connections[2*i-1]->pos.x =
668 (last.x + 3*bezier->points[i].p1.x + 3*bezier->points[i].p2.x +
669 bezier->points[i].p3.x)/8;
670 bezier->object.connections[2*i-1]->pos.y =
671 (last.y + 3*bezier->points[i].p1.y + 3*bezier->points[i].p2.y +
672 bezier->points[i].p3.y)/8;
673 bezier->object.connections[2*i-1]->directions =
674 find_slope_directions(slopepoint1, slopepoint2);
675 last = bezier->points[i].p3;
679 void
680 beziershape_update_boundingbox(BezierShape *bezier)
682 ElementBBExtras *extra;
683 PolyBBExtras pextra;
685 g_assert(bezier != NULL);
687 extra = &bezier->extra_spacing;
688 pextra.start_trans = pextra.end_trans =
689 pextra.start_long = pextra.end_long = 0;
690 pextra.middle_trans = extra->border_trans;
692 polybezier_bbox(&bezier->points[0],
693 bezier->numpoints,
694 &pextra, TRUE,
695 &bezier->object.bounding_box);
698 void
699 beziershape_simple_draw(BezierShape *bezier, DiaRenderer *renderer, real width)
701 BezPoint *points;
703 g_assert(bezier != NULL);
704 g_assert(renderer != NULL);
706 points = &bezier->points[0];
708 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
709 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
710 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_ROUND);
711 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
713 DIA_RENDERER_GET_CLASS(renderer)->fill_bezier(renderer, points, bezier->numpoints,&color_white);
714 DIA_RENDERER_GET_CLASS(renderer)->draw_bezier(renderer, points, bezier->numpoints,&color_black);
717 void
718 beziershape_draw_control_lines(BezierShape *bez, DiaRenderer *renderer)
720 Color line_colour = {0.0, 0.0, 0.6};
721 Point startpoint;
722 int i;
724 /* setup renderer ... */
725 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, 0);
726 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_DOTTED);
727 DIA_RENDERER_GET_CLASS(renderer)->set_dashlength(renderer, 1);
728 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_MITER);
729 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
731 startpoint = bez->points[0].p1;
732 for (i = 1; i < bez->numpoints; i++) {
733 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &startpoint, &bez->points[i].p1,
734 &line_colour);
735 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &bez->points[i].p2, &bez->points[i].p3,
736 &line_colour);
737 startpoint = bez->points[i].p3;
741 static void
742 new_handles_and_connections(BezierShape *bezier, int num_points)
744 Object *obj;
745 int i;
747 obj = &bezier->object;
749 for (i = 0; i < num_points-1; i++) {
750 obj->handles[3*i] = g_new(Handle, 1);
751 obj->handles[3*i+1] = g_new(Handle, 1);
752 obj->handles[3*i+2] = g_new(Handle, 1);
754 obj->handles[3*i]->connect_type = HANDLE_NONCONNECTABLE;
755 obj->handles[3*i]->connected_to = NULL;
756 obj->handles[3*i]->type = HANDLE_MINOR_CONTROL;
757 obj->handles[3*i]->id = HANDLE_RIGHTCTRL;
759 obj->handles[3*i+1]->connect_type = HANDLE_NONCONNECTABLE;
760 obj->handles[3*i+1]->connected_to = NULL;
761 obj->handles[3*i+1]->type = HANDLE_MINOR_CONTROL;
762 obj->handles[3*i+1]->id = HANDLE_LEFTCTRL;
764 obj->handles[3*i+2]->connect_type = HANDLE_NONCONNECTABLE;
765 obj->handles[3*i+2]->connected_to = NULL;
766 obj->handles[3*i+2]->type = HANDLE_MAJOR_CONTROL;
767 obj->handles[3*i+2]->id = HANDLE_BEZMAJOR;
769 obj->connections[2*i] = g_new0(ConnectionPoint, 1);
770 obj->connections[2*i+1] = g_new0(ConnectionPoint, 1);
771 obj->connections[2*i]->object = obj;
772 obj->connections[2*i+1]->object = obj;
776 void
777 beziershape_init(BezierShape *bezier, int num_points)
779 Object *obj;
780 int i;
782 obj = &bezier->object;
784 object_init(obj, 3*(num_points-1), 2*(num_points-1));
786 bezier->numpoints = num_points;
788 bezier->points = g_new(BezPoint, num_points);
789 bezier->points[0].type = BEZ_MOVE_TO;
790 bezier->corner_types = g_new(BezCornerType, num_points);
791 for (i = 1; i < num_points; i++) {
792 bezier->points[i].type = BEZ_CURVE_TO;
793 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
796 new_handles_and_connections(bezier, num_points);
798 /* The points are not assigned at this point, so don't try to use
799 them */
800 /* beziershape_update_data(bezier);*/
804 /** This function does *not* set up handles */
805 void
806 beziershape_set_points(BezierShape *bez, int num_points, BezPoint *points)
808 int i;
810 bez->numpoints = num_points;
812 if (bez->points)
813 g_free(bez->points);
815 bez->points = g_malloc((bez->numpoints)*sizeof(BezPoint));
817 for (i=0;i<bez->numpoints;i++) {
818 bez->points[i] = points[i];
822 void
823 beziershape_copy(BezierShape *from, BezierShape *to)
825 int i;
826 Object *toobj, *fromobj;
828 toobj = &to->object;
829 fromobj = &from->object;
831 object_copy(fromobj, toobj);
833 to->numpoints = from->numpoints;
835 to->points = g_new(BezPoint, to->numpoints);
836 to->corner_types = g_new(BezCornerType, to->numpoints);
838 for (i = 0; i < to->numpoints; i++) {
839 to->points[i] = from->points[i];
840 to->corner_types[i] = from->corner_types[i];
843 for (i = 0; i < to->object.num_handles; i++) {
844 to->object.handles[i] = g_new(Handle, 1);
845 setup_handle(to->object.handles[i], from->object.handles[i]->id);
847 for (i = 0; i < to->object.num_connections; i++) {
848 to->object.connections[i] = g_new0(ConnectionPoint, 1);
849 to->object.connections[i]->object = &to->object;
852 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
853 beziershape_update_data(to);
856 void
857 beziershape_destroy(BezierShape *bezier)
859 int i;
860 Handle **temp_handles;
861 ConnectionPoint **temp_cps;
863 /* Need to store these temporary since object.handles is
864 freed by object_destroy() */
865 temp_handles = g_new(Handle *, bezier->object.num_handles);
866 for (i = 0; i < bezier->object.num_handles; i++)
867 temp_handles[i] = bezier->object.handles[i];
869 temp_cps = g_new(ConnectionPoint *, bezier->object.num_connections);
870 for (i = 0; i < bezier->object.num_connections; i++)
871 temp_cps[i] = bezier->object.connections[i];
873 object_destroy(&bezier->object);
875 for (i = 0; i < bezier->object.num_handles; i++)
876 g_free(temp_handles[i]);
877 g_free(temp_handles);
879 for (i = 0; i < bezier->object.num_connections; i++)
880 g_free(temp_cps[i]);
881 g_free(temp_cps);
883 g_free(bezier->points);
884 g_free(bezier->corner_types);
888 void
889 beziershape_save(BezierShape *bezier, ObjectNode obj_node)
891 int i;
892 AttributeNode attr;
894 object_save(&bezier->object, obj_node);
896 attr = new_attribute(obj_node, "bez_points");
898 data_add_point(attr, &bezier->points[0].p1);
899 for (i = 1; i < bezier->numpoints; i++) {
900 data_add_point(attr, &bezier->points[i].p1);
901 data_add_point(attr, &bezier->points[i].p2);
902 if (i < bezier->numpoints - 1)
903 data_add_point(attr, &bezier->points[i].p3);
906 attr = new_attribute(obj_node, "corner_types");
907 for (i = 0; i < bezier->numpoints; i++)
908 data_add_enum(attr, bezier->corner_types[i]);
911 void
912 beziershape_load(BezierShape *bezier, ObjectNode obj_node)
914 int i;
915 AttributeNode attr;
916 DataNode data;
918 Object *obj = &bezier->object;
920 object_load(obj, obj_node);
922 attr = object_find_attribute(obj_node, "bez_points");
924 if (attr != NULL)
925 bezier->numpoints = attribute_num_data(attr) / 3 + 1;
926 else
927 bezier->numpoints = 0;
929 object_init(obj, 3 * (bezier->numpoints - 1), 2 * (bezier->numpoints - 1));
931 data = attribute_first_data(attr);
932 if (bezier->numpoints != 0) {
933 bezier->points = g_new(BezPoint, bezier->numpoints);
934 bezier->points[0].type = BEZ_MOVE_TO;
935 data_point(data, &bezier->points[0].p1);
936 bezier->points[0].p3 = bezier->points[0].p1;
937 data = data_next(data);
939 for (i = 1; i < bezier->numpoints; i++) {
940 bezier->points[i].type = BEZ_CURVE_TO;
941 data_point(data, &bezier->points[i].p1);
942 data = data_next(data);
943 data_point(data, &bezier->points[i].p2);
944 data = data_next(data);
945 if (i < bezier->numpoints - 1) {
946 data_point(data, &bezier->points[i].p3);
947 data = data_next(data);
948 } else
949 bezier->points[i].p3 = bezier->points[0].p1;
953 bezier->corner_types = g_new(BezCornerType, bezier->numpoints);
954 attr = object_find_attribute(obj_node, "corner_types");
955 if (!attr || attribute_num_data(attr) != bezier->numpoints) {
956 for (i = 0; i < bezier->numpoints; i++)
957 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
958 } else {
959 data = attribute_first_data(attr);
960 for (i = 0; i < bezier->numpoints; i++) {
961 bezier->corner_types[i] = data_enum(data);
962 data = data_next(data);
966 for (i = 0; i < bezier->numpoints - 1; i++) {
967 obj->handles[3*i] = g_new(Handle, 1);
968 obj->handles[3*i+1] = g_new(Handle, 1);
969 obj->handles[3*i+2] = g_new(Handle, 1);
971 setup_handle(obj->handles[3*i], HANDLE_RIGHTCTRL);
972 setup_handle(obj->handles[3*i+1], HANDLE_LEFTCTRL);
973 setup_handle(obj->handles[3*i+2], HANDLE_BEZMAJOR);
975 for (i = 0; i < bezier->object.num_connections; i++) {
976 obj->connections[i] = g_new0(ConnectionPoint, 1);
977 obj->connections[i]->object = obj;
980 beziershape_update_data(bezier);
983 static void
984 beziershape_point_change_free(struct PointChange *change)
986 if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
987 (change->type==TYPE_REMOVE_POINT && change->applied) ){
988 g_free(change->handle1);
989 g_free(change->handle2);
990 g_free(change->handle3);
991 g_free(change->cp1);
992 g_free(change->cp2);
993 change->handle1 = NULL;
994 change->handle2 = NULL;
995 change->handle3 = NULL;
996 change->cp1 = NULL;
997 change->cp2 = NULL;
1001 static void
1002 beziershape_point_change_apply(struct PointChange *change, Object *obj)
1004 change->applied = 1;
1005 switch (change->type) {
1006 case TYPE_ADD_POINT:
1007 add_handles((BezierShape *)obj, change->pos, &change->point,
1008 change->corner_type,
1009 change->handle1, change->handle2, change->handle3,
1010 change->cp1, change->cp2);
1011 break;
1012 case TYPE_REMOVE_POINT:
1013 object_unconnect(obj, change->handle1);
1014 object_unconnect(obj, change->handle2);
1015 object_unconnect(obj, change->handle3);
1016 remove_handles((BezierShape *)obj, change->pos);
1017 break;
1021 static void
1022 beziershape_point_change_revert(struct PointChange *change, Object *obj)
1024 switch (change->type) {
1025 case TYPE_ADD_POINT:
1026 remove_handles((BezierShape *)obj, change->pos);
1027 break;
1028 case TYPE_REMOVE_POINT:
1029 add_handles((BezierShape *)obj, change->pos, &change->point,
1030 change->corner_type,
1031 change->handle1, change->handle2, change->handle3,
1032 change->cp1, change->cp2);
1033 break;
1035 change->applied = 0;
1038 static ObjectChange *
1039 beziershape_create_point_change(BezierShape *bezier, enum change_type type,
1040 BezPoint *point, BezCornerType corner_type,
1041 int pos,
1042 Handle *handle1, Handle *handle2,
1043 Handle *handle3,
1044 ConnectionPoint *cp1, ConnectionPoint *cp2)
1046 struct PointChange *change;
1048 change = g_new(struct PointChange, 1);
1050 change->obj_change.apply =
1051 (ObjectChangeApplyFunc)beziershape_point_change_apply;
1052 change->obj_change.revert =
1053 (ObjectChangeRevertFunc)beziershape_point_change_revert;
1054 change->obj_change.free =
1055 (ObjectChangeFreeFunc)beziershape_point_change_free;
1057 change->type = type;
1058 change->applied = 1;
1059 change->point = *point;
1060 change->corner_type = corner_type;
1061 change->pos = pos;
1062 change->handle1 = handle1;
1063 change->handle2 = handle2;
1064 change->handle3 = handle3;
1065 change->cp1 = cp1;
1066 change->cp2 = cp2;
1068 return (ObjectChange *)change;
1071 static void
1072 beziershape_corner_change_apply(struct CornerChange *change, Object *obj)
1074 BezierShape *bez = (BezierShape *)obj;
1075 int handle_nr = get_handle_nr(bez, change->handle);
1076 int comp_nr = get_major_nr(handle_nr);
1078 beziershape_straighten_corner(bez, comp_nr);
1080 bez->corner_types[comp_nr] = change->new_type;
1081 if (comp_nr == 0)
1082 bez->corner_types[bez->numpoints-1] = change->new_type;
1083 if (comp_nr == bez->numpoints - 1)
1084 bez->corner_types[0] = change->new_type;
1086 change->applied = 1;
1089 static void
1090 beziershape_corner_change_revert(struct CornerChange *change, Object *obj)
1092 BezierShape *bez = (BezierShape *)obj;
1093 int handle_nr = get_handle_nr(bez, change->handle);
1094 int comp_nr = get_major_nr(handle_nr);
1096 bez->points[comp_nr].p2 = change->point_left;
1097 if (comp_nr == bez->numpoints - 1)
1098 bez->points[1].p1 = change->point_right;
1099 else
1100 bez->points[comp_nr+1].p1 = change->point_right;
1101 bez->corner_types[comp_nr] = change->old_type;
1102 if (comp_nr == 0)
1103 bez->corner_types[bez->numpoints-1] = change->new_type;
1104 if (comp_nr == bez->numpoints - 1)
1105 bez->corner_types[0] = change->new_type;
1107 change->applied = 0;
1110 static ObjectChange *
1111 beziershape_create_corner_change(BezierShape *bez, Handle *handle,
1112 Point *point_left, Point *point_right,
1113 BezCornerType old_corner_type,
1114 BezCornerType new_corner_type)
1116 struct CornerChange *change;
1118 change = g_new(struct CornerChange, 1);
1120 change->obj_change.apply =
1121 (ObjectChangeApplyFunc)beziershape_corner_change_apply;
1122 change->obj_change.revert =
1123 (ObjectChangeRevertFunc)beziershape_corner_change_revert;
1124 change->obj_change.free = (ObjectChangeFreeFunc)NULL;
1126 change->old_type = old_corner_type;
1127 change->new_type = new_corner_type;
1128 change->applied = 1;
1130 change->handle = handle;
1131 change->point_left = *point_left;
1132 change->point_right = *point_right;
1134 return (ObjectChange *)change;