arc autogap



Hi,
I finally upload this patch for arc autogap.
This is a partial and buggy implementation.
A first working step is adding a new way of handling arcs with the modify tool:
keep center and radius and change only the angle of one of the endpoints.
This can be done by holding shift while moving a green handle.

This code combined with a bisection search on the end angle should do it.
It is implemented and only works in a few cases. I have no time to debug this currently.
As Lars suggested, I release the code as is so someone can fix it before me.
All the best,

--
Grégoire Dooms
? arcs_autogap.diff
? multiple_arcs.dia
? app/Diagram1.dia
? app/Diagram1.dia.autosave
? objects/standard/arc_modified.c
Index: app/modify_tool.c
===================================================================
RCS file: /cvs/gnome/dia/app/modify_tool.c,v
retrieving revision 1.53
diff -u -r1.53 modify_tool.c
--- app/modify_tool.c   6 Apr 2005 19:43:55 -0000       1.53
+++ app/modify_tool.c   16 May 2005 15:22:31 -0000
@@ -36,6 +36,7 @@
 #include "prop_text.h"
 #include "gtk/gtk.h"
 
+
 static DiaObject *click_select_object(DDisplay *ddisp, Point *clickedpoint,
                                   GdkEventButton *event);
 static int do_if_clicked_handle(DDisplay *ddisp, ModifyTool *tool,
@@ -76,6 +77,18 @@
   return (Tool *)tool;
 }
 
+ModifierKeys gdk_event_to_dia_ModifierKeys(guint event_state){
+        ModifierKeys mod = MODIFIER_NONE;
+        if (event_state & GDK_SHIFT_MASK)
+                mod |= MODIFIER_SHIFT;
+   /*     Used intenally do not propagate
+    *     if (event_state & GDK_CONTROL_MASK) 
+                mod | MODIFIER_CONTROL;  
+                */
+        return mod;
+}
+
+        
 void
 free_modify_tool(Tool *tool)
 {
@@ -518,7 +531,7 @@
     /* Handle undo */
     objchange = tool->object->ops->move_handle(tool->object, tool->handle, 
                                               &to, connectionpoint,
-                                              HANDLE_MOVE_USER, 0);
+                                              HANDLE_MOVE_USER, gdk_event_to_dia_ModifierKeys(event->state));
     if (objchange != NULL) {
       undo_object_change(ddisp->diagram, tool->object, objchange);
     }
@@ -652,7 +665,7 @@
     object_add_updates(tool->object, ddisp->diagram);
     objchange = tool->object->ops->move_handle(tool->object, tool->handle,
                                               &tool->last_to, NULL,
-                                              HANDLE_MOVE_USER_FINAL,0);
+                                              
HANDLE_MOVE_USER_FINAL,gdk_event_to_dia_ModifierKeys(event->state));
     if (objchange != NULL) {
       undo_object_change(ddisp->diagram, tool->object, objchange);
     }
Index: lib/geometry.h
===================================================================
RCS file: /cvs/gnome/dia/lib/geometry.h,v
retrieving revision 1.26
diff -u -r1.26 geometry.h
--- lib/geometry.h      17 Mar 2005 18:43:02 -0000      1.26
+++ lib/geometry.h      16 May 2005 15:22:31 -0000
@@ -326,6 +326,17 @@
 }
 #endif
 
+G_INLINE_FUNC const Point *closest_to(const Point *to, const Point *p1, const Point *p2);
+#ifdef G_CAN_INLINE
+G_INLINE_FUNC const Point *
+closest_to(const Point *to, const Point *p1, const Point *p2)
+{
+  if (distance_point_point(to,p1) < distance_point_point(to,p2))
+          return p1;
+  return p2;
+}
+#endif
+
 G_INLINE_FUNC real distance_point_point_manhattan(const Point *p1, 
                                                   const Point *p2);
 #ifdef G_CAN_INLINE
