re-adding .pngs as binary
[dia.git] / lib / bezier_conn.c
blobc28940b4fdea08f09b2a193d5beb046a632e1d98
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 <assert.h>
26 #include <math.h>
27 #include <string.h> /* memcpy() */
29 #include "bezier_conn.h"
30 #include "intl.h"
31 #include "message.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 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 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 assert(pos > 0);
420 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 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, Renderer *renderer, real width)
574 BezPoint *points;
576 assert(bez != NULL);
577 assert(renderer != NULL);
579 points = &bez->points[0];
581 renderer->ops->set_linewidth(renderer, width);
582 renderer->ops->set_linestyle(renderer, LINESTYLE_SOLID);
583 renderer->ops->set_linejoin(renderer, LINEJOIN_ROUND);
584 renderer->ops->set_linecaps(renderer, LINECAPS_BUTT);
586 renderer->ops->draw_bezier(renderer, points, bez->numpoints, &color_black);
589 void
590 bezierconn_draw_control_lines(BezierConn *bez, Renderer *renderer)
592 Color line_colour = {0.0, 0.0, 0.6};
593 Point startpoint;
594 int i;
596 /* setup renderer ... */
597 renderer->ops->set_linewidth(renderer, 0);
598 renderer->ops->set_linestyle(renderer, LINESTYLE_DOTTED);
599 renderer->ops->set_dashlength(renderer, 1);
600 renderer->ops->set_linejoin(renderer, LINEJOIN_MITER);
601 renderer->ops->set_linecaps(renderer, LINECAPS_BUTT);
603 startpoint = bez->points[0].p1;
604 for (i = 1; i < bez->numpoints; i++) {
605 renderer->ops->draw_line(renderer, &startpoint, &bez->points[i].p1,
606 &line_colour);
607 renderer->ops->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 static void
664 bezierconn_set_points(BezierConn *bez, int num_points, BezPoint *points)
668 void
669 bezierconn_copy(BezierConn *from, BezierConn *to)
671 int i;
672 Object *toobj, *fromobj;
674 toobj = &to->object;
675 fromobj = &from->object;
677 object_copy(fromobj, toobj);
679 to->numpoints = from->numpoints;
681 to->points = g_new(BezPoint, to->numpoints);
682 to->corner_types = g_new(BezCornerType, to->numpoints);
684 for (i = 0; i < to->numpoints; i++) {
685 to->points[i] = from->points[i];
686 to->corner_types[i] = from->corner_types[i];
689 to->object.handles[0] = g_new(Handle,1);
690 *to->object.handles[0] = *from->object.handles[0];
691 for (i = 1; i < to->object.num_handles - 1; i++) {
692 to->object.handles[i] = g_new(Handle, 1);
693 setup_corner_handle(to->object.handles[i], from->object.handles[i]->id);
695 to->object.handles[to->object.num_handles-1] = g_new(Handle,1);
696 *to->object.handles[to->object.num_handles-1] =
697 *from->object.handles[to->object.num_handles-1];
699 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
700 bezierconn_update_data(to);
703 void
704 bezierconn_destroy(BezierConn *bez)
706 int i, nh;
707 Handle **temp_handles;
709 /* Need to store these temporary since object.handles is
710 freed by object_destroy() */
711 nh = bez->object.num_handles;
712 temp_handles = g_new(Handle *, nh);
713 for (i = 0; i < nh; i++)
714 temp_handles[i] = bez->object.handles[i];
716 object_destroy(&bez->object);
718 for (i = 0; i < nh; i++)
719 g_free(temp_handles[i]);
720 g_free(temp_handles);
722 g_free(bez->points);
723 g_free(bez->corner_types);
727 void
728 bezierconn_save(BezierConn *bez, ObjectNode obj_node)
730 int i;
731 AttributeNode attr;
733 object_save(&bez->object, obj_node);
735 attr = new_attribute(obj_node, "bez_points");
737 data_add_point(attr, &bez->points[0].p1);
738 for (i = 1; i < bez->numpoints; i++) {
739 data_add_point(attr, &bez->points[i].p1);
740 data_add_point(attr, &bez->points[i].p2);
741 data_add_point(attr, &bez->points[i].p3);
744 attr = new_attribute(obj_node, "corner_types");
745 for (i = 0; i < bez->numpoints; i++)
746 data_add_enum(attr, bez->corner_types[i]);
749 void
750 bezierconn_load(BezierConn *bez, ObjectNode obj_node) /* NOTE: Does object_init() */
752 int i;
753 AttributeNode attr;
754 DataNode data;
756 Object *obj = &bez->object;
758 object_load(obj, obj_node);
760 attr = object_find_attribute(obj_node, "bez_points");
762 if (attr != NULL)
763 bez->numpoints = (attribute_num_data(attr) + 2)/3;
764 else
765 bez->numpoints = 0;
767 object_init(obj, 3 * bez->numpoints - 2, 0);
769 data = attribute_first_data(attr);
770 if (bez->numpoints != 0) {
771 bez->points = g_new(BezPoint, bez->numpoints);
772 bez->points[0].type = BEZ_MOVE_TO;
773 data_point(data, &bez->points[0].p1);
774 data = data_next(data);
776 for (i = 1; i < bez->numpoints; i++) {
777 bez->points[i].type = BEZ_CURVE_TO;
778 data_point(data, &bez->points[i].p1);
779 data = data_next(data);
780 data_point(data, &bez->points[i].p2);
781 data = data_next(data);
782 data_point(data, &bez->points[i].p3);
783 data = data_next(data);
787 bez->corner_types = g_new(BezCornerType, bez->numpoints);
789 attr = object_find_attribute(obj_node, "corner_types");
790 /* if corner_types is missing or corrupt */
791 if (!attr || attribute_num_data(attr) != bez->numpoints) {
792 for (i = 0; i < bez->numpoints; i++) {
793 bez->corner_types[i] = BEZ_CORNER_SYMMETRIC;
795 } else {
796 data = attribute_first_data(attr);
797 for (i = 0; i < bez->numpoints; i++) {
798 bez->corner_types[i] = data_enum(data);
799 data = data_next(data);
803 obj->handles[0] = g_new(Handle, 1);
804 obj->handles[0]->connect_type = HANDLE_CONNECTABLE;
805 obj->handles[0]->connected_to = NULL;
806 obj->handles[0]->type = HANDLE_MAJOR_CONTROL;
807 obj->handles[0]->id = HANDLE_MOVE_STARTPOINT;
809 for (i = 1; i < bez->numpoints; i++) {
810 obj->handles[3*i-2] = g_new(Handle, 1);
811 setup_corner_handle(obj->handles[3*i-2], HANDLE_RIGHTCTRL);
812 obj->handles[3*i-1] = g_new(Handle, 1);
813 setup_corner_handle(obj->handles[3*i-1], HANDLE_LEFTCTRL);
814 obj->handles[3*i] = g_new(Handle, 1);
815 setup_corner_handle(obj->handles[3*i], HANDLE_BEZMAJOR);
818 obj->handles[obj->num_handles-1]->connect_type = HANDLE_CONNECTABLE;
819 obj->handles[obj->num_handles-1]->connected_to = NULL;
820 obj->handles[obj->num_handles-1]->type = HANDLE_MAJOR_CONTROL;
821 obj->handles[obj->num_handles-1]->id = HANDLE_MOVE_ENDPOINT;
823 bezierconn_update_data(bez);
826 static void
827 bezierconn_point_change_free(struct PointChange *change)
829 if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
830 (change->type==TYPE_REMOVE_POINT && change->applied) ){
831 g_free(change->handle1);
832 g_free(change->handle2);
833 g_free(change->handle3);
834 change->handle1 = NULL;
835 change->handle2 = NULL;
836 change->handle3 = NULL;
840 static void
841 bezierconn_point_change_apply(struct PointChange *change, Object *obj)
843 change->applied = 1;
844 switch (change->type) {
845 case TYPE_ADD_POINT:
846 add_handles((BezierConn *)obj, change->pos, &change->point,
847 change->corner_type,
848 change->handle1, change->handle2, change->handle3);
849 break;
850 case TYPE_REMOVE_POINT:
851 object_unconnect(obj, change->handle1);
852 object_unconnect(obj, change->handle2);
853 object_unconnect(obj, change->handle3);
854 remove_handles((BezierConn *)obj, change->pos);
855 break;
859 static void
860 bezierconn_point_change_revert(struct PointChange *change, Object *obj)
862 switch (change->type) {
863 case TYPE_ADD_POINT:
864 remove_handles((BezierConn *)obj, change->pos);
865 break;
866 case TYPE_REMOVE_POINT:
867 add_handles((BezierConn *)obj, change->pos, &change->point,
868 change->corner_type,
869 change->handle1, change->handle2, change->handle3);
871 if (change->connected_to1)
872 object_connect(obj, change->handle1, change->connected_to1);
873 if (change->connected_to2)
874 object_connect(obj, change->handle2, change->connected_to2);
875 if (change->connected_to3)
876 object_connect(obj, change->handle3, change->connected_to3);
878 break;
880 change->applied = 0;
883 static ObjectChange *
884 bezierconn_create_point_change(BezierConn *bez, enum change_type type,
885 BezPoint *point, BezCornerType corner_type,
886 int pos,
887 Handle *handle1, ConnectionPoint *connected_to1,
888 Handle *handle2, ConnectionPoint *connected_to2,
889 Handle *handle3, ConnectionPoint *connected_to3)
891 struct PointChange *change;
893 change = g_new(struct PointChange, 1);
895 change->obj_change.apply = (ObjectChangeApplyFunc) bezierconn_point_change_apply;
896 change->obj_change.revert = (ObjectChangeRevertFunc) bezierconn_point_change_revert;
897 change->obj_change.free = (ObjectChangeFreeFunc) bezierconn_point_change_free;
899 change->type = type;
900 change->applied = 1;
901 change->point = *point;
902 change->corner_type = corner_type;
903 change->pos = pos;
904 change->handle1 = handle1;
905 change->connected_to1 = connected_to1;
906 change->handle2 = handle2;
907 change->connected_to2 = connected_to2;
908 change->handle3 = handle3;
909 change->connected_to3 = connected_to3;
911 return (ObjectChange *)change;
914 static void
915 bezierconn_corner_change_apply(struct CornerChange *change,
916 Object *obj) {
917 BezierConn *bez = (BezierConn *)obj;
918 int handle_nr = get_handle_nr(bez, change->handle);
919 int comp_nr = get_major_nr(handle_nr);
921 bezierconn_straighten_corner(bez, comp_nr);
923 bez->corner_types[comp_nr] = change->new_type;
925 change->applied = 1;
928 static void
929 bezierconn_corner_change_revert(struct CornerChange *change,
930 Object *obj) {
931 BezierConn *bez = (BezierConn *)obj;
932 int handle_nr = get_handle_nr(bez, change->handle);
933 int comp_nr = get_major_nr(handle_nr);
935 bez->points[comp_nr].p2 = change->point_left;
936 bez->points[comp_nr+1].p1 = change->point_right;
937 bez->corner_types[comp_nr] = change->old_type;
939 change->applied = 0;
942 static ObjectChange *
943 bezierconn_create_corner_change(BezierConn *bez, Handle *handle,
944 Point *point_left, Point *point_right,
945 BezCornerType old_corner_type,
946 BezCornerType new_corner_type)
948 struct CornerChange *change;
950 change = g_new(struct CornerChange, 1);
952 change->obj_change.apply = (ObjectChangeApplyFunc) bezierconn_corner_change_apply;
953 change->obj_change.revert = (ObjectChangeRevertFunc) bezierconn_corner_change_revert;
954 change->obj_change.free = (ObjectChangeFreeFunc) NULL;
956 change->old_type = old_corner_type;
957 change->new_type = new_corner_type;
958 change->applied = 1;
960 change->handle = handle;
961 change->point_left = *point_left;
962 change->point_right = *point_right;
964 return (ObjectChange *)change;