added missing horizontal jumper icon
[dia.git] / lib / bezier_conn.c
blob38f55e8bca8001cd8bdb6a4287442b1eb47a69ef
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
4 * BezierConn Copyright (C) 1999 James Henstridge
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <math.h>
26 #include <string.h> /* memcpy() */
28 #include "bezier_conn.h"
29 #include "intl.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 struct PointChange {
43 ObjectChange obj_change;
45 enum change_type type;
46 int applied;
48 BezPoint point;
49 BezCornerType corner_type;
50 int pos;
51 /* owning ref when not applied for ADD_POINT
52 * owning ref when applied for REMOVE_POINT */
53 Handle *handle1, *handle2, *handle3;
54 /* NULL if not connected */
55 ConnectionPoint *connected_to1, *connected_to2, *connected_to3;
58 struct CornerChange {
59 ObjectChange obj_change;
60 /* Only one kind of corner_change */
61 int applied;
63 Handle *handle;
64 /* Old places when SET_CORNER_TYPE is applied */
65 Point point_left, point_right;
66 BezCornerType old_type, new_type;
69 static ObjectChange *
70 bezierconn_create_point_change(BezierConn *bez, enum change_type type,
71 BezPoint *point, BezCornerType corner_type,
72 int segment,
73 Handle *handle1, ConnectionPoint *connected_to1,
74 Handle *handle2, ConnectionPoint *connected_to2,
75 Handle *handle3, ConnectionPoint *connected_to3);
76 static ObjectChange *
77 bezierconn_create_corner_change(BezierConn *bez, Handle *handle,
78 Point *point_left, Point *point_right,
79 BezCornerType old_corner_type,
80 BezCornerType new_corner_type);
83 static void setup_corner_handle(Handle *handle, HandleId id)
85 handle->id = id;
86 handle->type = HANDLE_MINOR_CONTROL;
87 handle->connect_type = (id == HANDLE_BEZMAJOR) ?
88 HANDLE_CONNECTABLE : HANDLE_NONCONNECTABLE;
89 handle->connected_to = NULL;
92 static int get_handle_nr(BezierConn *bez, Handle *handle)
94 int i = 0;
95 for (i=0;i<bez->object.num_handles;i++) {
96 if (bez->object.handles[i] == handle)
97 return i;
99 return -1;
102 #define get_comp_nr(hnum) (((int)(hnum)+2)/3)
103 #define get_major_nr(hnum) (((int)(hnum)+1)/3)
105 void
106 bezierconn_move_handle(BezierConn *bez, Handle *handle,
107 Point *to, HandleMoveReason reason)
109 int handle_nr, comp_nr;
110 Point delta, pt;
112 delta = *to;
113 point_sub(&delta, &handle->pos);
115 handle_nr = get_handle_nr(bez, handle);
116 comp_nr = get_comp_nr(handle_nr);
117 switch(handle->id) {
118 case HANDLE_MOVE_STARTPOINT:
119 bez->points[0].p1 = *to;
120 /* shift adjacent point */
121 point_add(&bez->points[1].p1, &delta);
122 break;
123 case HANDLE_MOVE_ENDPOINT:
124 bez->points[bez->numpoints-1].p3 = *to;
125 /* shift adjacent point */
126 point_add(&bez->points[bez->numpoints-1].p2, &delta);
127 break;
128 case HANDLE_BEZMAJOR:
129 bez->points[comp_nr].p3 = *to;
130 /* shift adjacent point */
131 point_add(&bez->points[comp_nr].p2, &delta);
132 point_add(&bez->points[comp_nr+1].p1, &delta);
133 break;
134 case HANDLE_LEFTCTRL:
135 bez->points[comp_nr].p2 = *to;
136 if (comp_nr < bez->numpoints - 1) {
137 switch (bez->corner_types[comp_nr]) {
138 case BEZ_CORNER_SYMMETRIC:
139 pt = bez->points[comp_nr].p3;
140 point_sub(&pt, &bez->points[comp_nr].p2);
141 point_add(&pt, &bez->points[comp_nr].p3);
142 bez->points[comp_nr+1].p1 = pt;
143 break;
144 case BEZ_CORNER_SMOOTH: {
145 real len;
146 pt = bez->points[comp_nr+1].p1;
147 point_sub(&pt, &bez->points[comp_nr].p3);
148 len = point_len(&pt);
149 pt = bez->points[comp_nr].p2;
150 point_sub(&pt, &bez->points[comp_nr].p3);
151 if (point_len(&pt) > 0)
152 point_normalize(&pt);
153 else { pt.x = 1.0; pt.y = 0.0; }
154 point_scale(&pt, -len);
155 point_add(&pt, &bez->points[comp_nr].p3);
156 bez->points[comp_nr+1].p1 = pt;
157 break;
159 case BEZ_CORNER_CUSP:
160 /* Do nothing to the other guy */
161 break;
164 break;
165 case HANDLE_RIGHTCTRL:
166 bez->points[comp_nr].p1 = *to;
167 if (comp_nr > 1) {
168 switch (bez->corner_types[comp_nr-1]) {
169 case BEZ_CORNER_SYMMETRIC:
170 pt = bez->points[comp_nr - 1].p3;
171 point_sub(&pt, &bez->points[comp_nr].p1);
172 point_add(&pt, &bez->points[comp_nr - 1].p3);
173 bez->points[comp_nr-1].p2 = pt;
174 break;
175 case BEZ_CORNER_SMOOTH: {
176 real len;
177 pt = bez->points[comp_nr-1].p2;
178 point_sub(&pt, &bez->points[comp_nr-1].p3);
179 len = point_len(&pt);
180 pt = bez->points[comp_nr].p1;
181 point_sub(&pt, &bez->points[comp_nr-1].p3);
182 if (point_len(&pt) > 0)
183 point_normalize(&pt);
184 else { pt.x = 1.0; pt.y = 0.0; }
185 point_scale(&pt, -len);
186 point_add(&pt, &bez->points[comp_nr-1].p3);
187 bez->points[comp_nr-1].p2 = pt;
188 break;
190 case BEZ_CORNER_CUSP:
191 /* Do nothing to the other guy */
192 break;
195 break;
196 default:
197 message_error("Internal error in bezierconn_move_handle.\n");
198 break;
202 void
203 bezierconn_move(BezierConn *bez, Point *to)
205 Point p;
206 int i;
208 p = *to;
209 point_sub(&p, &bez->points[0].p1);
211 bez->points[0].p1 = *to;
212 for (i = 1; i < bez->numpoints; i++) {
213 point_add(&bez->points[i].p1, &p);
214 point_add(&bez->points[i].p2, &p);
215 point_add(&bez->points[i].p3, &p);
220 bezierconn_closest_segment(BezierConn *bez, Point *point, real line_width)
222 Point last;
223 int i;
224 real dist = G_MAXDOUBLE;
225 int closest;
227 closest = 0;
228 last = bez->points[0].p1;
229 for (i = 0; i < bez->numpoints - 1; i++) {
230 real new_dist = distance_bez_seg_point(&last, &bez->points[i+1].p1,
231 &bez->points[i+1].p2, &bez->points[i+1].p3,
232 line_width, point);
233 if (new_dist < dist) {
234 dist = new_dist;
235 closest = i;
237 last = bez->points[i+1].p3;
239 return closest;
242 Handle *
243 bezierconn_closest_handle(BezierConn *bez, Point *point)
245 int i, hn;
246 real dist;
247 Handle *closest;
249 closest = bez->object.handles[0];
250 dist = distance_point_point( point, &closest->pos);
251 for (i = 1, hn = 1; i < bez->numpoints; i++, hn++) {
252 real new_dist;
254 new_dist = distance_point_point(point, &bez->points[i].p1);
255 if (new_dist < dist) {
256 dist = new_dist;
257 closest = bez->object.handles[hn];
259 hn++;
261 new_dist = distance_point_point(point, &bez->points[i].p2);
262 if (new_dist < dist) {
263 dist = new_dist;
264 closest = bez->object.handles[hn];
266 hn++;
268 new_dist = distance_point_point(point, &bez->points[i].p3);
269 if (new_dist < dist) {
270 dist = new_dist;
271 closest = bez->object.handles[hn];
274 return closest;
277 Handle *
278 bezierconn_closest_major_handle(BezierConn *bez, Point *point)
280 Handle *closest = bezierconn_closest_handle(bez, point);
282 return bez->object.handles[3*get_major_nr(get_handle_nr(bez, closest))];
285 real
286 bezierconn_distance_from(BezierConn *bez, Point *point, real line_width)
288 return distance_bez_line_point(bez->points, bez->numpoints,
289 line_width, point);
292 static void
293 add_handles(BezierConn *bez, int pos, BezPoint *point,
294 BezCornerType corner_type, Handle *handle1,
295 Handle *handle2, Handle *handle3)
297 int i;
298 Object *obj;
300 g_assert(pos > 0);
302 obj = (Object *)bez;
303 bez->numpoints++;
304 bez->points = g_realloc(bez->points, bez->numpoints*sizeof(BezPoint));
305 bez->corner_types = g_realloc(bez->corner_types,
306 bez->numpoints * sizeof(BezCornerType));
308 for (i = bez->numpoints-1; i > pos; i--) {
309 bez->points[i] = bez->points[i-1];
310 bez->corner_types[i] = bez->corner_types[i-1];
312 bez->points[pos] = *point;
313 bez->points[pos].p1 = bez->points[pos+1].p1;
314 bez->points[pos+1].p1 = point->p1;
315 bez->corner_types[pos] = corner_type;
316 object_add_handle_at(obj, handle1, 3*pos-2);
317 object_add_handle_at(obj, handle2, 3*pos-1);
318 object_add_handle_at(obj, handle3, 3*pos);
320 if (pos==bez->numpoints-1) {
321 obj->handles[obj->num_handles-4]->type = HANDLE_MINOR_CONTROL;
322 obj->handles[obj->num_handles-4]->id = HANDLE_BEZMAJOR;
326 static void
327 remove_handles(BezierConn *bez, int pos)
329 int i;
330 Object *obj;
331 Handle *old_handle1, *old_handle2, *old_handle3;
332 Point tmppoint;
334 g_assert(pos > 0);
336 obj = (Object *)bez;
338 if (pos==obj->num_handles-1) {
339 obj->handles[obj->num_handles-4]->type = HANDLE_MAJOR_CONTROL;
340 obj->handles[obj->num_handles-4]->id = HANDLE_MOVE_ENDPOINT;
343 /* delete the points */
344 bez->numpoints--;
345 tmppoint = bez->points[pos].p1;
346 for (i = pos; i < bez->numpoints; i++) {
347 bez->points[i] = bez->points[i+1];
348 bez->corner_types[i] = bez->corner_types[i+1];
350 bez->points[pos].p1 = tmppoint;
351 bez->points = g_realloc(bez->points, bez->numpoints*sizeof(BezPoint));
352 bez->corner_types = g_realloc(bez->corner_types,
353 bez->numpoints * sizeof(BezCornerType));
355 old_handle1 = obj->handles[3*pos-3];
356 old_handle2 = obj->handles[3*pos-2];
357 old_handle3 = obj->handles[3*pos-1];
358 object_remove_handle(&bez->object, old_handle1);
359 object_remove_handle(&bez->object, old_handle2);
360 object_remove_handle(&bez->object, old_handle3);
364 /* Add a point by splitting segment into two, putting the new point at
365 'point' or, if NULL, in the middle */
366 ObjectChange *
367 bezierconn_add_segment(BezierConn *bez, int segment, Point *point)
369 BezPoint realpoint;
370 BezCornerType corner_type = BEZ_CORNER_SYMMETRIC;
371 Handle *new_handle1, *new_handle2, *new_handle3;
372 Point startpoint;
374 if (segment == 0)
375 startpoint = bez->points[0].p1;
376 else
377 startpoint = bez->points[segment].p3;
379 if (point == NULL) {
380 realpoint.p1.x = (startpoint.x + bez->points[segment+1].p3.x) / 6;
381 realpoint.p1.y = (startpoint.y + bez->points[segment+1].p3.y) / 6;
382 realpoint.p2.x = (startpoint.x + bez->points[segment+1].p3.x) / 3;
383 realpoint.p2.y = (startpoint.y + bez->points[segment+1].p3.y) / 3;
384 realpoint.p3.x = (startpoint.x + bez->points[segment+1].p3.x) / 2;
385 realpoint.p3.y = (startpoint.y + bez->points[segment+1].p3.y) / 2;
386 } else {
387 realpoint.p2.x = point->x+(startpoint.x - bez->points[segment+1].p3.x)/6;
388 realpoint.p2.y = point->y+(startpoint.y - bez->points[segment+1].p3.y)/6;
389 realpoint.p3 = *point;
390 /* this really goes into the next segment ... */
391 realpoint.p1.x = point->x-(startpoint.x - bez->points[segment+1].p3.x)/6;
392 realpoint.p1.y = point->y-(startpoint.y - bez->points[segment+1].p3.y)/6;
394 realpoint.type = BEZ_CURVE_TO;
396 new_handle1 = g_malloc(sizeof(Handle));
397 new_handle2 = g_malloc(sizeof(Handle));
398 new_handle3 = g_malloc(sizeof(Handle));
399 setup_corner_handle(new_handle1, HANDLE_RIGHTCTRL);
400 setup_corner_handle(new_handle2, HANDLE_LEFTCTRL);
401 setup_corner_handle(new_handle3, HANDLE_BEZMAJOR);
402 add_handles(bez, segment+1, &realpoint, corner_type,
403 new_handle1, new_handle2, new_handle3);
404 return bezierconn_create_point_change(bez, TYPE_ADD_POINT,
405 &realpoint, corner_type, segment+1,
406 new_handle1, NULL,
407 new_handle2, NULL,
408 new_handle3, NULL);
411 ObjectChange *
412 bezierconn_remove_segment(BezierConn *bez, int pos)
414 Handle *old_handle1, *old_handle2, *old_handle3;
415 ConnectionPoint *cpt1, *cpt2, *cpt3;
416 BezPoint old_point;
417 BezCornerType old_ctype;
419 g_assert(pos > 0);
420 g_assert(bez->numpoints > 2);
422 if (pos == bez->numpoints-1) pos--;
424 old_handle1 = bez->object.handles[3*pos-2];
425 old_handle2 = bez->object.handles[3*pos-1];
426 old_handle3 = bez->object.handles[3*pos];
427 old_point = bez->points[pos];
428 old_ctype = bez->corner_types[pos];
430 cpt1 = old_handle1->connected_to;
431 cpt2 = old_handle2->connected_to;
432 cpt3 = old_handle3->connected_to;
434 object_unconnect((Object *)bez, old_handle1);
435 object_unconnect((Object *)bez, old_handle2);
436 object_unconnect((Object *)bez, old_handle3);
438 remove_handles(bez, pos);
440 bezierconn_update_data(bez);
442 return bezierconn_create_point_change(bez, TYPE_REMOVE_POINT,
443 &old_point, old_ctype, pos,
444 old_handle1, cpt1,
445 old_handle2, cpt2,
446 old_handle3, cpt3);
449 static void
450 bezierconn_straighten_corner(BezierConn *bez, int comp_nr) {
451 /* Neat thing would be to have the kind of straigthening depend on
452 which handle was chosen: Mid-handle does average, other leaves that
453 handle where it is. */
454 switch (bez->corner_types[comp_nr]) {
455 case BEZ_CORNER_SYMMETRIC: {
456 Point pt1, pt2;
457 pt1 = bez->points[comp_nr].p3;
458 point_sub(&pt1, &bez->points[comp_nr].p2);
459 pt2 = bez->points[comp_nr].p3;
460 point_sub(&pt2, &bez->points[comp_nr+1].p1);
461 point_scale(&pt2, -1.0);
462 point_add(&pt1, &pt2);
463 point_scale(&pt1, 0.5);
464 pt2 = pt1;
465 point_scale(&pt1, -1.0);
466 point_add(&pt1, &bez->points[comp_nr].p3);
467 point_add(&pt2, &bez->points[comp_nr].p3);
468 bez->points[comp_nr].p2 = pt1;
469 bez->points[comp_nr+1].p1 = pt2;
470 bezierconn_update_data(bez);
472 break;
473 case BEZ_CORNER_SMOOTH: {
474 Point pt1, pt2;
475 real len1, len2;
476 pt1 = bez->points[comp_nr].p3;
477 point_sub(&pt1, &bez->points[comp_nr].p2);
478 pt2 = bez->points[comp_nr].p3;
479 point_sub(&pt2, &bez->points[comp_nr+1].p1);
480 len1 = point_len(&pt1);
481 len2 = point_len(&pt2);
482 point_scale(&pt2, -1.0);
483 if (len1 > 0)
484 point_normalize(&pt1);
485 if (len2 > 0)
486 point_normalize(&pt2);
487 point_add(&pt1, &pt2);
488 point_scale(&pt1, 0.5);
489 pt2 = pt1;
490 point_scale(&pt1, -len1);
491 point_add(&pt1, &bez->points[comp_nr].p3);
492 point_scale(&pt2, len2);
493 point_add(&pt2, &bez->points[comp_nr].p3);
494 bez->points[comp_nr].p2 = pt1;
495 bez->points[comp_nr+1].p1 = pt2;
496 bezierconn_update_data(bez);
498 break;
499 case BEZ_CORNER_CUSP:
500 break;
504 ObjectChange *
505 bezierconn_set_corner_type(BezierConn *bez, Handle *handle,
506 BezCornerType corner_type)
508 Handle *mid_handle;
509 Point old_left, old_right;
510 int old_type;
511 int handle_nr, comp_nr;
513 handle_nr = get_handle_nr(bez, handle);
515 switch (handle->id) {
516 case HANDLE_BEZMAJOR:
517 mid_handle = handle;
518 break;
519 case HANDLE_LEFTCTRL:
520 handle_nr++;
521 mid_handle = bez->object.handles[handle_nr];
522 break;
523 case HANDLE_RIGHTCTRL:
524 handle_nr--;
525 mid_handle = bez->object.handles[handle_nr];
526 break;
527 default:
528 message_warning(_("Internal error: Setting corner type of endpoint of bezier"));
529 return NULL;
532 comp_nr = get_major_nr(handle_nr);
534 old_type = bez->corner_types[comp_nr];
535 old_left = bez->points[comp_nr].p2;
536 old_right = bez->points[comp_nr+1].p1;
538 bez->corner_types[comp_nr] = corner_type;
540 bezierconn_straighten_corner(bez, comp_nr);
542 return bezierconn_create_corner_change(bez, mid_handle, &old_left, &old_right,
543 old_type, corner_type);
546 void
547 bezierconn_update_data(BezierConn *bez)
549 int i;
551 /* Update handles: */
552 bez->object.handles[0]->pos = bez->points[0].p1;
553 for (i = 1; i < bez->numpoints; i++) {
554 bez->object.handles[3*i-2]->pos = bez->points[i].p1;
555 bez->object.handles[3*i-1]->pos = bez->points[i].p2;
556 bez->object.handles[3*i]->pos = bez->points[i].p3;
560 void
561 bezierconn_update_boundingbox(BezierConn *bez)
563 g_assert(bez != NULL);
565 polybezier_bbox(&bez->points[0],
566 bez->numpoints,
567 &bez->extra_spacing, FALSE,
568 &bez->object.bounding_box);
571 void
572 bezierconn_simple_draw(BezierConn *bez, DiaRenderer *renderer, real width)
574 BezPoint *points;
576 g_assert(bez != NULL);
577 g_assert(renderer != NULL);
579 points = &bez->points[0];
581 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
582 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
583 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_ROUND);
584 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
586 DIA_RENDERER_GET_CLASS(renderer)->draw_bezier(renderer, points, bez->numpoints, &color_black);
589 void
590 bezierconn_draw_control_lines(BezierConn *bez, DiaRenderer *renderer)
592 Color line_colour = {0.0, 0.0, 0.6};
593 Point startpoint;
594 int i;
596 /* setup DiaRenderer ... */
597 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, 0);
598 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_DOTTED);
599 DIA_RENDERER_GET_CLASS(renderer)->set_dashlength(renderer, 1);
600 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_MITER);
601 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
603 startpoint = bez->points[0].p1;
604 for (i = 1; i < bez->numpoints; i++) {
605 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &startpoint, &bez->points[i].p1,
606 &line_colour);
607 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &bez->points[i].p2, &bez->points[i].p3,
608 &line_colour);
609 startpoint = bez->points[i].p3;
613 void
614 bezierconn_init(BezierConn *bez, int num_points)
616 Object *obj;
617 int i;
619 obj = &bez->object;
621 object_init(obj, 3*num_points-2, 0);
623 bez->numpoints = num_points;
625 bez->points = g_new(BezPoint, num_points);
626 bez->corner_types = g_new(BezCornerType, num_points);
627 bez->points[0].type = BEZ_MOVE_TO;
628 bez->corner_types[0] = BEZ_CORNER_SYMMETRIC;
629 for (i = 1; i < num_points; i++) {
630 bez->points[i].type = BEZ_CURVE_TO;
631 bez->corner_types[i] = BEZ_CORNER_SYMMETRIC;
634 obj->handles[0] = g_new(Handle,1);
635 obj->handles[0]->connect_type = HANDLE_CONNECTABLE;
636 obj->handles[0]->connected_to = NULL;
637 obj->handles[0]->type = HANDLE_MAJOR_CONTROL;
638 obj->handles[0]->id = HANDLE_MOVE_STARTPOINT;
640 for (i = 1; i < num_points; i++) {
641 obj->handles[3*i-2] = g_new(Handle, 1);
642 obj->handles[3*i-1] = g_new(Handle, 1);
643 obj->handles[3*i] = g_new(Handle, 1);
645 obj->handles[3*i-2]->connect_type = HANDLE_NONCONNECTABLE;
646 obj->handles[3*i-2]->connected_to = NULL;
647 obj->handles[3*i-2]->type = HANDLE_MINOR_CONTROL;
648 obj->handles[3*i-2]->id = HANDLE_RIGHTCTRL;
650 obj->handles[3*i-1]->connect_type = HANDLE_NONCONNECTABLE;
651 obj->handles[3*i-1]->connected_to = NULL;
652 obj->handles[3*i-1]->type = HANDLE_MINOR_CONTROL;
653 obj->handles[3*i-1]->id = HANDLE_LEFTCTRL;
655 obj->handles[3*i]->connect_type = HANDLE_CONNECTABLE;
656 obj->handles[3*i]->connected_to = NULL;
657 obj->handles[3*i]->type = HANDLE_MAJOR_CONTROL;
658 obj->handles[3*i]->id = HANDLE_MOVE_ENDPOINT;
660 bezierconn_update_data(bez);
663 /** This function does *not* set up handles */
664 void
665 bezierconn_set_points(BezierConn *bez, int num_points, BezPoint *points)
667 int i;
669 bez->numpoints = num_points;
671 if (bez->points)
672 g_free(bez->points);
674 bez->points = g_malloc((bez->numpoints)*sizeof(BezPoint));
676 for (i=0;i<bez->numpoints;i++) {
677 bez->points[i] = points[i];
681 void
682 bezierconn_copy(BezierConn *from, BezierConn *to)
684 int i;
685 Object *toobj, *fromobj;
687 toobj = &to->object;
688 fromobj = &from->object;
690 object_copy(fromobj, toobj);
692 to->numpoints = from->numpoints;
694 to->points = g_new(BezPoint, to->numpoints);
695 to->corner_types = g_new(BezCornerType, to->numpoints);
697 for (i = 0; i < to->numpoints; i++) {
698 to->points[i] = from->points[i];
699 to->corner_types[i] = from->corner_types[i];
702 to->object.handles[0] = g_new(Handle,1);
703 *to->object.handles[0] = *from->object.handles[0];
704 for (i = 1; i < to->object.num_handles - 1; i++) {
705 to->object.handles[i] = g_new(Handle, 1);
706 setup_corner_handle(to->object.handles[i], from->object.handles[i]->id);
708 to->object.handles[to->object.num_handles-1] = g_new(Handle,1);
709 *to->object.handles[to->object.num_handles-1] =
710 *from->object.handles[to->object.num_handles-1];
712 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
713 bezierconn_update_data(to);
716 void
717 bezierconn_destroy(BezierConn *bez)
719 int i, nh;
720 Handle **temp_handles;
722 /* Need to store these temporary since object.handles is
723 freed by object_destroy() */
724 nh = bez->object.num_handles;
725 temp_handles = g_new(Handle *, nh);
726 for (i = 0; i < nh; i++)
727 temp_handles[i] = bez->object.handles[i];
729 object_destroy(&bez->object);
731 for (i = 0; i < nh; i++)
732 g_free(temp_handles[i]);
733 g_free(temp_handles);
735 g_free(bez->points);
736 g_free(bez->corner_types);
740 void
741 bezierconn_save(BezierConn *bez, ObjectNode obj_node)
743 int i;
744 AttributeNode attr;
746 object_save(&bez->object, obj_node);
748 attr = new_attribute(obj_node, "bez_points");
750 data_add_point(attr, &bez->points[0].p1);
751 for (i = 1; i < bez->numpoints; i++) {
752 data_add_point(attr, &bez->points[i].p1);
753 data_add_point(attr, &bez->points[i].p2);
754 data_add_point(attr, &bez->points[i].p3);
757 attr = new_attribute(obj_node, "corner_types");
758 for (i = 0; i < bez->numpoints; i++)
759 data_add_enum(attr, bez->corner_types[i]);
762 void
763 bezierconn_load(BezierConn *bez, ObjectNode obj_node) /* NOTE: Does object_init() */
765 int i;
766 AttributeNode attr;
767 DataNode data;
769 Object *obj = &bez->object;
771 object_load(obj, obj_node);
773 attr = object_find_attribute(obj_node, "bez_points");
775 if (attr != NULL)
776 bez->numpoints = (attribute_num_data(attr) + 2)/3;
777 else
778 bez->numpoints = 0;
780 object_init(obj, 3 * bez->numpoints - 2, 0);
782 data = attribute_first_data(attr);
783 if (bez->numpoints != 0) {
784 bez->points = g_new(BezPoint, bez->numpoints);
785 bez->points[0].type = BEZ_MOVE_TO;
786 data_point(data, &bez->points[0].p1);
787 data = data_next(data);
789 for (i = 1; i < bez->numpoints; i++) {
790 bez->points[i].type = BEZ_CURVE_TO;
791 data_point(data, &bez->points[i].p1);
792 data = data_next(data);
793 data_point(data, &bez->points[i].p2);
794 data = data_next(data);
795 data_point(data, &bez->points[i].p3);
796 data = data_next(data);
800 bez->corner_types = g_new(BezCornerType, bez->numpoints);
802 attr = object_find_attribute(obj_node, "corner_types");
803 /* if corner_types is missing or corrupt */
804 if (!attr || attribute_num_data(attr) != bez->numpoints) {
805 for (i = 0; i < bez->numpoints; i++) {
806 bez->corner_types[i] = BEZ_CORNER_SYMMETRIC;
808 } else {
809 data = attribute_first_data(attr);
810 for (i = 0; i < bez->numpoints; i++) {
811 bez->corner_types[i] = data_enum(data);
812 data = data_next(data);
816 obj->handles[0] = g_new(Handle, 1);
817 obj->handles[0]->connect_type = HANDLE_CONNECTABLE;
818 obj->handles[0]->connected_to = NULL;
819 obj->handles[0]->type = HANDLE_MAJOR_CONTROL;
820 obj->handles[0]->id = HANDLE_MOVE_STARTPOINT;
822 for (i = 1; i < bez->numpoints; i++) {
823 obj->handles[3*i-2] = g_new(Handle, 1);
824 setup_corner_handle(obj->handles[3*i-2], HANDLE_RIGHTCTRL);
825 obj->handles[3*i-1] = g_new(Handle, 1);
826 setup_corner_handle(obj->handles[3*i-1], HANDLE_LEFTCTRL);
827 obj->handles[3*i] = g_new(Handle, 1);
828 setup_corner_handle(obj->handles[3*i], HANDLE_BEZMAJOR);
831 obj->handles[obj->num_handles-1]->connect_type = HANDLE_CONNECTABLE;
832 obj->handles[obj->num_handles-1]->connected_to = NULL;
833 obj->handles[obj->num_handles-1]->type = HANDLE_MAJOR_CONTROL;
834 obj->handles[obj->num_handles-1]->id = HANDLE_MOVE_ENDPOINT;
836 bezierconn_update_data(bez);
839 static void
840 bezierconn_point_change_free(struct PointChange *change)
842 if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
843 (change->type==TYPE_REMOVE_POINT && change->applied) ){
844 g_free(change->handle1);
845 g_free(change->handle2);
846 g_free(change->handle3);
847 change->handle1 = NULL;
848 change->handle2 = NULL;
849 change->handle3 = NULL;
853 static void
854 bezierconn_point_change_apply(struct PointChange *change, Object *obj)
856 change->applied = 1;
857 switch (change->type) {
858 case TYPE_ADD_POINT:
859 add_handles((BezierConn *)obj, change->pos, &change->point,
860 change->corner_type,
861 change->handle1, change->handle2, change->handle3);
862 break;
863 case TYPE_REMOVE_POINT:
864 object_unconnect(obj, change->handle1);
865 object_unconnect(obj, change->handle2);
866 object_unconnect(obj, change->handle3);
867 remove_handles((BezierConn *)obj, change->pos);
868 break;
872 static void
873 bezierconn_point_change_revert(struct PointChange *change, Object *obj)
875 switch (change->type) {
876 case TYPE_ADD_POINT:
877 remove_handles((BezierConn *)obj, change->pos);
878 break;
879 case TYPE_REMOVE_POINT:
880 add_handles((BezierConn *)obj, change->pos, &change->point,
881 change->corner_type,
882 change->handle1, change->handle2, change->handle3);
884 if (change->connected_to1)
885 object_connect(obj, change->handle1, change->connected_to1);
886 if (change->connected_to2)
887 object_connect(obj, change->handle2, change->connected_to2);
888 if (change->connected_to3)
889 object_connect(obj, change->handle3, change->connected_to3);
891 break;
893 change->applied = 0;
896 static ObjectChange *
897 bezierconn_create_point_change(BezierConn *bez, enum change_type type,
898 BezPoint *point, BezCornerType corner_type,
899 int pos,
900 Handle *handle1, ConnectionPoint *connected_to1,
901 Handle *handle2, ConnectionPoint *connected_to2,
902 Handle *handle3, ConnectionPoint *connected_to3)
904 struct PointChange *change;
906 change = g_new(struct PointChange, 1);
908 change->obj_change.apply = (ObjectChangeApplyFunc) bezierconn_point_change_apply;
909 change->obj_change.revert = (ObjectChangeRevertFunc) bezierconn_point_change_revert;
910 change->obj_change.free = (ObjectChangeFreeFunc) bezierconn_point_change_free;
912 change->type = type;
913 change->applied = 1;
914 change->point = *point;
915 change->corner_type = corner_type;
916 change->pos = pos;
917 change->handle1 = handle1;
918 change->connected_to1 = connected_to1;
919 change->handle2 = handle2;
920 change->connected_to2 = connected_to2;
921 change->handle3 = handle3;
922 change->connected_to3 = connected_to3;
924 return (ObjectChange *)change;
927 static void
928 bezierconn_corner_change_apply(struct CornerChange *change,
929 Object *obj) {
930 BezierConn *bez = (BezierConn *)obj;
931 int handle_nr = get_handle_nr(bez, change->handle);
932 int comp_nr = get_major_nr(handle_nr);
934 bezierconn_straighten_corner(bez, comp_nr);
936 bez->corner_types[comp_nr] = change->new_type;
938 change->applied = 1;
941 static void
942 bezierconn_corner_change_revert(struct CornerChange *change,
943 Object *obj) {
944 BezierConn *bez = (BezierConn *)obj;
945 int handle_nr = get_handle_nr(bez, change->handle);
946 int comp_nr = get_major_nr(handle_nr);
948 bez->points[comp_nr].p2 = change->point_left;
949 bez->points[comp_nr+1].p1 = change->point_right;
950 bez->corner_types[comp_nr] = change->old_type;
952 change->applied = 0;
955 static ObjectChange *
956 bezierconn_create_corner_change(BezierConn *bez, Handle *handle,
957 Point *point_left, Point *point_right,
958 BezCornerType old_corner_type,
959 BezCornerType new_corner_type)
961 struct CornerChange *change;
963 change = g_new(struct CornerChange, 1);
965 change->obj_change.apply = (ObjectChangeApplyFunc) bezierconn_corner_change_apply;
966 change->obj_change.revert = (ObjectChangeRevertFunc) bezierconn_corner_change_revert;
967 change->obj_change.free = (ObjectChangeFreeFunc) NULL;
969 change->old_type = old_corner_type;
970 change->new_type = new_corner_type;
971 change->applied = 1;
973 change->handle = handle;
974 change->point_left = *point_left;
975 change->point_right = *point_right;
977 return (ObjectChange *)change;