Index: objects/standard/arc.c
===================================================================
RCS file: /cvs/gnome/dia/objects/standard/arc.c,v
retrieving revision 1.39
diff -u -r1.39 arc.c
--- objects/standard/arc.c      21 May 2004 15:48:35 -0000      1.39
+++ objects/standard/arc.c      16 May 2005 15:22:34 -0000
@@ -60,6 +60,7 @@
 
 };
 
+/* updates both endpoints and arc->curve_distance */
 static ObjectChange* arc_move_handle(Arc *arc, Handle *handle,
                                     Point *to, ConnectionPoint *cp,
                                     HandleMoveReason reason, ModifierKeys modifiers);
@@ -83,6 +84,9 @@
 
 static void arc_save(Arc *arc, ObjectNode obj_node, const char *filename);
 static DiaObject *arc_load(ObjectNode obj_node, int version, const char *filename);
+static int arc_compute_midpoint(Arc *arc, const Point * ep0, const Point * ep1 , Point * midpoint);
+static void calculate_arc_object_edge(Arc *arc, real ang_start, real ang_end, DiaObject *obj, Point *target);
+static void arc_get_point_at_angle(Arc *arc, Point* point, real angle);
 
 static ObjectTypeOps arc_type_ops =
 {
@@ -243,8 +247,151 @@
       arc->curve_distance*dx/dist;
   }
 }
+/** returns the number of intersection the circle has with the horizontal line at y=horiz
+ * if 1 point intersects then *int1 is that point
+ * if 2 points intersect then *int1 and *int2 are these points
+ */
+static int
+arc_circle_intersects_horiz(const Arc *arc, real horiz, Point *int1, Point *int2){
+  /* inject y=horiz into r^2 = (x-x_c)^2 + (y-y_c)^2 
+   * this is r^2 = (x-x_c)^2 + (horiz-y_c)^2 
+   * translate to x^2 + b*x + c = 0 
+   * b = -2 x_c 
+   * c = x_c^2 - r^2 + (horiz-y_c)^2 
+   * and solve classically */
+  real b, c, delta;
+  b = -2.0 * arc->center.x;
+  c =  arc->center.x * arc->center.x + (horiz - arc->center.y) * (horiz - arc->center.y) - arc->radius * 
arc->radius;
+  delta = b*b - 4 * c;
+  if (delta < 0)
+          return 0;
+  else if (delta == 0){
+         int1->x = -b/2; 
+         int1->y = horiz;
+         return 1; 
+  }
+  else {
+         int1->x = (sqrt(delta)-b)/2; 
+         int1->y = horiz;
+         int2->x = (-sqrt(delta)-b)/2; 
+         int2->y = horiz;
+          return 2;
+  }
+}
+/** returns the number of intersection the circle has with the vertical line at x=vert
+ * if 1 point intersects then *int1 is that point
+ * if 2 points intersect then *int1 and *int2 are these points
+ */
+static int
+arc_circle_intersects_vert(const Arc *arc, real vert, Point *int1, Point *int2){
+  /* inject x=vert into r^2 = (x-x_c)^2 + (y-y_c)^2 
+   * this is r^2 = (vert-x_c)^2 + (y-y_c)^2 
+   * translate to y^2 + b*y + c = 0 and solve classically */
+  real b, c, delta;
+  b = -2*arc->center.y;
+  c =  arc->center.y * arc->center.y + (vert - arc->center.x) * (vert - arc->center.x) - arc->radius * 
arc->radius;
+  delta = b*b - 4 * c;
+  if (delta < 0)
+          return 0;
+  else if (delta == 0){
+         int1->y = -b/2; 
+         int1->x = vert;
+         return 1; 
+  }
+  else {
+         int1->y = (sqrt(delta)-b)/2; 
+         int1->x = vert;
+         int2->y = (-sqrt(delta)-b)/2; 
+         int2->x = vert;
+         return 2;
+  }
+}
+        
+        
+            
 
