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