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) + 1 (main point)
45 For historical reasons, the main point is the last cp.
48 ObjectChange obj_change
;
50 enum change_type type
;
54 BezCornerType corner_type
;
57 /* owning ref when not applied for ADD_POINT
58 * owning ref when applied for REMOVE_POINT */
59 Handle
*handle1
, *handle2
, *handle3
;
60 ConnectionPoint
*cp1
, *cp2
;
64 ObjectChange obj_change
;
65 /* Only one kind of corner_change */
69 /* Old places when SET_CORNER_TYPE is applied */
70 Point point_left
, point_right
;
71 BezCornerType old_type
, new_type
;
75 beziershape_create_point_change(BezierShape
*bezier
, enum change_type type
,
76 BezPoint
*point
, BezCornerType corner_type
,
78 Handle
*handle1
, Handle
*handle2
, Handle
*handle3
,
79 ConnectionPoint
*cp1
, ConnectionPoint
*cp2
);
81 beziershape_create_corner_change(BezierShape
*bezier
, Handle
*handle
,
82 Point
*point_left
, Point
*point_right
,
83 BezCornerType old_corner_type
,
84 BezCornerType new_corner_type
);
86 static void new_handles_and_connections(BezierShape
*bezier
, int num_points
);
88 static void setup_handle(Handle
*handle
, int handle_id
)
90 handle
->id
= handle_id
;
92 (handle_id
== HANDLE_BEZMAJOR
) ?
93 HANDLE_MAJOR_CONTROL
:
95 handle
->connect_type
= HANDLE_NONCONNECTABLE
;
96 handle
->connected_to
= NULL
;
100 static int get_handle_nr(BezierShape
*bezier
, Handle
*handle
)
103 for (i
= 0; i
< bezier
->object
.num_handles
; i
++) {
104 if (bezier
->object
.handles
[i
] == handle
)
110 #define get_comp_nr(hnum) ((int)(hnum)/3+1)
111 #define get_major_nr(hnum) (((int)(hnum)+2)/3)
114 beziershape_move_handle(BezierShape
*bezier
, Handle
*handle
,
115 Point
*to
, ConnectionPoint
*cp
,
116 HandleMoveReason reason
, ModifierKeys modifiers
)
118 int handle_nr
, comp_nr
, next_nr
, prev_nr
;
122 point_sub(&delta
, &handle
->pos
);
124 handle_nr
= get_handle_nr(bezier
, handle
);
125 comp_nr
= get_comp_nr(handle_nr
);
126 next_nr
= comp_nr
+ 1;
127 prev_nr
= comp_nr
- 1;
128 if (comp_nr
== bezier
->numpoints
- 1)
131 prev_nr
= bezier
->numpoints
- 1;
134 case HANDLE_BEZMAJOR
:
135 if (comp_nr
== bezier
->numpoints
- 1) {
136 bezier
->points
[comp_nr
].p3
= *to
;
137 bezier
->points
[0].p1
= bezier
->points
[0].p3
= *to
;
138 point_add(&bezier
->points
[comp_nr
].p2
, &delta
);
139 point_add(&bezier
->points
[1].p1
, &delta
);
141 bezier
->points
[comp_nr
].p3
= *to
;
142 point_add(&bezier
->points
[comp_nr
].p2
, &delta
);
143 point_add(&bezier
->points
[comp_nr
+1].p1
, &delta
);
146 case HANDLE_LEFTCTRL
:
147 bezier
->points
[comp_nr
].p2
= *to
;
148 switch (bezier
->corner_types
[comp_nr
]) {
149 case BEZ_CORNER_SYMMETRIC
:
150 pt
= bezier
->points
[comp_nr
].p3
;
151 point_sub(&pt
, &bezier
->points
[comp_nr
].p2
);
152 point_add(&pt
, &bezier
->points
[comp_nr
].p3
);
153 bezier
->points
[next_nr
].p1
= pt
;
155 case BEZ_CORNER_SMOOTH
: {
158 pt
= bezier
->points
[next_nr
].p1
;
159 point_sub(&pt
, &bezier
->points
[comp_nr
].p3
);
160 len
= point_len(&pt
);
162 pt
= bezier
->points
[comp_nr
].p3
;
163 point_sub(&pt
, &bezier
->points
[comp_nr
].p2
);
164 if (point_len(&pt
) > 0)
165 point_normalize(&pt
);
167 pt
.x
= 1.0; pt
.y
= 0.0;
169 point_scale(&pt
, len
);
170 point_add(&pt
, &bezier
->points
[comp_nr
].p3
);
171 bezier
->points
[next_nr
].p1
= pt
;
174 case BEZ_CORNER_CUSP
:
175 /* no mirror point movement required */
179 case HANDLE_RIGHTCTRL
:
180 bezier
->points
[comp_nr
].p1
= *to
;
181 switch (bezier
->corner_types
[prev_nr
]) {
182 case BEZ_CORNER_SYMMETRIC
:
183 pt
= bezier
->points
[prev_nr
].p3
;
184 point_sub(&pt
, &bezier
->points
[comp_nr
].p1
);
185 point_add(&pt
, &bezier
->points
[prev_nr
].p3
);
186 bezier
->points
[prev_nr
].p2
= pt
;
188 case BEZ_CORNER_SMOOTH
: {
191 pt
= bezier
->points
[prev_nr
].p2
;
192 point_sub(&pt
, &bezier
->points
[prev_nr
].p3
);
193 len
= point_len(&pt
);
195 pt
= bezier
->points
[prev_nr
].p3
;
196 point_sub(&pt
, &bezier
->points
[comp_nr
].p1
);
197 if (point_len(&pt
) > 0)
198 point_normalize(&pt
);
200 pt
.x
= 1.0; pt
.y
= 0.0;
202 point_scale(&pt
, len
);
203 point_add(&pt
, &bezier
->points
[prev_nr
].p3
);
204 bezier
->points
[prev_nr
].p2
= pt
;
207 case BEZ_CORNER_CUSP
:
208 /* no mirror point movement required */
213 message_error("Internal error in beziershape_move_handle.");
220 beziershape_move(BezierShape
*bezier
, Point
*to
)
226 point_sub(&p
, &bezier
->points
[0].p1
);
228 bezier
->points
[0].p1
= bezier
->points
[0].p3
= *to
;
229 for (i
= 1; i
< bezier
->numpoints
; i
++) {
230 point_add(&bezier
->points
[i
].p1
, &p
);
231 point_add(&bezier
->points
[i
].p2
, &p
);
232 point_add(&bezier
->points
[i
].p3
, &p
);
239 beziershape_closest_segment(BezierShape
*bezier
, Point
*point
, real line_width
)
243 real dist
= G_MAXDOUBLE
;
247 last
= bezier
->points
[0].p1
;
248 for (i
= 1; i
< bezier
->numpoints
; i
++) {
249 real new_dist
= distance_bez_seg_point(&last
, &bezier
->points
[i
].p1
,
250 &bezier
->points
[i
].p2
, &bezier
->points
[i
].p3
,
252 if (new_dist
< dist
) {
256 last
= bezier
->points
[i
].p3
;
262 beziershape_closest_handle(BezierShape
*bezier
, Point
*point
)
265 real dist
= G_MAXDOUBLE
;
266 Handle
*closest
= NULL
;
268 for (i
= 1, hn
= 0; i
< bezier
->numpoints
; i
++, hn
++) {
271 new_dist
= distance_point_point( point
, &bezier
->points
[i
].p1
);
272 if (new_dist
< dist
) {
274 closest
= bezier
->object
.handles
[hn
];
278 new_dist
= distance_point_point( point
, &bezier
->points
[i
].p2
);
279 if (new_dist
< dist
) {
281 closest
= bezier
->object
.handles
[hn
];
285 new_dist
= distance_point_point( point
, &bezier
->points
[i
].p3
);
286 if (new_dist
< dist
) {
288 closest
= bezier
->object
.handles
[hn
];
295 beziershape_closest_major_handle(BezierShape
*bezier
, Point
*point
)
297 Handle
*closest
= beziershape_closest_handle(bezier
, point
);
298 int pos
= get_major_nr(get_handle_nr(bezier
, closest
));
301 pos
= bezier
->numpoints
- 1;
302 return bezier
->object
.handles
[3*pos
- 1];
306 beziershape_distance_from(BezierShape
*bezier
, Point
*point
, real line_width
)
308 return distance_bez_shape_point(bezier
->points
, bezier
->numpoints
,
313 add_handles(BezierShape
*bezier
, int pos
, BezPoint
*point
,
314 BezCornerType corner_type
, Handle
*handle1
,
315 Handle
*handle2
, Handle
*handle3
,
316 ConnectionPoint
*cp1
, ConnectionPoint
*cp2
)
322 g_assert(pos
<= bezier
->numpoints
);
324 obj
= (DiaObject
*)bezier
;
327 if (pos
== bezier
->numpoints
- 1)
329 bezier
->points
= g_realloc(bezier
->points
,
330 bezier
->numpoints
* sizeof(BezPoint
));
331 bezier
->corner_types
= g_realloc(bezier
->corner_types
,
332 bezier
->numpoints
* sizeof(BezCornerType
));
334 for (i
= bezier
->numpoints
- 1; i
> pos
; i
--) {
335 bezier
->points
[i
] = bezier
->points
[i
-1];
336 bezier
->corner_types
[i
] =bezier
->corner_types
[i
-1];
338 bezier
->points
[pos
] = *point
;
339 bezier
->points
[pos
].p1
= bezier
->points
[next
].p1
;
340 bezier
->points
[next
].p1
= point
->p1
;
341 if (pos
== bezier
->numpoints
- 1)
342 bezier
->points
[0].p1
= bezier
->points
[0].p3
= bezier
->points
[pos
].p3
;
343 bezier
->corner_types
[pos
] = corner_type
;
344 object_add_handle_at((DiaObject
*)bezier
, handle1
, 3*pos
-3);
345 object_add_handle_at((DiaObject
*)bezier
, handle2
, 3*pos
-2);
346 object_add_handle_at((DiaObject
*)bezier
, handle3
, 3*pos
-1);
347 object_add_connectionpoint_at((DiaObject
*)bezier
, cp1
, 2*pos
-2);
348 object_add_connectionpoint_at((DiaObject
*)bezier
, cp2
, 2*pos
-1);
352 remove_handles(BezierShape
*bezier
, int pos
)
356 Handle
*old_handle1
, *old_handle2
, *old_handle3
;
357 ConnectionPoint
*old_cp1
, *old_cp2
;
362 g_assert(pos
< bezier
->numpoints
);
364 obj
= (DiaObject
*)bezier
;
366 /* delete the points */
368 tmppoint
= bezier
->points
[pos
].p1
;
369 if (pos
== bezier
->numpoints
) {
370 controlvector
= bezier
->points
[pos
-1].p3
;
371 point_sub(&controlvector
, &bezier
->points
[pos
].p1
);
373 for (i
= pos
; i
< bezier
->numpoints
; i
++) {
374 bezier
->points
[i
] = bezier
->points
[i
+1];
375 bezier
->corner_types
[i
] = bezier
->corner_types
[i
+1];
377 bezier
->points
[pos
].p1
= tmppoint
;
378 if (pos
== bezier
->numpoints
) {
379 /* If this was the last point, we also need to move points[0] and
380 the control point in points[1]. */
381 bezier
->points
[0].p1
= bezier
->points
[bezier
->numpoints
-1].p3
;
382 bezier
->points
[1].p1
= bezier
->points
[0].p1
;
383 point_sub(&bezier
->points
[1].p1
, &controlvector
);
385 bezier
->points
= g_realloc(bezier
->points
,
386 bezier
->numpoints
* sizeof(BezPoint
));
387 bezier
->corner_types
= g_realloc(bezier
->corner_types
,
388 bezier
->numpoints
* sizeof(BezCornerType
));
390 old_handle1
= obj
->handles
[3*pos
-3];
391 old_handle2
= obj
->handles
[3*pos
-2];
392 old_handle3
= obj
->handles
[3*pos
-1];
393 object_remove_handle(&bezier
->object
, old_handle1
);
394 object_remove_handle(&bezier
->object
, old_handle2
);
395 object_remove_handle(&bezier
->object
, old_handle3
);
396 old_cp1
= obj
->connections
[2*pos
-2];
397 old_cp2
= obj
->connections
[2*pos
-1];
398 object_remove_connectionpoint(&bezier
->object
, old_cp1
);
399 object_remove_connectionpoint(&bezier
->object
, old_cp2
);
403 /* Add a point by splitting segment into two, putting the new point at
404 'point' or, if NULL, in the middle */
406 beziershape_add_segment(BezierShape
*bezier
, int segment
, Point
*point
)
409 BezCornerType corner_type
= BEZ_CORNER_SYMMETRIC
;
410 Handle
*new_handle1
, *new_handle2
, *new_handle3
;
411 ConnectionPoint
*new_cp1
, *new_cp2
;
416 startpoint
= bezier
->points
[segment
-1].p3
;
418 startpoint
= bezier
->points
[0].p1
;
419 other
= bezier
->points
[segment
].p3
;
421 realpoint
.p1
.x
= (startpoint
.x
+ other
.x
)/6;
422 realpoint
.p1
.y
= (startpoint
.y
+ other
.y
)/6;
423 realpoint
.p2
.x
= (startpoint
.x
+ other
.x
)/3;
424 realpoint
.p2
.y
= (startpoint
.y
+ other
.y
)/3;
425 realpoint
.p3
.x
= (startpoint
.x
+ other
.x
)/2;
426 realpoint
.p3
.y
= (startpoint
.y
+ other
.y
)/2;
428 realpoint
.p2
.x
= point
->x
+(startpoint
.x
-other
.x
)/6;
429 realpoint
.p2
.y
= point
->y
+(startpoint
.y
-other
.y
)/6;
431 realpoint
.p3
= *point
;
432 /* this really goes into the next segment ... */
433 realpoint
.p1
.x
= point
->x
-(startpoint
.x
-other
.x
)/6;
434 realpoint
.p1
.y
= point
->y
-(startpoint
.y
-other
.y
)/6;
436 realpoint
.type
= BEZ_CURVE_TO
;
438 new_handle1
= g_new(Handle
, 1);
439 new_handle2
= g_new(Handle
, 1);
440 new_handle3
= g_new(Handle
, 1);
441 setup_handle(new_handle1
, HANDLE_RIGHTCTRL
);
442 setup_handle(new_handle2
, HANDLE_LEFTCTRL
);
443 setup_handle(new_handle3
, HANDLE_BEZMAJOR
);
444 new_cp1
= g_new0(ConnectionPoint
, 1);
445 new_cp2
= g_new0(ConnectionPoint
, 1);
446 new_cp1
->object
= &bezier
->object
;
447 new_cp2
->object
= &bezier
->object
;
448 add_handles(bezier
, segment
, &realpoint
, corner_type
,
449 new_handle1
, new_handle2
, new_handle3
, new_cp1
, new_cp2
);
450 return beziershape_create_point_change(bezier
, TYPE_ADD_POINT
,
451 &realpoint
, corner_type
, segment
,
452 new_handle1
, new_handle2
, new_handle3
,
457 beziershape_remove_segment(BezierShape
*bezier
, int pos
)
459 Handle
*old_handle1
, *old_handle2
, *old_handle3
;
460 ConnectionPoint
*old_cp1
, *old_cp2
;
462 BezCornerType old_ctype
;
465 g_assert(bezier
->numpoints
> 2);
466 g_assert(pos
< bezier
->numpoints
);
468 old_handle1
= bezier
->object
.handles
[3*pos
-3];
469 old_handle2
= bezier
->object
.handles
[3*pos
-2];
470 old_handle3
= bezier
->object
.handles
[3*pos
-1];
471 old_point
= bezier
->points
[pos
];
472 old_ctype
= bezier
->corner_types
[pos
];
474 old_cp1
= bezier
->object
.connections
[2*pos
-2];
475 old_cp2
= bezier
->object
.connections
[2*pos
-1];
477 object_unconnect((DiaObject
*)bezier
, old_handle1
);
478 object_unconnect((DiaObject
*)bezier
, old_handle2
);
479 object_unconnect((DiaObject
*)bezier
, old_handle3
);
481 remove_handles(bezier
, pos
);
483 beziershape_update_data(bezier
);
485 return beziershape_create_point_change(bezier
, TYPE_REMOVE_POINT
,
486 &old_point
, old_ctype
, pos
,
487 old_handle1
, old_handle2
, old_handle3
,
492 beziershape_straighten_corner(BezierShape
*bez
, int comp_nr
) {
495 if (comp_nr
== 0) comp_nr
= bez
->numpoints
- 1;
496 next_nr
= comp_nr
+ 1;
497 if (comp_nr
== bez
->numpoints
- 1)
499 /* Neat thing would be to have the kind of straigthening depend on
500 which handle was chosen: Mid-handle does average, other leaves that
501 handle where it is. */
502 bez
->points
[0].p3
= bez
->points
[0].p1
;
503 switch (bez
->corner_types
[comp_nr
]) {
504 case BEZ_CORNER_SYMMETRIC
: {
507 pt1
= bez
->points
[comp_nr
].p3
;
508 point_sub(&pt1
, &bez
->points
[comp_nr
].p2
);
509 pt2
= bez
->points
[comp_nr
].p3
;
510 point_sub(&pt2
, &bez
->points
[next_nr
].p1
);
511 point_scale(&pt2
, -1.0);
512 point_add(&pt1
, &pt2
);
513 point_scale(&pt1
, 0.5);
515 point_scale(&pt1
, -1.0);
516 point_add(&pt1
, &bez
->points
[comp_nr
].p3
);
517 point_add(&pt2
, &bez
->points
[comp_nr
].p3
);
518 bez
->points
[comp_nr
].p2
= pt1
;
519 bez
->points
[next_nr
].p1
= pt2
;
520 beziershape_update_data(bez
);
523 case BEZ_CORNER_SMOOTH
: {
526 pt1
= bez
->points
[comp_nr
].p3
;
527 point_sub(&pt1
, &bez
->points
[comp_nr
].p2
);
528 pt2
= bez
->points
[comp_nr
].p3
;
529 point_sub(&pt2
, &bez
->points
[next_nr
].p1
);
530 len1
= point_len(&pt1
);
531 len2
= point_len(&pt2
);
532 point_scale(&pt2
, -1.0);
534 point_normalize(&pt1
);
536 point_normalize(&pt2
);
537 point_add(&pt1
, &pt2
);
538 point_scale(&pt1
, 0.5);
540 point_scale(&pt1
, -len1
);
541 point_add(&pt1
, &bez
->points
[comp_nr
].p3
);
542 point_scale(&pt2
, len2
);
543 point_add(&pt2
, &bez
->points
[comp_nr
].p3
);
544 bez
->points
[comp_nr
].p2
= pt1
;
545 bez
->points
[next_nr
].p1
= pt2
;
546 beziershape_update_data(bez
);
549 case BEZ_CORNER_CUSP
:
552 bez
->points
[0].p1
= bez
->points
[0].p3
;
556 beziershape_set_corner_type(BezierShape
*bez
, Handle
*handle
,
557 BezCornerType corner_type
)
559 Handle
*mid_handle
= NULL
;
560 Point old_left
, old_right
;
562 int handle_nr
, comp_nr
;
564 handle_nr
= get_handle_nr(bez
, handle
);
566 switch (handle
->id
) {
567 case HANDLE_BEZMAJOR
:
570 case HANDLE_LEFTCTRL
:
572 if (handle_nr
== bez
->object
.num_handles
) handle_nr
= 0;
573 mid_handle
= bez
->object
.handles
[handle_nr
];
575 case HANDLE_RIGHTCTRL
:
577 if (handle_nr
< 0) handle_nr
= bez
->object
.num_handles
- 1;
578 mid_handle
= bez
->object
.handles
[handle_nr
];
581 g_assert_not_reached();
585 comp_nr
= get_major_nr(handle_nr
);
587 old_type
= bez
->corner_types
[comp_nr
];
588 old_left
= bez
->points
[comp_nr
].p2
;
589 if (comp_nr
== bez
->numpoints
- 1)
590 old_right
= bez
->points
[1].p1
;
592 old_right
= bez
->points
[comp_nr
+1].p1
;
595 g_message("Setting corner type on segment %d to %s", comp_nr
,
596 corner_type
== BEZ_CORNER_SYMMETRIC
? "symmetric" :
597 corner_type
== BEZ_CORNER_SMOOTH
? "smooth" : "cusp");
599 bez
->corner_types
[comp_nr
] = corner_type
;
601 bez
->corner_types
[bez
->numpoints
-1] = corner_type
;
602 else if (comp_nr
== bez
->numpoints
- 1)
603 bez
->corner_types
[0] = corner_type
;
605 beziershape_straighten_corner(bez
, comp_nr
);
607 return beziershape_create_corner_change(bez
, mid_handle
, &old_left
,
608 &old_right
, old_type
, corner_type
);
612 beziershape_update_data(BezierShape
*bezier
)
618 DiaObject
*obj
= &bezier
->object
;
620 /* handle the case of whole points array update (via set_prop) */
621 if (3*(bezier
->numpoints
-1) != obj
->num_handles
||
622 2*(bezier
->numpoints
-1) + 1 != obj
->num_connections
) {
623 object_unconnect_all(obj
); /* too drastic ? */
625 /* delete the old ones */
626 for (i
= 0; i
< obj
->num_handles
; i
++)
627 g_free(obj
->handles
[i
]);
628 g_free(obj
->handles
);
629 for (i
= 0; i
< obj
->num_connections
; i
++);
630 g_free(obj
->connections
[i
]);
631 g_free(obj
->connections
);
633 obj
->num_handles
= 3*(bezier
->numpoints
-1);
634 obj
->handles
= g_new(Handle
*, obj
->num_handles
);
635 obj
->num_connections
= 2*(bezier
->numpoints
-1) + 1;
636 obj
->connections
= g_new(ConnectionPoint
*, obj
->num_connections
);
638 new_handles_and_connections(bezier
, bezier
->numpoints
);
640 bezier
->corner_types
= g_realloc(bezier
->corner_types
, bezier
->numpoints
*sizeof(BezCornerType
));
641 for (i
= 0; i
< bezier
->numpoints
; i
++)
642 bezier
->corner_types
[i
] = BEZ_CORNER_SYMMETRIC
;
645 /* Update handles: */
646 bezier
->points
[0].p3
= bezier
->points
[0].p1
;
647 for (i
= 1; i
< bezier
->numpoints
; i
++) {
648 obj
->handles
[3*i
-3]->pos
= bezier
->points
[i
].p1
;
649 obj
->handles
[3*i
-2]->pos
= bezier
->points
[i
].p2
;
650 obj
->handles
[3*i
-1]->pos
= bezier
->points
[i
].p3
;
653 /* Update connection points: */
654 last
= bezier
->points
[0].p1
;
655 for (i
= 1; i
< bezier
->numpoints
; i
++) {
656 Point slopepoint1
, slopepoint2
;
657 slopepoint1
= bezier
->points
[i
].p1
;
658 point_sub(&slopepoint1
, &last
);
659 point_scale(&slopepoint1
, .5);
660 point_add(&slopepoint1
, &last
);
661 slopepoint2
= bezier
->points
[i
].p2
;
662 point_sub(&slopepoint2
, &bezier
->points
[i
].p3
);
663 point_scale(&slopepoint2
, .5);
664 point_add(&slopepoint2
, &bezier
->points
[i
].p3
);
666 obj
->connections
[2*i
-2]->pos
= last
;
667 obj
->connections
[2*i
-2]->directions
=
668 find_slope_directions(last
, bezier
->points
[i
].p1
);
669 obj
->connections
[2*i
-1]->pos
.x
=
670 (last
.x
+ 3*bezier
->points
[i
].p1
.x
+ 3*bezier
->points
[i
].p2
.x
+
671 bezier
->points
[i
].p3
.x
)/8;
672 obj
->connections
[2*i
-1]->pos
.y
=
673 (last
.y
+ 3*bezier
->points
[i
].p1
.y
+ 3*bezier
->points
[i
].p2
.y
+
674 bezier
->points
[i
].p3
.y
)/8;
675 obj
->connections
[2*i
-1]->directions
=
676 find_slope_directions(slopepoint1
, slopepoint2
);
677 last
= bezier
->points
[i
].p3
;
680 /* Find the middle of the object (or some approximation at least) */
681 minp
= maxp
= bezier
->points
[0].p1
;
682 for (i
= 1; i
< bezier
->numpoints
; i
++) {
683 Point p
= bezier
->points
[i
].p3
;
684 if (p
.x
< minp
.x
) minp
.x
= p
.x
;
685 if (p
.x
> maxp
.x
) maxp
.x
= p
.x
;
686 if (p
.y
< minp
.y
) minp
.y
= p
.y
;
687 if (p
.y
> maxp
.y
) maxp
.y
= p
.y
;
689 obj
->connections
[obj
->num_connections
-1]->pos
.x
= (minp
.x
+ maxp
.x
) / 2;
690 obj
->connections
[obj
->num_connections
-1]->pos
.y
= (minp
.y
+ maxp
.y
) / 2;
691 obj
->connections
[obj
->num_connections
-1]->directions
= DIR_ALL
;
695 beziershape_update_boundingbox(BezierShape
*bezier
)
697 ElementBBExtras
*extra
;
700 g_assert(bezier
!= NULL
);
702 extra
= &bezier
->extra_spacing
;
703 pextra
.start_trans
= pextra
.end_trans
=
704 pextra
.start_long
= pextra
.end_long
= 0;
705 pextra
.middle_trans
= extra
->border_trans
;
707 polybezier_bbox(&bezier
->points
[0],
710 &bezier
->object
.bounding_box
);
714 beziershape_simple_draw(BezierShape
*bezier
, DiaRenderer
*renderer
, real width
)
718 g_assert(bezier
!= NULL
);
719 g_assert(renderer
!= NULL
);
721 points
= &bezier
->points
[0];
723 DIA_RENDERER_GET_CLASS(renderer
)->set_linewidth(renderer
, width
);
724 DIA_RENDERER_GET_CLASS(renderer
)->set_linestyle(renderer
, LINESTYLE_SOLID
);
725 DIA_RENDERER_GET_CLASS(renderer
)->set_linejoin(renderer
, LINEJOIN_ROUND
);
726 DIA_RENDERER_GET_CLASS(renderer
)->set_linecaps(renderer
, LINECAPS_BUTT
);
728 DIA_RENDERER_GET_CLASS(renderer
)->fill_bezier(renderer
, points
, bezier
->numpoints
,&color_white
);
729 DIA_RENDERER_GET_CLASS(renderer
)->draw_bezier(renderer
, points
, bezier
->numpoints
,&color_black
);
733 beziershape_draw_control_lines(BezierShape
*bez
, DiaRenderer
*renderer
)
735 Color line_colour
= {0.0, 0.0, 0.6};
739 /* setup renderer ... */
740 DIA_RENDERER_GET_CLASS(renderer
)->set_linewidth(renderer
, 0);
741 DIA_RENDERER_GET_CLASS(renderer
)->set_linestyle(renderer
, LINESTYLE_DOTTED
);
742 DIA_RENDERER_GET_CLASS(renderer
)->set_dashlength(renderer
, 1);
743 DIA_RENDERER_GET_CLASS(renderer
)->set_linejoin(renderer
, LINEJOIN_MITER
);
744 DIA_RENDERER_GET_CLASS(renderer
)->set_linecaps(renderer
, LINECAPS_BUTT
);
746 startpoint
= bez
->points
[0].p1
;
747 for (i
= 1; i
< bez
->numpoints
; i
++) {
748 DIA_RENDERER_GET_CLASS(renderer
)->draw_line(renderer
, &startpoint
, &bez
->points
[i
].p1
,
750 DIA_RENDERER_GET_CLASS(renderer
)->draw_line(renderer
, &bez
->points
[i
].p2
, &bez
->points
[i
].p3
,
752 startpoint
= bez
->points
[i
].p3
;
757 new_handles_and_connections(BezierShape
*bezier
, int num_points
)
762 obj
= &bezier
->object
;
764 for (i
= 0; i
< num_points
-1; i
++) {
765 obj
->handles
[3*i
] = g_new(Handle
, 1);
766 obj
->handles
[3*i
+1] = g_new(Handle
, 1);
767 obj
->handles
[3*i
+2] = g_new(Handle
, 1);
769 obj
->handles
[3*i
]->connect_type
= HANDLE_NONCONNECTABLE
;
770 obj
->handles
[3*i
]->connected_to
= NULL
;
771 obj
->handles
[3*i
]->type
= HANDLE_MINOR_CONTROL
;
772 obj
->handles
[3*i
]->id
= HANDLE_RIGHTCTRL
;
774 obj
->handles
[3*i
+1]->connect_type
= HANDLE_NONCONNECTABLE
;
775 obj
->handles
[3*i
+1]->connected_to
= NULL
;
776 obj
->handles
[3*i
+1]->type
= HANDLE_MINOR_CONTROL
;
777 obj
->handles
[3*i
+1]->id
= HANDLE_LEFTCTRL
;
779 obj
->handles
[3*i
+2]->connect_type
= HANDLE_NONCONNECTABLE
;
780 obj
->handles
[3*i
+2]->connected_to
= NULL
;
781 obj
->handles
[3*i
+2]->type
= HANDLE_MAJOR_CONTROL
;
782 obj
->handles
[3*i
+2]->id
= HANDLE_BEZMAJOR
;
784 obj
->connections
[2*i
] = g_new0(ConnectionPoint
, 1);
785 obj
->connections
[2*i
+1] = g_new0(ConnectionPoint
, 1);
786 obj
->connections
[2*i
]->object
= obj
;
787 obj
->connections
[2*i
+1]->object
= obj
;
788 obj
->connections
[2*i
]->flags
= 0;
789 obj
->connections
[2*i
+1]->flags
= 0;
793 obj
->connections
[obj
->num_connections
-1] = g_new0(ConnectionPoint
, 1);
794 obj
->connections
[obj
->num_connections
-1]->object
= obj
;
795 obj
->connections
[obj
->num_connections
-1]->flags
= CP_FLAGS_MAIN
;
799 beziershape_init(BezierShape
*bezier
, int num_points
)
804 obj
= &bezier
->object
;
806 object_init(obj
, 3*(num_points
-1), 2*(num_points
-1) + 1);
808 bezier
->numpoints
= num_points
;
810 bezier
->points
= g_new(BezPoint
, num_points
);
811 bezier
->points
[0].type
= BEZ_MOVE_TO
;
812 bezier
->corner_types
= g_new(BezCornerType
, num_points
);
813 for (i
= 1; i
< num_points
; i
++) {
814 bezier
->points
[i
].type
= BEZ_CURVE_TO
;
815 bezier
->corner_types
[i
] = BEZ_CORNER_SYMMETRIC
;
818 new_handles_and_connections(bezier
, num_points
);
820 /* The points are not assigned at this point, so don't try to use
822 /* beziershape_update_data(bezier);*/
826 /** This function does *not* set up handles */
828 beziershape_set_points(BezierShape
*bez
, int num_points
, BezPoint
*points
)
832 bez
->numpoints
= num_points
;
837 bez
->points
= g_malloc((bez
->numpoints
)*sizeof(BezPoint
));
839 for (i
=0;i
<bez
->numpoints
;i
++) {
840 bez
->points
[i
] = points
[i
];
845 beziershape_copy(BezierShape
*from
, BezierShape
*to
)
848 DiaObject
*toobj
, *fromobj
;
851 fromobj
= &from
->object
;
853 object_copy(fromobj
, toobj
);
855 to
->numpoints
= from
->numpoints
;
857 to
->points
= g_new(BezPoint
, to
->numpoints
);
858 to
->corner_types
= g_new(BezCornerType
, to
->numpoints
);
860 for (i
= 0; i
< to
->numpoints
; i
++) {
861 to
->points
[i
] = from
->points
[i
];
862 to
->corner_types
[i
] = from
->corner_types
[i
];
865 for (i
= 0; i
< toobj
->num_handles
; i
++) {
866 toobj
->handles
[i
] = g_new(Handle
, 1);
867 setup_handle(toobj
->handles
[i
], fromobj
->handles
[i
]->id
);
869 for (i
= 0; i
< toobj
->num_connections
; i
++) {
870 toobj
->connections
[i
] = g_new0(ConnectionPoint
, 1);
871 toobj
->connections
[i
]->object
= &to
->object
;
872 toobj
->connections
[i
]->flags
= fromobj
->connections
[i
]->flags
;
875 memcpy(&to
->extra_spacing
,&from
->extra_spacing
,sizeof(to
->extra_spacing
));
876 beziershape_update_data(to
);
880 beziershape_destroy(BezierShape
*bezier
)
883 Handle
**temp_handles
;
884 ConnectionPoint
**temp_cps
;
886 /* Need to store these temporary since object.handles is
887 freed by object_destroy() */
888 temp_handles
= g_new(Handle
*, bezier
->object
.num_handles
);
889 for (i
= 0; i
< bezier
->object
.num_handles
; i
++)
890 temp_handles
[i
] = bezier
->object
.handles
[i
];
892 temp_cps
= g_new(ConnectionPoint
*, bezier
->object
.num_connections
);
893 for (i
= 0; i
< bezier
->object
.num_connections
; i
++)
894 temp_cps
[i
] = bezier
->object
.connections
[i
];
896 object_destroy(&bezier
->object
);
898 for (i
= 0; i
< bezier
->object
.num_handles
; i
++)
899 g_free(temp_handles
[i
]);
900 g_free(temp_handles
);
902 for (i
= 0; i
< bezier
->object
.num_connections
; i
++)
906 g_free(bezier
->points
);
907 g_free(bezier
->corner_types
);
912 beziershape_save(BezierShape
*bezier
, ObjectNode obj_node
)
917 object_save(&bezier
->object
, obj_node
);
919 attr
= new_attribute(obj_node
, "bez_points");
921 data_add_point(attr
, &bezier
->points
[0].p1
);
922 for (i
= 1; i
< bezier
->numpoints
; i
++) {
923 data_add_point(attr
, &bezier
->points
[i
].p1
);
924 data_add_point(attr
, &bezier
->points
[i
].p2
);
925 if (i
< bezier
->numpoints
- 1)
926 data_add_point(attr
, &bezier
->points
[i
].p3
);
929 attr
= new_attribute(obj_node
, "corner_types");
930 for (i
= 0; i
< bezier
->numpoints
; i
++)
931 data_add_enum(attr
, bezier
->corner_types
[i
]);
935 beziershape_load(BezierShape
*bezier
, ObjectNode obj_node
)
941 DiaObject
*obj
= &bezier
->object
;
943 object_load(obj
, obj_node
);
945 attr
= object_find_attribute(obj_node
, "bez_points");
948 bezier
->numpoints
= attribute_num_data(attr
) / 3 + 1;
950 bezier
->numpoints
= 0;
952 object_init(obj
, 3 * (bezier
->numpoints
- 1),
953 2 * (bezier
->numpoints
- 1) + 1);
955 data
= attribute_first_data(attr
);
956 if (bezier
->numpoints
!= 0) {
957 bezier
->points
= g_new(BezPoint
, bezier
->numpoints
);
958 bezier
->points
[0].type
= BEZ_MOVE_TO
;
959 data_point(data
, &bezier
->points
[0].p1
);
960 bezier
->points
[0].p3
= bezier
->points
[0].p1
;
961 data
= data_next(data
);
963 for (i
= 1; i
< bezier
->numpoints
; i
++) {
964 bezier
->points
[i
].type
= BEZ_CURVE_TO
;
965 data_point(data
, &bezier
->points
[i
].p1
);
966 data
= data_next(data
);
967 data_point(data
, &bezier
->points
[i
].p2
);
968 data
= data_next(data
);
969 if (i
< bezier
->numpoints
- 1) {
970 data_point(data
, &bezier
->points
[i
].p3
);
971 data
= data_next(data
);
973 bezier
->points
[i
].p3
= bezier
->points
[0].p1
;
977 bezier
->corner_types
= g_new(BezCornerType
, bezier
->numpoints
);
978 attr
= object_find_attribute(obj_node
, "corner_types");
979 if (!attr
|| attribute_num_data(attr
) != bezier
->numpoints
) {
980 for (i
= 0; i
< bezier
->numpoints
; i
++)
981 bezier
->corner_types
[i
] = BEZ_CORNER_SYMMETRIC
;
983 data
= attribute_first_data(attr
);
984 for (i
= 0; i
< bezier
->numpoints
; i
++) {
985 bezier
->corner_types
[i
] = data_enum(data
);
986 data
= data_next(data
);
990 for (i
= 0; i
< bezier
->numpoints
- 1; i
++) {
991 obj
->handles
[3*i
] = g_new(Handle
, 1);
992 obj
->handles
[3*i
+1] = g_new(Handle
, 1);
993 obj
->handles
[3*i
+2] = g_new(Handle
, 1);
995 setup_handle(obj
->handles
[3*i
], HANDLE_RIGHTCTRL
);
996 setup_handle(obj
->handles
[3*i
+1], HANDLE_LEFTCTRL
);
997 setup_handle(obj
->handles
[3*i
+2], HANDLE_BEZMAJOR
);
999 for (i
= 0; i
< obj
->num_connections
; i
++) {
1000 obj
->connections
[i
] = g_new0(ConnectionPoint
, 1);
1001 obj
->connections
[i
]->object
= obj
;
1003 obj
->connections
[obj
->num_connections
-1]->flags
= CP_FLAGS_MAIN
;
1005 beziershape_update_data(bezier
);
1009 beziershape_point_change_free(struct PointChange
*change
)
1011 if ( (change
->type
==TYPE_ADD_POINT
&& !change
->applied
) ||
1012 (change
->type
==TYPE_REMOVE_POINT
&& change
->applied
) ){
1013 g_free(change
->handle1
);
1014 g_free(change
->handle2
);
1015 g_free(change
->handle3
);
1016 g_free(change
->cp1
);
1017 g_free(change
->cp2
);
1018 change
->handle1
= NULL
;
1019 change
->handle2
= NULL
;
1020 change
->handle3
= NULL
;
1027 beziershape_point_change_apply(struct PointChange
*change
, DiaObject
*obj
)
1029 change
->applied
= 1;
1030 switch (change
->type
) {
1031 case TYPE_ADD_POINT
:
1032 add_handles((BezierShape
*)obj
, change
->pos
, &change
->point
,
1033 change
->corner_type
,
1034 change
->handle1
, change
->handle2
, change
->handle3
,
1035 change
->cp1
, change
->cp2
);
1037 case TYPE_REMOVE_POINT
:
1038 object_unconnect(obj
, change
->handle1
);
1039 object_unconnect(obj
, change
->handle2
);
1040 object_unconnect(obj
, change
->handle3
);
1041 remove_handles((BezierShape
*)obj
, change
->pos
);
1047 beziershape_point_change_revert(struct PointChange
*change
, DiaObject
*obj
)
1049 switch (change
->type
) {
1050 case TYPE_ADD_POINT
:
1051 remove_handles((BezierShape
*)obj
, change
->pos
);
1053 case TYPE_REMOVE_POINT
:
1054 add_handles((BezierShape
*)obj
, change
->pos
, &change
->point
,
1055 change
->corner_type
,
1056 change
->handle1
, change
->handle2
, change
->handle3
,
1057 change
->cp1
, change
->cp2
);
1060 change
->applied
= 0;
1063 static ObjectChange
*
1064 beziershape_create_point_change(BezierShape
*bezier
, enum change_type type
,
1065 BezPoint
*point
, BezCornerType corner_type
,
1067 Handle
*handle1
, Handle
*handle2
,
1069 ConnectionPoint
*cp1
, ConnectionPoint
*cp2
)
1071 struct PointChange
*change
;
1073 change
= g_new(struct PointChange
, 1);
1075 change
->obj_change
.apply
=
1076 (ObjectChangeApplyFunc
)beziershape_point_change_apply
;
1077 change
->obj_change
.revert
=
1078 (ObjectChangeRevertFunc
)beziershape_point_change_revert
;
1079 change
->obj_change
.free
=
1080 (ObjectChangeFreeFunc
)beziershape_point_change_free
;
1082 change
->type
= type
;
1083 change
->applied
= 1;
1084 change
->point
= *point
;
1085 change
->corner_type
= corner_type
;
1087 change
->handle1
= handle1
;
1088 change
->handle2
= handle2
;
1089 change
->handle3
= handle3
;
1093 return (ObjectChange
*)change
;
1097 beziershape_corner_change_apply(struct CornerChange
*change
, DiaObject
*obj
)
1099 BezierShape
*bez
= (BezierShape
*)obj
;
1100 int handle_nr
= get_handle_nr(bez
, change
->handle
);
1101 int comp_nr
= get_major_nr(handle_nr
);
1103 beziershape_straighten_corner(bez
, comp_nr
);
1105 bez
->corner_types
[comp_nr
] = change
->new_type
;
1107 bez
->corner_types
[bez
->numpoints
-1] = change
->new_type
;
1108 if (comp_nr
== bez
->numpoints
- 1)
1109 bez
->corner_types
[0] = change
->new_type
;
1111 change
->applied
= 1;
1115 beziershape_corner_change_revert(struct CornerChange
*change
, DiaObject
*obj
)
1117 BezierShape
*bez
= (BezierShape
*)obj
;
1118 int handle_nr
= get_handle_nr(bez
, change
->handle
);
1119 int comp_nr
= get_major_nr(handle_nr
);
1121 bez
->points
[comp_nr
].p2
= change
->point_left
;
1122 if (comp_nr
== bez
->numpoints
- 1)
1123 bez
->points
[1].p1
= change
->point_right
;
1125 bez
->points
[comp_nr
+1].p1
= change
->point_right
;
1126 bez
->corner_types
[comp_nr
] = change
->old_type
;
1128 bez
->corner_types
[bez
->numpoints
-1] = change
->new_type
;
1129 if (comp_nr
== bez
->numpoints
- 1)
1130 bez
->corner_types
[0] = change
->new_type
;
1132 change
->applied
= 0;
1135 static ObjectChange
*
1136 beziershape_create_corner_change(BezierShape
*bez
, Handle
*handle
,
1137 Point
*point_left
, Point
*point_right
,
1138 BezCornerType old_corner_type
,
1139 BezCornerType new_corner_type
)
1141 struct CornerChange
*change
;
1143 change
= g_new(struct CornerChange
, 1);
1145 change
->obj_change
.apply
=
1146 (ObjectChangeApplyFunc
)beziershape_corner_change_apply
;
1147 change
->obj_change
.revert
=
1148 (ObjectChangeRevertFunc
)beziershape_corner_change_revert
;
1149 change
->obj_change
.free
= (ObjectChangeFreeFunc
)NULL
;
1151 change
->old_type
= old_corner_type
;
1152 change
->new_type
= new_corner_type
;
1153 change
->applied
= 1;
1155 change
->handle
= handle
;
1156 change
->point_left
= *point_left
;
1157 change
->point_right
= *point_right
;
1159 return (ObjectChange
*)change
;