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