1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1999 Alexander Larsson
3 *
4 * Bezier object Copyright (C) 1999 Lars R. Clausen
5 * Conversion to use BezierConn by James Henstridge.
6 *
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.
11 *
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.
16 *
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.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <assert.h>
27 #include <math.h>
28
29 #include "intl.h"
30 #include "object.h"
31 #include "bezier_conn.h"
32 #include "connectionpoint.h"
33 #include "diarenderer.h"
34 #include "attributes.h"
35 #include "widgets.h"
36 #include "diamenu.h"
37 #include "message.h"
38 #include "properties.h"
39 #include "create.h"
40
41 #include "tool-icons.h"
42
43 #define DEFAULT_WIDTH 0.15
44
45 typedef struct _Bezierline Bezierline;
46
47 struct _Bezierline {
48 BezierConn bez;
49
50 Color line_color;
51 LineStyle line_style;
52 real dashlength;
53 real line_width;
54 Arrow start_arrow, end_arrow;
55 real absolute_start_gap, absolute_end_gap;
56 };
57
58
59 static ObjectChange* bezierline_move_handle(Bezierline *bezierline, Handle *handle,
60 Point *to, ConnectionPoint *cp,
61 HandleMoveReason reason, ModifierKeys modifiers);
62 static ObjectChange* bezierline_move(Bezierline *bezierline, Point *to);
63 static void bezierline_select(Bezierline *bezierline, Point *clicked_point,
64 DiaRenderer *interactive_renderer);
65 static void bezierline_draw(Bezierline *bezierline, DiaRenderer *renderer);
66 static DiaObject *bezierline_create(Point *startpoint,
67 void *user_data,
68 Handle **handle1,
69 Handle **handle2);
70 static real bezierline_distance_from(Bezierline *bezierline, Point *point);
71 static void bezierline_update_data(Bezierline *bezierline);
72 static void bezierline_destroy(Bezierline *bezierline);
73 static DiaObject *bezierline_copy(Bezierline *bezierline);
74
75 static PropDescription *bezierline_describe_props(Bezierline *bezierline);
76 static void bezierline_get_props(Bezierline *bezierline, GPtrArray *props);
77 static void bezierline_set_props(Bezierline *bezierline, GPtrArray *props);
78
79 static void bezierline_save(Bezierline *bezierline, ObjectNode obj_node,
80 const char *filename);
81 static DiaObject *bezierline_load(ObjectNode obj_node, int version,
82 const char *filename);
83 static DiaMenu *bezierline_get_object_menu(Bezierline *bezierline, Point *clickedpoint);
84
85 static void compute_gap_points(Bezierline *bezierline, Point *gap_points);
86 static real approx_bez_length(BezierConn *bez);
87 static void exchange_bez_gap_points(BezierConn * bez, Point* gap_points);
88
89 static ObjectTypeOps bezierline_type_ops =
90 {
91 (CreateFunc)bezierline_create, /* create */
92 (LoadFunc) bezierline_load, /* load */
93 (SaveFunc) bezierline_save, /* save */
94 (GetDefaultsFunc) NULL,
95 (ApplyDefaultsFunc) NULL
96 };
97
98 static DiaObjectType bezierline_type =
99 {
100 "Standard - BezierLine", /* name */
101 0, /* version */
102 (char **) bezierline_icon, /* pixmap */
103
104 &bezierline_type_ops, /* ops */
105 NULL, /* pixmap_file */
106 0 /* default_user_data */
107 };
108
109 DiaObjectType *_bezierline_type = (DiaObjectType *) &bezierline_type;
110
111
112 static ObjectOps bezierline_ops = {
113 (DestroyFunc) bezierline_destroy,
114 (DrawFunc) bezierline_draw,
115 (DistanceFunc) bezierline_distance_from,
116 (SelectFunc) bezierline_select,
117 (CopyFunc) bezierline_copy,
118 (MoveFunc) bezierline_move,
119 (MoveHandleFunc) bezierline_move_handle,
120 (GetPropertiesFunc) object_create_props_dialog,
121 (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
122 (ObjectMenuFunc) bezierline_get_object_menu,
123 (DescribePropsFunc) bezierline_describe_props,
124 (GetPropsFunc) bezierline_get_props,
125 (SetPropsFunc) bezierline_set_props,
126 (TextEditFunc) 0,
127 (ApplyPropertiesListFunc) object_apply_props,
128 };
129
130 static PropNumData gap_range = { -G_MAXFLOAT, G_MAXFLOAT, 0.1};
131
132 static PropDescription bezierline_props[] = {
133 BEZCONN_COMMON_PROPERTIES,
134 PROP_STD_LINE_WIDTH,
135 PROP_STD_LINE_COLOUR,
136 PROP_STD_LINE_STYLE,
137 PROP_STD_START_ARROW,
138 PROP_STD_END_ARROW,
139 PROP_FRAME_BEGIN("gaps",0,N_("Line gaps")),
140 { "absolute_start_gap", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
141 N_("Absolute start gap"), NULL, &gap_range },
142 { "absolute_end_gap", PROP_TYPE_REAL, PROP_FLAG_VISIBLE,
143 N_("Absolute end gap"), NULL, &gap_range },
144 PROP_FRAME_END("gaps",0),
145 PROP_DESC_END
146 };
147
148 static PropDescription *
bezierline_describe_props(Bezierline * bezierline)149 bezierline_describe_props(Bezierline *bezierline)
150 {
151 if (bezierline_props[0].quark == 0)
152 prop_desc_list_calculate_quarks(bezierline_props);
153 return bezierline_props;
154 }
155
156 static PropOffset bezierline_offsets[] = {
157 BEZCONN_COMMON_PROPERTIES_OFFSETS,
158 { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(Bezierline, line_width) },
159 { "line_colour", PROP_TYPE_COLOUR, offsetof(Bezierline, line_color) },
160 { "line_style", PROP_TYPE_LINESTYLE,
161 offsetof(Bezierline, line_style), offsetof(Bezierline, dashlength) },
162 { "start_arrow", PROP_TYPE_ARROW, offsetof(Bezierline, start_arrow) },
163 { "end_arrow", PROP_TYPE_ARROW, offsetof(Bezierline, end_arrow) },
164 PROP_OFFSET_FRAME_BEGIN("gaps"),
165 { "absolute_start_gap", PROP_TYPE_REAL, offsetof(Bezierline, absolute_start_gap) },
166 { "absolute_end_gap", PROP_TYPE_REAL, offsetof(Bezierline, absolute_end_gap) },
167 PROP_OFFSET_FRAME_END("gaps"),
168 { NULL, 0, 0 }
169 };
170
171 static void
bezierline_get_props(Bezierline * bezierline,GPtrArray * props)172 bezierline_get_props(Bezierline *bezierline, GPtrArray *props)
173 {
174 object_get_props_from_offsets(&bezierline->bez.object, bezierline_offsets,
175 props);
176 }
177
178 static void
bezierline_set_props(Bezierline * bezierline,GPtrArray * props)179 bezierline_set_props(Bezierline *bezierline, GPtrArray *props)
180 {
181 object_set_props_from_offsets(&bezierline->bez.object, bezierline_offsets,
182 props);
183 bezierline_update_data(bezierline);
184 }
185
186 static real
bezierline_distance_from(Bezierline * bezierline,Point * point)187 bezierline_distance_from(Bezierline *bezierline, Point *point)
188 {
189 BezierConn *bez = &bezierline->bez;
190 if (connpoint_is_autogap(bez->object.handles[0]->connected_to) ||
191 connpoint_is_autogap(bez->object.handles[3*(bez->numpoints-1)]->connected_to) ||
192 bezierline->absolute_start_gap || bezierline->absolute_end_gap) {
193 Point gap_points[4];
194 real distance;
195 compute_gap_points(bezierline, gap_points);
196 exchange_bez_gap_points(bez,gap_points);
197 distance = bezierconn_distance_from(bez, point, bezierline->line_width);
198 exchange_bez_gap_points(bez,gap_points);
199 return distance;
200 } else {
201 return bezierconn_distance_from(bez, point, bezierline->line_width);
202 }
203 }
204
bezierline_closest_segment(Bezierline * bezierline,Point * point)205 static int bezierline_closest_segment(Bezierline *bezierline, Point *point) {
206 BezierConn *bez = &bezierline->bez;
207 return bezierconn_closest_segment(bez, point, bezierline->line_width);
208 }
209
210 static void
bezierline_select(Bezierline * bezierline,Point * clicked_point,DiaRenderer * interactive_renderer)211 bezierline_select(Bezierline *bezierline, Point *clicked_point,
212 DiaRenderer *interactive_renderer)
213 {
214 bezierconn_update_data(&bezierline->bez);
215 }
216
217 static ObjectChange*
bezierline_move_handle(Bezierline * bezierline,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)218 bezierline_move_handle(Bezierline *bezierline, Handle *handle,
219 Point *to, ConnectionPoint *cp,
220 HandleMoveReason reason, ModifierKeys modifiers)
221 {
222 assert(bezierline!=NULL);
223 assert(handle!=NULL);
224 assert(to!=NULL);
225
226 if (reason == HANDLE_MOVE_CREATE || reason == HANDLE_MOVE_CREATE_FINAL) {
227 /* During creation, change the control points */
228 BezierConn *bez = &bezierline->bez;
229 Point dist = bez->points[0].p1;
230
231 point_sub(&dist, to);
232 dist.y = 0;
233 point_scale(&dist, 0.332);
234
235 bezierconn_move_handle(bez, handle, to, cp, reason, modifiers);
236
237 bez->points[1].p1 = bez->points[0].p1;
238 point_sub(&bez->points[1].p1, &dist);
239 bez->points[1].p2 = *to;
240 point_add(&bez->points[1].p2, &dist);
241 } else {
242 bezierconn_move_handle(&bezierline->bez, handle, to, cp, reason, modifiers);
243 }
244
245 bezierline_update_data(bezierline);
246
247 return NULL;
248 }
249
250
251 static ObjectChange*
bezierline_move(Bezierline * bezierline,Point * to)252 bezierline_move(Bezierline *bezierline, Point *to)
253 {
254 bezierconn_move(&bezierline->bez, to);
255 bezierline_update_data(bezierline);
256
257 return NULL;
258 }
259
exchange_bez_gap_points(BezierConn * bez,Point * gap_points)260 static void exchange_bez_gap_points(BezierConn * bez, Point* gap_points)
261 {
262 Point tmp_points[4];
263 tmp_points[0] = bez->points[0].p1;
264 tmp_points[1] = bez->points[1].p1;
265 tmp_points[2] = bez->points[bez->numpoints-1].p2;
266 tmp_points[3] = bez->points[bez->numpoints-1].p3;
267 bez->points[0].p1 = gap_points[0];
268 bez->points[1].p1 = gap_points[1];
269 bez->points[bez->numpoints-1].p2 = gap_points[2];
270 bez->points[bez->numpoints-1].p3 = gap_points[3];
271 gap_points[0] = tmp_points[0];
272 gap_points[1] = tmp_points[1];
273 gap_points[2] = tmp_points[2];
274 gap_points[3] = tmp_points[3];
275 }
approx_bez_length(BezierConn * bez)276 static real approx_bez_length(BezierConn *bez)
277 {
278 /* Approximates the length of the bezier curve
279 * by the length of the polyline joining its points */
280 Point *current, *last, vec;
281 real length = .0;
282 int i;
283 current = &bez->points[0].p1;
284 for (i=1; i < bez->numpoints ; i++){
285 last = current;
286 current = &bez->points[i].p3;
287 point_copy(&vec,last);
288 point_sub(&vec,current);
289 length += point_len(&vec);
290 }
291 return length;
292 }
293
compute_gap_points(Bezierline * bezierline,Point * gap_points)294 static void compute_gap_points(Bezierline *bezierline, Point *gap_points)
295 {
296 real first_length, last_length, bez_length;
297 BezierConn *bez = &bezierline->bez;
298 Point vec_start, vec_end;
299
300
301 gap_points[0] = bez->points[0].p1;
302 gap_points[1] = bez->points[1].p1;
303 gap_points[2] = bez->points[bez->numpoints-1].p2;
304 gap_points[3] = bez->points[bez->numpoints-1].p3;
305
306 point_copy(&vec_start, &gap_points[1]);
307 point_sub(&vec_start, &gap_points[0]);
308 point_normalize(&vec_start); /* unit vector pointing from first point */
309 point_copy(&vec_end, &gap_points[2]);
310 point_sub(&vec_end, &gap_points[3]);
311 point_normalize(&vec_end); /* unit vector pointing from last point */
312
313
314 bez_length = approx_bez_length(bez) ;
315 first_length = distance_point_point(&gap_points[0],&gap_points[1]);
316 last_length = distance_point_point(&gap_points[2],&gap_points[3]);
317
318 if (connpoint_is_autogap(bez->object.handles[0]->connected_to) &&
319 (bez->object.handles[0])->connected_to != NULL &&
320 (bez->object.handles[0])->connected_to->object != NULL ) {
321 Point end;
322 point_copy(&end, &gap_points[0]);
323 point_add_scaled(&end, &vec_start, bez_length); /* far away on the same slope */
324 end = calculate_object_edge(&gap_points[0], &end,
325 (bez->object.handles[0])->connected_to->object);
326 point_sub(&end, &gap_points[0]); /* vector from old start to new start */
327 /* move points */
328 point_add(&gap_points[0], &end);
329 point_add(&gap_points[1], &end);
330 }
331
332 if (connpoint_is_autogap(bez->object.handles[3*(bez->numpoints-1)]->connected_to) &&
333 (bez->object.handles[3*(bez->numpoints-1)])->connected_to != NULL &&
334 (bez->object.handles[3*(bez->numpoints-1)])->connected_to->object != NULL) {
335 Point end;
336 point_copy(&end, &gap_points[3]);
337 point_add_scaled(&end, &vec_end, bez_length); /* far away on the same slope */
338 end = calculate_object_edge(&gap_points[3], &end,
339 (bez->object.handles[3*(bez->numpoints-1)])->connected_to->object);
340 point_sub(&end, &gap_points[3]); /* vector from old end to new end */
341 /* move points */
342 point_add(&gap_points[3], &end);
343 point_add(&gap_points[2], &end);
344 }
345
346
347 /* adds the absolute start gap according to the slope at the first point */
348 point_add_scaled(&gap_points[0], &vec_start, bezierline->absolute_start_gap);
349 point_add_scaled(&gap_points[1], &vec_start, bezierline->absolute_start_gap);
350
351 /* adds the absolute end gap according to the slope at the last point */
352 point_add_scaled(&gap_points[2], &vec_end, bezierline->absolute_end_gap);
353 point_add_scaled(&gap_points[3], &vec_end, bezierline->absolute_end_gap);
354
355
356 }
357 static void
bezierline_draw(Bezierline * bezierline,DiaRenderer * renderer)358 bezierline_draw(Bezierline *bezierline, DiaRenderer *renderer)
359 {
360 Point gap_points[4]; /* two first and two last bez points */
361
362 BezierConn *bez = &bezierline->bez;
363 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
364
365 renderer_ops->set_linewidth(renderer, bezierline->line_width);
366 renderer_ops->set_linestyle(renderer, bezierline->line_style);
367 renderer_ops->set_dashlength(renderer, bezierline->dashlength);
368 renderer_ops->set_linejoin(renderer, LINEJOIN_MITER);
369 renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
370
371 if (connpoint_is_autogap(bez->object.handles[0]->connected_to) ||
372 connpoint_is_autogap(bez->object.handles[3*(bez->numpoints-1)]->connected_to) ||
373 bezierline->absolute_start_gap || bezierline->absolute_end_gap) {
374
375 compute_gap_points(bezierline,gap_points);
376 exchange_bez_gap_points(bez,gap_points);
377 renderer_ops->draw_bezier_with_arrows(renderer, bez->points, bez->numpoints,
378 bezierline->line_width,
379 &bezierline->line_color,
380 &bezierline->start_arrow,
381 &bezierline->end_arrow);
382 exchange_bez_gap_points(bez,gap_points);
383 } else {
384 renderer_ops->draw_bezier_with_arrows(renderer, bez->points, bez->numpoints,
385 bezierline->line_width,
386 &bezierline->line_color,
387 &bezierline->start_arrow,
388 &bezierline->end_arrow);
389 }
390
391 #if 0
392 renderer_ops->draw_bezier(renderer, bez->points, bez->numpoints,
393 &bezierline->line_color);
394
395 if (bezierline->start_arrow.type != ARROW_NONE) {
396 arrow_draw(renderer, bezierline->start_arrow.type,
397 &bez->points[0].p1, &bez->points[1].p1,
398 bezierline->start_arrow.length, bezierline->start_arrow.width,
399 bezierline->line_width,
400 &bezierline->line_color, &color_white);
401 }
402 if (bezierline->end_arrow.type != ARROW_NONE) {
403 arrow_draw(renderer, bezierline->end_arrow.type,
404 &bez->points[bez->numpoints-1].p3,
405 &bez->points[bez->numpoints-1].p2,
406 bezierline->end_arrow.length, bezierline->end_arrow.width,
407 bezierline->line_width,
408 &bezierline->line_color, &color_white);
409 }
410 #endif
411
412 /* Only display lines while selected. Calling dia_object_is_selected()
413 * every time may take a little long. Some time can be saved by storing
414 * whether the object is currently selected in bezierline_select, and
415 * only checking while selected. But we'll do that if needed.
416 */
417 if (renderer->is_interactive &&
418 dia_object_is_selected(&bezierline->bez.object)) {
419 bezierconn_draw_control_lines(&bezierline->bez, renderer);
420 }
421 }
422
423 static DiaObject *
bezierline_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)424 bezierline_create(Point *startpoint,
425 void *user_data,
426 Handle **handle1,
427 Handle **handle2)
428 {
429 Bezierline *bezierline;
430 BezierConn *bez;
431 DiaObject *obj;
432 Point defaultlen = { .3, .3 };
433
434 bezierline = g_new0(Bezierline, 1);
435 bez = &bezierline->bez;
436 obj = &bez->object;
437
438 obj->type = &bezierline_type;
439 obj->ops = &bezierline_ops;
440
441 if (user_data == NULL) {
442 bezierconn_init(bez, 2);
443
444 bez->points[0].p1 = *startpoint;
445 bez->points[1].p1 = *startpoint;
446 point_add(&bez->points[1].p1, &defaultlen);
447 bez->points[1].p2 = bez->points[1].p1;
448 point_add(&bez->points[1].p2, &defaultlen);
449 bez->points[1].p3 = bez->points[1].p2;
450 point_add(&bez->points[1].p3, &defaultlen);
451 } else {
452 BezierCreateData *bcd = (BezierCreateData*)user_data;
453
454 bezierconn_init(bez, bcd->num_points);
455 bezierconn_set_points(bez, bcd->num_points, bcd->points);
456 }
457
458 bezierline->line_width = attributes_get_default_linewidth();
459 bezierline->line_color = attributes_get_foreground();
460 attributes_get_default_line_style(&bezierline->line_style,
461 &bezierline->dashlength);
462 bezierline->start_arrow = attributes_get_default_start_arrow();
463 bezierline->end_arrow = attributes_get_default_end_arrow();
464
465 *handle1 = bez->object.handles[0];
466 *handle2 = bez->object.handles[3];
467
468 bezierline_update_data(bezierline);
469
470 return &bezierline->bez.object;
471 }
472
473 static void
bezierline_destroy(Bezierline * bezierline)474 bezierline_destroy(Bezierline *bezierline)
475 {
476 bezierconn_destroy(&bezierline->bez);
477 }
478
479 static DiaObject *
bezierline_copy(Bezierline * bezierline)480 bezierline_copy(Bezierline *bezierline)
481 {
482 Bezierline *newbezierline;
483 BezierConn *bez, *newbez;
484 DiaObject *newobj;
485
486 bez = &bezierline->bez;
487
488 newbezierline = g_new0(Bezierline, 1);
489 newbez = &newbezierline->bez;
490 newobj = &bez->object;
491
492 bezierconn_copy(bez, newbez);
493
494 newbezierline->line_color = bezierline->line_color;
495 newbezierline->line_width = bezierline->line_width;
496 newbezierline->line_style = bezierline->line_style;
497 newbezierline->dashlength = bezierline->dashlength;
498 newbezierline->start_arrow = bezierline->start_arrow;
499 newbezierline->end_arrow = bezierline->end_arrow;
500 newbezierline->absolute_start_gap = bezierline->absolute_start_gap;
501 newbezierline->absolute_end_gap = bezierline->absolute_end_gap;
502
503 return &newbezierline->bez.object;
504 }
505
506
507 static void
bezierline_update_data(Bezierline * bezierline)508 bezierline_update_data(Bezierline *bezierline)
509 {
510 BezierConn *bez = &bezierline->bez;
511 DiaObject *obj = &bez->object;
512 PolyBBExtras *extra = &bez->extra_spacing;
513
514 bezierconn_update_data(bez);
515
516 extra->start_trans = extra->start_long =
517 extra->middle_trans =
518 extra->end_trans = extra->end_long = (bezierline->line_width / 2.0);
519
520 obj->position = bez->points[0].p1;
521
522 if (connpoint_is_autogap(bez->object.handles[0]->connected_to) ||
523 connpoint_is_autogap(bez->object.handles[3*(bez->numpoints-1)]->connected_to) ||
524 bezierline->absolute_start_gap || bezierline->absolute_end_gap ||
525 bezierline->start_arrow.type != ARROW_NONE || bezierline->end_arrow.type != ARROW_NONE) {
526 Point gap_points[4];
527 Rectangle bbox_union = {bez->points[0].p1.x, bez->points[0].p1.y, bez->points[0].p1.x, bez->points[0].p1.y};
528 compute_gap_points(bezierline, gap_points);
529 exchange_bez_gap_points(bez,gap_points);
530 /* further modifying the points data, accounts for corrcet arrow and bezier bounding box */
531 if (bezierline->start_arrow.type != ARROW_NONE) {
532 Rectangle bbox;
533 Point move_arrow, move_line;
534 Point to = bez->points[0].p1, from = bez->points[1].p1;
535
536 calculate_arrow_point(&bezierline->start_arrow, &to, &from, &move_arrow, &move_line, bezierline->line_width);
537 point_sub(&to, &move_arrow);
538 point_sub(&bez->points[0].p1, &move_line);
539 arrow_bbox (&bezierline->start_arrow, bezierline->line_width, &to, &from, &bbox);
540 rectangle_union (&bbox_union, &bbox);
541 }
542 if (bezierline->end_arrow.type != ARROW_NONE) {
543 Rectangle bbox;
544 Point move_arrow, move_line;
545 int num_points = bez->numpoints;
546 Point to = bez->points[num_points-1].p3, from = bez->points[num_points-1].p2;
547
548 calculate_arrow_point(&bezierline->end_arrow, &to, &from, &move_arrow, &move_line, bezierline->line_width);
549 point_sub(&to, &move_arrow);
550 point_sub(&bez->points[num_points-1].p3, &move_line);
551 arrow_bbox (&bezierline->end_arrow, bezierline->line_width, &to, &from, &bbox);
552 rectangle_union (&bbox_union, &bbox);
553 }
554 bezierconn_update_boundingbox(bez);
555 rectangle_union (&obj->bounding_box, &bbox_union);
556 exchange_bez_gap_points(bez,gap_points);
557 } else {
558 bezierconn_update_boundingbox(bez);
559 }
560 /* add control points to the bounding box, needed to make them visible when showing all
561 * and to remove traces from them */
562 {
563 int i, num_points = bez->numpoints;
564 obj->enclosing_box = obj->bounding_box;
565 /* starting with the second point, the first one is MOVE_TO */
566 for (i = 1; i < num_points; ++i) {
567 if (bez->points[i].type != BEZ_CURVE_TO)
568 continue;
569 rectangle_add_point(&obj->enclosing_box, &bez->points[i].p1);
570 rectangle_add_point(&obj->enclosing_box, &bez->points[i].p2);
571 }
572 }
573 }
574
575 static void
bezierline_save(Bezierline * bezierline,ObjectNode obj_node,const char * filename)576 bezierline_save(Bezierline *bezierline, ObjectNode obj_node,
577 const char *filename)
578 {
579 if (connpoint_is_autogap(bezierline->bez.object.handles[0]->connected_to) ||
580 connpoint_is_autogap(bezierline->bez.object.handles[3*(bezierline->bez.numpoints-1)]->connected_to) ||
581 bezierline->absolute_start_gap || bezierline->absolute_end_gap) {
582 Point gap_points[4];
583 compute_gap_points(bezierline, gap_points);
584 exchange_bez_gap_points(&bezierline->bez,gap_points);
585 bezierconn_update_boundingbox(&bezierline->bez);
586 exchange_bez_gap_points(&bezierline->bez,gap_points);
587 }
588 bezierconn_save(&bezierline->bez, obj_node);
589
590 if (!color_equals(&bezierline->line_color, &color_black))
591 data_add_color(new_attribute(obj_node, "line_color"),
592 &bezierline->line_color);
593
594 if (bezierline->line_width != 0.1)
595 data_add_real(new_attribute(obj_node, PROP_STDNAME_LINE_WIDTH),
596 bezierline->line_width);
597
598 if (bezierline->line_style != LINESTYLE_SOLID)
599 data_add_enum(new_attribute(obj_node, "line_style"),
600 bezierline->line_style);
601
602 if (bezierline->line_style != LINESTYLE_SOLID &&
603 bezierline->dashlength != DEFAULT_LINESTYLE_DASHLEN)
604 data_add_real(new_attribute(obj_node, "dashlength"),
605 bezierline->dashlength);
606
607 if (bezierline->start_arrow.type != ARROW_NONE) {
608 save_arrow(obj_node, &bezierline->start_arrow, "start_arrow",
609 "start_arrow_length", "start_arrow_width");
610 }
611
612 if (bezierline->end_arrow.type != ARROW_NONE) {
613 save_arrow(obj_node, &bezierline->end_arrow, "end_arrow",
614 "end_arrow_length", "end_arrow_width");
615 }
616
617 if (bezierline->absolute_start_gap)
618 data_add_real(new_attribute(obj_node, "absolute_start_gap"),
619 bezierline->absolute_start_gap);
620 if (bezierline->absolute_end_gap)
621 data_add_real(new_attribute(obj_node, "absolute_end_gap"),
622 bezierline->absolute_end_gap);
623 }
624
625 static DiaObject *
bezierline_load(ObjectNode obj_node,int version,const char * filename)626 bezierline_load(ObjectNode obj_node, int version, const char *filename)
627 {
628 Bezierline *bezierline;
629 BezierConn *bez;
630 DiaObject *obj;
631 AttributeNode attr;
632
633 bezierline = g_new0(Bezierline, 1);
634
635 bez = &bezierline->bez;
636 obj = &bez->object;
637
638 obj->type = &bezierline_type;
639 obj->ops = &bezierline_ops;
640
641 bezierconn_load(bez, obj_node);
642
643 bezierline->line_color = color_black;
644 attr = object_find_attribute(obj_node, "line_color");
645 if (attr != NULL)
646 data_color(attribute_first_data(attr), &bezierline->line_color);
647
648 bezierline->line_width = 0.1;
649 attr = object_find_attribute(obj_node, PROP_STDNAME_LINE_WIDTH);
650 if (attr != NULL)
651 bezierline->line_width = data_real(attribute_first_data(attr));
652
653 bezierline->line_style = LINESTYLE_SOLID;
654 attr = object_find_attribute(obj_node, "line_style");
655 if (attr != NULL)
656 bezierline->line_style = data_enum(attribute_first_data(attr));
657
658 bezierline->dashlength = DEFAULT_LINESTYLE_DASHLEN;
659 attr = object_find_attribute(obj_node, "dashlength");
660 if (attr != NULL)
661 bezierline->dashlength = data_real(attribute_first_data(attr));
662
663 load_arrow(obj_node, &bezierline->start_arrow, "start_arrow",
664 "start_arrow_length", "start_arrow_width");
665
666 load_arrow(obj_node, &bezierline->end_arrow, "end_arrow",
667 "end_arrow_length", "end_arrow_width");
668
669 bezierline->absolute_start_gap = 0.0;
670 attr = object_find_attribute(obj_node, "absolute_start_gap");
671 if (attr != NULL)
672 bezierline->absolute_start_gap = data_real( attribute_first_data(attr) );
673 bezierline->absolute_end_gap = 0.0;
674 attr = object_find_attribute(obj_node, "absolute_end_gap");
675 if (attr != NULL)
676 bezierline->absolute_end_gap = data_real( attribute_first_data(attr) );
677
678 /* if "screws up the bounding box if auto_gap" it must be fixed there
679 * not by copying some meaningless bounding_box before this function call!
680 * But the real fix is in connectionpoint.c(connpoint_is_autogap)
681 */
682 bezierline_update_data(bezierline);
683
684 return &bezierline->bez.object;
685 }
686
687 static ObjectChange *
bezierline_add_segment_callback(DiaObject * obj,Point * clicked,gpointer data)688 bezierline_add_segment_callback (DiaObject *obj, Point *clicked, gpointer data)
689 {
690 Bezierline *bezierline = (Bezierline*) obj;
691 int segment;
692 ObjectChange *change;
693
694 segment = bezierline_closest_segment(bezierline, clicked);
695 change = bezierconn_add_segment(&bezierline->bez, segment, clicked);
696 bezierline_update_data(bezierline);
697 return change;
698 }
699
700 static ObjectChange *
bezierline_delete_segment_callback(DiaObject * obj,Point * clicked,gpointer data)701 bezierline_delete_segment_callback (DiaObject *obj, Point *clicked, gpointer data)
702 {
703 int seg_nr;
704 Bezierline *bezierline = (Bezierline*) obj;
705 ObjectChange *change;
706
707 seg_nr = bezierconn_closest_segment(&bezierline->bez, clicked,
708 bezierline->line_width);
709
710 change = bezierconn_remove_segment(&bezierline->bez, seg_nr+1);
711 bezierline_update_data(bezierline);
712 return change;
713 }
714
715 static ObjectChange *
bezierline_set_corner_type_callback(DiaObject * obj,Point * clicked,gpointer data)716 bezierline_set_corner_type_callback (DiaObject *obj, Point *clicked, gpointer data)
717 {
718 Handle *closest;
719 Bezierline *bezierline = (Bezierline*) obj;
720 ObjectChange *change;
721
722 closest = bezierconn_closest_major_handle(&bezierline->bez, clicked);
723 change = bezierconn_set_corner_type(&bezierline->bez, closest,
724 GPOINTER_TO_INT(data));
725
726 bezierline_update_data(bezierline);
727 return change;
728 }
729
730 static DiaMenuItem bezierline_menu_items[] = {
731 { N_("Add segment"), bezierline_add_segment_callback, NULL, 1 },
732 { N_("Delete segment"), bezierline_delete_segment_callback, NULL, 1 },
733 { NULL, NULL, NULL, 1 },
734 { N_("Symmetric control"), bezierline_set_corner_type_callback,
735 GINT_TO_POINTER(BEZ_CORNER_SYMMETRIC), 1 },
736 { N_("Smooth control"), bezierline_set_corner_type_callback,
737 GINT_TO_POINTER(BEZ_CORNER_SMOOTH), 1 },
738 { N_("Cusp control"), bezierline_set_corner_type_callback,
739 GINT_TO_POINTER(BEZ_CORNER_CUSP), 1 },
740 };
741
742 static DiaMenu bezierline_menu = {
743 "Bezierline",
744 sizeof(bezierline_menu_items)/sizeof(DiaMenuItem),
745 bezierline_menu_items,
746 NULL
747 };
748
749 static DiaMenu *
bezierline_get_object_menu(Bezierline * bezierline,Point * clickedpoint)750 bezierline_get_object_menu(Bezierline *bezierline, Point *clickedpoint)
751 {
752 Handle *closest;
753 gboolean closest_is_endpoint;
754 BezCornerType ctype = 42; /* nothing */
755 gint i;
756
757 closest = bezierconn_closest_major_handle(&bezierline->bez, clickedpoint);
758 if (closest->id == HANDLE_MOVE_STARTPOINT ||
759 closest->id == HANDLE_MOVE_ENDPOINT)
760 closest_is_endpoint = TRUE;
761 else
762 closest_is_endpoint = FALSE;
763
764 for (i = 0; i < bezierline->bez.numpoints; i++)
765 if (bezierline->bez.object.handles[3*i] == closest) {
766 ctype = bezierline->bez.corner_types[i];
767 break;
768 }
769
770 /* Set entries sensitive/selected etc here */
771 bezierline_menu_items[0].active = 1;
772 bezierline_menu_items[1].active = bezierline->bez.numpoints > 2;
773 bezierline_menu_items[3].active = !closest_is_endpoint &&
774 (ctype != BEZ_CORNER_SYMMETRIC);
775 bezierline_menu_items[4].active = !closest_is_endpoint &&
776 (ctype != BEZ_CORNER_SMOOTH);
777 bezierline_menu_items[5].active = !closest_is_endpoint &&
778 (ctype != BEZ_CORNER_CUSP);
779 return &bezierline_menu;
780 }
781