1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
4 * BezierConn Copyright (C) 1999 James Henstridge
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include <string.h> /* memcpy() */
28 #include "bezier_conn.h"
31 #include "diarenderer.h"
33 #define HANDLE_BEZMAJOR (HANDLE_CUSTOM1)
34 #define HANDLE_LEFTCTRL (HANDLE_CUSTOM2)
35 #define HANDLE_RIGHTCTRL (HANDLE_CUSTOM3)
43 ObjectChange obj_change
;
45 enum change_type type
;
49 BezCornerType corner_type
;
51 /* owning ref when not applied for ADD_POINT
52 * owning ref when applied for REMOVE_POINT */
53 Handle
*handle1
, *handle2
, *handle3
;
54 /* NULL if not connected */
55 ConnectionPoint
*connected_to1
, *connected_to2
, *connected_to3
;
59 ObjectChange obj_change
;
60 /* Only one kind of corner_change */
64 /* Old places when SET_CORNER_TYPE is applied */
65 Point point_left
, point_right
;
66 BezCornerType old_type
, new_type
;
70 bezierconn_create_point_change(BezierConn
*bez
, enum change_type type
,
71 BezPoint
*point
, BezCornerType corner_type
,
73 Handle
*handle1
, ConnectionPoint
*connected_to1
,
74 Handle
*handle2
, ConnectionPoint
*connected_to2
,
75 Handle
*handle3
, ConnectionPoint
*connected_to3
);
77 bezierconn_create_corner_change(BezierConn
*bez
, Handle
*handle
,
78 Point
*point_left
, Point
*point_right
,
79 BezCornerType old_corner_type
,
80 BezCornerType new_corner_type
);
82 /** Set up a handle for any part of a bezierconn
83 * @param handle A handle to set up.
84 * @param id Handle id (HANDLE_BEZMAJOR or HANDLE_BEZMINOER)
87 setup_handle(Handle
*handle
, HandleId id
)
90 handle
->type
= HANDLE_MINOR_CONTROL
;
91 handle
->connect_type
= (id
== HANDLE_BEZMAJOR
) ?
92 HANDLE_CONNECTABLE
: HANDLE_NONCONNECTABLE
;
93 handle
->connected_to
= NULL
;
96 /** Get the number in the array of handles that a given handle has.
97 * @param bez A bezierconn object with handles set up.
98 * @param handle A handle object.
99 * @returns The index in bex->object.handles of the handle object, or -1 if
100 * `handle' is not in the array.
103 get_handle_nr(BezierConn
*bez
, Handle
*handle
)
106 for (i
=0;i
<bez
->object
.num_handles
;i
++) {
107 if (bez
->object
.handles
[i
] == handle
)
113 void new_handles(BezierConn
*bez
, int num_points
);
115 #define get_comp_nr(hnum) (((int)(hnum)+2)/3)
116 #define get_major_nr(hnum) (((int)(hnum)+1)/3)
118 /** Function called to move one of the handles associated with the
120 * @param obj The object whose handle is being moved.
121 * @param handle The handle being moved.
122 * @param pos The position it has been moved to (corrected for
123 * vertical/horizontal only movement).
124 * @param cp If non-NULL, the connectionpoint found at this position.
125 * If @a cp is NULL, there may or may not be a connectionpoint.
126 * @param reason ignored
127 * @param modifiers ignored
131 bezierconn_move_handle(BezierConn
*bez
, Handle
*handle
,
132 Point
*to
, ConnectionPoint
*cp
,
133 HandleMoveReason reason
, ModifierKeys modifiers
)
135 int handle_nr
, comp_nr
;
139 point_sub(&delta
, &handle
->pos
);
141 handle_nr
= get_handle_nr(bez
, handle
);
142 comp_nr
= get_comp_nr(handle_nr
);
144 case HANDLE_MOVE_STARTPOINT
:
145 bez
->points
[0].p1
= *to
;
146 /* shift adjacent point */
147 point_add(&bez
->points
[1].p1
, &delta
);
149 case HANDLE_MOVE_ENDPOINT
:
150 bez
->points
[bez
->numpoints
-1].p3
= *to
;
151 /* shift adjacent point */
152 point_add(&bez
->points
[bez
->numpoints
-1].p2
, &delta
);
154 case HANDLE_BEZMAJOR
:
155 bez
->points
[comp_nr
].p3
= *to
;
156 /* shift adjacent point */
157 point_add(&bez
->points
[comp_nr
].p2
, &delta
);
158 point_add(&bez
->points
[comp_nr
+1].p1
, &delta
);
160 case HANDLE_LEFTCTRL
:
161 bez
->points
[comp_nr
].p2
= *to
;
162 if (comp_nr
< bez
->numpoints
- 1) {
163 switch (bez
->corner_types
[comp_nr
]) {
164 case BEZ_CORNER_SYMMETRIC
:
165 pt
= bez
->points
[comp_nr
].p3
;
166 point_sub(&pt
, &bez
->points
[comp_nr
].p2
);
167 point_add(&pt
, &bez
->points
[comp_nr
].p3
);
168 bez
->points
[comp_nr
+1].p1
= pt
;
170 case BEZ_CORNER_SMOOTH
: {
172 pt
= bez
->points
[comp_nr
+1].p1
;
173 point_sub(&pt
, &bez
->points
[comp_nr
].p3
);
174 len
= point_len(&pt
);
175 pt
= bez
->points
[comp_nr
].p2
;
176 point_sub(&pt
, &bez
->points
[comp_nr
].p3
);
177 if (point_len(&pt
) > 0)
178 point_normalize(&pt
);
179 else { pt
.x
= 1.0; pt
.y
= 0.0; }
180 point_scale(&pt
, -len
);
181 point_add(&pt
, &bez
->points
[comp_nr
].p3
);
182 bez
->points
[comp_nr
+1].p1
= pt
;
185 case BEZ_CORNER_CUSP
:
186 /* Do nothing to the other guy */
191 case HANDLE_RIGHTCTRL
:
192 bez
->points
[comp_nr
].p1
= *to
;
194 switch (bez
->corner_types
[comp_nr
-1]) {
195 case BEZ_CORNER_SYMMETRIC
:
196 pt
= bez
->points
[comp_nr
- 1].p3
;
197 point_sub(&pt
, &bez
->points
[comp_nr
].p1
);
198 point_add(&pt
, &bez
->points
[comp_nr
- 1].p3
);
199 bez
->points
[comp_nr
-1].p2
= pt
;
201 case BEZ_CORNER_SMOOTH
: {
203 pt
= bez
->points
[comp_nr
-1].p2
;
204 point_sub(&pt
, &bez
->points
[comp_nr
-1].p3
);
205 len
= point_len(&pt
);
206 pt
= bez
->points
[comp_nr
].p1
;
207 point_sub(&pt
, &bez
->points
[comp_nr
-1].p3
);
208 if (point_len(&pt
) > 0)
209 point_normalize(&pt
);
210 else { pt
.x
= 1.0; pt
.y
= 0.0; }
211 point_scale(&pt
, -len
);
212 point_add(&pt
, &bez
->points
[comp_nr
-1].p3
);
213 bez
->points
[comp_nr
-1].p2
= pt
;
216 case BEZ_CORNER_CUSP
:
217 /* Do nothing to the other guy */
223 message_error("Internal error in bezierconn_move_handle.\n");
230 /** Function called to move the entire object.
231 * @param obj The object being moved.
232 * @param pos Where the object is being moved to. This is the first point
233 * of the points array.
237 bezierconn_move(BezierConn
*bez
, Point
*to
)
243 point_sub(&p
, &bez
->points
[0].p1
);
245 bez
->points
[0].p1
= *to
;
246 for (i
= 1; i
< bez
->numpoints
; i
++) {
247 point_add(&bez
->points
[i
].p1
, &p
);
248 point_add(&bez
->points
[i
].p2
, &p
);
249 point_add(&bez
->points
[i
].p3
, &p
);
255 /** Return the segment of the bezierconn closest to a given point.
256 * @param bez The bezierconn object
257 * @param point A point to find the closest segment to.
258 * @param line_width Line width of the bezier line.
259 * @returns The index of the segment closest to point.
262 bezierconn_closest_segment(BezierConn
*bez
, Point
*point
, real line_width
)
266 real dist
= G_MAXDOUBLE
;
270 last
= bez
->points
[0].p1
;
271 for (i
= 0; i
< bez
->numpoints
- 1; i
++) {
272 real new_dist
= distance_bez_seg_point(&last
, &bez
->points
[i
+1].p1
,
273 &bez
->points
[i
+1].p2
, &bez
->points
[i
+1].p3
,
275 if (new_dist
< dist
) {
279 last
= bez
->points
[i
+1].p3
;
284 /** Return the handle closest to a given point.
285 * @param bez A bezierconn object
286 * @param point A point to find distances from
287 * @returns The handle on `bez' closest to `point'.
288 * @bugs Why isn't this just a function on object that scans the handles?
291 bezierconn_closest_handle(BezierConn
*bez
, Point
*point
)
297 closest
= bez
->object
.handles
[0];
298 dist
= distance_point_point( point
, &closest
->pos
);
299 for (i
= 1, hn
= 1; i
< bez
->numpoints
; i
++, hn
++) {
302 new_dist
= distance_point_point(point
, &bez
->points
[i
].p1
);
303 if (new_dist
< dist
) {
305 closest
= bez
->object
.handles
[hn
];
309 new_dist
= distance_point_point(point
, &bez
->points
[i
].p2
);
310 if (new_dist
< dist
) {
312 closest
= bez
->object
.handles
[hn
];
316 new_dist
= distance_point_point(point
, &bez
->points
[i
].p3
);
317 if (new_dist
< dist
) {
319 closest
= bez
->object
.handles
[hn
];
325 /** Retrun the major handle for the control point with the handle closest to
327 * @param bez A bezier connection
328 * @param point A point
329 * @returns The major (middle) handle of the bezier control that has the
330 * handle closest to point.
331 * @bugs Don't we really want the major handle that's actually closest to
332 * the point? This is used in connection with object menus and could cause
333 * some unexpected selection of handles if a different segment has a control
334 * point close to the major handle.
337 bezierconn_closest_major_handle(BezierConn
*bez
, Point
*point
)
339 Handle
*closest
= bezierconn_closest_handle(bez
, point
);
341 return bez
->object
.handles
[3*get_major_nr(get_handle_nr(bez
, closest
))];
344 /** Return the distance from a bezier to a point.
345 * @param bez A bezierconn object.
346 * @param point A point to compare with.
347 * @param line_width The line width of the bezier line.
348 * @returns The shortest distance from the point to any part of the bezier.
351 bezierconn_distance_from(BezierConn
*bez
, Point
*point
, real line_width
)
353 return distance_bez_line_point(bez
->points
, bez
->numpoints
,
357 /** Add a trio of handles to a bezier.
358 * @param bez The bezierconn having handles added.
359 * @param pos Where in the list of segments to add the handle
360 * @param point The bezier point to add. This should already be initialized
361 * with sensible positions.
362 * @param corner_type What kind of corner this bezpoint should be.
363 * @param handle1 The handle that will be put on the bezier line.
364 * @param handle2 The handle that will be put before handle1
365 * @param handle3 The handle that will be put after handle1
366 * @bugs check that the handle ordering is correctly described.
369 add_handles(BezierConn
*bez
, int pos
, BezPoint
*point
,
370 BezCornerType corner_type
, Handle
*handle1
,
371 Handle
*handle2
, Handle
*handle3
)
378 obj
= (DiaObject
*)bez
;
380 bez
->points
= g_realloc(bez
->points
, bez
->numpoints
*sizeof(BezPoint
));
381 bez
->corner_types
= g_realloc(bez
->corner_types
,
382 bez
->numpoints
* sizeof(BezCornerType
));
384 for (i
= bez
->numpoints
-1; i
> pos
; i
--) {
385 bez
->points
[i
] = bez
->points
[i
-1];
386 bez
->corner_types
[i
] = bez
->corner_types
[i
-1];
388 bez
->points
[pos
] = *point
;
389 bez
->points
[pos
].p1
= bez
->points
[pos
+1].p1
;
390 bez
->points
[pos
+1].p1
= point
->p1
;
391 bez
->corner_types
[pos
] = corner_type
;
392 object_add_handle_at(obj
, handle1
, 3*pos
-2);
393 object_add_handle_at(obj
, handle2
, 3*pos
-1);
394 object_add_handle_at(obj
, handle3
, 3*pos
);
396 if (pos
==bez
->numpoints
-1) {
397 obj
->handles
[obj
->num_handles
-4]->type
= HANDLE_MINOR_CONTROL
;
398 obj
->handles
[obj
->num_handles
-4]->id
= HANDLE_BEZMAJOR
;
402 /** Remove a trio of handles from a bezierconn.
403 * @param bez The bezierconn to remove handles from.
404 * @param pos The position in the bezierpoint array to remove handles and
408 remove_handles(BezierConn
*bez
, int pos
)
412 Handle
*old_handle1
, *old_handle2
, *old_handle3
;
417 obj
= (DiaObject
*)bez
;
419 if (pos
==obj
->num_handles
-1) {
420 obj
->handles
[obj
->num_handles
-4]->type
= HANDLE_MAJOR_CONTROL
;
421 obj
->handles
[obj
->num_handles
-4]->id
= HANDLE_MOVE_ENDPOINT
;
424 /* delete the points */
426 tmppoint
= bez
->points
[pos
].p1
;
427 for (i
= pos
; i
< bez
->numpoints
; i
++) {
428 bez
->points
[i
] = bez
->points
[i
+1];
429 bez
->corner_types
[i
] = bez
->corner_types
[i
+1];
431 bez
->points
[pos
].p1
= tmppoint
;
432 bez
->points
= g_realloc(bez
->points
, bez
->numpoints
*sizeof(BezPoint
));
433 bez
->corner_types
= g_realloc(bez
->corner_types
,
434 bez
->numpoints
* sizeof(BezCornerType
));
436 old_handle1
= obj
->handles
[3*pos
-2];
437 old_handle2
= obj
->handles
[3*pos
-1];
438 old_handle3
= obj
->handles
[3*pos
];
439 object_remove_handle(&bez
->object
, old_handle1
);
440 object_remove_handle(&bez
->object
, old_handle2
);
441 object_remove_handle(&bez
->object
, old_handle3
);
445 /** Add a point by splitting segment into two, putting the new point at
446 * 'point' or, if NULL, in the middle. This function will attempt to come
447 * up with reasonable placements for the control points.
448 * @param bez The bezierconn to add the segment to.
449 * @param segment Which segment to split.
450 * @param point Where to put the new corner, or NULL if undetermined.
451 * @returns An ObjectChange object with undo information for the split.
454 bezierconn_add_segment(BezierConn
*bez
, int segment
, Point
*point
)
457 BezCornerType corner_type
= BEZ_CORNER_SYMMETRIC
;
458 Handle
*new_handle1
, *new_handle2
, *new_handle3
;
462 startpoint
= bez
->points
[0].p1
;
464 startpoint
= bez
->points
[segment
].p3
;
467 realpoint
.p1
.x
= (startpoint
.x
+ bez
->points
[segment
+1].p3
.x
) / 6;
468 realpoint
.p1
.y
= (startpoint
.y
+ bez
->points
[segment
+1].p3
.y
) / 6;
469 realpoint
.p2
.x
= (startpoint
.x
+ bez
->points
[segment
+1].p3
.x
) / 3;
470 realpoint
.p2
.y
= (startpoint
.y
+ bez
->points
[segment
+1].p3
.y
) / 3;
471 realpoint
.p3
.x
= (startpoint
.x
+ bez
->points
[segment
+1].p3
.x
) / 2;
472 realpoint
.p3
.y
= (startpoint
.y
+ bez
->points
[segment
+1].p3
.y
) / 2;
474 realpoint
.p2
.x
= point
->x
+(startpoint
.x
- bez
->points
[segment
+1].p3
.x
)/6;
475 realpoint
.p2
.y
= point
->y
+(startpoint
.y
- bez
->points
[segment
+1].p3
.y
)/6;
476 realpoint
.p3
= *point
;
477 /* this really goes into the next segment ... */
478 realpoint
.p1
.x
= point
->x
-(startpoint
.x
- bez
->points
[segment
+1].p3
.x
)/6;
479 realpoint
.p1
.y
= point
->y
-(startpoint
.y
- bez
->points
[segment
+1].p3
.y
)/6;
481 realpoint
.type
= BEZ_CURVE_TO
;
483 new_handle1
= g_malloc(sizeof(Handle
));
484 new_handle2
= g_malloc(sizeof(Handle
));
485 new_handle3
= g_malloc(sizeof(Handle
));
486 setup_handle(new_handle1
, HANDLE_RIGHTCTRL
);
487 setup_handle(new_handle2
, HANDLE_LEFTCTRL
);
488 setup_handle(new_handle3
, HANDLE_BEZMAJOR
);
489 add_handles(bez
, segment
+1, &realpoint
, corner_type
,
490 new_handle1
, new_handle2
, new_handle3
);
491 return bezierconn_create_point_change(bez
, TYPE_ADD_POINT
,
492 &realpoint
, corner_type
, segment
+1,
498 /** Remove a segment from a bezierconn.
499 * @param bez The bezierconn to remove a segment from.
500 * @param pos The index of the segment to remove.
501 * @returns Undo information for the segment removal.
504 bezierconn_remove_segment(BezierConn
*bez
, int pos
)
506 Handle
*old_handle1
, *old_handle2
, *old_handle3
;
507 ConnectionPoint
*cpt1
, *cpt2
, *cpt3
;
509 BezCornerType old_ctype
;
512 g_assert(bez
->numpoints
> 2);
514 if (pos
== bez
->numpoints
-1) pos
--;
516 old_handle1
= bez
->object
.handles
[3*pos
-2];
517 old_handle2
= bez
->object
.handles
[3*pos
-1];
518 old_handle3
= bez
->object
.handles
[3*pos
];
519 old_point
= bez
->points
[pos
];
520 old_ctype
= bez
->corner_types
[pos
];
522 cpt1
= old_handle1
->connected_to
;
523 cpt2
= old_handle2
->connected_to
;
524 cpt3
= old_handle3
->connected_to
;
526 object_unconnect((DiaObject
*)bez
, old_handle1
);
527 object_unconnect((DiaObject
*)bez
, old_handle2
);
528 object_unconnect((DiaObject
*)bez
, old_handle3
);
530 remove_handles(bez
, pos
);
532 bezierconn_update_data(bez
);
534 return bezierconn_create_point_change(bez
, TYPE_REMOVE_POINT
,
535 &old_point
, old_ctype
, pos
,
541 /** Update a corner to have less freedom in its control handles, arranging
542 * the control points at some reasonable places.
543 * @param bez A bezierconn to straighten a corner of
544 * @param comp_nr The index into the corner_types array of the corner to
546 * @bugs what happens if we're going from symmetric to smooth?
549 bezierconn_straighten_corner(BezierConn
*bez
, int comp_nr
)
551 /* Neat thing would be to have the kind of straigthening depend on
552 which handle was chosen: Mid-handle does average, other leaves that
553 handle where it is. */
554 switch (bez
->corner_types
[comp_nr
]) {
555 case BEZ_CORNER_SYMMETRIC
: {
557 pt1
= bez
->points
[comp_nr
].p3
;
558 point_sub(&pt1
, &bez
->points
[comp_nr
].p2
);
559 pt2
= bez
->points
[comp_nr
].p3
;
560 point_sub(&pt2
, &bez
->points
[comp_nr
+1].p1
);
561 point_scale(&pt2
, -1.0);
562 point_add(&pt1
, &pt2
);
563 point_scale(&pt1
, 0.5);
565 point_scale(&pt1
, -1.0);
566 point_add(&pt1
, &bez
->points
[comp_nr
].p3
);
567 point_add(&pt2
, &bez
->points
[comp_nr
].p3
);
568 bez
->points
[comp_nr
].p2
= pt1
;
569 bez
->points
[comp_nr
+1].p1
= pt2
;
570 bezierconn_update_data(bez
);
573 case BEZ_CORNER_SMOOTH
: {
576 pt1
= bez
->points
[comp_nr
].p3
;
577 point_sub(&pt1
, &bez
->points
[comp_nr
].p2
);
578 pt2
= bez
->points
[comp_nr
].p3
;
579 point_sub(&pt2
, &bez
->points
[comp_nr
+1].p1
);
580 len1
= point_len(&pt1
);
581 len2
= point_len(&pt2
);
582 point_scale(&pt2
, -1.0);
584 point_normalize(&pt1
);
586 point_normalize(&pt2
);
587 point_add(&pt1
, &pt2
);
588 point_scale(&pt1
, 0.5);
590 point_scale(&pt1
, -len1
);
591 point_add(&pt1
, &bez
->points
[comp_nr
].p3
);
592 point_scale(&pt2
, len2
);
593 point_add(&pt2
, &bez
->points
[comp_nr
].p3
);
594 bez
->points
[comp_nr
].p2
= pt1
;
595 bez
->points
[comp_nr
+1].p1
= pt2
;
596 bezierconn_update_data(bez
);
599 case BEZ_CORNER_CUSP
:
604 /** Change the corner type of a bezier line.
605 * @param bez The bezierconn that has the corner
606 * @param handle The handle whose corner should be set.
607 * @param corner_type What type of corner the handle should have.
608 * @returns Undo information about the corner change.
611 bezierconn_set_corner_type(BezierConn
*bez
, Handle
*handle
,
612 BezCornerType corner_type
)
615 Point old_left
, old_right
;
617 int handle_nr
, comp_nr
;
619 handle_nr
= get_handle_nr(bez
, handle
);
621 switch (handle
->id
) {
622 case HANDLE_BEZMAJOR
:
625 case HANDLE_LEFTCTRL
:
627 mid_handle
= bez
->object
.handles
[handle_nr
];
629 case HANDLE_RIGHTCTRL
:
631 mid_handle
= bez
->object
.handles
[handle_nr
];
634 message_warning(_("Internal error: Setting corner type of endpoint of bezier"));
638 comp_nr
= get_major_nr(handle_nr
);
640 old_type
= bez
->corner_types
[comp_nr
];
641 old_left
= bez
->points
[comp_nr
].p2
;
642 old_right
= bez
->points
[comp_nr
+1].p1
;
644 bez
->corner_types
[comp_nr
] = corner_type
;
646 bezierconn_straighten_corner(bez
, comp_nr
);
648 return bezierconn_create_corner_change(bez
, mid_handle
, &old_left
, &old_right
,
649 old_type
, corner_type
);
652 /** Update handle array and handle positions after changes.
653 * @param bez A bezierconn to update.
656 bezierconn_update_data(BezierConn
*bez
)
659 DiaObject
*obj
= &bez
->object
;
661 /* handle the case of whole points array update (via set_prop) */
662 if (3*bez
->numpoints
-2 != obj
->num_handles
) {
663 g_assert(0 == obj
->num_connections
);
665 for (i
= 0; i
< obj
->num_handles
; i
++)
666 g_free(obj
->handles
[i
]);
667 g_free(obj
->handles
);
669 obj
->num_handles
= 3*bez
->numpoints
-2;
670 obj
->handles
= g_new(Handle
*, obj
->num_handles
);
672 new_handles(bez
, bez
->numpoints
);
675 /* Update handles: */
676 bez
->object
.handles
[0]->pos
= bez
->points
[0].p1
;
677 for (i
= 1; i
< bez
->numpoints
; i
++) {
678 bez
->object
.handles
[3*i
-2]->pos
= bez
->points
[i
].p1
;
679 bez
->object
.handles
[3*i
-1]->pos
= bez
->points
[i
].p2
;
680 bez
->object
.handles
[3*i
]->pos
= bez
->points
[i
].p3
;
684 /** Update the boundingbox of the connection.
685 * @param bez A bezier line to update bounding box for.
688 bezierconn_update_boundingbox(BezierConn
*bez
)
690 g_assert(bez
!= NULL
);
692 polybezier_bbox(&bez
->points
[0],
694 &bez
->extra_spacing
, FALSE
,
695 &bez
->object
.bounding_box
);
698 /** Draw the main line of a bezier conn.
699 * Note that this sets the linestyle, linejoin and linecaps to hardcoded
701 * @param bez The bezier conn to draw.
702 * @param renderer The renderer to draw with.
703 * @param width The linewidth of the bezier.
706 bezierconn_simple_draw(BezierConn
*bez
, DiaRenderer
*renderer
, real width
)
710 g_assert(bez
!= NULL
);
711 g_assert(renderer
!= NULL
);
713 points
= &bez
->points
[0];
715 DIA_RENDERER_GET_CLASS(renderer
)->set_linewidth(renderer
, width
);
716 DIA_RENDERER_GET_CLASS(renderer
)->set_linestyle(renderer
, LINESTYLE_SOLID
);
717 DIA_RENDERER_GET_CLASS(renderer
)->set_linejoin(renderer
, LINEJOIN_ROUND
);
718 DIA_RENDERER_GET_CLASS(renderer
)->set_linecaps(renderer
, LINECAPS_BUTT
);
720 DIA_RENDERER_GET_CLASS(renderer
)->draw_bezier(renderer
, points
, bez
->numpoints
, &color_black
);
723 /** Draw the control lines from the points of the bezier conn.
724 * Note that the control lines are hardcoded to be dotted with dash length 1.
725 * @param bez The bezier conn to draw control lines for.
726 * @param renderer A renderer to draw with.
729 bezierconn_draw_control_lines(BezierConn
*bez
, DiaRenderer
*renderer
)
731 Color line_colour
= {0.0, 0.0, 0.6};
735 /* setup DiaRenderer ... */
736 DIA_RENDERER_GET_CLASS(renderer
)->set_linewidth(renderer
, 0);
737 DIA_RENDERER_GET_CLASS(renderer
)->set_linestyle(renderer
, LINESTYLE_DOTTED
);
738 DIA_RENDERER_GET_CLASS(renderer
)->set_dashlength(renderer
, 1);
739 DIA_RENDERER_GET_CLASS(renderer
)->set_linejoin(renderer
, LINEJOIN_MITER
);
740 DIA_RENDERER_GET_CLASS(renderer
)->set_linecaps(renderer
, LINECAPS_BUTT
);
742 startpoint
= bez
->points
[0].p1
;
743 for (i
= 1; i
< bez
->numpoints
; i
++) {
744 DIA_RENDERER_GET_CLASS(renderer
)->draw_line(renderer
, &startpoint
, &bez
->points
[i
].p1
,
746 DIA_RENDERER_GET_CLASS(renderer
)->draw_line(renderer
, &bez
->points
[i
].p2
, &bez
->points
[i
].p3
,
748 startpoint
= bez
->points
[i
].p3
;
752 /** Create all handles used by a bezier conn.
753 * @param bez A bezierconn object initialized with room for 3*num_points-2
755 * @param num_points The number of points of the bezierconn.
758 new_handles(BezierConn
*bez
, int num_points
)
765 obj
->handles
[0] = g_new(Handle
,1);
766 obj
->handles
[0]->connect_type
= HANDLE_CONNECTABLE
;
767 obj
->handles
[0]->connected_to
= NULL
;
768 obj
->handles
[0]->type
= HANDLE_MAJOR_CONTROL
;
769 obj
->handles
[0]->id
= HANDLE_MOVE_STARTPOINT
;
771 for (i
= 1; i
< num_points
; i
++) {
772 obj
->handles
[3*i
-2] = g_new(Handle
, 1);
773 obj
->handles
[3*i
-1] = g_new(Handle
, 1);
774 obj
->handles
[3*i
] = g_new(Handle
, 1);
776 setup_handle(obj
->handles
[3*i
-2], HANDLE_RIGHTCTRL
);
777 setup_handle(obj
->handles
[3*i
-1], HANDLE_LEFTCTRL
);
779 obj
->handles
[3*i
]->connect_type
= HANDLE_CONNECTABLE
;
780 obj
->handles
[3*i
]->connected_to
= NULL
;
781 obj
->handles
[3*i
]->type
= HANDLE_MAJOR_CONTROL
;
782 obj
->handles
[3*i
]->id
= HANDLE_MOVE_ENDPOINT
;
786 /** Initialize a bezierconn object with the given amount of points.
787 * The points array of the bezierconn object should be previously
788 * initialized with appropriate positions.
789 * This will set up handles and make all corners symmetric.
790 * @param bez A newly allocated bezierconn object.
791 * @param num_points The initial number of points on the curve.
794 bezierconn_init(BezierConn
*bez
, int num_points
)
801 object_init(obj
, 3*num_points
-2, 0);
803 bez
->numpoints
= num_points
;
805 bez
->points
= g_new(BezPoint
, num_points
);
806 bez
->corner_types
= g_new(BezCornerType
, num_points
);
807 bez
->points
[0].type
= BEZ_MOVE_TO
;
808 bez
->corner_types
[0] = BEZ_CORNER_SYMMETRIC
;
809 for (i
= 1; i
< num_points
; i
++) {
810 bez
->points
[i
].type
= BEZ_CURVE_TO
;
811 bez
->corner_types
[i
] = BEZ_CORNER_SYMMETRIC
;
814 new_handles(bez
, num_points
);
816 bezierconn_update_data(bez
);
819 /** Set a bezierconn to use the given array of points.
820 * This function does *not* set up handles
821 * @param bez A bezierconn to operate on
822 * @param num_points The number of points in the `points' array.
823 * @param points The new points that this bezier should be set to use.
826 bezierconn_set_points(BezierConn
*bez
, int num_points
, BezPoint
*points
)
830 bez
->numpoints
= num_points
;
835 bez
->points
= g_malloc((bez
->numpoints
)*sizeof(BezPoint
));
837 for (i
=0;i
<bez
->numpoints
;i
++) {
838 bez
->points
[i
] = points
[i
];
843 /** Copy a bezierconn objects. This function in turn invokes object_copy.
844 * @param from The object to copy from.
845 * @param to The object to copy to.
848 bezierconn_copy(BezierConn
*from
, BezierConn
*to
)
851 DiaObject
*toobj
, *fromobj
;
854 fromobj
= &from
->object
;
856 object_copy(fromobj
, toobj
);
858 to
->numpoints
= from
->numpoints
;
860 to
->points
= g_new(BezPoint
, to
->numpoints
);
861 to
->corner_types
= g_new(BezCornerType
, to
->numpoints
);
863 for (i
= 0; i
< to
->numpoints
; i
++) {
864 to
->points
[i
] = from
->points
[i
];
865 to
->corner_types
[i
] = from
->corner_types
[i
];
868 to
->object
.handles
[0] = g_new(Handle
,1);
869 *to
->object
.handles
[0] = *from
->object
.handles
[0];
870 for (i
= 1; i
< to
->object
.num_handles
- 1; i
++) {
871 to
->object
.handles
[i
] = g_new(Handle
, 1);
872 setup_handle(to
->object
.handles
[i
], from
->object
.handles
[i
]->id
);
874 to
->object
.handles
[to
->object
.num_handles
-1] = g_new(Handle
,1);
875 *to
->object
.handles
[to
->object
.num_handles
-1] =
876 *from
->object
.handles
[to
->object
.num_handles
-1];
878 memcpy(&to
->extra_spacing
,&from
->extra_spacing
,sizeof(to
->extra_spacing
));
879 bezierconn_update_data(to
);
882 /** Destroy a bezierconn object.
883 * @param bez An object to destroy. This function in turn calls object_destroy
884 * and frees structures allocated by this bezierconn, but not the object itself
887 bezierconn_destroy(BezierConn
*bez
)
890 Handle
**temp_handles
;
892 /* Need to store these temporary since object.handles is
893 freed by object_destroy() */
894 nh
= bez
->object
.num_handles
;
895 temp_handles
= g_new(Handle
*, nh
);
896 for (i
= 0; i
< nh
; i
++)
897 temp_handles
[i
] = bez
->object
.handles
[i
];
899 object_destroy(&bez
->object
);
901 for (i
= 0; i
< nh
; i
++)
902 g_free(temp_handles
[i
]);
903 g_free(temp_handles
);
906 g_free(bez
->corner_types
);
910 /** Save the data defined by a bezierconn object to XML.
911 * @param bez The object to save.
912 * @param obj_node The XML node to save it into
913 * @bugs shouldn't this have version?
916 bezierconn_save(BezierConn
*bez
, ObjectNode obj_node
)
921 object_save(&bez
->object
, obj_node
);
923 attr
= new_attribute(obj_node
, "bez_points");
925 data_add_point(attr
, &bez
->points
[0].p1
);
926 for (i
= 1; i
< bez
->numpoints
; i
++) {
927 data_add_point(attr
, &bez
->points
[i
].p1
);
928 data_add_point(attr
, &bez
->points
[i
].p2
);
929 data_add_point(attr
, &bez
->points
[i
].p3
);
932 attr
= new_attribute(obj_node
, "corner_types");
933 for (i
= 0; i
< bez
->numpoints
; i
++)
934 data_add_enum(attr
, bez
->corner_types
[i
]);
937 /** Load a bezierconn object from XML.
938 * Does object_init() on the bezierconn object.
939 * @param bez A newly allocated bezierconn object to load into.
940 * @param obj_node The XML node to load from.
941 * @bugs shouldn't this have version?
942 * @bugs Couldn't this use the setup_handles function defined above?
945 bezierconn_load(BezierConn
*bez
, ObjectNode obj_node
)
951 DiaObject
*obj
= &bez
->object
;
953 object_load(obj
, obj_node
);
955 attr
= object_find_attribute(obj_node
, "bez_points");
958 bez
->numpoints
= (attribute_num_data(attr
) + 2)/3;
962 object_init(obj
, 3 * bez
->numpoints
- 2, 0);
964 data
= attribute_first_data(attr
);
965 if (bez
->numpoints
!= 0) {
966 bez
->points
= g_new(BezPoint
, bez
->numpoints
);
967 bez
->points
[0].type
= BEZ_MOVE_TO
;
968 data_point(data
, &bez
->points
[0].p1
);
969 data
= data_next(data
);
971 for (i
= 1; i
< bez
->numpoints
; i
++) {
972 bez
->points
[i
].type
= BEZ_CURVE_TO
;
973 data_point(data
, &bez
->points
[i
].p1
);
974 data
= data_next(data
);
975 data_point(data
, &bez
->points
[i
].p2
);
976 data
= data_next(data
);
977 data_point(data
, &bez
->points
[i
].p3
);
978 data
= data_next(data
);
982 bez
->corner_types
= g_new(BezCornerType
, bez
->numpoints
);
984 attr
= object_find_attribute(obj_node
, "corner_types");
985 /* if corner_types is missing or corrupt */
986 if (!attr
|| attribute_num_data(attr
) != bez
->numpoints
) {
987 for (i
= 0; i
< bez
->numpoints
; i
++) {
988 bez
->corner_types
[i
] = BEZ_CORNER_SYMMETRIC
;
991 data
= attribute_first_data(attr
);
992 for (i
= 0; i
< bez
->numpoints
; i
++) {
993 bez
->corner_types
[i
] = data_enum(data
);
994 data
= data_next(data
);
998 obj
->handles
[0] = g_new(Handle
, 1);
999 obj
->handles
[0]->connect_type
= HANDLE_CONNECTABLE
;
1000 obj
->handles
[0]->connected_to
= NULL
;
1001 obj
->handles
[0]->type
= HANDLE_MAJOR_CONTROL
;
1002 obj
->handles
[0]->id
= HANDLE_MOVE_STARTPOINT
;
1004 for (i
= 1; i
< bez
->numpoints
; i
++) {
1005 obj
->handles
[3*i
-2] = g_new(Handle
, 1);
1006 setup_handle(obj
->handles
[3*i
-2], HANDLE_RIGHTCTRL
);
1007 obj
->handles
[3*i
-1] = g_new(Handle
, 1);
1008 setup_handle(obj
->handles
[3*i
-1], HANDLE_LEFTCTRL
);
1009 obj
->handles
[3*i
] = g_new(Handle
, 1);
1010 setup_handle(obj
->handles
[3*i
], HANDLE_BEZMAJOR
);
1013 obj
->handles
[obj
->num_handles
-1]->connect_type
= HANDLE_CONNECTABLE
;
1014 obj
->handles
[obj
->num_handles
-1]->connected_to
= NULL
;
1015 obj
->handles
[obj
->num_handles
-1]->type
= HANDLE_MAJOR_CONTROL
;
1016 obj
->handles
[obj
->num_handles
-1]->id
= HANDLE_MOVE_ENDPOINT
;
1018 bezierconn_update_data(bez
);
1021 /*** Undo support ***/
1023 /** Free undo information about adding or removing points.
1024 * @param change The undo information to free.
1027 bezierconn_point_change_free(struct PointChange
*change
)
1029 if ( (change
->type
==TYPE_ADD_POINT
&& !change
->applied
) ||
1030 (change
->type
==TYPE_REMOVE_POINT
&& change
->applied
) ){
1031 g_free(change
->handle1
);
1032 g_free(change
->handle2
);
1033 g_free(change
->handle3
);
1034 change
->handle1
= NULL
;
1035 change
->handle2
= NULL
;
1036 change
->handle3
= NULL
;
1040 /** Apply a point addition/removal.
1041 * @param change The change to apply.
1042 * @param obj The object (must be a BezierConn) to apply the change to.
1045 bezierconn_point_change_apply(struct PointChange
*change
, DiaObject
*obj
)
1047 change
->applied
= 1;
1048 switch (change
->type
) {
1049 case TYPE_ADD_POINT
:
1050 add_handles((BezierConn
*)obj
, change
->pos
, &change
->point
,
1051 change
->corner_type
,
1052 change
->handle1
, change
->handle2
, change
->handle3
);
1054 case TYPE_REMOVE_POINT
:
1055 object_unconnect(obj
, change
->handle1
);
1056 object_unconnect(obj
, change
->handle2
);
1057 object_unconnect(obj
, change
->handle3
);
1058 remove_handles((BezierConn
*)obj
, change
->pos
);
1063 /** Revert (unapply) a point addition/removal.
1064 * @param change The change to revert.
1065 * @param obj The object (must be a BezierConn) to revert the change of.
1068 bezierconn_point_change_revert(struct PointChange
*change
, DiaObject
*obj
)
1070 switch (change
->type
) {
1071 case TYPE_ADD_POINT
:
1072 remove_handles((BezierConn
*)obj
, change
->pos
);
1074 case TYPE_REMOVE_POINT
:
1075 add_handles((BezierConn
*)obj
, change
->pos
, &change
->point
,
1076 change
->corner_type
,
1077 change
->handle1
, change
->handle2
, change
->handle3
);
1079 if (change
->connected_to1
)
1080 object_connect(obj
, change
->handle1
, change
->connected_to1
);
1081 if (change
->connected_to2
)
1082 object_connect(obj
, change
->handle2
, change
->connected_to2
);
1083 if (change
->connected_to3
)
1084 object_connect(obj
, change
->handle3
, change
->connected_to3
);
1088 change
->applied
= 0;
1091 /** Create undo information about a point being added or removed.
1092 * @param bez The object that the change applies to (ignored)
1093 * @param type The type of change (either TYPE_ADD_POINT or TYPE_REMOVE_POINT)
1094 * @param point The point being added or removed.
1095 * @param corner_type Which type of corner is at the point.
1096 * @param pos The position of the point.
1097 * @param handle1 The first (central) handle.
1098 * @param connected_to1 What the first handle is connected to.
1099 * @param handle2 The second (left-hand) handle.
1100 * @param connected_to2 What the second handle is connected to.
1101 * @param handle3 The third (right-hand) handle.
1102 * @param connected_to3 What the third handle is connected to.
1103 * @returns Newly created undo information.
1104 * @bugs Describe what the state of the point and handles should be at start.
1105 * @bugs Can these handles be connected to anything at all?
1107 static ObjectChange
*
1108 bezierconn_create_point_change(BezierConn
*bez
, enum change_type type
,
1109 BezPoint
*point
, BezCornerType corner_type
,
1111 Handle
*handle1
, ConnectionPoint
*connected_to1
,
1112 Handle
*handle2
, ConnectionPoint
*connected_to2
,
1113 Handle
*handle3
, ConnectionPoint
*connected_to3
)
1115 struct PointChange
*change
;
1117 change
= g_new(struct PointChange
, 1);
1119 change
->obj_change
.apply
= (ObjectChangeApplyFunc
) bezierconn_point_change_apply
;
1120 change
->obj_change
.revert
= (ObjectChangeRevertFunc
) bezierconn_point_change_revert
;
1121 change
->obj_change
.free
= (ObjectChangeFreeFunc
) bezierconn_point_change_free
;
1123 change
->type
= type
;
1124 change
->applied
= 1;
1125 change
->point
= *point
;
1126 change
->corner_type
= corner_type
;
1128 change
->handle1
= handle1
;
1129 change
->connected_to1
= connected_to1
;
1130 change
->handle2
= handle2
;
1131 change
->connected_to2
= connected_to2
;
1132 change
->handle3
= handle3
;
1133 change
->connected_to3
= connected_to3
;
1135 return (ObjectChange
*)change
;
1138 /** Apply a change of corner type. This may change the position of the
1139 * control handles by calling bezierconn_straighten_corner.
1140 * @param change The undo information to apply.
1141 * @param obj The object to apply the undo information too.
1144 bezierconn_corner_change_apply(struct CornerChange
*change
,
1147 BezierConn
*bez
= (BezierConn
*)obj
;
1148 int handle_nr
= get_handle_nr(bez
, change
->handle
);
1149 int comp_nr
= get_major_nr(handle_nr
);
1151 bezierconn_straighten_corner(bez
, comp_nr
);
1153 bez
->corner_types
[comp_nr
] = change
->new_type
;
1155 change
->applied
= 1;
1158 /** Revert (unapply) a change of corner type. This may move the position
1159 * of the control handles to what they were before applying.
1160 * @param change Undo information to apply.
1161 * @param obj The bezierconn object to apply the change to.
1164 bezierconn_corner_change_revert(struct CornerChange
*change
,
1167 BezierConn
*bez
= (BezierConn
*)obj
;
1168 int handle_nr
= get_handle_nr(bez
, change
->handle
);
1169 int comp_nr
= get_major_nr(handle_nr
);
1171 bez
->points
[comp_nr
].p2
= change
->point_left
;
1172 bez
->points
[comp_nr
+1].p1
= change
->point_right
;
1173 bez
->corner_types
[comp_nr
] = change
->old_type
;
1175 change
->applied
= 0;
1178 /** Create new undo information about a changing the type of a corner.
1179 * Note that the created ObjectChange object has nothing in it that needs
1181 * @param bez The bezierconn object this applies to.
1182 * @param handle The handle of the corner being changed.
1183 * @param point_left The position of the left control handle.
1184 * @param point_right The position of the right control handle.
1185 * @param old_corner_type The corner type before applying.
1186 * @param new_corner_type The corner type being changed to.
1187 * @returns Newly allocated undo information.
1189 static ObjectChange
*
1190 bezierconn_create_corner_change(BezierConn
*bez
, Handle
*handle
,
1191 Point
*point_left
, Point
*point_right
,
1192 BezCornerType old_corner_type
,
1193 BezCornerType new_corner_type
)
1195 struct CornerChange
*change
;
1197 change
= g_new(struct CornerChange
, 1);
1199 change
->obj_change
.apply
= (ObjectChangeApplyFunc
) bezierconn_corner_change_apply
;
1200 change
->obj_change
.revert
= (ObjectChangeRevertFunc
) bezierconn_corner_change_revert
;
1201 change
->obj_change
.free
= (ObjectChangeFreeFunc
) NULL
;
1203 change
->old_type
= old_corner_type
;
1204 change
->new_type
= new_corner_type
;
1205 change
->applied
= 1;
1207 change
->handle
= handle
;
1208 change
->point_left
= *point_left
;
1209 change
->point_right
= *point_right
;
1211 return (ObjectChange
*)change
;