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.
27 #include <string.h> /* memcpy() */
29 #include "beziershape.h"
31 #include "diarenderer.h"
33 #define HANDLE_BEZMAJOR (HANDLE_CUSTOM1)
34 #define HANDLE_LEFTCTRL (HANDLE_CUSTOM2)
35 #define HANDLE_RIGHTCTRL (HANDLE_CUSTOM3)
43 # of handles = 3*(numpoints-1)
44 # of connections = 2*(numpoints-1)
47 ObjectChange obj_change
;
49 enum change_type type
;
53 BezCornerType corner_type
;
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
;
63 ObjectChange obj_change
;
64 /* Only one kind of corner_change */
68 /* Old places when SET_CORNER_TYPE is applied */
69 Point point_left
, point_right
;
70 BezCornerType old_type
, new_type
;
74 beziershape_create_point_change(BezierShape
*bezier
, enum change_type type
,
75 BezPoint
*point
, BezCornerType corner_type
,
77 Handle
*handle1
, Handle
*handle2
, Handle
*handle3
,
78 ConnectionPoint
*cp1
, ConnectionPoint
*cp2
);
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
;
89 (handle_id
== HANDLE_BEZMAJOR
) ?
90 HANDLE_MAJOR_CONTROL
:
92 handle
->connect_type
= HANDLE_NONCONNECTABLE
;
93 handle
->connected_to
= NULL
;
97 static int get_handle_nr(BezierShape
*bezier
, Handle
*handle
)
100 for (i
= 0; i
< bezier
->object
.num_handles
; i
++) {
101 if (bezier
->object
.handles
[i
] == handle
)
107 #define get_comp_nr(hnum) ((int)(hnum)/3+1)
108 #define get_major_nr(hnum) (((int)(hnum)+2)/3)
111 beziershape_move_handle(BezierShape
*bezier
, Handle
*handle
,
112 Point
*to
, HandleMoveReason reason
)
114 int handle_nr
, comp_nr
, next_nr
, prev_nr
;
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)
127 prev_nr
= bezier
->numpoints
- 1;
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
);
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
);
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
;
151 case BEZ_CORNER_SMOOTH
: {
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
);
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
;
170 case BEZ_CORNER_CUSP
:
171 /* no mirror point movement required */
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
;
184 case BEZ_CORNER_SMOOTH
: {
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
);
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
;
203 case BEZ_CORNER_CUSP
:
204 /* no mirror point movement required */
209 message_error("Internal error in beziershape_move_handle.");
215 beziershape_move(BezierShape
*bezier
, Point
*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
)
236 real dist
= G_MAXDOUBLE
;
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
,
245 if (new_dist
< dist
) {
249 last
= bezier
->points
[i
].p3
;
255 beziershape_closest_handle(BezierShape
*bezier
, Point
*point
)
258 real dist
= G_MAXDOUBLE
;
259 Handle
*closest
= NULL
;
261 for (i
= 1, hn
= 0; i
< bezier
->numpoints
; i
++, hn
++) {
264 new_dist
= distance_point_point( point
, &bezier
->points
[i
].p1
);
265 if (new_dist
< dist
) {
267 closest
= bezier
->object
.handles
[hn
];
271 new_dist
= distance_point_point( point
, &bezier
->points
[i
].p2
);
272 if (new_dist
< dist
) {
274 closest
= bezier
->object
.handles
[hn
];
278 new_dist
= distance_point_point( point
, &bezier
->points
[i
].p3
);
279 if (new_dist
< dist
) {
281 closest
= bezier
->object
.handles
[hn
];
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
));
294 pos
= bezier
->numpoints
- 1;
295 return bezier
->object
.handles
[3*pos
- 1];
299 beziershape_distance_from(BezierShape
*bezier
, Point
*point
, real line_width
)
301 return distance_bez_shape_point(bezier
->points
, bezier
->numpoints
,
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
)
315 g_assert(pos
<= bezier
->numpoints
);
317 obj
= (Object
*)bezier
;
320 if (pos
== bezier
->numpoints
- 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
);
345 remove_handles(BezierShape
*bezier
, int pos
)
349 Handle
*old_handle1
, *old_handle2
, *old_handle3
;
350 ConnectionPoint
*old_cp1
, *old_cp2
;
355 g_assert(pos
< bezier
->numpoints
);
357 obj
= (Object
*)bezier
;
359 /* delete the points */
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 */
399 beziershape_add_segment(BezierShape
*bezier
, int segment
, Point
*point
)
402 BezCornerType corner_type
= BEZ_CORNER_SYMMETRIC
;
403 Handle
*new_handle1
, *new_handle2
, *new_handle3
;
404 ConnectionPoint
*new_cp1
, *new_cp2
;
409 startpoint
= bezier
->points
[segment
-1].p3
;
411 startpoint
= bezier
->points
[0].p1
;
412 other
= bezier
->points
[segment
].p3
;
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;
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
,
450 beziershape_remove_segment(BezierShape
*bezier
, int pos
)
452 Handle
*old_handle1
, *old_handle2
, *old_handle3
;
453 ConnectionPoint
*old_cp1
, *old_cp2
;
455 BezCornerType old_ctype
;
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
,
485 beziershape_straighten_corner(BezierShape
*bez
, int comp_nr
) {
488 if (comp_nr
== 0) comp_nr
= bez
->numpoints
- 1;
489 next_nr
= comp_nr
+ 1;
490 if (comp_nr
== bez
->numpoints
- 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
: {
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);
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
);
516 case BEZ_CORNER_SMOOTH
: {
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);
527 point_normalize(&pt1
);
529 point_normalize(&pt2
);
530 point_add(&pt1
, &pt2
);
531 point_scale(&pt1
, 0.5);
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
);
542 case BEZ_CORNER_CUSP
:
545 bez
->points
[0].p1
= bez
->points
[0].p3
;
549 beziershape_set_corner_type(BezierShape
*bez
, Handle
*handle
,
550 BezCornerType corner_type
)
552 Handle
*mid_handle
= NULL
;
553 Point old_left
, old_right
;
555 int handle_nr
, comp_nr
;
557 handle_nr
= get_handle_nr(bez
, handle
);
559 switch (handle
->id
) {
560 case HANDLE_BEZMAJOR
:
563 case HANDLE_LEFTCTRL
:
565 if (handle_nr
== bez
->object
.num_handles
) handle_nr
= 0;
566 mid_handle
= bez
->object
.handles
[handle_nr
];
568 case HANDLE_RIGHTCTRL
:
570 if (handle_nr
< 0) handle_nr
= bez
->object
.num_handles
- 1;
571 mid_handle
= bez
->object
.handles
[handle_nr
];
574 g_assert_not_reached();
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
;
585 old_right
= bez
->points
[comp_nr
+1].p1
;
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");
592 bez
->corner_types
[comp_nr
] = corner_type
;
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
);
605 beziershape_update_data(BezierShape
*bezier
)
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
;
633 beziershape_update_boundingbox(BezierShape
*bezier
)
635 ElementBBExtras
*extra
;
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],
648 &bezier
->object
.bounding_box
);
652 beziershape_simple_draw(BezierShape
*bezier
, DiaRenderer
*renderer
, real width
)
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
);
671 beziershape_draw_control_lines(BezierShape
*bez
, DiaRenderer
*renderer
)
673 Color line_colour
= {0.0, 0.0, 0.6};
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
,
688 DIA_RENDERER_GET_CLASS(renderer
)->draw_line(renderer
, &bez
->points
[i
].p2
, &bez
->points
[i
].p3
,
690 startpoint
= bez
->points
[i
].p3
;
695 beziershape_init(BezierShape
*bezier
, int num_points
)
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
742 /* beziershape_update_data(bezier);*/
746 /** This function does *not* set up handles */
748 beziershape_set_points(BezierShape
*bez
, int num_points
, BezPoint
*points
)
752 bez
->numpoints
= num_points
;
757 bez
->points
= g_malloc((bez
->numpoints
)*sizeof(BezPoint
));
759 for (i
=0;i
<bez
->numpoints
;i
++) {
760 bez
->points
[i
] = points
[i
];
765 beziershape_copy(BezierShape
*from
, BezierShape
*to
)
768 Object
*toobj
, *fromobj
;
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
);
799 beziershape_destroy(BezierShape
*bezier
)
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
++)
825 g_free(bezier
->points
);
826 g_free(bezier
->corner_types
);
831 beziershape_save(BezierShape
*bezier
, ObjectNode obj_node
)
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
]);
854 beziershape_load(BezierShape
*bezier
, ObjectNode obj_node
)
860 Object
*obj
= &bezier
->object
;
862 object_load(obj
, obj_node
);
864 attr
= object_find_attribute(obj_node
, "bez_points");
867 bezier
->numpoints
= attribute_num_data(attr
) / 3 + 1;
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
);
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
;
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
);
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
);
935 change
->handle1
= NULL
;
936 change
->handle2
= NULL
;
937 change
->handle3
= NULL
;
944 beziershape_point_change_apply(struct PointChange
*change
, Object
*obj
)
947 switch (change
->type
) {
949 add_handles((BezierShape
*)obj
, change
->pos
, &change
->point
,
951 change
->handle1
, change
->handle2
, change
->handle3
,
952 change
->cp1
, change
->cp2
);
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
);
964 beziershape_point_change_revert(struct PointChange
*change
, Object
*obj
)
966 switch (change
->type
) {
968 remove_handles((BezierShape
*)obj
, change
->pos
);
970 case TYPE_REMOVE_POINT
:
971 add_handles((BezierShape
*)obj
, change
->pos
, &change
->point
,
973 change
->handle1
, change
->handle2
, change
->handle3
,
974 change
->cp1
, change
->cp2
);
980 static ObjectChange
*
981 beziershape_create_point_change(BezierShape
*bezier
, enum change_type type
,
982 BezPoint
*point
, BezCornerType corner_type
,
984 Handle
*handle1
, Handle
*handle2
,
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
;
1000 change
->applied
= 1;
1001 change
->point
= *point
;
1002 change
->corner_type
= corner_type
;
1004 change
->handle1
= handle1
;
1005 change
->handle2
= handle2
;
1006 change
->handle3
= handle3
;
1010 return (ObjectChange
*)change
;
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
;
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;
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
;
1042 bez
->points
[comp_nr
+1].p1
= change
->point_right
;
1043 bez
->corner_types
[comp_nr
] = change
->old_type
;
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
;