+static real
+arc_compute_curve_distance(const Arc *arc, const Point *start, const Point *end, const Point *mid)
+{
+    Point a,b;
+    real tmp,cd;
 
+    b = *mid;
+    point_sub(&b, start);
+   
+    a = *end;
+    point_sub(&a, start);
+
+    tmp = point_dot(&a,&b);
+    cd =
+      sqrt(fabs(point_dot(&b,&b) - tmp*tmp/point_dot(&a,&a)));
+    
+    if (a.x*b.y - a.y*b.x < 0) 
+      cd = - cd;
+    return cd;
+}
+
+/** rotates p around the center by an angle given in radians 
+ * a positive angle is ccw on the screen*/
+static void
+rotate_point_around_point(Point *p, const Point *center, real angle)
+{
+        real radius;
+        real a;
+        point_sub(p,center);
+        radius = point_len(p);
+        a = -atan2(p->y,p->x); /* y axis points down*/
+        a += angle;
+        p->x = cos(a); p->y = -sin(a);/* y axis points down*/
+        point_scale(p,radius);
+        point_add(p,center);
+}
+
+
+/* finds the point intersecting the full circle 
+ * on the vector defined by the center and Point *to
+ * that point is returned in Point *best if 1 is returned */
+static int
+arc_find_radial(const Arc *arc, const Point *to, Point *best)
+{
+        Point tmp;
+        tmp = *to;
+        point_sub(&tmp, &arc->center);
+        point_normalize(&tmp);
+        point_scale(&tmp,arc->radius);
+        point_add(&tmp, &arc->center);
+        *best = tmp;
+        return 1;
+        
+}
+
+/* finds the closest point intersecting the full circle 
+ * at any position on the vertical and horizontal lines going through Point to
+ * that point is returned in Point *best if 1 is returned */
+static int
+arc_find_closest_vert_horiz(const Arc *arc, const Point *to, Point *best){
+     Point i1,i2;
+     int nh,nv;
+        nh = arc_circle_intersects_horiz(arc, to->y, &i1, &i2);
+        if (nh==2){
+           *best = *closest_to(to,&i1,&i2);
+        }
+        else if (nh==1) {
+           *best = i1;
+        }
+        nv = arc_circle_intersects_vert(arc, to->x, &i1, &i2);
+        if (nv==2){
+           Point tmp;
+           tmp = *closest_to(to,&i1,&i2);
+           *best = *closest_to(to,&tmp,best);
+        }
+        else if (nv==1) {
+           *best = *closest_to(to,&i1,best);
+        }
+        if (nv|nh)
+                return 1;
+        return 0;
+}
 static ObjectChange*
 arc_move_handle(Arc *arc, Handle *handle,
                Point *to, ConnectionPoint *cp,
@@ -253,26 +400,40 @@
   assert(arc!=NULL);
   assert(handle!=NULL);
   assert(to!=NULL);
-
   if (handle->id == HANDLE_MIDDLE) {
-    Point a,b;
-    real tmp;
-
-    b = *to;
-    point_sub(&b, &arc->connection.endpoints[0]);
-   
-    a = arc->connection.endpoints[1];
-    point_sub(&a, &arc->connection.endpoints[0]);
-
-    tmp = point_dot(&a,&b);
-    arc->curve_distance =
-      sqrt(fabs(point_dot(&b,&b) - tmp*tmp/point_dot(&a,&a)));
-    
-    if (a.x*b.y - a.y*b.x < 0) 
-      arc->curve_distance = -arc->curve_distance;
+          printf("curve_dist: %.2f \n",arc->curve_distance);
+          arc->curve_distance = arc_compute_curve_distance(arc, &arc->connection.endpoints[0], 
&arc->connection.endpoints[1], to);
+          printf("curve_dist: %.2f \n",arc->curve_distance);
 
   } else {
-    connection_move_handle(&arc->connection, handle->id, to, cp, reason, modifiers);
+        Point best;
+        printf("Modifiers: %d \n",modifiers);
+        if (modifiers & MODIFIER_SHIFT)
+        /* if(arc->end_arrow.type == ARROW_NONE)*/
+        {
+          printf("SHIFT USED, to at %.2f %.2f  ",to->x,to->y);
+          if (arc_find_radial(arc, to, &best)){
+            /* needs to move two handles at the same time 
+             * compute pos of middle handle */
+            Point midpoint;
+            int ok;
+            if (handle == (&arc->connection.endpoint_handles[0]))
+              ok = arc_compute_midpoint(arc, &best , &arc->connection.endpoints[1], &midpoint);
+            else
+              ok = arc_compute_midpoint(arc,  &arc->connection.endpoints[0], &best , &midpoint);
+            if (!ok)
+              return NULL;
+            connection_move_handle(&arc->connection, handle->id, &best, cp, reason, modifiers);
+            /* recompute curve distance equiv. move middle handle */
+            arc->curve_distance = arc_compute_curve_distance(arc, &arc->connection.endpoints[0], 
&arc->connection.endpoints[1], &midpoint);
+            printf("curve_dist: %.2f \n",arc->curve_distance);
+          }
+          else {
+            printf("NO best\n");
+          }
+       } else {
+          connection_move_handle(&arc->connection, handle->id, to, cp, reason, modifiers);
+       }
   }
 
   arc_update_data(arc);
@@ -297,27 +458,139 @@
   return NULL;
 }
 
