* added GDK_PIXBUF_LIBS in order to create pixbuf.dll
[dia.git] / lib / beziershape.c
blobabd74bd58d249147aba272fd0a4f1659018d3282
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 setup_handle(Handle *handle, int handle_id)
87 handle->id = handle_id;
88 handle->type =
89 (handle_id == HANDLE_BEZMAJOR) ?
90 HANDLE_MAJOR_CONTROL :
91 HANDLE_MINOR_CONTROL;
92 handle->connect_type = HANDLE_NONCONNECTABLE;
93 handle->connected_to = NULL;
97 static int get_handle_nr(BezierShape *bezier, Handle *handle)
99 int i = 0;
100 for (i = 0; i < bezier->object.num_handles; i++) {
101 if (bezier->object.handles[i] == handle)
102 return i;
104 return -1;
107 #define get_comp_nr(hnum) ((int)(hnum)/3+1)
108 #define get_major_nr(hnum) (((int)(hnum)+2)/3)
110 void
111 beziershape_move_handle(BezierShape *bezier, Handle *handle,
112 Point *to, HandleMoveReason reason)
114 int handle_nr, comp_nr, next_nr, prev_nr;
115 Point delta, pt;
117 delta = *to;
118 point_sub(&delta, &handle->pos);
120 handle_nr = get_handle_nr(bezier, handle);
121 comp_nr = get_comp_nr(handle_nr);
122 next_nr = comp_nr + 1;
123 prev_nr = comp_nr - 1;
124 if (comp_nr == bezier->numpoints - 1)
125 next_nr = 1;
126 if (comp_nr == 1)
127 prev_nr = bezier->numpoints - 1;
129 switch(handle->id) {
130 case HANDLE_BEZMAJOR:
131 if (comp_nr == bezier->numpoints - 1) {
132 bezier->points[comp_nr].p3 = *to;
133 bezier->points[0].p1 = bezier->points[0].p3 = *to;
134 point_add(&bezier->points[comp_nr].p2, &delta);
135 point_add(&bezier->points[1].p1, &delta);
136 } else {
137 bezier->points[comp_nr].p3 = *to;
138 point_add(&bezier->points[comp_nr].p2, &delta);
139 point_add(&bezier->points[comp_nr+1].p1, &delta);
141 break;
142 case HANDLE_LEFTCTRL:
143 bezier->points[comp_nr].p2 = *to;
144 switch (bezier->corner_types[comp_nr]) {
145 case BEZ_CORNER_SYMMETRIC:
146 pt = bezier->points[comp_nr].p3;
147 point_sub(&pt, &bezier->points[comp_nr].p2);
148 point_add(&pt, &bezier->points[comp_nr].p3);
149 bezier->points[next_nr].p1 = pt;
150 break;
151 case BEZ_CORNER_SMOOTH: {
152 real len;
154 pt = bezier->points[next_nr].p1;
155 point_sub(&pt, &bezier->points[comp_nr].p3);
156 len = point_len(&pt);
158 pt = bezier->points[comp_nr].p3;
159 point_sub(&pt, &bezier->points[comp_nr].p2);
160 if (point_len(&pt) > 0)
161 point_normalize(&pt);
162 else {
163 pt.x = 1.0; pt.y = 0.0;
165 point_scale(&pt, len);
166 point_add(&pt, &bezier->points[comp_nr].p3);
167 bezier->points[next_nr].p1 = pt;
168 break;
170 case BEZ_CORNER_CUSP:
171 /* no mirror point movement required */
172 break;
174 break;
175 case HANDLE_RIGHTCTRL:
176 bezier->points[comp_nr].p1 = *to;
177 switch (bezier->corner_types[prev_nr]) {
178 case BEZ_CORNER_SYMMETRIC:
179 pt = bezier->points[prev_nr].p3;
180 point_sub(&pt, &bezier->points[comp_nr].p1);
181 point_add(&pt, &bezier->points[prev_nr].p3);
182 bezier->points[prev_nr].p2 = pt;
183 break;
184 case BEZ_CORNER_SMOOTH: {
185 real len;
187 pt = bezier->points[prev_nr].p2;
188 point_sub(&pt, &bezier->points[prev_nr].p3);
189 len = point_len(&pt);
191 pt = bezier->points[prev_nr].p3;
192 point_sub(&pt, &bezier->points[comp_nr].p1);
193 if (point_len(&pt) > 0)
194 point_normalize(&pt);
195 else {
196 pt.x = 1.0; pt.y = 0.0;
198 point_scale(&pt, len);
199 point_add(&pt, &bezier->points[prev_nr].p3);
200 bezier->points[prev_nr].p2 = pt;
201 break;
203 case BEZ_CORNER_CUSP:
204 /* no mirror point movement required */
205 break;
207 break;
208 default:
209 message_error("Internal error in beziershape_move_handle.");
210 break;
214 void
215 beziershape_move(BezierShape *bezier, Point *to)
217 Point p;
218 int i;
220 p = *to;
221 point_sub(&p, &bezier->points[0].p1);
223 bezier->points[0].p1 = bezier->points[0].p3 = *to;
224 for (i = 1; i < bezier->numpoints; i++) {
225 point_add(&bezier->points[i].p1, &p);
226 point_add(&bezier->points[i].p2, &p);
227 point_add(&bezier->points[i].p3, &p);
232 beziershape_closest_segment(BezierShape *bezier, Point *point, real line_width)
234 Point last;
235 int i;
236 real dist = G_MAXDOUBLE;
237 int closest;
239 closest = 0;
240 last = bezier->points[0].p1;
241 for (i = 1; i < bezier->numpoints; i++) {
242 real new_dist = distance_bez_seg_point(&last, &bezier->points[i].p1,
243 &bezier->points[i].p2, &bezier->points[i].p3,
244 line_width, point);
245 if (new_dist < dist) {
246 dist = new_dist;
247 closest = i;
249 last = bezier->points[i].p3;
251 return closest;
254 Handle *
255 beziershape_closest_handle(BezierShape *bezier, Point *point)
257 int i, hn;
258 real dist = G_MAXDOUBLE;
259 Handle *closest = NULL;
261 for (i = 1, hn = 0; i < bezier->numpoints; i++, hn++) {
262 real new_dist;
264 new_dist = distance_point_point( point, &bezier->points[i].p1);
265 if (new_dist < dist) {
266 dist = new_dist;
267 closest = bezier->object.handles[hn];
269 hn++;
271 new_dist = distance_point_point( point, &bezier->points[i].p2);
272 if (new_dist < dist) {
273 dist = new_dist;
274 closest = bezier->object.handles[hn];
277 hn++;
278 new_dist = distance_point_point( point, &bezier->points[i].p3);
279 if (new_dist < dist) {
280 dist = new_dist;
281 closest = bezier->object.handles[hn];
284 return closest;
287 Handle *
288 beziershape_closest_major_handle(BezierShape *bezier, Point *point)
290 Handle *closest = beziershape_closest_handle(bezier, point);
291 int pos = get_major_nr(get_handle_nr(bezier, closest));
293 if (pos == 0)
294 pos = bezier->numpoints - 1;
295 return bezier->object.handles[3*pos - 1];
298 real
299 beziershape_distance_from(BezierShape *bezier, Point *point, real line_width)
301 return distance_bez_shape_point(bezier->points, bezier->numpoints,
302 line_width, point);
305 static void
306 add_handles(BezierShape *bezier, int pos, BezPoint *point,
307 BezCornerType corner_type, Handle *handle1,
308 Handle *handle2, Handle *handle3,
309 ConnectionPoint *cp1, ConnectionPoint *cp2)
311 int i, next;
312 Object *obj;
314 g_assert(pos >= 1);
315 g_assert(pos <= bezier->numpoints);
317 obj = (Object *)bezier;
318 bezier->numpoints++;
319 next = pos + 1;
320 if (pos == bezier->numpoints - 1)
321 next = 1;
322 bezier->points = g_realloc(bezier->points,
323 bezier->numpoints * sizeof(BezPoint));
324 bezier->corner_types = g_realloc(bezier->corner_types,
325 bezier->numpoints * sizeof(BezCornerType));
327 for (i = bezier->numpoints - 1; i > pos; i--) {
328 bezier->points[i] = bezier->points[i-1];
329 bezier->corner_types[i] =bezier->corner_types[i-1];
331 bezier->points[pos] = *point;
332 bezier->points[pos].p1 = bezier->points[next].p1;
333 bezier->points[next].p1 = point->p1;
334 if (pos == bezier->numpoints - 1)
335 bezier->points[0].p1 = bezier->points[0].p3 = bezier->points[pos].p3;
336 bezier->corner_types[pos] = corner_type;
337 object_add_handle_at((Object*)bezier, handle1, 3*pos-3);
338 object_add_handle_at((Object*)bezier, handle2, 3*pos-2);
339 object_add_handle_at((Object*)bezier, handle3, 3*pos-1);
340 object_add_connectionpoint_at((Object *)bezier, cp1, 2*pos-1);
341 object_add_connectionpoint_at((Object *)bezier, cp2, 2*pos);
344 static void
345 remove_handles(BezierShape *bezier, int pos)
347 int i;
348 Object *obj;
349 Handle *old_handle1, *old_handle2, *old_handle3;
350 ConnectionPoint *old_cp1, *old_cp2;
351 Point tmppoint;
352 Point controlvector;
354 g_assert(pos > 0);
355 g_assert(pos < bezier->numpoints);
357 obj = (Object *)bezier;
359 /* delete the points */
360 bezier->numpoints--;
361 tmppoint = bezier->points[pos].p1;
362 if (pos == bezier->numpoints) {
363 controlvector = bezier->points[pos-1].p3;
364 point_sub(&controlvector, &bezier->points[pos].p1);
366 for (i = pos; i < bezier->numpoints; i++) {
367 bezier->points[i] = bezier->points[i+1];
368 bezier->corner_types[i] = bezier->corner_types[i+1];
370 bezier->points[pos].p1 = tmppoint;
371 if (pos == bezier->numpoints) {
372 /* If this was the last point, we also need to move points[0] and
373 the control point in points[1]. */
374 bezier->points[0].p1 = bezier->points[bezier->numpoints-1].p3;
375 bezier->points[1].p1 = bezier->points[0].p1;
376 point_sub(&bezier->points[1].p1, &controlvector);
378 bezier->points = g_realloc(bezier->points,
379 bezier->numpoints * sizeof(BezPoint));
380 bezier->corner_types = g_realloc(bezier->corner_types,
381 bezier->numpoints * sizeof(BezCornerType));
383 old_handle1 = obj->handles[3*pos-3];
384 old_handle2 = obj->handles[3*pos-2];
385 old_handle3 = obj->handles[3*pos-1];
386 object_remove_handle(&bezier->object, old_handle1);
387 object_remove_handle(&bezier->object, old_handle2);
388 object_remove_handle(&bezier->object, old_handle3);
389 old_cp1 = obj->connections[2*pos-2];
390 old_cp2 = obj->connections[2*pos-1];
391 object_remove_connectionpoint(&bezier->object, old_cp1);
392 object_remove_connectionpoint(&bezier->object, old_cp2);
396 /* Add a point by splitting segment into two, putting the new point at
397 'point' or, if NULL, in the middle */
398 ObjectChange *
399 beziershape_add_segment(BezierShape *bezier, int segment, Point *point)
401 BezPoint realpoint;
402 BezCornerType corner_type = BEZ_CORNER_SYMMETRIC;
403 Handle *new_handle1, *new_handle2, *new_handle3;
404 ConnectionPoint *new_cp1, *new_cp2;
405 Point startpoint;
406 Point other;
408 if (segment != 1)
409 startpoint = bezier->points[segment-1].p3;
410 else
411 startpoint = bezier->points[0].p1;
412 other = bezier->points[segment].p3;
413 if (point == NULL) {
414 realpoint.p1.x = (startpoint.x + other.x)/6;
415 realpoint.p1.y = (startpoint.y + other.y)/6;
416 realpoint.p2.x = (startpoint.x + other.x)/3;
417 realpoint.p2.y = (startpoint.y + other.y)/3;
418 realpoint.p3.x = (startpoint.x + other.x)/2;
419 realpoint.p3.y = (startpoint.y + other.y)/2;
420 } else {
421 realpoint.p2.x = point->x+(startpoint.x-other.x)/6;
422 realpoint.p2.y = point->y+(startpoint.y-other.y)/6;
424 realpoint.p3 = *point;
425 /* this really goes into the next segment ... */
426 realpoint.p1.x = point->x-(startpoint.x-other.x)/6;
427 realpoint.p1.y = point->y-(startpoint.y-other.y)/6;
429 realpoint.type = BEZ_CURVE_TO;
431 new_handle1 = g_new(Handle, 1);
432 new_handle2 = g_new(Handle, 1);
433 new_handle3 = g_new(Handle, 1);
434 setup_handle(new_handle1, HANDLE_RIGHTCTRL);
435 setup_handle(new_handle2, HANDLE_LEFTCTRL);
436 setup_handle(new_handle3, HANDLE_BEZMAJOR);
437 new_cp1 = g_new0(ConnectionPoint, 1);
438 new_cp2 = g_new0(ConnectionPoint, 1);
439 new_cp1->object = &bezier->object;
440 new_cp2->object = &bezier->object;
441 add_handles(bezier, segment, &realpoint, corner_type,
442 new_handle1, new_handle2, new_handle3, new_cp1, new_cp2);
443 return beziershape_create_point_change(bezier, TYPE_ADD_POINT,
444 &realpoint, corner_type, segment,
445 new_handle1, new_handle2, new_handle3,
446 new_cp1, new_cp2);
449 ObjectChange *
450 beziershape_remove_segment(BezierShape *bezier, int pos)
452 Handle *old_handle1, *old_handle2, *old_handle3;
453 ConnectionPoint *old_cp1, *old_cp2;
454 BezPoint old_point;
455 BezCornerType old_ctype;
457 g_assert(pos > 0);
458 g_assert(bezier->numpoints > 2);
459 g_assert(pos < bezier->numpoints);
461 old_handle1 = bezier->object.handles[3*pos-3];
462 old_handle2 = bezier->object.handles[3*pos-2];
463 old_handle3 = bezier->object.handles[3*pos-1];
464 old_point = bezier->points[pos];
465 old_ctype = bezier->corner_types[pos];
467 old_cp1 = bezier->object.connections[2*pos-2];
468 old_cp2 = bezier->object.connections[2*pos-1];
470 object_unconnect((Object *)bezier, old_handle1);
471 object_unconnect((Object *)bezier, old_handle2);
472 object_unconnect((Object *)bezier, old_handle3);
474 remove_handles(bezier, pos);
476 beziershape_update_data(bezier);
478 return beziershape_create_point_change(bezier, TYPE_REMOVE_POINT,
479 &old_point, old_ctype, pos,
480 old_handle1, old_handle2, old_handle3,
481 old_cp1, old_cp2);
484 static void
485 beziershape_straighten_corner(BezierShape *bez, int comp_nr) {
486 int next_nr;
488 if (comp_nr == 0) comp_nr = bez->numpoints - 1;
489 next_nr = comp_nr + 1;
490 if (comp_nr == bez->numpoints - 1)
491 next_nr = 1;
492 /* Neat thing would be to have the kind of straigthening depend on
493 which handle was chosen: Mid-handle does average, other leaves that
494 handle where it is. */
495 bez->points[0].p3 = bez->points[0].p1;
496 switch (bez->corner_types[comp_nr]) {
497 case BEZ_CORNER_SYMMETRIC: {
498 Point pt1, pt2;
500 pt1 = bez->points[comp_nr].p3;
501 point_sub(&pt1, &bez->points[comp_nr].p2);
502 pt2 = bez->points[comp_nr].p3;
503 point_sub(&pt2, &bez->points[next_nr].p1);
504 point_scale(&pt2, -1.0);
505 point_add(&pt1, &pt2);
506 point_scale(&pt1, 0.5);
507 pt2 = pt1;
508 point_scale(&pt1, -1.0);
509 point_add(&pt1, &bez->points[comp_nr].p3);
510 point_add(&pt2, &bez->points[comp_nr].p3);
511 bez->points[comp_nr].p2 = pt1;
512 bez->points[next_nr].p1 = pt2;
513 beziershape_update_data(bez);
515 break;
516 case BEZ_CORNER_SMOOTH: {
517 Point pt1, pt2;
518 real len1, len2;
519 pt1 = bez->points[comp_nr].p3;
520 point_sub(&pt1, &bez->points[comp_nr].p2);
521 pt2 = bez->points[comp_nr].p3;
522 point_sub(&pt2, &bez->points[next_nr].p1);
523 len1 = point_len(&pt1);
524 len2 = point_len(&pt2);
525 point_scale(&pt2, -1.0);
526 if (len1 > 0)
527 point_normalize(&pt1);
528 if (len2 > 0)
529 point_normalize(&pt2);
530 point_add(&pt1, &pt2);
531 point_scale(&pt1, 0.5);
532 pt2 = pt1;
533 point_scale(&pt1, -len1);
534 point_add(&pt1, &bez->points[comp_nr].p3);
535 point_scale(&pt2, len2);
536 point_add(&pt2, &bez->points[comp_nr].p3);
537 bez->points[comp_nr].p2 = pt1;
538 bez->points[next_nr].p1 = pt2;
539 beziershape_update_data(bez);
541 break;
542 case BEZ_CORNER_CUSP:
543 break;
545 bez->points[0].p1 = bez->points[0].p3;
548 ObjectChange *
549 beziershape_set_corner_type(BezierShape *bez, Handle *handle,
550 BezCornerType corner_type)
552 Handle *mid_handle = NULL;
553 Point old_left, old_right;
554 int old_type;
555 int handle_nr, comp_nr;
557 handle_nr = get_handle_nr(bez, handle);
559 switch (handle->id) {
560 case HANDLE_BEZMAJOR:
561 mid_handle = handle;
562 break;
563 case HANDLE_LEFTCTRL:
564 handle_nr++;
565 if (handle_nr == bez->object.num_handles) handle_nr = 0;
566 mid_handle = bez->object.handles[handle_nr];
567 break;
568 case HANDLE_RIGHTCTRL:
569 handle_nr--;
570 if (handle_nr < 0) handle_nr = bez->object.num_handles - 1;
571 mid_handle = bez->object.handles[handle_nr];
572 break;
573 default:
574 g_assert_not_reached();
575 break;
578 comp_nr = get_major_nr(handle_nr);
580 old_type = bez->corner_types[comp_nr];
581 old_left = bez->points[comp_nr].p2;
582 if (comp_nr == bez->numpoints - 1)
583 old_right = bez->points[1].p1;
584 else
585 old_right = bez->points[comp_nr+1].p1;
587 #if 0
588 g_message("Setting corner type on segment %d to %s", comp_nr,
589 corner_type == BEZ_CORNER_SYMMETRIC ? "symmetric" :
590 corner_type == BEZ_CORNER_SMOOTH ? "smooth" : "cusp");
591 #endif
592 bez->corner_types[comp_nr] = corner_type;
593 if (comp_nr == 0)
594 bez->corner_types[bez->numpoints-1] = corner_type;
595 else if (comp_nr == bez->numpoints - 1)
596 bez->corner_types[0] = corner_type;
598 beziershape_straighten_corner(bez, comp_nr);
600 return beziershape_create_corner_change(bez, mid_handle, &old_left,
601 &old_right, old_type, corner_type);
604 void
605 beziershape_update_data(BezierShape *bezier)
607 int i;
608 Point last;
610 /* Update handles: */
611 bezier->points[0].p3 = bezier->points[0].p1;
612 for (i = 1; i < bezier->numpoints; i++) {
613 bezier->object.handles[3*i-3]->pos = bezier->points[i].p1;
614 bezier->object.handles[3*i-2]->pos = bezier->points[i].p2;
615 bezier->object.handles[3*i-1]->pos = bezier->points[i].p3;
618 /* Update connection points: */
619 last = bezier->points[0].p1;
620 for (i = 1; i < bezier->numpoints; i++) {
621 bezier->object.connections[2*i-2]->pos = last;
622 bezier->object.connections[2*i-1]->pos.x =
623 (last.x + 3*bezier->points[i].p1.x + 3*bezier->points[i].p2.x +
624 bezier->points[i].p3.x)/8;
625 bezier->object.connections[2*i-1]->pos.y =
626 (last.y + 3*bezier->points[i].p1.y + 3*bezier->points[i].p2.y +
627 bezier->points[i].p3.y)/8;
628 last = bezier->points[i].p3;
632 void
633 beziershape_update_boundingbox(BezierShape *bezier)
635 ElementBBExtras *extra;
636 PolyBBExtras pextra;
638 g_assert(bezier != NULL);
640 extra = &bezier->extra_spacing;
641 pextra.start_trans = pextra.end_trans =
642 pextra.start_long = pextra.end_long = 0;
643 pextra.middle_trans = extra->border_trans;
645 polybezier_bbox(&bezier->points[0],
646 bezier->numpoints,
647 &pextra, TRUE,
648 &bezier->object.bounding_box);
651 void
652 beziershape_simple_draw(BezierShape *bezier, DiaRenderer *renderer, real width)
654 BezPoint *points;
656 g_assert(bezier != NULL);
657 g_assert(renderer != NULL);
659 points = &bezier->points[0];
661 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, width);
662 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
663 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_ROUND);
664 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
666 DIA_RENDERER_GET_CLASS(renderer)->fill_bezier(renderer, points, bezier->numpoints,&color_white);
667 DIA_RENDERER_GET_CLASS(renderer)->draw_bezier(renderer, points, bezier->numpoints,&color_black);
670 void
671 beziershape_draw_control_lines(BezierShape *bez, DiaRenderer *renderer)
673 Color line_colour = {0.0, 0.0, 0.6};
674 Point startpoint;
675 int i;
677 /* setup renderer ... */
678 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, 0);
679 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_DOTTED);
680 DIA_RENDERER_GET_CLASS(renderer)->set_dashlength(renderer, 1);
681 DIA_RENDERER_GET_CLASS(renderer)->set_linejoin(renderer, LINEJOIN_MITER);
682 DIA_RENDERER_GET_CLASS(renderer)->set_linecaps(renderer, LINECAPS_BUTT);
684 startpoint = bez->points[0].p1;
685 for (i = 1; i < bez->numpoints; i++) {
686 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &startpoint, &bez->points[i].p1,
687 &line_colour);
688 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &bez->points[i].p2, &bez->points[i].p3,
689 &line_colour);
690 startpoint = bez->points[i].p3;
694 void
695 beziershape_init(BezierShape *bezier, int num_points)
697 Object *obj;
698 int i;
700 obj = &bezier->object;
702 object_init(obj, 3*(num_points-1), 2*(num_points-1));
704 bezier->numpoints = num_points;
706 bezier->points = g_new(BezPoint, num_points);
707 bezier->points[0].type = BEZ_MOVE_TO;
708 bezier->corner_types = g_new(BezCornerType, num_points);
709 for (i = 1; i < num_points; i++) {
710 bezier->points[i].type = BEZ_CURVE_TO;
711 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
714 for (i = 0; i < num_points-1; i++) {
715 obj->handles[3*i] = g_new(Handle, 1);
716 obj->handles[3*i+1] = g_new(Handle, 1);
717 obj->handles[3*i+2] = g_new(Handle, 1);
719 obj->handles[3*i]->connect_type = HANDLE_NONCONNECTABLE;
720 obj->handles[3*i]->connected_to = NULL;
721 obj->handles[3*i]->type = HANDLE_MINOR_CONTROL;
722 obj->handles[3*i]->id = HANDLE_RIGHTCTRL;
724 obj->handles[3*i+1]->connect_type = HANDLE_NONCONNECTABLE;
725 obj->handles[3*i+1]->connected_to = NULL;
726 obj->handles[3*i+1]->type = HANDLE_MINOR_CONTROL;
727 obj->handles[3*i+1]->id = HANDLE_LEFTCTRL;
729 obj->handles[3*i+2]->connect_type = HANDLE_NONCONNECTABLE;
730 obj->handles[3*i+2]->connected_to = NULL;
731 obj->handles[3*i+2]->type = HANDLE_MAJOR_CONTROL;
732 obj->handles[3*i+2]->id = HANDLE_BEZMAJOR;
734 obj->connections[2*i] = g_new0(ConnectionPoint, 1);
735 obj->connections[2*i+1] = g_new0(ConnectionPoint, 1);
736 obj->connections[2*i]->object = obj;
737 obj->connections[2*i+1]->object = obj;
740 /* The points are not assigned at this point, so don't try to use
741 them */
742 /* beziershape_update_data(bezier);*/
746 /** This function does *not* set up handles */
747 void
748 beziershape_set_points(BezierShape *bez, int num_points, BezPoint *points)
750 int i;
752 bez->numpoints = num_points;
754 if (bez->points)
755 g_free(bez->points);
757 bez->points = g_malloc((bez->numpoints)*sizeof(BezPoint));
759 for (i=0;i<bez->numpoints;i++) {
760 bez->points[i] = points[i];
764 void
765 beziershape_copy(BezierShape *from, BezierShape *to)
767 int i;
768 Object *toobj, *fromobj;
770 toobj = &to->object;
771 fromobj = &from->object;
773 object_copy(fromobj, toobj);
775 to->numpoints = from->numpoints;
777 to->points = g_new(BezPoint, to->numpoints);
778 to->corner_types = g_new(BezCornerType, to->numpoints);
780 for (i = 0; i < to->numpoints; i++) {
781 to->points[i] = from->points[i];
782 to->corner_types[i] = from->corner_types[i];
785 for (i = 0; i < to->object.num_handles; i++) {
786 to->object.handles[i] = g_new(Handle, 1);
787 setup_handle(to->object.handles[i], from->object.handles[i]->id);
789 for (i = 0; i < to->object.num_connections; i++) {
790 to->object.connections[i] = g_new0(ConnectionPoint, 1);
791 to->object.connections[i]->object = &to->object;
794 memcpy(&to->extra_spacing,&from->extra_spacing,sizeof(to->extra_spacing));
795 beziershape_update_data(to);
798 void
799 beziershape_destroy(BezierShape *bezier)
801 int i;
802 Handle **temp_handles;
803 ConnectionPoint **temp_cps;
805 /* Need to store these temporary since object.handles is
806 freed by object_destroy() */
807 temp_handles = g_new(Handle *, bezier->object.num_handles);
808 for (i = 0; i < bezier->object.num_handles; i++)
809 temp_handles[i] = bezier->object.handles[i];
811 temp_cps = g_new(ConnectionPoint *, bezier->object.num_connections);
812 for (i = 0; i < bezier->object.num_connections; i++)
813 temp_cps[i] = bezier->object.connections[i];
815 object_destroy(&bezier->object);
817 for (i = 0; i < bezier->object.num_handles; i++)
818 g_free(temp_handles[i]);
819 g_free(temp_handles);
821 for (i = 0; i < bezier->object.num_connections; i++)
822 g_free(temp_cps[i]);
823 g_free(temp_cps);
825 g_free(bezier->points);
826 g_free(bezier->corner_types);
830 void
831 beziershape_save(BezierShape *bezier, ObjectNode obj_node)
833 int i;
834 AttributeNode attr;
836 object_save(&bezier->object, obj_node);
838 attr = new_attribute(obj_node, "bez_points");
840 data_add_point(attr, &bezier->points[0].p1);
841 for (i = 1; i < bezier->numpoints; i++) {
842 data_add_point(attr, &bezier->points[i].p1);
843 data_add_point(attr, &bezier->points[i].p2);
844 if (i < bezier->numpoints - 1)
845 data_add_point(attr, &bezier->points[i].p3);
848 attr = new_attribute(obj_node, "corner_types");
849 for (i = 0; i < bezier->numpoints; i++)
850 data_add_enum(attr, bezier->corner_types[i]);
853 void
854 beziershape_load(BezierShape *bezier, ObjectNode obj_node)
856 int i;
857 AttributeNode attr;
858 DataNode data;
860 Object *obj = &bezier->object;
862 object_load(obj, obj_node);
864 attr = object_find_attribute(obj_node, "bez_points");
866 if (attr != NULL)
867 bezier->numpoints = attribute_num_data(attr) / 3 + 1;
868 else
869 bezier->numpoints = 0;
871 object_init(obj, 3 * (bezier->numpoints - 1), 2 * (bezier->numpoints - 1));
873 data = attribute_first_data(attr);
874 if (bezier->numpoints != 0) {
875 bezier->points = g_new(BezPoint, bezier->numpoints);
876 bezier->points[0].type = BEZ_MOVE_TO;
877 data_point(data, &bezier->points[0].p1);
878 bezier->points[0].p3 = bezier->points[0].p1;
879 data = data_next(data);
881 for (i = 1; i < bezier->numpoints; i++) {
882 bezier->points[i].type = BEZ_CURVE_TO;
883 data_point(data, &bezier->points[i].p1);
884 data = data_next(data);
885 data_point(data, &bezier->points[i].p2);
886 data = data_next(data);
887 if (i < bezier->numpoints - 1) {
888 data_point(data, &bezier->points[i].p3);
889 data = data_next(data);
890 } else
891 bezier->points[i].p3 = bezier->points[0].p1;
895 bezier->corner_types = g_new(BezCornerType, bezier->numpoints);
896 attr = object_find_attribute(obj_node, "corner_types");
897 if (!attr || attribute_num_data(attr) != bezier->numpoints) {
898 for (i = 0; i < bezier->numpoints; i++)
899 bezier->corner_types[i] = BEZ_CORNER_SYMMETRIC;
900 } else {
901 data = attribute_first_data(attr);
902 for (i = 0; i < bezier->numpoints; i++) {
903 bezier->corner_types[i] = data_enum(data);
904 data = data_next(data);
908 for (i = 0; i < bezier->numpoints - 1; i++) {
909 obj->handles[3*i] = g_new(Handle, 1);
910 obj->handles[3*i+1] = g_new(Handle, 1);
911 obj->handles[3*i+2] = g_new(Handle, 1);
913 setup_handle(obj->handles[3*i], HANDLE_RIGHTCTRL);
914 setup_handle(obj->handles[3*i+1], HANDLE_LEFTCTRL);
915 setup_handle(obj->handles[3*i+2], HANDLE_BEZMAJOR);
917 for (i = 0; i < bezier->object.num_connections; i++) {
918 obj->connections[i] = g_new0(ConnectionPoint, 1);
919 obj->connections[i]->object = obj;
922 beziershape_update_data(bezier);
925 static void
926 beziershape_point_change_free(struct PointChange *change)
928 if ( (change->type==TYPE_ADD_POINT && !change->applied) ||
929 (change->type==TYPE_REMOVE_POINT && change->applied) ){
930 g_free(change->handle1);
931 g_free(change->handle2);
932 g_free(change->handle3);
933 g_free(change->cp1);
934 g_free(change->cp2);
935 change->handle1 = NULL;
936 change->handle2 = NULL;
937 change->handle3 = NULL;
938 change->cp1 = NULL;
939 change->cp2 = NULL;
943 static void
944 beziershape_point_change_apply(struct PointChange *change, Object *obj)
946 change->applied = 1;
947 switch (change->type) {
948 case TYPE_ADD_POINT:
949 add_handles((BezierShape *)obj, change->pos, &change->point,
950 change->corner_type,
951 change->handle1, change->handle2, change->handle3,
952 change->cp1, change->cp2);
953 break;
954 case TYPE_REMOVE_POINT:
955 object_unconnect(obj, change->handle1);
956 object_unconnect(obj, change->handle2);
957 object_unconnect(obj, change->handle3);
958 remove_handles((BezierShape *)obj, change->pos);
959 break;
963 static void
964 beziershape_point_change_revert(struct PointChange *change, Object *obj)
966 switch (change->type) {
967 case TYPE_ADD_POINT:
968 remove_handles((BezierShape *)obj, change->pos);
969 break;
970 case TYPE_REMOVE_POINT:
971 add_handles((BezierShape *)obj, change->pos, &change->point,
972 change->corner_type,
973 change->handle1, change->handle2, change->handle3,
974 change->cp1, change->cp2);
975 break;
977 change->applied = 0;
980 static ObjectChange *
981 beziershape_create_point_change(BezierShape *bezier, enum change_type type,
982 BezPoint *point, BezCornerType corner_type,
983 int pos,
984 Handle *handle1, Handle *handle2,
985 Handle *handle3,
986 ConnectionPoint *cp1, ConnectionPoint *cp2)
988 struct PointChange *change;
990 change = g_new(struct PointChange, 1);
992 change->obj_change.apply =
993 (ObjectChangeApplyFunc)beziershape_point_change_apply;
994 change->obj_change.revert =
995 (ObjectChangeRevertFunc)beziershape_point_change_revert;
996 change->obj_change.free =
997 (ObjectChangeFreeFunc)beziershape_point_change_free;
999 change->type = type;
1000 change->applied = 1;
1001 change->point = *point;
1002 change->corner_type = corner_type;
1003 change->pos = pos;
1004 change->handle1 = handle1;
1005 change->handle2 = handle2;
1006 change->handle3 = handle3;
1007 change->cp1 = cp1;
1008 change->cp2 = cp2;
1010 return (ObjectChange *)change;
1013 static void
1014 beziershape_corner_change_apply(struct CornerChange *change, Object *obj)
1016 BezierShape *bez = (BezierShape *)obj;
1017 int handle_nr = get_handle_nr(bez, change->handle);
1018 int comp_nr = get_major_nr(handle_nr);
1020 beziershape_straighten_corner(bez, comp_nr);
1022 bez->corner_types[comp_nr] = change->new_type;
1023 if (comp_nr == 0)
1024 bez->corner_types[bez->numpoints-1] = change->new_type;
1025 if (comp_nr == bez->numpoints - 1)
1026 bez->corner_types[0] = change->new_type;
1028 change->applied = 1;
1031 static void
1032 beziershape_corner_change_revert(struct CornerChange *change, Object *obj)
1034 BezierShape *bez = (BezierShape *)obj;
1035 int handle_nr = get_handle_nr(bez, change->handle);
1036 int comp_nr = get_major_nr(handle_nr);
1038 bez->points[comp_nr].p2 = change->point_left;
1039 if (comp_nr == bez->numpoints - 1)
1040 bez->points[1].p1 = change->point_right;
1041 else
1042 bez->points[comp_nr+1].p1 = change->point_right;
1043 bez->corner_types[comp_nr] = change->old_type;
1044 if (comp_nr == 0)
1045 bez->corner_types[bez->numpoints-1] = change->new_type;
1046 if (comp_nr == bez->numpoints - 1)
1047 bez->corner_types[0] = change->new_type;
1049 change->applied = 0;
1052 static ObjectChange *
1053 beziershape_create_corner_change(BezierShape *bez, Handle *handle,
1054 Point *point_left, Point *point_right,
1055 BezCornerType old_corner_type,
1056 BezCornerType new_corner_type)
1058 struct CornerChange *change;
1060 change = g_new(struct CornerChange, 1);
1062 change->obj_change.apply =
1063 (ObjectChangeApplyFunc)beziershape_corner_change_apply;
1064 change->obj_change.revert =
1065 (ObjectChangeRevertFunc)beziershape_corner_change_revert;
1066 change->obj_change.free = (ObjectChangeFreeFunc)NULL;
1068 change->old_type = old_corner_type;
1069 change->new_type = new_corner_type;
1070 change->applied = 1;
1072 change->handle = handle;
1073 change->point_left = *point_left;
1074 change->point_right = *point_right;
1076 return (ObjectChange *)change;