1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include <config.h>
20
21 #define _BSD_SOURCE 1 /* to get finite */
22 #include <math.h>
23 #include <assert.h>
24
25 #include "intl.h"
26 #include "object.h"
27 #include "connection.h"
28 #include "connectionpoint.h"
29 #include "diarenderer.h"
30 #include "attributes.h"
31 #include "widgets.h"
32 #include "arrows.h"
33 #include "properties.h"
34
35 #include "tool-icons.h"
36
37 #define DEFAULT_WIDTH 0.25
38
39 #define HANDLE_MIDDLE HANDLE_CUSTOM1
40
41 /* If you wan debug spew */
42 #define TRACE(fun) /* fun */
43
44 typedef struct _Arc Arc;
45
46 struct _Arc {
47 Connection connection;
48
49 Handle middle_handle;
50
51 Color arc_color;
52 real curve_distance;
53 real line_width;
54 LineStyle line_style;
55 real dashlength;
56 Arrow start_arrow, end_arrow;
57
58 /* Calculated parameters: */
59 real radius;
60 Point center;
61 real angle1, angle2;
62
63 };
64
65 /* updates both endpoints and arc->curve_distance */
66 static ObjectChange* arc_move_handle(Arc *arc, Handle *handle,
67 Point *to, ConnectionPoint *cp,
68 HandleMoveReason reason, ModifierKeys modifiers);
69 static ObjectChange* arc_move(Arc *arc, Point *to);
70 static void arc_select(Arc *arc, Point *clicked_point,
71 DiaRenderer *interactive_renderer);
72 static void arc_draw(Arc *arc, DiaRenderer *renderer);
73 static DiaObject *arc_create(Point *startpoint,
74 void *user_data,
75 Handle **handle1,
76 Handle **handle2);
77 static real arc_distance_from(Arc *arc, Point *point);
78 static void arc_update_data(Arc *arc);
79 static void arc_update_handles(Arc *arc);
80 static void arc_destroy(Arc *arc);
81 static DiaObject *arc_copy(Arc *arc);
82
83 static PropDescription *arc_describe_props(Arc *arc);
84 static void arc_get_props(Arc *arc, GPtrArray *props);
85 static void arc_set_props(Arc *arc, GPtrArray *props);
86
87 static void arc_save(Arc *arc, ObjectNode obj_node, const char *filename);
88 static DiaObject *arc_load(ObjectNode obj_node, int version, const char *filename);
89 static int arc_compute_midpoint(Arc *arc, const Point * ep0, const Point * ep1 , Point * midpoint);
90 static void calculate_arc_object_edge(Arc *arc, real ang_start, real ang_end, DiaObject *obj, Point *target, gboolean clockwiseness);
91 static void arc_get_point_at_angle(Arc *arc, Point* point, real angle);
92 real round_angle(real angle);
93 real get_middle_arc_angle(real angle1, real angle2, gboolean clock);
94
95 static ObjectTypeOps arc_type_ops =
96 {
97 (CreateFunc) arc_create,
98 (LoadFunc) arc_load,
99 (SaveFunc) arc_save,
100 (GetDefaultsFunc) NULL,
101 (ApplyDefaultsFunc) NULL
102 };
103
104 DiaObjectType arc_type =
105 {
106 "Standard - Arc", /* name */
107 0, /* version */
108 (char **) arc_icon, /* pixmap */
109
110 &arc_type_ops /* ops */
111 };
112
113 DiaObjectType *_arc_type = (DiaObjectType *) &arc_type;
114
115 static ObjectOps arc_ops = {
116 (DestroyFunc) arc_destroy,
117 (DrawFunc) arc_draw,
118 (DistanceFunc) arc_distance_from,
119 (SelectFunc) arc_select,
120 (CopyFunc) arc_copy,
121 (MoveFunc) arc_move,
122 (MoveHandleFunc) arc_move_handle,
123 (GetPropertiesFunc) object_create_props_dialog,
124 (ApplyPropertiesDialogFunc) object_apply_props_from_dialog,
125 (ObjectMenuFunc) NULL,
126 (DescribePropsFunc) arc_describe_props,
127 (GetPropsFunc) arc_get_props,
128 (SetPropsFunc) arc_set_props,
129 (TextEditFunc) 0,
130 (ApplyPropertiesListFunc) object_apply_props,
131 };
132
133 static PropDescription arc_props[] = {
134 OBJECT_COMMON_PROPERTIES,
135 PROP_STD_LINE_WIDTH,
136 PROP_STD_LINE_COLOUR,
137 PROP_STD_LINE_STYLE,
138 PROP_STD_START_ARROW,
139 PROP_STD_END_ARROW,
140 { "curve_distance", PROP_TYPE_REAL, 0,
141 N_("Curve distance"), NULL },
142 PROP_DESC_END
143 };
144
145 static PropDescription *
arc_describe_props(Arc * arc)146 arc_describe_props(Arc *arc)
147 {
148 if (arc_props[0].quark == 0)
149 prop_desc_list_calculate_quarks(arc_props);
150 return arc_props;
151 }
152
153 static PropOffset arc_offsets[] = {
154 OBJECT_COMMON_PROPERTIES_OFFSETS,
155 { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH, offsetof(Arc, line_width) },
156 { "line_colour", PROP_TYPE_COLOUR, offsetof(Arc, arc_color) },
157 { "line_style", PROP_TYPE_LINESTYLE,
158 offsetof(Arc, line_style), offsetof(Arc, dashlength) },
159 { "start_arrow", PROP_TYPE_ARROW, offsetof(Arc, start_arrow) },
160 { "end_arrow", PROP_TYPE_ARROW, offsetof(Arc, end_arrow) },
161 { "curve_distance", PROP_TYPE_REAL, offsetof(Arc, curve_distance) },
162 { "start_point", PROP_TYPE_POINT, offsetof(Connection, endpoints[0]) },
163 { "end_point", PROP_TYPE_POINT, offsetof(Connection, endpoints[1]) },
164 { NULL, 0, 0 }
165 };
166
167 static void
arc_get_props(Arc * arc,GPtrArray * props)168 arc_get_props(Arc *arc, GPtrArray *props)
169 {
170 object_get_props_from_offsets(&arc->connection.object,
171 arc_offsets, props);
172 }
173
174 static void
arc_set_props(Arc * arc,GPtrArray * props)175 arc_set_props(Arc *arc, GPtrArray *props)
176 {
177 object_set_props_from_offsets(&arc->connection.object,
178 arc_offsets, props);
179 arc_update_data(arc);
180 }
181
182 static int
in_angle(real angle,real startangle,real endangle)183 in_angle(real angle, real startangle, real endangle)
184 {
185 if (startangle > endangle) { /* passes 360 degrees */
186 endangle += 360.0;
187 if (angle<startangle)
188 angle += 360;
189 }
190 return (angle>=startangle) && (angle<=endangle);
191 }
192
193 static real
arc_distance_from(Arc * arc,Point * point)194 arc_distance_from(Arc *arc, Point *point)
195 {
196 Point *endpoints;
197 Point from_center;
198 real angle;
199 real d, d2;
200
201 endpoints = &arc->connection.endpoints[0];
202
203 from_center = *point;
204 point_sub(&from_center, &arc->center);
205
206 angle = -atan2(from_center.y, from_center.x)*180.0/M_PI;
207 if (angle<0)
208 angle+=360.0;
209
210 if (in_angle(angle, arc->angle1, arc->angle2)) {
211 d = fabs(sqrt(point_dot(&from_center, &from_center)) - arc->radius);
212 d -= arc->line_width/2.0;
213 if (d<0)
214 d = 0.0;
215 return d;
216 } else {
217 d = distance_point_point(&endpoints[0], point);
218 d2 = distance_point_point(&endpoints[1], point);
219
220 return MIN(d,d2);
221 }
222 }
223
224 static void
arc_select(Arc * arc,Point * clicked_point,DiaRenderer * interactive_renderer)225 arc_select(Arc *arc, Point *clicked_point,
226 DiaRenderer *interactive_renderer)
227 {
228 arc_update_handles(arc);
229 }
230
231 static void
arc_update_handles(Arc * arc)232 arc_update_handles(Arc *arc)
233 {
234 Point *middle_pos;
235 real dist,dx,dy;
236
237 Connection *conn = &arc->connection;
238
239 connection_update_handles(conn);
240
241 middle_pos = &arc->middle_handle.pos;
242 middle_pos->x = (conn->endpoints[0].x + conn->endpoints[1].x) / 2.0;
243 middle_pos->y = (conn->endpoints[0].y + conn->endpoints[1].y) / 2.0;
244
245 dx = conn->endpoints[1].x - conn->endpoints[0].x;
246 dy = conn->endpoints[1].y - conn->endpoints[0].y;
247
248 dist = sqrt(dx*dx + dy*dy);
249 if (dist > 0.000001) {
250 middle_pos->x -= arc->curve_distance*dy/dist;
251 middle_pos->y += arc->curve_distance*dx/dist;
252 }
253 }
254
255 static real
arc_compute_curve_distance(const Arc * arc,const Point * start,const Point * end,const Point * mid)256 arc_compute_curve_distance(const Arc *arc, const Point *start, const Point *end, const Point *mid)
257 {
258 Point a,b;
259 real tmp,cd;
260
261 b = *mid;
262 point_sub(&b, start);
263
264 a = *end;
265 point_sub(&a, start);
266
267 tmp = point_dot(&a,&b);
268 cd =
269 sqrt(fabs(point_dot(&b,&b) - tmp*tmp/point_dot(&a,&a)));
270
271 if (a.x*b.y - a.y*b.x < 0)
272 cd = - cd;
273 return cd;
274 }
275
276 /** rotates p around the center by an angle given in radians
277 * a positive angle is ccw on the screen*/
278 static void
rotate_point_around_point(Point * p,const Point * center,real angle)279 rotate_point_around_point(Point *p, const Point *center, real angle)
280 {
281 real radius;
282 real a;
283 point_sub(p,center);
284 radius = point_len(p);
285 a = -atan2(p->y,p->x); /* y axis points down*/
286 a += angle;
287 p->x = cos(a); p->y = -sin(a);/* y axis points down*/
288 point_scale(p,radius);
289 point_add(p,center);
290 }
291
292
293 /* finds the point intersecting the full circle
294 * on the vector defined by the center and Point *to
295 * that point is returned in Point *best if 1 is returned */
296 static int
arc_find_radial(const Arc * arc,const Point * to,Point * best)297 arc_find_radial(const Arc *arc, const Point *to, Point *best)
298 {
299 Point tmp;
300 tmp = *to;
301 point_sub(&tmp, &arc->center);
302 point_normalize(&tmp);
303 point_scale(&tmp,arc->radius);
304 point_add(&tmp, &arc->center);
305 *best = tmp;
306 return 1;
307
308 }
309
310 static ObjectChange*
arc_move_handle(Arc * arc,Handle * handle,Point * to,ConnectionPoint * cp,HandleMoveReason reason,ModifierKeys modifiers)311 arc_move_handle(Arc *arc, Handle *handle,
312 Point *to, ConnectionPoint *cp,
313 HandleMoveReason reason, ModifierKeys modifiers)
314 {
315 assert(arc!=NULL);
316 assert(handle!=NULL);
317 assert(to!=NULL);
318 /* A minimum distance between our three points needs to be maintained
319 * Otherwise our math will get unstable with unpredictable results. */
320 {
321 const Point *p1, *p2;
322
323 if (handle->id == HANDLE_MIDDLE) {
324 p1 = &arc->connection.endpoints[0];
325 p2 = &arc->connection.endpoints[1];
326 } else {
327 p1 = &arc->middle_handle.pos;
328 p2 = &arc->connection.endpoints[(handle == (&arc->connection.endpoint_handles[0])) ? 1 : 0];
329 }
330 if ( (distance_point_point (to, p1) < 0.01)
331 || (distance_point_point (to, p2) < 0.01))
332 return NULL;
333 }
334
335 if (handle->id == HANDLE_MIDDLE) {
336 TRACE(printf("curve_dist: %.2f \n",arc->curve_distance));
337 arc->curve_distance = arc_compute_curve_distance(arc, &arc->connection.endpoints[0], &arc->connection.endpoints[1], to);
338 TRACE(printf("curve_dist: %.2f \n",arc->curve_distance));
339
340 } else {
341 Point best;
342 TRACE(printf("Modifiers: %d \n",modifiers));
343 if (modifiers & MODIFIER_SHIFT)
344 /* if(arc->end_arrow.type == ARROW_NONE)*/
345 {
346 TRACE(printf("SHIFT USED, to at %.2f %.2f ",to->x,to->y));
347 if (arc_find_radial(arc, to, &best)){
348 /* needs to move two handles at the same time
349 * compute pos of middle handle */
350 Point midpoint;
351 int ok;
352 if (handle == (&arc->connection.endpoint_handles[0]))
353 ok = arc_compute_midpoint(arc, &best , &arc->connection.endpoints[1], &midpoint);
354 else
355 ok = arc_compute_midpoint(arc, &arc->connection.endpoints[0], &best , &midpoint);
356 if (!ok)
357 return NULL;
358 connection_move_handle(&arc->connection, handle->id, &best, cp, reason, modifiers);
359 /* recompute curve distance equiv. move middle handle */
360 arc->curve_distance = arc_compute_curve_distance(arc, &arc->connection.endpoints[0], &arc->connection.endpoints[1], &midpoint);
361 TRACE(printf("curve_dist: %.2f \n",arc->curve_distance));
362 }
363 else {
364 TRACE(printf("NO best\n"));
365 }
366 } else {
367 connection_move_handle(&arc->connection, handle->id, to, cp, reason, modifiers);
368 }
369 }
370
371 arc_update_data(arc);
372
373 return NULL;
374 }
375
376 static ObjectChange*
arc_move(Arc * arc,Point * to)377 arc_move(Arc *arc, Point *to)
378 {
379 Point start_to_end;
380 Point *endpoints = &arc->connection.endpoints[0];
381
382 start_to_end = endpoints[1];
383 point_sub(&start_to_end, &endpoints[0]);
384
385 endpoints[1] = endpoints[0] = *to;
386 point_add(&endpoints[1], &start_to_end);
387
388 arc_update_data(arc);
389
390 return NULL;
391 }
392
393 static int
arc_compute_midpoint(Arc * arc,const Point * ep0,const Point * ep1,Point * midpoint)394 arc_compute_midpoint(Arc *arc, const Point * ep0, const Point * ep1 , Point * midpoint)
395 {
396 real angle;
397 Point midpos;
398 Point *oep0, *oep1;
399
400 oep0 = &arc->connection.endpoints[0];
401 oep1 = &arc->connection.endpoints[1];
402
403 /* angle is total delta of angle of both endpoints */
404 angle = -atan2(ep0->y - arc->center.y, ep0->x - arc->center.x); /* angle of new */
405 angle -= -atan2(oep0->y - arc->center.y, oep0->x - arc->center.x); /* minus angle of old */
406 angle += -atan2(ep1->y - arc->center.y, ep1->x - arc->center.x); /* plus angle of new */
407 angle -= -atan2(oep1->y - arc->center.y, oep1->x - arc->center.x); /* minus angle of old */
408 if (!finite(angle)){
409 return 0;
410 }
411 if (angle < -1 * M_PI){
412 TRACE(printf("angle: %.2f ",angle));
413 angle += 2*M_PI;
414 TRACE(printf("angle: %.2f ",angle));
415 }
416 if (angle > 1 * M_PI){
417 TRACE(printf("angle: %.2f ",angle));
418 angle -= 2*M_PI;
419 TRACE(printf("angle: %.2f ",angle));
420 }
421
422 midpos = arc->middle_handle.pos;
423 /*rotate middle handle by half the angle */
424 TRACE(printf("\nmidpos before: %.2f %.2f \n",midpos.x, midpos.y));
425 rotate_point_around_point(&midpos, &arc->center, angle/2);
426 TRACE(printf("\nmidpos after : %.2f %.2f \n",midpos.x, midpos.y));
427 *midpoint = midpos;
428 return 1;
429 }
430 /** updates point to the point on the arc at angle angle degrees */
arc_get_point_at_angle(Arc * arc,Point * point,real angle)431 void arc_get_point_at_angle(Arc *arc, Point* point, real angle)
432 {
433 Point vec;
434 vec.x = cos(angle/180.0*M_PI);
435 vec.y = -sin(angle/180.0*M_PI);
436 point_copy(point,&arc->center);
437 point_add_scaled(point,&vec,arc->radius);
438 }
439 /** returns the angle in [0,360[ corresponding to this angle*/
round_angle(real angle)440 real round_angle(real angle){
441 real a = angle;
442 while (a<0) a+=360;
443 while (a>=360) a-=360;
444 return a;
445 }
446 /** returns the angle in the middle from angle1 to angle2*/
get_middle_arc_angle(real angle1,real angle2,gboolean clock)447 real get_middle_arc_angle(real angle1, real angle2, gboolean clock)
448 {
449 real delta;
450 angle1 = round_angle(angle1);
451 angle2 = round_angle(angle2);
452 delta = (angle2-angle1);
453 if (delta<0) delta+=360;
454 if (clock)
455 return round_angle(angle1-(360-delta)/2);
456 else
457 return round_angle(angle1+delta/2);
458 }
459
460 #undef TRACE_DIST
461 /* PRE: ang_start should be outside object.
462 * ang_end should be inside
463 * if both are inside or if ang_start is very close , then the point at ang_start is returned
464 */
465 static void
calculate_arc_object_edge(Arc * arc,real ang_start,real ang_end,DiaObject * obj,Point * target,gboolean clockwiseness)466 calculate_arc_object_edge(Arc *arc, real ang_start, real ang_end, DiaObject *obj, Point *target, gboolean clockwiseness)
467 {
468 #define MAXITER 25
469 #ifdef TRACE_DIST
470 real trace[MAXITER];
471 real disttrace[MAXITER];
472 int j = 0;
473 #endif
474 real mid1, mid2, mid3;
475 real dist;
476 int i = 0;
477
478 mid1 = ang_start;
479 mid2 = get_middle_arc_angle(ang_start, ang_end, clockwiseness);
480 mid3 = ang_end;
481
482 TRACE(printf("Find middle angle between %f� and %f�\n",ang_start,ang_end));
483 /* If the other end is inside the object */
484 arc_get_point_at_angle(arc,target,mid1);
485 dist = obj->ops->distance_from(obj, target );
486 if (dist < 0.001){
487 TRACE(printf("Point at %f�: %f,%f is very close to object: %f, returning it\n",mid1, target->x, target->y, dist));
488 return ;
489 }
490 do {
491 arc_get_point_at_angle(arc, target, mid2);
492 dist = obj->ops->distance_from(obj, target);
493 #ifdef TRACE_DIST
494 trace[i] = mid2;
495 disttrace[i] = dist;
496 #endif
497 i++;
498
499 if (dist < 0.0000001) {
500 mid3 = mid2;
501 } else {
502 mid1 = mid2;
503 }
504 mid2 = get_middle_arc_angle(mid1,mid3,clockwiseness);
505
506 } while (i < MAXITER && (dist < 0.0000001 || dist > 0.001));
507
508 #ifdef TRACE_DIST
509 for (j = 0; j < i; j++) {
510 arc_get_point_at_angle(arc,target,trace[j]);
511 printf("%d: %f � : %f,%f :%f\n", j, trace[j],target->x,target->y, disttrace[j]);
512 }
513 #endif
514 arc_get_point_at_angle(arc,target,mid2);
515 return ;
516 }
517 static void
arc_draw(Arc * arc,DiaRenderer * renderer)518 arc_draw(Arc *arc, DiaRenderer *renderer)
519 {
520 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
521 Point *endpoints;
522 Point gaptmp[3];
523 ConnectionPoint *start_cp, *end_cp;
524
525 assert(arc != NULL);
526 assert(renderer != NULL);
527
528 endpoints = &arc->connection.endpoints[0];
529
530 gaptmp[0] = endpoints[0];
531 gaptmp[1] = endpoints[1];
532 start_cp = arc->connection.endpoint_handles[0].connected_to;
533 end_cp = arc->connection.endpoint_handles[1].connected_to;
534
535 TRACE(printf("drawing arc:\n start:%f �:%f,%f \tend:%f �:%f,%f\n",arc->angle1,endpoints[0].x,endpoints[0].y, arc->angle2,endpoints[1].x,endpoints[1].y));
536
537 if (connpoint_is_autogap(start_cp)) {
538 TRACE(printf("computing start intersection\ncurve_distance: %f\n",arc->curve_distance));
539 if (arc->curve_distance < 0)
540 calculate_arc_object_edge(arc, arc->angle1, arc->angle2, start_cp->object, &gaptmp[0], FALSE);
541 else
542 calculate_arc_object_edge(arc, arc->angle2, arc->angle1, start_cp->object, &gaptmp[0], TRUE);
543 }
544 if (connpoint_is_autogap(end_cp)) {
545 TRACE(printf("computing end intersection\ncurve_distance: %f\n",arc->curve_distance));
546 if (arc->curve_distance < 0)
547 calculate_arc_object_edge(arc, arc->angle2, arc->angle1, end_cp->object, &gaptmp[1], TRUE);
548 else
549 calculate_arc_object_edge(arc, arc->angle1, arc->angle2, end_cp->object, &gaptmp[1], FALSE);
550 }
551
552 /* compute new middle_point */
553 arc_compute_midpoint(arc, &gaptmp[0], &gaptmp[1], &gaptmp[2]);
554
555 renderer_ops->set_linewidth(renderer, arc->line_width);
556 renderer_ops->set_linestyle(renderer, arc->line_style);
557 renderer_ops->set_dashlength(renderer, arc->dashlength);
558 renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
559
560 /* Special case when almost line: */
561 if (fabs(arc->curve_distance) <= 0.01) {
562 TRACE(printf("drawing like a line\n"));
563 renderer_ops->draw_line_with_arrows(renderer,
564 &gaptmp[0], &gaptmp[1],
565 arc->line_width,
566 &arc->arc_color,
567 &arc->start_arrow,
568 &arc->end_arrow);
569 return;
570 }
571
572 renderer_ops->draw_arc_with_arrows(renderer,
573 &gaptmp[0],
574 &gaptmp[1],
575 &gaptmp[2],
576 arc->line_width,
577 &arc->arc_color,
578 &arc->start_arrow,
579 &arc->end_arrow);
580 }
581
582 static DiaObject *
arc_create(Point * startpoint,void * user_data,Handle ** handle1,Handle ** handle2)583 arc_create(Point *startpoint,
584 void *user_data,
585 Handle **handle1,
586 Handle **handle2)
587 {
588 Arc *arc;
589 Connection *conn;
590 DiaObject *obj;
591 Point defaultlen = { 1.0, 1.0 };
592
593 arc = g_malloc0(sizeof(Arc));
594
595 arc->line_width = attributes_get_default_linewidth();
596 arc->curve_distance = 1.0;
597 arc->arc_color = attributes_get_foreground();
598 attributes_get_default_line_style(&arc->line_style, &arc->dashlength);
599 arc->start_arrow = attributes_get_default_start_arrow();
600 arc->end_arrow = attributes_get_default_end_arrow();
601
602 conn = &arc->connection;
603 conn->endpoints[0] = *startpoint;
604 conn->endpoints[1] = *startpoint;
605 point_add(&conn->endpoints[1], &defaultlen);
606
607 obj = &conn->object;
608
609 obj->type = &arc_type;;
610 obj->ops = &arc_ops;
611
612 connection_init(conn, 3, 0);
613
614 obj->handles[2] = &arc->middle_handle;
615 arc->middle_handle.id = HANDLE_MIDDLE;
616 arc->middle_handle.type = HANDLE_MINOR_CONTROL;
617 arc->middle_handle.connect_type = HANDLE_NONCONNECTABLE;
618 arc->middle_handle.connected_to = NULL;
619
620 arc_update_data(arc);
621
622 *handle1 = obj->handles[0];
623 *handle2 = obj->handles[1];
624 return &arc->connection.object;
625 }
626
627 static void
arc_destroy(Arc * arc)628 arc_destroy(Arc *arc)
629 {
630 connection_destroy(&arc->connection);
631 }
632
633 static DiaObject *
arc_copy(Arc * arc)634 arc_copy(Arc *arc)
635 {
636 Arc *newarc;
637 Connection *conn, *newconn;
638 DiaObject *newobj;
639
640 conn = &arc->connection;
641
642 newarc = g_malloc0(sizeof(Arc));
643 newconn = &newarc->connection;
644 newobj = &newconn->object;
645
646 connection_copy(conn, newconn);
647
648 newarc->arc_color = arc->arc_color;
649 newarc->curve_distance = arc->curve_distance;
650 newarc->line_width = arc->line_width;
651 newarc->line_style = arc->line_style;
652 newarc->dashlength = arc->dashlength;
653 newarc->start_arrow = arc->start_arrow;
654 newarc->end_arrow = arc->end_arrow;
655 newarc->radius = arc->radius;
656 newarc->center = arc->center;
657 newarc->angle1 = arc->angle1;
658 newarc->angle2 = arc->angle2;
659
660 newobj->handles[2] = &newarc->middle_handle;
661
662 newarc->middle_handle = arc->middle_handle;
663
664 arc_update_data(arc);
665
666 return &newarc->connection.object;
667 }
668
669 /* copied from lib/diarenderer.c, the one there should be removed */
670 static gboolean
is_right_hand(const Point * a,const Point * b,const Point * c)671 is_right_hand (const Point *a, const Point *b, const Point *c)
672 {
673 Point dot1, dot2;
674
675 dot1 = *a;
676 point_sub(&dot1, c);
677 point_normalize(&dot1);
678 dot2 = *b;
679 point_sub(&dot2, c);
680 point_normalize(&dot2);
681 return point_cross(&dot1, &dot2) > 0;
682 }
683
684 static void
arc_update_data(Arc * arc)685 arc_update_data(Arc *arc)
686 {
687 Connection *conn = &arc->connection;
688 LineBBExtras *extra =&conn->extra_spacing;
689 DiaObject *obj = &conn->object;
690 Point *endpoints;
691 real x1,y1,x2,y2,xc,yc;
692 real lensq, alpha, radius;
693 real angle1, angle2;
694 gboolean righthand;
695
696 endpoints = &arc->connection.endpoints[0];
697 x1 = endpoints[0].x;
698 y1 = endpoints[0].y;
699 x2 = endpoints[1].x;
700 y2 = endpoints[1].y;
701
702 lensq = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
703 radius = lensq/(8*arc->curve_distance) + arc->curve_distance/2.0;
704
705 if (lensq == 0.0)
706 alpha = 1.0; /* arbitrary, but /not/ 1/0 */
707 else
708 alpha = (radius - arc->curve_distance) / sqrt(lensq);
709
710 xc = (x1 + x2) / 2.0 + (y2 - y1)*alpha;
711 yc = (y1 + y2) / 2.0 + (x1 - x2)*alpha;
712
713 angle1 = -atan2(y1-yc, x1-xc)*180.0/M_PI;
714 if (angle1<0)
715 angle1+=360.0;
716 angle2 = -atan2(y2-yc, x2-xc)*180.0/M_PI;
717 if (angle2<0)
718 angle2+=360.0;
719
720 if (radius<0.0) {
721 real tmp;
722 tmp = angle1;
723 angle1 = angle2;
724 angle2 = tmp;
725 radius = -radius;
726 }
727
728 arc->radius = radius;
729 arc->center.x = xc; arc->center.y = yc;
730 arc->angle1 = angle1;
731 arc->angle2 = angle2;
732
733 /* LineBBExtras not applicable to calculate the arrows bounding box */
734 extra->start_trans =
735 extra->end_trans =
736 extra->start_long =
737 extra->end_long = (arc->line_width / 2.0);
738
739 /* updates midpoint */
740 arc_update_handles(arc);
741 /* startpoint, midpoint, endpoint */
742 righthand = is_right_hand (&endpoints[0], &arc->middle_handle.pos, &endpoints[1]);
743 connection_update_boundingbox(conn);
744
745 /* fix boundingbox for arc's special shape XXX find a more elegant way: */
746 if (in_angle(0, arc->angle1, arc->angle2)) {
747 /* rigth side, y does not matter if included */
748 Point pt = { arc->center.x + arc->radius + (arc->line_width / 2.0), y1 };
749 rectangle_add_point (&obj->bounding_box, &pt);
750 }
751 if (in_angle(90, arc->angle1, arc->angle2)) {
752 /* top side, x does not matter if included */
753 Point pt = {x1, arc->center.y - arc->radius - (arc->line_width / 2.0) };
754 rectangle_add_point (&obj->bounding_box, &pt);
755 }
756 if (in_angle(180, arc->angle1, arc->angle2)) {
757 /* left side, y does not matter if included */
758 Point pt = { arc->center.x - arc->radius - (arc->line_width / 2.0), y1 };
759 rectangle_add_point (&obj->bounding_box, &pt);
760 }
761 if (in_angle(270, arc->angle1, arc->angle2)) {
762 /* bootom side, x does not matter if included */
763 Point pt = { x1, arc->center.y + arc->radius + (arc->line_width / 2.0) };
764 rectangle_add_point (&obj->bounding_box, &pt);
765 }
766 if (arc->start_arrow.type != ARROW_NONE) {
767 /* a good from-point would be the chord of arrow length, but draw_arc_with_arrows currently uses the tangent
768 * For big arcs the difference is not huge and the minimum size of small arcs should be limited by the arror length.
769 */
770 Rectangle bbox = {0,};
771 real tmp;
772 Point move_arrow, move_line;
773 Point to = arc->connection.endpoints[0];
774 Point from = to;
775 point_sub (&from, &arc->center);
776 tmp = from.x;
777 if (righthand)
778 from.x = -from.y, from.y = tmp;
779 else
780 from.x = from.y, from.y = -tmp;
781 point_add (&from, &to);
782
783 calculate_arrow_point(&arc->start_arrow, &to, &from,
784 &move_arrow, &move_line, arc->line_width);
785 /* move them */
786 point_sub(&to, &move_arrow);
787 point_sub(&from, &move_line);
788 arrow_bbox(&arc->start_arrow, arc->line_width, &to, &from, &bbox);
789 rectangle_union(&obj->bounding_box, &bbox);
790 }
791 if (arc->end_arrow.type != ARROW_NONE) {
792 Rectangle bbox = {0,};
793 real tmp;
794 Point move_arrow, move_line;
795 Point to = arc->connection.endpoints[1];
796 Point from = to;
797 point_sub (&from, &arc->center);
798 tmp = from.x;
799 if (righthand)
800 from.x = from.y, from.y = -tmp;
801 else
802 from.x = -from.y, from.y = tmp;
803 point_add (&from, &to);
804 calculate_arrow_point(&arc->end_arrow, &to, &from,
805 &move_arrow, &move_line, arc->line_width);
806 /* move them */
807 point_sub(&to, &move_arrow);
808 point_sub(&from, &move_line);
809 arrow_bbox(&arc->end_arrow, arc->line_width, &to, &from, &bbox);
810 rectangle_union(&obj->bounding_box, &bbox);
811 }
812
813 obj->position = conn->endpoints[0];
814 }
815
816 static void
arc_save(Arc * arc,ObjectNode obj_node,const char * filename)817 arc_save(Arc *arc, ObjectNode obj_node, const char *filename)
818 {
819 connection_save(&arc->connection, obj_node);
820
821 if (!color_equals(&arc->arc_color, &color_black))
822 data_add_color(new_attribute(obj_node, "arc_color"),
823 &arc->arc_color);
824
825 if (arc->curve_distance != 0.1)
826 data_add_real(new_attribute(obj_node, "curve_distance"),
827 arc->curve_distance);
828
829 if (arc->line_width != 0.1)
830 data_add_real(new_attribute(obj_node, PROP_STDNAME_LINE_WIDTH),
831 arc->line_width);
832
833 if (arc->line_style != LINESTYLE_SOLID)
834 data_add_enum(new_attribute(obj_node, "line_style"),
835 arc->line_style);
836
837 if (arc->line_style != LINESTYLE_SOLID &&
838 arc->dashlength != DEFAULT_LINESTYLE_DASHLEN)
839 data_add_real(new_attribute(obj_node, "dashlength"),
840 arc->dashlength);
841
842 if (arc->start_arrow.type != ARROW_NONE) {
843 save_arrow(obj_node, &arc->start_arrow, "start_arrow",
844 "start_arrow_length", "start_arrow_width");
845 }
846
847 if (arc->end_arrow.type != ARROW_NONE) {
848 save_arrow(obj_node, &arc->end_arrow, "end_arrow",
849 "end_arrow_length", "end_arrow_width");
850 }
851 }
852
853 static DiaObject *
arc_load(ObjectNode obj_node,int version,const char * filename)854 arc_load(ObjectNode obj_node, int version, const char *filename)
855 {
856 Arc *arc;
857 Connection *conn;
858 DiaObject *obj;
859 AttributeNode attr;
860
861 arc = g_malloc0(sizeof(Arc));
862
863 conn = &arc->connection;
864 obj = &conn->object;
865
866 obj->type = &arc_type;
867 obj->ops = &arc_ops;
868
869 connection_load(conn, obj_node);
870
871 arc->arc_color = color_black;
872 attr = object_find_attribute(obj_node, "arc_color");
873 if (attr != NULL)
874 data_color(attribute_first_data(attr), &arc->arc_color);
875
876 arc->curve_distance = 0.1;
877 attr = object_find_attribute(obj_node, "curve_distance");
878 if (attr != NULL)
879 arc->curve_distance = data_real(attribute_first_data(attr));
880
881 arc->line_width = 0.1;
882 attr = object_find_attribute(obj_node, PROP_STDNAME_LINE_WIDTH);
883 if (attr != NULL)
884 arc->line_width = data_real(attribute_first_data(attr));
885
886 arc->line_style = LINESTYLE_SOLID;
887 attr = object_find_attribute(obj_node, "line_style");
888 if (attr != NULL)
889 arc->line_style = data_enum(attribute_first_data(attr));
890
891 arc->dashlength = DEFAULT_LINESTYLE_DASHLEN;
892 attr = object_find_attribute(obj_node, "dashlength");
893 if (attr != NULL)
894 arc->dashlength = data_real(attribute_first_data(attr));
895
896 load_arrow(obj_node, &arc->start_arrow, "start_arrow",
897 "start_arrow_length", "start_arrow_width");
898
899 load_arrow(obj_node, &arc->end_arrow, "end_arrow",
900 "end_arrow_length", "end_arrow_width");
901
902 connection_init(conn, 3, 0);
903
904 obj->handles[2] = &arc->middle_handle;
905 arc->middle_handle.id = HANDLE_MIDDLE;
906 arc->middle_handle.type = HANDLE_MINOR_CONTROL;
907 arc->middle_handle.connect_type = HANDLE_NONCONNECTABLE;
908 arc->middle_handle.connected_to = NULL;
909
910 arc_update_data(arc);
911
912 return &arc->connection.object;
913 }
914
915
916
917