From a9613e6853f820399219f1f54681bbe7c43d6c21 Mon Sep 17 00:00:00 2001
From: Edward Hennessy <[EMAIL PROTECTED]>
Date: Fri, 18 Jul 2008 20:24:46 -0700
Subject: [PATCH] Altered point selection mechanism to use distance instead of 
just a bounding box.

This patch improves the mechanism used to select objects when using a single
point.  In addition to using the bounding box, this patch calculates the
distance between the selection point and the object itself.  This calculated
distance provides a more accurate means to determine the actual object the user
selected.
---
 gschem/src/o_find.c           |    6 ++-
 libgeda/include/prototype.h   |   10 +++++
 libgeda/src/o_arc_basic.c     |   90 +++++++++++++++++++++++++++++++++++++++++
 libgeda/src/o_basic.c         |   61 +++++++++++++++++++++++++++
 libgeda/src/o_box_basic.c     |   48 ++++++++++++++++++++++
 libgeda/src/o_circle_basic.c  |   27 ++++++++++++
 libgeda/src/o_complex_basic.c |   37 +++++++++++++++++
 libgeda/src/o_line_basic.c    |   59 +++++++++++++++++++++++++++
 libgeda/src/o_picture.c       |   40 ++++++++++++++++++
 libgeda/src/o_text_basic.c    |   39 ++++++++++++++++++
 10 files changed, 415 insertions(+), 2 deletions(-)