+static int 
+arc_compute_midpoint(Arc *arc, const Point * ep0, const Point * ep1 , Point * midpoint)
+{
+            real angle;
+            Point midpos;
+            Point *oep0, *oep1;
+            
+            oep0 = &arc->connection.endpoints[0];
+            oep1 = &arc->connection.endpoints[1];
+
+            /* angle is total delta of angle of both endpoints */
+            angle = -atan2(ep0->y - arc->center.y, ep0->x - arc->center.x); /* angle of new */
+            angle -= -atan2(oep0->y - arc->center.y, oep0->x - arc->center.x); /* minus angle of old */
+            angle += -atan2(ep1->y - arc->center.y, ep1->x - arc->center.x); /* plus angle of new */
+            angle -= -atan2(oep1->y - arc->center.y, oep1->x - arc->center.x); /* minus angle of old */
+            if (!finite(angle)){
+                    return 0;
+            }
+            if (angle < -1 * M_PI){
+                    printf("angle: %.2f ",angle);
+                    angle += 2*M_PI;
+                    printf("angle: %.2f ",angle);
+            }
+            if (angle > 1 * M_PI){
+                    printf("angle: %.2f ",angle);
+                    angle -= 2*M_PI;
+                    printf("angle: %.2f ",angle);
+            }
+
+            midpos = arc->middle_handle.pos;
+            /*rotate middle handle by half the angle */
+            printf("\nmidpos before: %.2f %.2f \n",midpos.x, midpos.y);
+            rotate_point_around_point(&midpos, &arc->center, angle/2); 
+            printf("\nmidpos after : %.2f %.2f \n",midpos.x, midpos.y);
+            *midpoint = midpos;
+            return 1;
+}
+/** updates point to the point on the arc at angle angle */
+void arc_get_point_at_angle(Arc *arc, Point* point, real angle)
+{
+        Point vec;
+        vec.x = cos(angle/180.0*M_PI);
+        vec.y = sin(angle/180.0*M_PI);
+        point_copy(point,&arc->center);
+        point_add_scaled(point,&vec,arc->radius);
+}
+static void
+calculate_arc_object_edge(Arc *arc, real ang_start, real ang_end, DiaObject *obj, Point *target) 
+{
+#define MAXITER 25
+#ifdef TRACE_DIST
+  real trace[MAXITER];
+  real disttrace[MAXITER];
+#endif
+  real mid1, mid2, mid3;
+  real dist;
+  int i = 0;
+
+  mid1 = ang_start;
+  mid2 = (ang_start + ang_end)/2;
+  mid3 = ang_end;
+
+  /* If the other end is inside the object */
+  arc_get_point_at_angle(arc,target,mid3);
+  dist = obj->ops->distance_from(obj, target );
+  if (dist < 0.001){
+          arc_get_point_at_angle(arc,target,mid1);
+          return ;
+  }
+  do {
+    arc_get_point_at_angle(arc, target, mid2);
+    dist = obj->ops->distance_from(obj, target);
+    if (dist < 0.0000001) {
+      mid1 = mid2;
+    } else {
+      mid3 = mid2;
+    }
+    mid2 = (mid1 + mid3) / 2;
+    
+#ifdef TRACE_DIST
+    trace[i] = mid2;
+    disttrace[i] = dist;
+#endif
+    i++;
+  } while (i < MAXITER && (dist < 0.0000001 || dist > 0.001));
+  
+#ifdef TRACE_DIST
+  if (i == MAXITER) {
+    for (i = 0; i < MAXITER; i++) {
+      printf("%d: %f, %f: %f\n", i, trace[i].x, trace[i].y, disttrace[i]);
+    }
+    printf("i = %d, dist = %f\n", i, dist);
+  }
+#endif
+  arc_get_point_at_angle(arc,target,mid2);
+  return ;
+}
 static void
 arc_draw(Arc *arc, DiaRenderer *renderer)
 {
   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
   Point *endpoints;
-  real width;
+  Point gaptmp[3];
+  ConnectionPoint *start_cp, *end_cp;  
     
   assert(arc != NULL);
   assert(renderer != NULL);
 
   endpoints = &arc->connection.endpoints[0];
 
+  gaptmp[0] = endpoints[0];
+  gaptmp[1] = endpoints[1];
+  start_cp = arc->connection.endpoint_handles[0].connected_to;
+  end_cp = arc->connection.endpoint_handles[1].connected_to;
+
+  if (connpoint_is_autogap(start_cp)) 
+     calculate_arc_object_edge(arc, arc->angle1, arc->angle2, start_cp->object, &gaptmp[0]);
+  if (connpoint_is_autogap(end_cp)) 
+     calculate_arc_object_edge(arc, arc->angle2, arc->angle1, end_cp->object, &gaptmp[1]);
+
+  /* compute new middle_point */
+  arc_compute_midpoint(arc, &gaptmp[0], &gaptmp[1], &gaptmp[2]); 
+
   renderer_ops->set_linewidth(renderer, arc->line_width);
   renderer_ops->set_linestyle(renderer, arc->line_style);
   renderer_ops->set_dashlength(renderer, arc->dashlength);
   renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
   
   /* Special case when almost line: */
-  if (fabs(arc->curve_distance) <= 0.0001) {
+  if (fabs(arc->curve_distance) <= 0.01) {
+          printf("drawing like a line\n"); 
     renderer_ops->draw_line_with_arrows(renderer,
-                                        &endpoints[0], &endpoints[1],
+                                        &gaptmp[0], &gaptmp[1],
                                         arc->line_width,
                                         &arc->arc_color,
                                         &arc->start_arrow,
@@ -326,15 +599,13 @@
   }
 
   renderer_ops->draw_arc_with_arrows(renderer,
-                                     &arc->connection.endpoints[0],
-                                     &arc->connection.endpoints[1],
-                                     &arc->middle_handle.pos,
+                                     &gaptmp[0],
+                                     &gaptmp[1],
+                                     &gaptmp[2],
                                      arc->line_width,
                                      &arc->arc_color,
                                      &arc->start_arrow,
                                      &arc->end_arrow);
-  width = 2*arc->radius;
-  
 }
 
 static DiaObject *


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]