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.
21 /** \file bezier_conn.c Allows to construct object consisting of bezier lines */
28 #include <string.h> /* memcpy() */
30 #include "bezier_conn.h"
33 #include "diarenderer.h"
35 #define HANDLE_BEZMAJOR (HANDLE_CUSTOM1)
36 #define HANDLE_LEFTCTRL (HANDLE_CUSTOM2)
37 #define HANDLE_RIGHTCTRL (HANDLE_CUSTOM3)
45 ObjectChange obj_change
;
47 enum change_type type
;
51 BezCornerType corner_type
;
53 /* owning ref when not applied for ADD_POINT
54 * owning ref when applied for REMOVE_POINT */
55 Handle
*handle1
, *handle2
, *handle3
;
56 /* NULL if not connected */
57 ConnectionPoint
*connected_to1
, *connected_to2
, *connected_to3
;
61 ObjectChange obj_change
;
62 /* Only one kind of corner_change */
66 /* Old places when SET_CORNER_TYPE is applied */
67 Point point_left
, point_right
;
68 BezCornerType old_type
, new_type
;
72 bezierconn_create_point_change(BezierConn
*bez
, enum change_type type
,
73 BezPoint
*point
, BezCornerType corner_type
,
75 Handle
*handle1
, ConnectionPoint
*connected_to1
,
76 Handle
*handle2
, ConnectionPoint
*connected_to2
,
77 Handle
*handle3
, ConnectionPoint
*connected_to3
);
79 bezierconn_create_corner_change(BezierConn
*bez
, Handle
*handle
,
80 Point
*point_left
, Point
*point_right
,
81 BezCornerType old_corner_type
,
82 BezCornerType new_corner_type
);
84 /** Set up a handle for any part of a bezierconn
85 * @param handle A handle to set up.
86 * @param id Handle id (HANDLE_BEZMAJOR or HANDLE_BEZMINOER)
89 setup_handle(Handle
*handle
, HandleId id
)
92 handle
->type
= HANDLE_MINOR_CONTROL
;
93 handle
->connect_type
= (id
== HANDLE_BEZMAJOR
) ?
94 HANDLE_CONNECTABLE
: HANDLE_NONCONNECTABLE
;
95 handle
->connected_to
= NULL
;
98 /** Get the number in the array of handles that a given handle has.
99 * @param bez A bezierconn object with handles set up.
100 * @param handle A handle object.
101 * @returns The index in bex->object.handles of the handle object, or -1 if
102 * `handle' is not in the array.
105 get_handle_nr(BezierConn
*bez
, Handle
*handle
)
108 for (i
=0;i
<bez
->object
.num_handles
;i
++) {
109 if (bez
->object
.handles
[i
] == handle
)
115 void new_handles(BezierConn
*bez
, int num_points
);
117 #define get_comp_nr(hnum) (((int)(hnum)+2)/3)
118 #define get_major_nr(hnum) (((int)(hnum)+1)/3)
120 /** Function called to move one of the handles associated with the
122 * @param obj The object whose handle is being moved.
123 * @param handle The handle being moved.
124 * @param pos The position it has been moved to (corrected for
125 * vertical/horizontal only movement).
126 * @param cp If non-NULL, the connectionpoint found at this position.
127 * If @a cp is NULL, there may or may not be a connectionpoint.
128 * @param reason ignored
129 * @param modifiers ignored
133 bezierconn_move_handle(BezierConn
*bez
, Handle
*handle
,
134 Point
*to
, ConnectionPoint
*cp
,
135 HandleMoveReason reason
, ModifierKeys modifiers
)
137 int handle_nr
, comp_nr
;
141 point_sub(&delta
, &handle
->pos
);
143 handle_nr
= get_handle_nr(bez
, handle
);
144 comp_nr
= get_comp_nr(handle_nr
);
146 case HANDLE_MOVE_STARTPOINT
:
147 bez
->points
[0].p1
= *to
;
148 /* shift adjacent point */
149 point_add(&bez
->points
[1].p1
, &delta
);
151 case HANDLE_MOVE_ENDPOINT
:
152 bez
->points
[bez
->numpoints
-1].p3
= *to
;
153 /* shift adjacent point */
154 point_add(&bez
->points
[bez
->numpoints
-1].p2
, &delta
);
156 case HANDLE_BEZMAJOR
:
157 bez
->points
[comp_nr
].p3
= *to
;
158 /* shift adjacent point */
159 point_add(&bez
->points
[comp_nr
].p2
, &delta
);
160 point_add(&bez
->points
[comp_nr
+1].p1
, &delta
);
162 case HANDLE_LEFTCTRL
:
163 bez
->points
[comp_nr
].p2
= *to
;
164 if (comp_nr
< bez
->numpoints
- 1) {
165 switch (bez
->corner_types
[comp_nr
]) {
166 case BEZ_CORNER_SYMMETRIC
:
167 pt
= bez
->points
[comp_nr
].p3
;
168 point_sub(&pt
, &bez
->points
[comp_nr
].p2
);
169 point_add(&pt
, &bez
->points
[comp_nr
].p3
);
170 bez
->points
[comp_nr
+1].p1
= pt
;
172 case BEZ_CORNER_SMOOTH
: {
174 pt
= bez
->points
[comp_nr
+1].p1
;
175 point_sub(&pt
, &bez
->points
[comp_nr
].p3
);
176 len
= point_len(&pt
);
177 pt
= bez
->points
[comp_nr
].p2
;
178 point_sub(&pt
, &bez
->points
[comp_nr
].p3
);
179 if (point_len(&pt
) > 0)
180 point_normalize(&pt
);
181 else { pt
.x
= 1.0; pt
.y
= 0.0; }
182 point_scale(&pt
, -len
);
183 point_add(&pt
, &bez
->points
[comp_nr
].p3
);
184 bez
->points
[comp_nr
+1].p1
= pt
;
187 case BEZ_CORNER_CUSP
:
188 /* Do nothing to the other guy */
193 case HANDLE_RIGHTCTRL
:
194 bez
->points
[comp_nr
].p1
= *to
;
196 switch (bez
->corner_types
[comp_nr
-1]) {
197 case BEZ_CORNER_SYMMETRIC
:
198 pt
= bez
->points
[comp_nr
- 1].p3
;
199 point_sub(&pt
, &bez
->points
[comp_nr
].p1
);
200 point_add(&pt
, &bez
->points
[comp_nr
- 1].p3
);
201 bez
->points
[comp_nr
-1].p2
= pt
;
203 case BEZ_CORNER_SMOOTH
: {
205 pt
= bez
->points
[comp_nr
-1].p2
;
206 point_sub(&pt
, &bez
->points
[comp_nr
-1].p3
);
207 len
= point_len(&pt
);
208 pt
= bez
->points
[comp_nr
].p1
;
209 point_sub(&pt
, &bez
->points
[comp_nr
-1].p3
);
210 if (point_len(&pt
) > 0)
211 point_normalize(&pt
);
212 else { pt
.x
= 1.0; pt
.y
= 0.0; }
213 point_scale(&pt
, -len
);
214 point_add(&pt
, &bez
->points
[comp_nr
-1].p3
);
215 bez
->points
[comp_nr
-1].p2
= pt
;
218 case BEZ_CORNER_CUSP
:
219 /* Do nothing to the other guy */
225 message_error("Internal error in bezierconn_move_handle.\n");
232 /** Function called to move the entire object.
233 * @param obj The object being moved.
234 * @param pos Where the object is being moved to. This is the first point
235 * of the points array.
239 bezierconn_move(BezierConn
*bez
, Point
*to
)
245 point_sub(&p
, &bez
->points
[0].p1
);
247 bez
->points
[0].p1
= *to
;
248 for (i
= 1; i
< bez
->numpoints
; i
++) {
249 point_add(&bez
->points
[i
].p1
, &p
);
250 point_add(&bez
->points
[i
].p2
, &p
);
251 point_add(&bez
->points
[i
].p3
, &p
);
257 /** Return the segment of the bezierconn closest to a given point.
258 * @param bez The bezierconn object
259 * @param point A point to find the closest segment to.
260 * @param line_width Line width of the bezier line.
261 * @returns The index of the segment closest to point.
264 bezierconn_closest_segment(BezierConn
*bez
, Point
*point
, real line_width
)
268 real dist
= G_MAXDOUBLE
;
272 last
= bez
->points
[0].p1
;
273 for (i
= 0; i
< bez
->numpoints
- 1; i
++) {
274 real new_dist
= distance_bez_seg_point(&last
, &bez
->points
[i
+1].p1
,
275 &bez
->points
[i
+1].p2
, &bez
->points
[i
+1].p3
,
277 if (new_dist
< dist
) {
281 last
= bez
->points
[i
+1].p3
;
286 /** Return the handle closest to a given point.
287 * @param bez A bezierconn object
288 * @param point A point to find distances from
289 * @return The handle on `bez' closest to `point'.
290 * @bug Why isn't this just a function on object that scans the handles?
293 bezierconn_closest_handle(BezierConn
*bez
, Point
*point
)
299 closest
= bez
->object
.handles
[0];
300 dist
= distance_point_point( point
, &closest
->pos
);
301 for (i
= 1, hn
= 1; i
< bez
->numpoints
; i
++, hn
++) {
304 new_dist
= distance_point_point(point
, &bez
->points
[i
].p1
);
305 if (new_dist
< dist
) {
307 closest
= bez
->object
.handles
[hn
];
311 new_dist
= distance_point_point(point
, &bez
->points
[i
].p2
);
312 if (new_dist
< dist
) {
314 closest
= bez
->object
.handles
[hn
];
318 new_dist
= distance_point_point(point
, &bez
->points
[i
].p3
);
319 if (new_dist
< dist
) {
321 closest
= bez
->object
.handles
[hn
];
327 /** Retrun the major handle for the control point with the handle closest to
329 * @param bez A bezier connection
330 * @param point A point
331 * @return The major (middle) handle of the bezier control that has the
332 * handle closest to point.
333 * @bug Don't we really want the major handle that's actually closest to
334 * the point? This is used in connection with object menus and could cause
335 * some unexpected selection of handles if a different segment has a control
336 * point close to the major handle.
339 bezierconn_closest_major_handle(BezierConn
*bez
, Point
*point
)
341 Handle
*closest
= bezierconn_closest_handle(bez
, point
);
343 return bez
->object
.handles
[3*get_major_nr(get_handle_nr(bez
, closest
))];
346 /** Return the distance from a bezier to a point.
347 * @param bez A bezierconn object.
348 * @param point A point to compare with.
349 * @param line_width The line width of the bezier line.
350 * @returns The shortest distance from the point to any part of the bezier.
353 bezierconn_distance_from(BezierConn
*bez
, Point
*point
, real line_width
)
355 return distance_bez_line_point(bez
->points
, bez
->numpoints
,
359 /** Add a trio of handles to a bezier.
360 * @param bez The bezierconn having handles added.
361 * @param pos Where in the list of segments to add the handle
362 * @param point The bezier point to add. This should already be initialized
363 * with sensible positions.
364 * @param corner_type What kind of corner this bezpoint should be.
365 * @param handle1 The handle that will be put on the bezier line.
366 * @param handle2 The handle that will be put before handle1
367 * @param handle3 The handle that will be put after handle1
368 * @bug check that the handle ordering is correctly described.
371 add_handles(BezierConn
*bez
, int pos
, BezPoint
*point
,
372 BezCornerType corner_type
, Handle
*handle1
,
373 Handle
*handle2
, Handle
*handle3
)
380 obj
= (DiaObject
*)bez
;
382 bez
->points
= g_realloc(bez
->points
, bez
->numpoints
*sizeof(BezPoint
));
383 bez
->corner_types
= g_realloc(bez
->corner_types
,
384 bez
->numpoints
* sizeof(BezCornerType
));
386 for (i
= bez
->numpoints
-1; i
> pos
; i
--) {
387 bez
->points
[i
] = bez
->points
[i
-1];
388 bez
->corner_types
[i
] = bez
->corner_types
[i
-1];
390 bez
->points
[pos
] = *point
;
391 bez
->points
[pos
].p1
= bez
->points
[pos
+1].p1
;
392 bez
->points
[pos
+1].p1
= point
->p1
;
393 bez
->corner_types
[pos
] = corner_type
;
394 object_add_handle_at(obj
, handle1
, 3*pos
-2);
395 object_add_handle_at(obj
, handle2
, 3*pos
-1);
396 object_add_handle_at(obj
, handle3
, 3*pos
);
398 if (pos
==bez
->numpoints
-1) {
399 obj
->handles
[obj
->num_handles
-4]->type
= HANDLE_MINOR_CONTROL
;
400 obj
->handles
[obj
->num_handles
-4]->id
= HANDLE_BEZMAJOR
;
404 /** Remove a trio of handles from a bezierconn.
405 * @param bez The bezierconn to remove handles from.
406 * @param pos The position in the bezierpoint array to remove handles and
410 remove_handles(BezierConn
*bez
, int pos
)
414 Handle
*old_handle1
, *old_handle2
, *old_handle3
;
419 obj
= (DiaObject
*)bez
;
421 if (pos
==obj
->num_handles
-1) {
422 obj
->handles
[obj
->num_handles
-4]->type
= HANDLE_MAJOR_CONTROL
;
423 obj
->handles
[obj
->num_handles
-4]->id
= HANDLE_MOVE_ENDPOINT
;
426 /* delete the points */
428 tmppoint
= bez
->points
[pos
].p1
;
429 for (i
= pos
; i
< bez
->numpoints
; i
++) {
430 bez
->points
[i
] = bez
->points
[i
+1];
431 bez
->corner_types
[i
] = bez
->corner_types
[i
+1];
433 bez
->points
[pos
].p1
= tmppoint
;
434 bez
->points
= g_realloc(bez
->points
, bez
->numpoints
*sizeof(BezPoint
));
435 bez
->corner_types
= g_realloc(bez
->corner_types
,
436 bez
->numpoints
* sizeof(BezCornerType
));
438 old_handle1
= obj
->handles
[3*pos
-2];
439 old_handle2
= obj
->handles
[3*pos
-1];
440 old_handle3
= obj
->handles
[3*pos
];
441 object_remove_handle(&bez
->object
, old_handle1
);
442 object_remove_handle(&bez
->object
, old_handle2
);
443 object_remove_handle(&bez
->object
, old_handle3
);
447 /** Add a point by splitting segment into two, putting the new point at
448 * 'point' or, if NULL, in the middle. This function will attempt to come
449 * up with reasonable placements for the control points.
450 * @param bez The bezierconn to add the segment to.
451 * @param segment Which segment to split.
452 * @param point Where to put the new corner, or NULL if undetermined.
453 * @returns An ObjectChange object with undo information for the split.
456 bezierconn_add_segment(BezierConn
*bez
, int segment
, Point
*point
)
459 BezCornerType corner_type
= BEZ_CORNER_SYMMETRIC
;
460 Handle
*new_handle1
, *new_handle2
, *new_handle3
;
464 startpoint
= bez
->points
[0].p1
;
466 startpoint
= bez
->points
[segment
].p3
;
469 realpoint
.p1
.x
= (startpoint
.x
+ bez
->points
[segment
+1].p3
.x
) / 6;
470 realpoint
.p1
.y
= (startpoint
.y
+ bez
->points
[segment
+1].p3
.y
) / 6;
471 realpoint
.p2
.x
= (startpoint
.x
+ bez
->points
[segment
+1].p3
.x
) / 3;
472 realpoint
.p2
.y
= (startpoint
.y
+ bez
->points
[segment
+1].p3
.y
) / 3;
473 realpoint
.p3
.x
= (startpoint
.x
+ bez
->points
[segment
+1].p3
.x
) / 2;
474 realpoint
.p3
.y
= (startpoint
.y
+ bez
->points
[segment
+1].p3
.y
) / 2;
476 realpoint
.p2
.x
= point
->x
+(startpoint
.x
- bez
->points
[segment
+1].p3
.x
)/6;
477 realpoint
.p2
.y
= point
->y
+(startpoint
.y
- bez
->points
[segment
+1].p3
.y
)/6;
478 realpoint
.p3
= *point
;
479 /* this really goes into the next segment ... */
480 realpoint
.p1
.x
= point
->x
-(startpoint
.x
- bez
->points
[segment
+1].p3
.x
)/6;
481 realpoint
.p1
.y
= point
->y
-(startpoint
.y
- bez
->points
[segment
+1].p3
.y
)/6;
483 realpoint
.type
= BEZ_CURVE_TO
;
485 new_handle1
= g_malloc(sizeof(Handle
));
486 new_handle2
= g_malloc(sizeof(Handle
));
487 new_handle3
= g_malloc(sizeof(Handle
));
488 setup_handle(new_handle1
, HANDLE_RIGHTCTRL
);
489 setup_handle(new_handle2
, HANDLE_LEFTCTRL
);
490 setup_handle(new_handle3
, HANDLE_BEZMAJOR
);
491 add_handles(bez
, segment
+1, &realpoint
, corner_type
,
492 new_handle1
, new_handle2
, new_handle3
);
493 return bezierconn_create_point_change(bez
, TYPE_ADD_POINT
,
494 &realpoint
, corner_type
, segment
+1,
500 /** Remove a segment from a bezierconn.
501 * @param bez The bezierconn to remove a segment from.
502 * @param pos The index of the segment to remove.
503 * @returns Undo information for the segment removal.
506 bezierconn_remove_segment(BezierConn
*bez
, int pos
)
508 Handle
*old_handle1
, *old_handle2
, *old_handle3
;
509 ConnectionPoint
*cpt1
, *cpt2
, *cpt3
;
511 BezCornerType old_ctype
;
514 g_assert(bez
->numpoints
> 2);
516 if (pos
== bez
->numpoints
-1) pos
--;
518 old_handle1
= bez
->object
.handles
[3*pos
-2];
519 old_handle2
= bez
->object
.handles
[3*pos
-1];
520 old_handle3
= bez
->object
.handles
[3*pos
];
521 old_point
= bez
->points
[pos
];
522 old_ctype
= bez
->corner_types
[pos
];
524 cpt1
= old_handle1
->connected_to
;
525 cpt2
= old_handle2
->connected_to
;
526 cpt3
= old_handle3
->connected_to
;
528 object_unconnect((DiaObject
*)bez
, old_handle1
);
529 object_unconnect((DiaObject
*)bez
, old_handle2
);
530 object_unconnect((DiaObject
*)bez
, old_handle3
);
532 remove_handles(bez
, pos
);
534 bezierconn_update_data(bez
);
536 return bezierconn_create_point_change(bez
, TYPE_REMOVE_POINT
,
537 &old_point
, old_ctype
, pos
,
543 /** Update a corner to have less freedom in its control handles, arranging
544 * the control points at some reasonable places.
545 * @param bez A bezierconn to straighten a corner of
546 * @param comp_nr The index into the corner_types array of the corner to
548 * @bug what happens if we're going from symmetric to smooth?
551 bezierconn_straighten_corner(BezierConn
*bez
, int comp_nr
)
553 /* Neat thing would be to have the kind of straigthening depend on
554 which handle was chosen: Mid-handle does average, other leaves that
555 handle where it is. */
556 switch (bez
->corner_types
[comp_nr
]) {
557 case BEZ_CORNER_SYMMETRIC
: {
559 pt1
= bez
->points
[comp_nr
].p3
;
560 point_sub(&pt1
, &bez
->points
[comp_nr
].p2
);
561 pt2
= bez
->points
[comp_nr
].p3
;
562 point_sub(&pt2
, &bez
->points
[comp_nr
+1].p1
);
563 point_scale(&pt2
, -1.0);
564 point_add(&pt1
, &pt2
);
565 point_scale(&pt1
, 0.5);
567 point_scale(&pt1
, -1.0);
568 point_add(&pt1
, &bez
->points
[comp_nr
].p3
);
569 point_add(&pt2
, &bez
->points
[comp_nr
].p3
);
570 bez
->points
[comp_nr
].p2
= pt1
;
571 bez
->points
[comp_nr
+1].p1
= pt2
;
572 bezierconn_update_data(bez
);
575 case BEZ_CORNER_SMOOTH
: {
578 pt1
= bez
->points
[comp_nr
].p3
;
579 point_sub(&pt1
, &bez
->points
[comp_nr
].p2
);
580 pt2
= bez
->points
[comp_nr
].p3
;
581 point_sub(&pt2
, &bez
->points
[comp_nr
+1].p1
);
582 len1
= point_len(&pt1
);
583 len2
= point_len(&pt2
);
584 point_scale(&pt2
, -1.0);
586 point_normalize(&pt1
);
588 point_normalize(&pt2
);
589 point_add(&pt1
, &pt2
);
590 point_scale(&pt1
, 0.5);
592 point_scale(&pt1
, -len1
);
593 point_add(&pt1
, &bez
->points
[comp_nr
].p3
);
594 point_scale(&pt2
, len2
);
595 point_add(&pt2
, &bez
->points
[comp_nr
].p3
);
596 bez
->points
[comp_nr
].p2
= pt1
;
597 bez
->points
[comp_nr
+1].p1
= pt2
;
598 bezierconn_update_data(bez
);
601 case BEZ_CORNER_CUSP
:
606 /** Change the corner type of a bezier line.
607 * @param bez The bezierconn that has the corner
608 * @param handle The handle whose corner should be set.
609 * @param corner_type What type of corner the handle should have.
610 * @returns Undo information about the corner change.
613 bezierconn_set_corner_type(BezierConn
*bez
, Handle
*handle
,
614 BezCornerType corner_type
)
617 Point old_left
, old_right
;
619 int handle_nr
, comp_nr
;
621 handle_nr
= get_handle_nr(bez
, handle
);
623 switch (handle
->id
) {
624 case HANDLE_BEZMAJOR
:
627 case HANDLE_LEFTCTRL
:
629 mid_handle
= bez
->object
.handles
[handle_nr
];
631 case HANDLE_RIGHTCTRL
:
633 mid_handle
= bez
->object
.handles
[handle_nr
];
636 message_warning(_("Internal error: Setting corner type of endpoint of bezier"));
640 comp_nr
= get_major_nr(handle_nr
);
642 old_type
= bez
->corner_types
[comp_nr
];
643 old_left
= bez
->points
[comp_nr
].p2
;
644 old_right
= bez
->points
[comp_nr
+1].p1
;
646 bez
->corner_types
[comp_nr
] = corner_type
;
648 bezierconn_straighten_corner(bez
, comp_nr
);
650 return bezierconn_create_corner_change(bez
, mid_handle
, &old_left
, &old_right
,
651 old_type
, corner_type
);
654 /** Update handle array and handle positions after changes.
655 * @param bez A bezierconn to update.
658 bezierconn_update_data(BezierConn
*bez
)
661 DiaObject
*obj
= &bez
->object
;
663 /* handle the case of whole points array update (via set_prop) */
664 if (3*bez
->numpoints
-2 != obj
->num_handles
) {
665 g_assert(0 == obj
->num_connections
);
667 for (i
= 0; i
< obj
->num_handles
; i
++)
668 g_free(obj
->handles
[i
]);
669 g_free(obj
->handles
);
671 obj
->num_handles
= 3*bez
->numpoints
-2;
672 obj
->handles
= g_new(Handle
*, obj
->num_handles
);
674 new_handles(bez
, bez
->numpoints
);
677 /* Update handles: */
678 bez
->object
.handles
[0]->pos
= bez
->points
[0].p1
;
679 for (i
= 1; i
< bez
->numpoints
; i
++) {
680 bez
->object
.handles
[3*i
-2]->pos
= bez
->points
[i
].p1
;
681 bez
->object
.handles
[3*i
-1]->pos
= bez
->points
[i
].p2
;
682 bez
->object
.handles
[3*i
]->pos
= bez
->points
[i
].p3
;
686 /** Update the boundingbox of the connection.
687 * @param bez A bezier line to update bounding box for.
690 bezierconn_update_boundingbox(BezierConn
*bez
)
692 g_assert(bez
!= NULL
);
694 polybezier_bbox(&bez
->points
[0],
696 &bez
->extra_spacing
, FALSE
,
697 &bez
->object
.bounding_box
);
700 /** Draw the main line of a bezier conn.
701 * Note that this sets the linestyle, linejoin and linecaps to hardcoded
703 * @param bez The bezier conn to draw.
704 * @param renderer The renderer to draw with.
705 * @param width The linewidth of the bezier.
708 bezierconn_simple_draw(BezierConn
*bez
, DiaRenderer
*renderer
, real width
)
712 g_assert(bez
!= NULL
);
713 g_assert(renderer
!= NULL
);
715 points
= &bez
->points
[0];
717 DIA_RENDERER_GET_CLASS(renderer
)->set_linewidth(renderer
, width
);
718 DIA_RENDERER_GET_CLASS(renderer
)->set_linestyle(renderer
, LINESTYLE_SOLID
);
719 DIA_RENDERER_GET_CLASS(renderer
)->set_linejoin(renderer
, LINEJOIN_ROUND
);
720 DIA_RENDERER_GET_CLASS(renderer
)->set_linecaps(renderer
, LINECAPS_BUTT
);
722 DIA_RENDERER_GET_CLASS(renderer
)->draw_bezier(renderer
, points
, bez
->numpoints
, &color_black
);
725 /** Draw the control lines from the points of the bezier conn.
726 * Note that the control lines are hardcoded to be dotted with dash length 1.
727 * @param bez The bezier conn to draw control lines for.
728 * @param renderer A renderer to draw with.
731 bezierconn_draw_control_lines(BezierConn
*bez
, DiaRenderer
*renderer
)
733 Color line_colour
= {0.0, 0.0, 0.6};
737 /* setup DiaRenderer ... */
738 DIA_RENDERER_GET_CLASS(renderer
)->set_linewidth(renderer
, 0);
739 DIA_RENDERER_GET_CLASS(renderer
)->set_linestyle(renderer
, LINESTYLE_DOTTED
);
740 DIA_RENDERER_GET_CLASS(renderer
)->set_dashlength(renderer
, 1);
741 DIA_RENDERER_GET_CLASS(renderer
)->set_linejoin(renderer
, LINEJOIN_MITER
);
742 DIA_RENDERER_GET_CLASS(renderer
)->set_linecaps(renderer
, LINECAPS_BUTT
);
744 startpoint
= bez
->points
[0].p1
;
745 for (i
= 1; i
< bez
->numpoints
; i
++) {
746 DIA_RENDERER_GET_CLASS(renderer
)->draw_line(renderer
, &startpoint
, &bez
->points
[i
].p1
,
748 DIA_RENDERER_GET_CLASS(renderer
)->draw_line(renderer
, &bez
->points
[i
].p2
, &bez
->points
[i
].p3
,
750 startpoint
= bez
->points
[i
].p3
;
754 /** Create all handles used by a bezier conn.
755 * @param bez A bezierconn object initialized with room for 3*num_points-2
757 * @param num_points The number of points of the bezierconn.
760 new_handles(BezierConn
*bez
, int num_points
)
767 obj
->handles
[0] = g_new(Handle
,1);
768 obj
->handles
[0]->connect_type
= HANDLE_CONNECTABLE
;
769 obj
->handles
[0]->connected_to
= NULL
;
770 obj
->handles
[0]->type
= HANDLE_MAJOR_CONTROL
;
771 obj
->handles
[0]->id
= HANDLE_MOVE_STARTPOINT
;
773 for (i
= 1; i
< num_points
; i
++) {
774 obj
->handles
[3*i
-2] = g_new(Handle
, 1);
775 obj
->handles
[3*i
-1] = g_new(Handle
, 1);
776 obj
->handles
[3*i
] = g_new(Handle
, 1);
778 setup_handle(obj
->handles
[3*i
-2], HANDLE_RIGHTCTRL
);
779 setup_handle(obj
->handles
[3*i
-1], HANDLE_LEFTCTRL
);
781 obj
->handles
[3*i
]->connect_type
= HANDLE_CONNECTABLE
;
782 obj
->handles
[3*i
]->connected_to
= NULL
;
783 obj
->handles
[3*i
]->type
= HANDLE_MAJOR_CONTROL
;
784 obj
->handles
[3*i
]->id
= HANDLE_MOVE_ENDPOINT
;
788 /** Initialize a bezierconn object with the given amount of points.
789 * The points array of the bezierconn object should be previously
790 * initialized with appropriate positions.
791 * This will set up handles and make all corners symmetric.
792 * @param bez A newly allocated bezierconn object.
793 * @param num_points The initial number of points on the curve.
796 bezierconn_init(BezierConn
*bez
, int num_points
)
803 object_init(obj
, 3*num_points
-2, 0);
805 bez
->numpoints
= num_points
;
807 bez
->points
= g_new(BezPoint
, num_points
);
808 bez
->corner_types
= g_new(BezCornerType
, num_points
);
809 bez
->points
[0].type
= BEZ_MOVE_TO
;
810 bez
->corner_types
[0] = BEZ_CORNER_SYMMETRIC
;
811 for (i
= 1; i
< num_points
; i
++) {
812 bez
->points
[i
].type
= BEZ_CURVE_TO
;
813 bez
->corner_types
[i
] = BEZ_CORNER_SYMMETRIC
;
816 new_handles(bez
, num_points
);
818 bezierconn_update_data(bez
);
821 /** Set a bezierconn to use the given array of points.
822 * This function does *not* set up handles
823 * @param bez A bezierconn to operate on
824 * @param num_points The number of points in the `points' array.
825 * @param points The new points that this bezier should be set to use.
828 bezierconn_set_points(BezierConn
*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 /** Copy a bezierconn objects. This function in turn invokes object_copy.
846 * @param from The object to copy from.
847 * @param to The object to copy to.
850 bezierconn_copy(BezierConn
*from
, BezierConn
*to
)
853 DiaObject
*toobj
, *fromobj
;
856 fromobj
= &from
->object
;
858 object_copy(fromobj
, toobj
);
860 to
->numpoints
= from
->numpoints
;
862 to
->points
= g_new(BezPoint
, to
->numpoints
);
863 to
->corner_types
= g_new(BezCornerType
, to
->numpoints
);
865 for (i
= 0; i
< to
->numpoints
; i
++) {
866 to
->points
[i
] = from
->points
[i
];
867 to
->corner_types
[i
] = from
->corner_types
[i
];
870 to
->object
.handles
[0] = g_new(Handle
,1);
871 *to
->object
.handles
[0] = *from
->object
.handles
[0];
872 for (i
= 1; i
< to
->object
.num_handles
- 1; i
++) {
873 to
->object
.handles
[i
] = g_new(Handle
, 1);
874 setup_handle(to
->object
.handles
[i
], from
->object
.handles
[i
]->id
);
876 to
->object
.handles
[to
->object
.num_handles
-1] = g_new(Handle
,1);
877 *to
->object
.handles
[to
->object
.num_handles
-1] =
878 *from
->object
.handles
[to
->object
.num_handles
-1];
880 memcpy(&to
->extra_spacing
,&from
->extra_spacing
,sizeof(to
->extra_spacing
));
881 bezierconn_update_data(to
);
884 /** Destroy a bezierconn object.
885 * @param bez An object to destroy. This function in turn calls object_destroy
886 * and frees structures allocated by this bezierconn, but not the object itself
889 bezierconn_destroy(BezierConn
*bez
)
892 Handle
**temp_handles
;
894 /* Need to store these temporary since object.handles is
895 freed by object_destroy() */
896 nh
= bez
->object
.num_handles
;
897 temp_handles
= g_new(Handle
*, nh
);
898 for (i
= 0; i
< nh
; i
++)
899 temp_handles
[i
] = bez
->object
.handles
[i
];
901 object_destroy(&bez
->object
);
903 for (i
= 0; i
< nh
; i
++)
904 g_free(temp_handles
[i
]);
905 g_free(temp_handles
);
908 g_free(bez
->corner_types
);
912 /** Save the data defined by a bezierconn object to XML.
913 * @param bez The object to save.
914 * @param obj_node The XML node to save it into
915 * @bug shouldn't this have version?
918 bezierconn_save(BezierConn
*bez
, ObjectNode obj_node
)
923 object_save(&bez
->object
, obj_node
);
925 attr
= new_attribute(obj_node
, "bez_points");
927 data_add_point(attr
, &bez
->points
[0].p1
);
928 for (i
= 1; i
< bez
->numpoints
; i
++) {
929 data_add_point(attr
, &bez
->points
[i
].p1
);
930 data_add_point(attr
, &bez
->points
[i
].p2
);
931 data_add_point(attr
, &bez
->points
[i
].p3
);
934 attr
= new_attribute(obj_node
, "corner_types");
935 for (i
= 0; i
< bez
->numpoints
; i
++)
936 data_add_enum(attr
, bez
->corner_types
[i
]);
939 /** Load a bezierconn object from XML.
940 * Does object_init() on the bezierconn object.
941 * @param bez A newly allocated bezierconn object to load into.
942 * @param obj_node The XML node to load from.
943 * @bug shouldn't this have version?
944 * @bug Couldn't this use the setup_handles function defined above?
947 bezierconn_load(BezierConn
*bez
, ObjectNode obj_node
)
953 DiaObject
*obj
= &bez
->object
;
955 object_load(obj
, obj_node
);
957 attr
= object_find_attribute(obj_node
, "bez_points");
960 bez
->numpoints
= (attribute_num_data(attr
) + 2)/3;
964 object_init(obj
, 3 * bez
->numpoints
- 2, 0);
966 data
= attribute_first_data(attr
);
967 if (bez
->numpoints
!= 0) {
968 bez
->points
= g_new(BezPoint
, bez
->numpoints
);
969 bez
->points
[0].type
= BEZ_MOVE_TO
;
970 data_point(data
, &bez
->points
[0].p1
);
971 data
= data_next(data
);
973 for (i
= 1; i
< bez
->numpoints
; i
++) {
974 bez
->points
[i
].type
= BEZ_CURVE_TO
;
975 data_point(data
, &bez
->points
[i
].p1
);
976 data
= data_next(data
);
977 data_point(data
, &bez
->points
[i
].p2
);
978 data
= data_next(data
);
979 data_point(data
, &bez
->points
[i
].p3
);
980 data
= data_next(data
);
984 bez
->corner_types
= g_new(BezCornerType
, bez
->numpoints
);
986 attr
= object_find_attribute(obj_node
, "corner_types");
987 /* if corner_types is missing or corrupt */
988 if (!attr
|| attribute_num_data(attr
) != bez
->numpoints
) {
989 for (i
= 0; i
< bez
->numpoints
; i
++) {
990 bez
->corner_types
[i
] = BEZ_CORNER_SYMMETRIC
;
993 data
= attribute_first_data(attr
);
994 for (i
= 0; i
< bez
->numpoints
; i
++) {
995 bez
->corner_types
[i
] = data_enum(data
);
996 data
= data_next(data
);
1000 obj
->handles
[0] = g_new(Handle
, 1);
1001 obj
->handles
[0]->connect_type
= HANDLE_CONNECTABLE
;
1002 obj
->handles
[0]->connected_to
= NULL
;
1003 obj
->handles
[0]->type
= HANDLE_MAJOR_CONTROL
;
1004 obj
->handles
[0]->id
= HANDLE_MOVE_STARTPOINT
;
1006 for (i
= 1; i
< bez
->numpoints
; i
++) {
1007 obj
->handles
[3*i
-2] = g_new(Handle
, 1);
1008 setup_handle(obj
->handles
[3*i
-2], HANDLE_RIGHTCTRL
);
1009 obj
->handles
[3*i
-1] = g_new(Handle
, 1);
1010 setup_handle(obj
->handles
[3*i
-1], HANDLE_LEFTCTRL
);
1011 obj
->handles
[3*i
] = g_new(Handle
, 1);
1012 setup_handle(obj
->handles
[3*i
], HANDLE_BEZMAJOR
);
1015 obj
->handles
[obj
->num_handles
-1]->connect_type
= HANDLE_CONNECTABLE
;
1016 obj
->handles
[obj
->num_handles
-1]->connected_to
= NULL
;
1017 obj
->handles
[obj
->num_handles
-1]->type
= HANDLE_MAJOR_CONTROL
;
1018 obj
->handles
[obj
->num_handles
-1]->id
= HANDLE_MOVE_ENDPOINT
;
1020 bezierconn_update_data(bez
);
1023 /*** Undo support ***/
1025 /** Free undo information about adding or removing points.
1026 * @param change The undo information to free.
1029 bezierconn_point_change_free(struct PointChange
*change
)
1031 if ( (change
->type
==TYPE_ADD_POINT
&& !change
->applied
) ||
1032 (change
->type
==TYPE_REMOVE_POINT
&& change
->applied
) ){
1033 g_free(change
->handle1
);
1034 g_free(change
->handle2
);
1035 g_free(change
->handle3
);
1036 change
->handle1
= NULL
;
1037 change
->handle2
= NULL
;
1038 change
->handle3
= NULL
;
1042 /** Apply a point addition/removal.
1043 * @param change The change to apply.
1044 * @param obj The object (must be a BezierConn) to apply the change to.
1047 bezierconn_point_change_apply(struct PointChange
*change
, DiaObject
*obj
)
1049 change
->applied
= 1;
1050 switch (change
->type
) {
1051 case TYPE_ADD_POINT
:
1052 add_handles((BezierConn
*)obj
, change
->pos
, &change
->point
,
1053 change
->corner_type
,
1054 change
->handle1
, change
->handle2
, change
->handle3
);
1056 case TYPE_REMOVE_POINT
:
1057 object_unconnect(obj
, change
->handle1
);
1058 object_unconnect(obj
, change
->handle2
);
1059 object_unconnect(obj
, change
->handle3
);
1060 remove_handles((BezierConn
*)obj
, change
->pos
);
1065 /** Revert (unapply) a point addition/removal.
1066 * @param change The change to revert.
1067 * @param obj The object (must be a BezierConn) to revert the change of.
1070 bezierconn_point_change_revert(struct PointChange
*change
, DiaObject
*obj
)
1072 switch (change
->type
) {
1073 case TYPE_ADD_POINT
:
1074 remove_handles((BezierConn
*)obj
, change
->pos
);
1076 case TYPE_REMOVE_POINT
:
1077 add_handles((BezierConn
*)obj
, change
->pos
, &change
->point
,
1078 change
->corner_type
,
1079 change
->handle1
, change
->handle2
, change
->handle3
);
1081 if (change
->connected_to1
)
1082 object_connect(obj
, change
->handle1
, change
->connected_to1
);
1083 if (change
->connected_to2
)
1084 object_connect(obj
, change
->handle2
, change
->connected_to2
);
1085 if (change
->connected_to3
)
1086 object_connect(obj
, change
->handle3
, change
->connected_to3
);
1090 change
->applied
= 0;
1093 /** Create undo information about a point being added or removed.
1094 * @param bez The object that the change applies to (ignored)
1095 * @param type The type of change (either TYPE_ADD_POINT or TYPE_REMOVE_POINT)
1096 * @param point The point being added or removed.
1097 * @param corner_type Which type of corner is at the point.
1098 * @param pos The position of the point.
1099 * @param handle1 The first (central) handle.
1100 * @param connected_to1 What the first handle is connected to.
1101 * @param handle2 The second (left-hand) handle.
1102 * @param connected_to2 What the second handle is connected to.
1103 * @param handle3 The third (right-hand) handle.
1104 * @param connected_to3 What the third handle is connected to.
1105 * @return Newly created undo information.
1106 * @bug Describe what the state of the point and handles should be at start.
1107 * @bug Can these handles be connected to anything at all?
1109 static ObjectChange
*
1110 bezierconn_create_point_change(BezierConn
*bez
, enum change_type type
,
1111 BezPoint
*point
, BezCornerType corner_type
,
1113 Handle
*handle1
, ConnectionPoint
*connected_to1
,
1114 Handle
*handle2
, ConnectionPoint
*connected_to2
,
1115 Handle
*handle3
, ConnectionPoint
*connected_to3
)
1117 struct PointChange
*change
;
1119 change
= g_new(struct PointChange
, 1);
1121 change
->obj_change
.apply
= (ObjectChangeApplyFunc
) bezierconn_point_change_apply
;
1122 change
->obj_change
.revert
= (ObjectChangeRevertFunc
) bezierconn_point_change_revert
;
1123 change
->obj_change
.free
= (ObjectChangeFreeFunc
) bezierconn_point_change_free
;
1125 change
->type
= type
;
1126 change
->applied
= 1;
1127 change
->point
= *point
;
1128 change
->corner_type
= corner_type
;
1130 change
->handle1
= handle1
;
1131 change
->connected_to1
= connected_to1
;
1132 change
->handle2
= handle2
;
1133 change
->connected_to2
= connected_to2
;
1134 change
->handle3
= handle3
;
1135 change
->connected_to3
= connected_to3
;
1137 return (ObjectChange
*)change
;
1140 /** Apply a change of corner type. This may change the position of the
1141 * control handles by calling bezierconn_straighten_corner.
1142 * @param change The undo information to apply.
1143 * @param obj The object to apply the undo information too.
1146 bezierconn_corner_change_apply(struct CornerChange
*change
,
1149 BezierConn
*bez
= (BezierConn
*)obj
;
1150 int handle_nr
= get_handle_nr(bez
, change
->handle
);
1151 int comp_nr
= get_major_nr(handle_nr
);
1153 bezierconn_straighten_corner(bez
, comp_nr
);
1155 bez
->corner_types
[comp_nr
] = change
->new_type
;
1157 change
->applied
= 1;
1160 /** Revert (unapply) a change of corner type. This may move the position
1161 * of the control handles to what they were before applying.
1162 * @param change Undo information to apply.
1163 * @param obj The bezierconn object to apply the change to.
1166 bezierconn_corner_change_revert(struct CornerChange
*change
,
1169 BezierConn
*bez
= (BezierConn
*)obj
;
1170 int handle_nr
= get_handle_nr(bez
, change
->handle
);
1171 int comp_nr
= get_major_nr(handle_nr
);
1173 bez
->points
[comp_nr
].p2
= change
->point_left
;
1174 bez
->points
[comp_nr
+1].p1
= change
->point_right
;
1175 bez
->corner_types
[comp_nr
] = change
->old_type
;
1177 change
->applied
= 0;
1180 /** Create new undo information about a changing the type of a corner.
1181 * Note that the created ObjectChange object has nothing in it that needs
1183 * @param bez The bezierconn object this applies to.
1184 * @param handle The handle of the corner being changed.
1185 * @param point_left The position of the left control handle.
1186 * @param point_right The position of the right control handle.
1187 * @param old_corner_type The corner type before applying.
1188 * @param new_corner_type The corner type being changed to.
1189 * @returns Newly allocated undo information.
1191 static ObjectChange
*
1192 bezierconn_create_corner_change(BezierConn
*bez
, Handle
*handle
,
1193 Point
*point_left
, Point
*point_right
,
1194 BezCornerType old_corner_type
,
1195 BezCornerType new_corner_type
)
1197 struct CornerChange
*change
;
1199 change
= g_new(struct CornerChange
, 1);
1201 change
->obj_change
.apply
= (ObjectChangeApplyFunc
) bezierconn_corner_change_apply
;
1202 change
->obj_change
.revert
= (ObjectChangeRevertFunc
) bezierconn_corner_change_revert
;
1203 change
->obj_change
.free
= (ObjectChangeFreeFunc
) NULL
;
1205 change
->old_type
= old_corner_type
;
1206 change
->new_type
= new_corner_type
;
1207 change
->applied
= 1;
1209 change
->handle
= handle
;
1210 change
->point_left
= *point_left
;
1211 change
->point_right
= *point_right
;
1213 return (ObjectChange
*)change
;