diff --git a/gschem/src/o_find.c b/gschem/src/o_find.c
index 54236e1..b060df2 100644
--- a/gschem/src/o_find.c
+++ b/gschem/src/o_find.c
@@ -57,7 +57,8 @@ gboolean o_find_object(GSCHEM_TOPLEVEL *w_current, int w_x, 
int w_y,
   while (o_current != NULL) {
     if (inside_region(o_current->w_left - w_slack, o_current->w_top - w_slack,
                       o_current->w_right + w_slack, o_current->w_bottom + 
w_slack,
-                      w_x, w_y)) {
+                      w_x, w_y) &&
+        o_shortest_distance( o_current, w_x, w_y ) < w_slack ) {
       if (o_current->sel_func != NULL &&
          o_current->type != OBJ_HEAD &&
          (o_current->visibility == VISIBLE ||
@@ -89,7 +90,8 @@ gboolean o_find_object(GSCHEM_TOPLEVEL *w_current, int w_x, 
int w_y,
          o_current != toplevel->page_current->object_lastplace) {
     if (inside_region(o_current->w_left - w_slack, o_current->w_top - w_slack,
                       o_current->w_right + w_slack, o_current->w_bottom + 
w_slack,
-                      w_x, w_y)) {
+                      w_x, w_y) &&
+        o_shortest_distance( o_current, w_x, w_y ) < w_slack ) {
       
       if (o_current->sel_func != NULL &&
           o_current->type != OBJ_HEAD &&
diff --git a/libgeda/include/prototype.h b/libgeda/include/prototype.h
index c6f6fc0..7d6d680 100644
--- a/libgeda/include/prototype.h
+++ b/libgeda/include/prototype.h
@@ -97,6 +97,8 @@ void o_arc_rotate_world(TOPLEVEL *toplevel, int 
world_centerx, int world_centery
 void o_arc_mirror_world(TOPLEVEL *toplevel, int world_centerx, int 
world_centery, OBJECT *object);
 void o_arc_recalc(TOPLEVEL *toplevel, OBJECT *o_current);
 void world_get_arc_bounds(TOPLEVEL *toplevel, OBJECT *object, int *left, int 
*top, int *right, int *bottom);
+double o_arc_shortest_distance(OBJECT *object, int x, int y);
+gboolean o_arc_within_sweep(OBJECT* object, int x, int y);
 
 /* o_attrib.c */
 ATTRIB *o_attrib_search(GList *list, OBJECT *item);
@@ -139,6 +141,8 @@ void o_set_fill_options(TOPLEVEL *toplevel, OBJECT 
*o_current, OBJECT_FILLING ty
 void o_translate_world (TOPLEVEL *toplevel, gint dx, gint dy, OBJECT *object);
 void o_rotate_world(TOPLEVEL *toplevel, int world_centerx, int world_centery, 
int angle, OBJECT *object);
 void o_mirror_world(TOPLEVEL *toplevel, int world_centerx, int world_centery, 
OBJECT *object);
+double o_shortest_distance(OBJECT *object, int x, int y);
+
 /* o_box_basic.c */
 OBJECT *o_box_add(TOPLEVEL *toplevel, OBJECT *object_list, char type, int 
color, int x1, int y1, int x2, int y2);
 OBJECT *o_box_copy(TOPLEVEL *toplevel, OBJECT *list_tail, OBJECT *o_current);
@@ -148,6 +152,7 @@ void o_box_rotate_world(TOPLEVEL *toplevel, int 
world_centerx, int world_centery
 void o_box_mirror_world(TOPLEVEL *toplevel, int world_centerx, int 
world_centery, OBJECT *object);
 void o_box_recalc(TOPLEVEL *toplevel, OBJECT *o_current);
 void world_get_box_bounds(TOPLEVEL *toplevel, OBJECT *object, int *left, int 
*top, int *right, int *bottom);
+double o_box_shortest_distance(OBJECT *object, int x, int y);
 
 /* o_bus_basic.c */
 void world_get_bus_bounds(TOPLEVEL *toplevel, OBJECT *object, int *left, int 
*top, int *right, int *bottom);
@@ -173,6 +178,7 @@ void o_circle_rotate_world(TOPLEVEL *toplevel, int 
world_centerx, int world_cent
 void o_circle_mirror_world(TOPLEVEL *toplevel, int world_centerx, int 
world_centery, OBJECT *object);
 void o_circle_recalc(TOPLEVEL *toplevel, OBJECT *o_current);
 void world_get_circle_bounds(TOPLEVEL *toplevel, OBJECT *object, int *left, 
int *top, int *right, int *bottom);
+double o_circle_shortest_distance(OBJECT *object, int x, int y);
 
 /* o_complex_basic.c */
 int world_get_single_object_bounds(TOPLEVEL *toplevel, OBJECT *o_current,
@@ -210,6 +216,7 @@ void o_complex_mirror_world(TOPLEVEL *toplevel, int 
world_centerx, int world_cen
 OBJECT *o_complex_return_pin_object(OBJECT *object, char *pin);
 int  o_complex_count_pins(OBJECT *object);
 void o_complex_check_symversion(TOPLEVEL* toplevel, OBJECT* object);
+double o_complex_shortest_distance(OBJECT *object, int x, int y);
 
 /* o_embed.c */
 void o_embed(TOPLEVEL *toplevel, OBJECT *o_current);
@@ -227,6 +234,7 @@ void world_get_line_bounds(TOPLEVEL *toplevel, OBJECT 
*object, int *left, int *t
 void o_line_scale_world(TOPLEVEL *toplevel, int x_scale, int y_scale, OBJECT 
*object);
 int o_line_visible(TOPLEVEL *toplevel, LINE *line, int *x1, int *y1, int *x2, 
int *y2);
 double o_line_length(OBJECT *object);
+double o_line_shortest_distance(OBJECT *object, int x, int y);
 
 /* o_list.c */
 OBJECT *o_list_copy_to(TOPLEVEL *toplevel, OBJECT *list_head, OBJECT 
*selected, int flag, OBJECT **return_end);
@@ -275,6 +283,7 @@ guint8 *o_picture_mask_data(GdkPixbuf *image);
 void o_picture_embed(TOPLEVEL *toplevel, OBJECT *object);
 void o_picture_unembed(TOPLEVEL *toplevel, OBJECT *object);
 GdkPixbuf *o_picture_pixbuf_from_buffer (gchar *file_content, gsize 
file_length, GError **err);
+double o_picture_shortest_distance(OBJECT *object, int x, int y);
 
 /* o_pin_basic.c */
 void world_get_pin_bounds(TOPLEVEL *toplevel, OBJECT *object, int *left, int 
*top, int *right, int *bottom);
@@ -314,6 +323,7 @@ OBJECT *o_text_copy(TOPLEVEL *toplevel, OBJECT *list_tail, 
OBJECT *o_current);
 void o_text_freeallfonts(TOPLEVEL *toplevel);
 void o_text_rotate_world(TOPLEVEL *toplevel, int world_centerx, int 
world_centery, int angle, OBJECT *object);
 void o_text_mirror_world(TOPLEVEL *toplevel, int world_centerx, int 
world_centery, OBJECT *object);
+double o_text_shortest_distance(OBJECT *object, int x, int y);
 
 /* s_attrib.c */
 int s_attrib_add_entry(char *new_attrib);
diff --git a/libgeda/src/o_arc_basic.c b/libgeda/src/o_arc_basic.c
index 0055bee..2a333fc 100644
--- a/libgeda/src/o_arc_basic.c
+++ b/libgeda/src/o_arc_basic.c
@@ -1238,3 +1238,93 @@ void o_arc_print_phantom(TOPLEVEL *toplevel, FILE *fp,
          x,y, radius, arc_width);
 }
 
+/*! \brief Calculates the distance between the given point and the closest
+ * point on the perimeter of the arc.
+ *
+ *  \param [in] object The object, where object->arc != NULL.
+ *  \param [in] x The x coordinate of the given point.
+ *  \param [in] y The y coordinate of the given point.
+ *  \return The shortest distance from the object to the point.
+ */
+double o_arc_shortest_distance(OBJECT *object, int x, int y)
+{
+  double radius;
+  double shortest_distance;
+
+  g_assert(object != NULL);
+  g_assert(object->arc != NULL);
+
+  radius = ((double) object->arc->width) / 2.0;
+
+  if ( o_arc_within_sweep(object, x, y) ) {
+    double distance_to_center;
+    double dx;
+    double dy;
+
+    dx = ((double) x) - ((double) object->arc->x);
+    dy = ((double) y) - ((double) object->arc->y);
+
+    distance_to_center = sqrt((dx*dx) + (dy*dy));
+
+    shortest_distance = fabs(distance_to_center - radius);
+  }
+  else {
+    double angle;
+    double distance_to_end0;
+    double distance_to_end1;
+    double dx;
+    double dy;
+
+    angle = G_PI * ((double) object->arc->start_angle ) / 180;
+
+    dx = ((double) x) - radius*cos(angle) - ((double) object->arc->x);    
+    dy = ((double) y) - radius*sin(angle) - ((double) object->arc->y);    
+
+    distance_to_end0 = sqrt((dx*dx) + (dy*dy));
+
+    angle += G_PI * ((double) object->arc->end_angle ) / 180;
+    
+    dx = ((double) x) - radius*cos(angle) - ((double) object->arc->x);    
+    dy = ((double) y) - radius*sin(angle) - ((double) object->arc->y);    
+
+    distance_to_end1 = sqrt((dx*dx) + (dy*dy));
+
+    shortest_distance = min(distance_to_end0, distance_to_end1);
+  }
+
+  return shortest_distance;
+}
+
+/*! \brief Determines if a point lies within the sweep of the arc.
+ *
+ *  \param [in] object The object, where object->arc != NULL.
+ *  \param [in] x The x coordinate of the given point.
+ *  \param [in] y The y coordinate of the given point.
+ *  \return TRUE if the point lies within the sweep of the arc.
+ */
+gboolean o_arc_within_sweep(OBJECT* object, int x, int y)
+{
+  double a0;
+  double a1;
+  double angle;
+  double dx;
+  double dy;
+
+  g_assert(object != NULL);
+  g_assert(object->arc != NULL);
+
+  dx = ((double) x) - ((double) object->arc->x);
+  dy = ((double) y) - ((double) object->arc->y);
+ 
+  angle = 180 * atan2(dy, dx) / G_PI;
+
+  a0 = (double) object->arc->start_angle; 
+  a1 = ((double) object->arc->end_angle) + a0; 
+
+  while (angle < a0) {
+    angle+=360;
+  }
+
+  return (angle < a1);
+}
+
diff --git a/libgeda/src/o_basic.c b/libgeda/src/o_basic.c
index 5426c54..d74154b 100644
--- a/libgeda/src/o_basic.c
+++ b/libgeda/src/o_basic.c
@@ -366,3 +366,64 @@ void o_mirror_world (TOPLEVEL *toplevel, int 
world_centerx, int world_centery, O
     (*func) (toplevel, world_centerx, world_centery, object);
   }
 }
+
+/*! \brief Calculates the distance between the given point and the closest
+ * point on the given object.
+ *
+ *  \param [in] object The given object.
+ *  \param [in] x The x coordinate of the given point.
+ *  \param [in] y The y coordinate of the given point.
+ *  \return The shortest distance from the object to the point. If the
+ *  distance cannot be calculated, this function returns a really large
+ *  number (DBL_MAX).
+ */
+double o_shortest_distance(OBJECT *object, int x, int y)
+{
+  double shortest_distance = DBL_MAX;
+
+  g_assert(object != NULL);
+
+  switch(object->type) {
+
+    case(OBJ_ARC):
+      shortest_distance = o_arc_shortest_distance( object, x, y );
+      break;
+
+    case(OBJ_BOX):
+      shortest_distance = o_box_shortest_distance( object, x, y );
+      break;
+
+    case(OBJ_BUS):
+    case(OBJ_LINE):
+    case(OBJ_NET):
+    case(OBJ_PIN):
+      shortest_distance = o_line_shortest_distance( object, x, y );
+      break;
+
+    case(OBJ_CIRCLE):
+      shortest_distance = o_circle_shortest_distance( object, x, y );
+      break;
+
+    case(OBJ_COMPLEX):
+    case(OBJ_PLACEHOLDER):
+      shortest_distance = o_complex_shortest_distance( object, x, y );
+      break;
+
+    case(OBJ_HEAD):
+      break;
+
+    case(OBJ_PICTURE):
+      shortest_distance = o_picture_shortest_distance( object, x, y );
+      break;
+
+    case(OBJ_TEXT):
+      shortest_distance = o_text_shortest_distance( object, x, y );
+      break;
+
+    default:
+      g_critical ("o_shortest_distance: object %p has bad type '%c'\n",
+                  object, object->type);
+  }
+  return shortest_distance;
+}
+
diff --git a/libgeda/src/o_box_basic.c b/libgeda/src/o_box_basic.c
index dd488eb..7224fb7 100644
--- a/libgeda/src/o_box_basic.c
+++ b/libgeda/src/o_box_basic.c
@@ -1440,4 +1440,52 @@ void o_box_print_hatch(TOPLEVEL *toplevel, FILE *fp,
   }
 }
 
+/*! \brief Calculates the distance between the given point and the closest
+ * point on the perimeter of the box.
+ *
+ *  \param [in] object The object, where object->box != NULL.
+ *  \param [in] x The x coordinate of the given point.
+ *  \param [in] y The y coordinate of the given point.
+ *  \return The shortest distance from the object to the point.
+ */
+double o_box_shortest_distance(OBJECT *object, int x, int y)
+{
+  double dx;
+  double dy;
+  double shortest_distance;
+  double x0;
+  double x1;
+  double y0;
+  double y1;
+
+  g_assert(object != NULL);
+  g_assert(object->box != NULL);
+
+  x0 = (double) min(object->box->upper_x, object->box->lower_x);
+  x1 = (double) max(object->box->upper_x, object->box->lower_x);
+  y0 = (double) min(object->box->upper_y, object->box->lower_y);
+  y1 = (double) max(object->box->upper_y, object->box->lower_y);
+
+  dx = min(((double) x)-x0, x1-((double) x));
+  dy = min(((double) y)-y0, y1-((double) y));
+
+  if ( dx < 0 ) {
+    if ( dy < 0 ) {
+      shortest_distance = sqrt((dx*dx) + (dy*dy));
+    }
+    else {
+      shortest_distance = fabs(dx);
+    }
+  }
+  else {
+    if ( dy < 0 ) {
+      shortest_distance = fabs(dy);
+    }
+    else {
+      shortest_distance = min(dx,dy);
+    }
+  }
+
+  return shortest_distance;
+}
 
diff --git a/libgeda/src/o_circle_basic.c b/libgeda/src/o_circle_basic.c
index 0a58d4e..1a82117 100644
--- a/libgeda/src/o_circle_basic.c
+++ b/libgeda/src/o_circle_basic.c
@@ -1133,4 +1133,31 @@ void o_circle_print_hatch(TOPLEVEL *toplevel, FILE *fp,
   }
 }
 
+/*! \brief Calculates the distance between the given point and the closest
+ * point on the perimeter of the circle.
+ *
+ *  \param [in] object The object, where object->circle != NULL.
+ *  \param [in] x The x coordinate of the given point.
+ *  \param [in] y The y coordinate of the given point.
+ *  \return The shortest distance from the object to the point.
+ */
+double o_circle_shortest_distance(OBJECT *object, int x, int y)
+{
+  double distance_to_center;
+  double dx;
+  double dy;
+  double shortest_distance;
+
+  g_assert(object != NULL);
+  g_assert(object->circle != NULL);
+
+  dx = ((double) x) - ((double) object->circle->center_x);
+  dy = ((double) y) - ((double) object->circle->center_y);
+
+  distance_to_center = sqrt((dx*dx) + (dy*dy));
+
+  shortest_distance = fabs(distance_to_center - object->circle->radius);
+
+  return shortest_distance;
+}
 
diff --git a/libgeda/src/o_complex_basic.c b/libgeda/src/o_complex_basic.c
index 39703fb..0adf15a 100644
--- a/libgeda/src/o_complex_basic.c
+++ b/libgeda/src/o_complex_basic.c
@@ -1507,3 +1507,40 @@ done:
   g_free(outside);
   g_free(refdes);
 }
+
+/*! \brief Calculates the distance between the given point and the closest
+ * point on an object within the complex object.
+ *
+ *  \param [in] object The object, where object->complex != NULL.
+ *  \param [in] x The x coordinate of the given point.
+ *  \param [in] y The y coordinate of the given point.
+ *  \return The shortest distance from the object to the point. If the
+ *  distance cannot be calculated, this function returns a really large
+ *  number (DBL_MAX).
+ */
+double o_complex_shortest_distance(OBJECT *object, int x, int y)
+{
+  double distance;
+  double shortest_distance = DBL_MAX;
+  OBJECT *temp;
+
+  g_assert(object != NULL);
+  g_assert(object->complex != NULL);
+
+  temp = object->complex->prim_objs;
+
+  if (temp != NULL) {
+    temp = temp->next;
+  }
+    
+  while (temp != NULL) {
+    distance = o_shortest_distance(temp, x, y);
+  
+    shortest_distance = min(shortest_distance, distance);
+  
+    temp = temp->next;
+  }
+
+  return shortest_distance;
+}
+
diff --git a/libgeda/src/o_line_basic.c b/libgeda/src/o_line_basic.c
index 034f0dd..f9e6b9f 100644
--- a/libgeda/src/o_line_basic.c
+++ b/libgeda/src/o_line_basic.c
@@ -1234,3 +1234,62 @@ double o_line_length(OBJECT *object)
                 
   return(length);
 }
+
+/*! \brief Calculates the distance between the given point and the closest
+ *  point on the given line segment.
+ *
+ *  If the closest point on the line resides beyond the line segment's
+ *  end point, this function returns the distance from the given point to the
+ *  closest end point.
+ *
+ *  \param [in] object The object, where object->line != NULL.
+ *  \param [in] x The x coordinate of the given point.
+ *  \param [in] y The y coordinate of the given point.
+ *  \return The shortest distance from the object to the point.
+ */
+double o_line_shortest_distance(OBJECT *object, int x, int y)
+{
+  double cx;
+  double cy;
+  double dx;
+  double dx0;
+  double dy;
+  double dy0;
+  double ldx;
+  double ldy;
+  double lx0;
+  double ly0;
+  double shortest_distance;
+  double t;
+
+  g_assert(object != NULL);
+  g_assert(object->line != NULL);
+
+  lx0 = (double) object->line->x[0];
+  ly0 = (double) object->line->y[0];
+  ldx = ((double) object->line->x[1]) - ((double) object->line->x[0]);
+  ldy = ((double) object->line->y[1]) - ((double) object->line->y[0]);
+
+  /* calculate parametric value of perpendicular intersection */
+  dx0 = ldx * ( x - lx0 );
+  dy0 = ldy * ( y - ly0 );
+        
+  t = (dx0 + dy0) / ((ldx*ldx) + (ldy*ldy));
+  
+  /* constrain the parametric value to a point on the line */ 
+  t = max(t, 0);
+  t = min(t, 1);
+        
+  /* calculate closest point on the line */
+  cx = t * ldx + lx0;
+  cy = t * ldy + ly0;
+ 
+  /* calculate distance to closest point */ 
+  dx = x-cx;
+  dy = y-cy;
+      
+  shortest_distance = sqrt( (dx*dx) + (dy*dy) );
+
+  return shortest_distance;
+}
+
diff --git a/libgeda/src/o_picture.c b/libgeda/src/o_picture.c
index acc9b0c..350a41a 100644
--- a/libgeda/src/o_picture.c
+++ b/libgeda/src/o_picture.c
@@ -1036,3 +1036,43 @@ GdkPixbuf *o_picture_pixbuf_from_buffer (gchar 
*file_content,
 
   return pixbuf;
 }
+
+/*! \brief Calculates the distance between the given point and the closest
+ * point in the picture.
+ *
+ *  Interrior points within the picture return a distance of zero.
+ *
+ *  \param [in] object The object, where object->picture != NULL.
+ *  \param [in] x The x coordinate of the given point.
+ *  \param [in] y The y coordinate of the given point.
+ *  \return The shortest distance from the object to the point.
+ */
+double o_picture_shortest_distance(OBJECT *object, int x, int y)
+{
+  double dx;
+  double dy;
+  double shortest_distance;
+  double x0;
+  double x1;
+  double y0;
+  double y1;
+
+  g_assert(object != NULL);
+  g_assert(object->picture != NULL);
+
+  x0 = (double) min(object->picture->upper_x, object->picture->lower_x);
+  x1 = (double) max(object->picture->upper_x, object->picture->lower_x);
+  y0 = (double) min(object->picture->upper_y, object->picture->lower_y);
+  y1 = (double) max(object->picture->upper_y, object->picture->lower_y);
+
+  dx = min(((double) x)-x0, x1-((double) x));
+  dy = min(((double) y)-y0, y1-((double) y));
+
+  dx = min(dx, 0);
+  dy = min(dy, 0);
+
+  shortest_distance = sqrt((dx*dx) + (dy*dy));
+
+  return shortest_distance;
+}
+
diff --git a/libgeda/src/o_text_basic.c b/libgeda/src/o_text_basic.c
index 468b442..ec82c74 100644
--- a/libgeda/src/o_text_basic.c
+++ b/libgeda/src/o_text_basic.c
@@ -1852,3 +1852,42 @@ void o_text_mirror_world(TOPLEVEL *toplevel,
   o_text_recreate(toplevel, object);
 }
 
+/*! \brief Calculates the distance between the given point and the closest
+ *  point on the text.
+ *
+ *  This function will calculate the distance to the text regardless
+ *  if the text is visible or not.
+ *
+ *  \param [in] object The object, where object->text != NULL.
+ *  \param [in] x The x coordinate of the given point.
+ *  \param [in] y The y coordinate of the given point.
+ *  \return The shortest distance from the object to the point. If the
+ *  distance cannot be calculated, this function returns a really large
+ *  number (DBL_MAX).
+ */
+double o_text_shortest_distance(OBJECT *object, int x, int y)
+{
+  double distance;
+  double shortest_distance = DBL_MAX;
+  OBJECT *temp;
+
+  g_assert(object != NULL);
+  g_assert(object->text != NULL);
+
+  temp = object->text->prim_objs;
+
+  if (temp != NULL) {
+    temp = temp->next;
+  }
+    
+  while (temp != NULL) {
+    distance = o_shortest_distance(temp, x, y);
+  
+    shortest_distance = min(shortest_distance, distance);
+  
+    temp = temp->next;
+  }
+
+  return shortest_distance;
+}
+
-- 
1.5.6


_______________________________________________
geda-dev mailing list
[email protected]
http://www.seul.org/cgi-bin/mailman/listinfo/geda-dev

Reply via email to