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