/*
 * FIG : Facility for Interactive Generation of figures
 * Copyright (c) 1985-1988 by Supoj Sutanthavibul
 * Parts Copyright (c) 1989-2002 by Brian V. Smith
 * Parts Copyright (c) 1991 by Paul King
 *
 * Any party obtaining a copy of these files is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and documentation
 * files (the "Software"), including without limitation the rights to use,
 * copy, modify, merge, publish distribute, sublicense and/or sell copies of
 * the Software, and to permit persons who receive copies from any such
 * party to do so, with the only requirement being that the above copyright
 * and this permission notice remain intact.
 *
 */

#include "fig.h"
#include "resources.h"
#include "object.h"
#include "mode.h"
#include "u_list.h"
#include "u_search.h"
#include "w_drawprim.h"
#include "w_layers.h"
#include "w_setup.h"
#include "w_zoom.h"

#define TOLERANCE ((int)((display_zoomscale < 20.0? 4: 14) * \
			PIX_PER_INCH/DISPLAY_PIX_PER_INCH/display_zoomscale))

static		(*manipulate) ();
static		(*handlerproc_left) ();
static		(*handlerproc_middle) ();
static		(*handlerproc_right) ();
static int	type;
static long	objectcount;
static long	n;
static int	csr_x, csr_y;

static F_point	point1, point2;

static F_arc      *a;
static F_ellipse  *e;
static F_line     *l;
static F_spline   *s;
static F_text     *t;
static F_compound *c;

/*
 * (px, py) is the control point on the
 * circumference of an arc which is the
 * closest to (x, y)
 */

Boolean
next_arc_found(x, y, tolerance, px, py, shift)
    int		    x, y, tolerance, *px, *py;
    int		    shift;
{
    int		    i;

    if (!arc_in_mask())
	return False;
    if (a == NULL)
	if (shift)
	    a = last_arc(objects.arcs, a);
    	else
	    a = objects.arcs;
    else if (shift)
	a = prev_arc(objects.arcs, a);

    for (; a != NULL; a = (shift? prev_arc(objects.arcs, a): a->next), n++) {
	if (!active_layer(a->depth))
	    continue;
	for (i = 0; i < 3; i++) {
	    if ((abs(a->point[i].x - x) <= tolerance) &&
		(abs(a->point[i].y - y) <= tolerance)) {
		*px = a->point[i].x;
		*py = a->point[i].y;
		return True;
	    }
	}
    }
    return False;
}

/*
 * (px, py) is the point on the circumference
 * of an ellipse which is the closest to (x, y)
 */

Boolean
next_ellipse_found(x, y, tolerance, px, py, shift)
    int		    x, y, tolerance, *px, *py;
    int		    shift;
{
    double	    a, b, dx, dy;
    double	    dis, r, tol;

    if (!ellipse_in_mask())
	return False;
    if (e == NULL)
	if (shift)
	    e = last_ellipse(objects.ellipses);
    	else
	    e = objects.ellipses;
    else if (shift)
	e = prev_ellipse(objects.ellipses, e);

    tol = (double) tolerance;
    for (; e != NULL; e = (shift? prev_ellipse(objects.ellipses, e): e->next), n++) {
	if (!active_layer(e->depth))
	    continue;
	dx = x - e->center.x;
	dy = y - e->center.y;
	a = e->radiuses.x;
	b = e->radiuses.y;
	/* prevent sqrt(0) core dumps */
	if (dx == 0 && dy == 0)
	    dis = 0.0;		/* so we return below */
	else
	    dis = sqrt(dx * dx + dy * dy);
	if (dis < tol) {
	    *px = e->center.x;
	    *py = e->center.y;
	    return True;
	}
	if (abs(x - e->start.x) <= tolerance && abs(y - e->start.y) <= tolerance) {
	    *px = e->start.x;
	    *py = e->start.y;
	    return True;
	}
	if (abs(x - e->end.x) <= tolerance && abs(y - e->end.y) <= tolerance) {
	    *px = e->end.x;
	    *py = e->end.y;
	    return True;
	}
	if (a * dy == 0 && b * dx == 0)
	    r = 0.0;		/* prevent core dumps */
	else
	    r = a * b * dis / sqrt(1.0 * b * b * dx * dx + 1.0 * a * a * dy * dy);
	if (fabs(dis - r) <= tol) {
	    *px = round(r * dx / dis + (double)e->center.x);
	    *py = round(r * dy / dis + (double)e->center.y);
	    return True;
	}
    }
    return False;
}

/* 
 * Return the pointer to lines object if the
 * search is successful otherwise return
 * NULL.  The value returned via (px, py) is
 * the closest point on the vector to point (x, y)
 */

Boolean
next_line_found(x, y, tolerance, px, py, shift)
    int		    x, y, tolerance, *px, *py, shift;
{
    F_point	   *point;
    int		    x1, y1, x2, y2;
    float	    tol2;

    tol2 = (float) tolerance *tolerance;

    if (!anyline_in_mask())
	return False;
    if (l == NULL)
	if (shift)
	    l = last_line(objects.lines, l);
    	else
	    l = objects.lines;
    else if (shift)
	l = prev_line(objects.lines, l);

    for (; l != NULL; l = (shift? prev_line(objects.lines, l): l->next), n++) {
	if (!active_layer(l->depth))
	    continue;
	if (validline_in_mask(l)) {
	    point = l->points;
	    x1 = point->x;
	    y1 = point->y;
	    if (abs(x - x1) <= tolerance && abs(y - y1) <= tolerance) {
		*px = x1;
		*py = y1;
		return True;
	    }
	    for (point = point->next; point != NULL; point = point->next) {
		x2 = point->x;
		y2 = point->y;
		if (close_to_vector(x1, y1, x2, y2, x, y, tolerance, tol2, px, py)) {
		    return True;
		}
		x1 = x2;
		y1 = y2;
	    }
	}
    }
    return False;
}

/*
 * Return the pointer to lines object if the
 * search is successful otherwise return NULL.
 */

Boolean
next_spline_found(x, y, tolerance, px, py, shift)
    int		    x, y, tolerance, *px, *py;
    int		    shift;
{
    F_point	   *point;
    int		    x1, y1, x2, y2;
    float	    tol2;

    if (!anyspline_in_mask())
	return False;
    if (s == NULL)
	if (shift)
	    s = last_spline(objects.splines);
    	else
	    s = objects.splines;
    else if (shift)
	s = prev_spline(objects.splines, s);

    tol2 = (float) tolerance *tolerance;

    for (; s != NULL; s = (shift? prev_spline(objects.splines, s): s->next), n++) {
	if (!active_layer(s->depth))
	    continue;
	if (validspline_in_mask(s)) {
	    point = s->points;
	    x1 = point->x;
	    y1 = point->y;
	    for (point = point->next; point != NULL; point = point->next) {
		x2 = point->x;
		y2 = point->y;
		if (close_to_vector(x1, y1, x2, y2, x, y, tolerance, tol2,
				    px, py))
		    return True;
		x1 = x2;
		y1 = y2;
	    }
	}
    }
    return False;
}

Boolean
next_text_found(x, y, tolerance, px, py, shift)
    int		    x, y, tolerance, *px, *py;
    int		    shift;
{
    int		    dum;

    if (!anytext_in_mask())
	return False;
    if (t == NULL)
	if (shift)
	    t = last_text(objects.texts);
    	else
	    t = objects.texts;
    else if (shift)
	t = prev_text(objects.texts, t);

    for (; t != NULL; t = (shift? prev_text(objects.texts, t): t->next), n++) {
	if (!active_layer(t->depth))
	    continue;
	if (validtext_in_mask(t)) {
	    if (in_text_bound(t, x, y, &dum, False)) {
		*px = x;
		*py = y;
		return True;
	    }
	}
    }
    return False;
}

Boolean
next_compound_found(x, y, tolerance, px, py, shift)
    int		    x, y, tolerance, *px, *py;
    int		    shift;
{
    float	    tol2;

    if (!compound_in_mask())
	return False;
    if (c == NULL)
	if (shift)
	    c = last_compound(objects.compounds);
    	else
	    c = objects.compounds;
    else if (shift)
	c = prev_compound(objects.compounds, c);

    tol2 = tolerance * tolerance;

    for (; c != NULL; c = (shift? prev_compound(objects.compounds, c): c->next), n++) {
	if (!any_active_in_compound(c))
		continue;
	if (close_to_vector(c->nwcorner.x, c->nwcorner.y, c->nwcorner.x,
			    c->secorner.y, x, y, tolerance, tol2, px, py))
	    return True;
	if (close_to_vector(c->secorner.x, c->secorner.y, c->nwcorner.x,
			    c->secorner.y, x, y, tolerance, tol2, px, py))
	    return True;
	if (close_to_vector(c->secorner.x, c->secorner.y, c->secorner.x,
			    c->nwcorner.y, x, y, tolerance, tol2, px, py))
	    return True;
	if (close_to_vector(c->nwcorner.x, c->nwcorner.y, c->secorner.x,
			    c->nwcorner.y, x, y, tolerance, tol2, px, py))
	    return True;
    }
    return False;
}

show_objecthighlight()
{
    if (highlighting)
	return;
    highlighting = 1;
    toggle_objecthighlight();
}

erase_objecthighlight()
{
    if (!highlighting)
	return;
    highlighting = 0;
    toggle_objecthighlight();
    if (type == -1) {
	e = NULL;
	type = O_ELLIPSE;
    }
}

toggle_objecthighlight()
{
    switch (type) {
    case O_ELLIPSE:
	toggle_ellipsehighlight(e);
	break;
    case O_POLYLINE:
	toggle_linehighlight(l);
	break;
    case O_SPLINE:
	toggle_splinehighlight(s);
	break;
    case O_TEXT:
	toggle_texthighlight(t);
	break;
    case O_ARC:
	toggle_archighlight(a);
	break;
    case O_COMPOUND:
	toggle_compoundhighlight(c);
	break;
    default:
	toggle_csrhighlight(csr_x, csr_y);
    }
}

static void
init_search()
{
    if (highlighting)
	erase_objecthighlight();
    else {
	objectcount = 0;
	if (ellipse_in_mask())
	    for (e = objects.ellipses; e != NULL; e = e->next)
		objectcount++;
	if (anyline_in_mask())
	    for (l = objects.lines; l != NULL; l = l->next)
		if (validline_in_mask(l))
		    objectcount++;
	if (anyspline_in_mask())
	    for (s = objects.splines; s != NULL; s = s->next)
		if (validspline_in_mask(s))
		    objectcount++;
	if (anytext_in_mask())
	    for (t = objects.texts; t != NULL; t = t->next)
		if (validtext_in_mask(t))
		    objectcount++;
	if (arc_in_mask())
	    for (a = objects.arcs; a != NULL; a = a->next)
		objectcount++;
	if (compound_in_mask())
	    for (c = objects.compounds; c != NULL; c = c->next)
		objectcount++;
	e = NULL;
	type = O_ELLIPSE;
    }
}

void
do_object_search(x, y, shift)
    int		    x, y;
    unsigned int    shift;	/* Shift Key Status from XEvent */
{
    int		    px, py;
    Boolean	    found = False;

    init_search();
    for (n = 0; n < objectcount;) {
	switch (type) {
	  case O_ELLIPSE:
	    found = next_ellipse_found(x, y, TOLERANCE, &px, &py, shift);
	    break;
	  case O_POLYLINE:
	    found = next_line_found(x, y, TOLERANCE, &px, &py, shift);
	    break;
	  case O_SPLINE:
	    found = next_spline_found(x, y, TOLERANCE, &px, &py, shift);
	    break;
	  case O_TEXT:
	    found = next_text_found(x, y, TOLERANCE, &px, &py, shift);
	    break;
	  case O_ARC:
	    found = next_arc_found(x, y, TOLERANCE, &px, &py, shift);
	    break;
	  case O_COMPOUND:
	    found = next_compound_found(x, y, TOLERANCE, &px, &py, shift);
	    break;
	}

	if (found)
	    break;

	switch (type) {
	  case O_ELLIPSE:
	    type = O_POLYLINE;
	    l = NULL;
	    break;
	  case O_POLYLINE:
	    type = O_SPLINE;
	    s = NULL;
	    break;
	  case O_SPLINE:
	    type = O_TEXT;
	    t = NULL;
	    break;
	  case O_TEXT:
	    type = O_ARC;
	    a = NULL;
	    break;
	  case O_ARC:
	    type = O_COMPOUND;
	    c = NULL;
	    break;
	  case O_COMPOUND:
	    type = O_ELLIPSE;
	    e = NULL;
	    break;
	}
    }
    if (!found) {		/* nothing found */
	csr_x = x;
	csr_y = y;
	type = -1;
	show_objecthighlight();
    } else if (shift) {		/* show selected object */
	show_objecthighlight();
    } else if (manipulate) {	/* user selected an object */
	erase_objecthighlight();
	switch (type) {
	  case O_ELLIPSE:
	    manipulate(e, type, x, y, px, py);
	    break;
	  case O_POLYLINE:
	    manipulate(l, type, x, y, px, py);
	    break;
	  case O_SPLINE:
	    manipulate(s, type, x, y, px, py);
	    break;
	  case O_TEXT:
	    manipulate(t, type, x, y, px, py);
	    break;
	  case O_ARC:
	    manipulate(a, type, x, y, px, py);
	    break;
	  case O_COMPOUND:
	    manipulate(c, type, x, y, px, py);
	    break;
	}
    }
}

void
object_search_left(x, y, shift)
    int		    x, y;
    unsigned int    shift;	/* Shift Key Status from XEvent */
{
    manipulate = handlerproc_left;
    do_object_search(x, y, shift);
}

void
object_search_middle(x, y, shift)
    int		    x, y;
    unsigned int    shift;	/* Shift Key Status from XEvent */
{
    manipulate = handlerproc_middle;
    do_object_search(x, y, shift);
}

void
object_search_right(x, y, shift)
    int		    x, y;
    unsigned int    shift;	/* Shift Key Status from XEvent */
{
    manipulate = handlerproc_right;
    do_object_search(x, y, shift);
}

Boolean
next_arc_point_found(x, y, tol, point_num, shift)
    int		    x, y, tol, *point_num, shift;
/* dirty trick - point_num is called as a `F_point *point_num' */
{
    int		    i;

    if (!arc_in_mask())
	return False;
    if (a == NULL)
	if (shift)
	    a = last_arc(objects.arcs, a);
    	else
	    a = objects.arcs;
    else if (shift)
	a = prev_arc(objects.arcs, a);

    for (; a != NULL; a = (shift? prev_arc(objects.arcs, a): a->next), n++) {
	if (!active_layer(a->depth))
	    continue;
	for (i = 0; i < 3; i++) {
	    if (abs(a->point[i].x - x) <= tol &&
		abs(a->point[i].y - y) <= tol) {
		*point_num = i;
		return True;
	    }
	}
    }
    return False;
}

Boolean
next_ellipse_point_found(x, y, tol, point_num, shift)
    int		    x, y, tol, *point_num, shift;
/* dirty trick - point_num is called as a `F_point *point_num' */
{

    if (!ellipse_in_mask())
	return False;
    if (e == NULL)
	if (shift)
	    e = last_ellipse(objects.ellipses);
    	else
	    e = objects.ellipses;
    else if (shift)
	e = prev_ellipse(objects.ellipses, e);

    for (; e != NULL; e = (shift? prev_ellipse(objects.ellipses, e): e->next), n++) {
	if (!active_layer(e->depth))
	    continue;
	if (abs(e->start.x - x) <= tol && abs(e->start.y - y) <= tol) {
	    *point_num = 0;
	    return True;
	}
	if (abs(e->end.x - x) <= tol && abs(e->end.y - y) <= tol) {
	    *point_num = 1;
	    return True;
	}
    }
    return False;
}

Boolean
next_line_point_found(x, y, tol, p, q, shift)
    int		    x, y, tol, shift;
    F_point	  **p, **q;
{
    F_point	   *a, *b;

    if (!anyline_in_mask())
	return False;
    if (l == NULL)
	if (shift)
	    l = last_line(objects.lines, l);
    	else
	    l = objects.lines;
    else if (shift)
	l = prev_line(objects.lines, l);

    for (; l != NULL; l = (shift? prev_line(objects.lines, l): l->next)) {
	if (!active_layer(l->depth))
	    continue;
	if (validline_in_mask(l)) {
	    n++;
	    for (a = NULL, b = l->points; b != NULL; a = b, b = b->next) {
		if (abs(b->x - x) <= tol && abs(b->y - y) <= tol) {
		    *p = a;
		    *q = b;
		    return True;
		}
	    }
	}
    }
    return False;
}

Boolean
next_spline_point_found(x, y, tol, p, q, shift)
    int		    x, y, tol, shift;
    F_point	  **p, **q;
{
    if (!anyspline_in_mask())
	return False;
    if (s == NULL)
	if (shift)
	    s = last_spline(objects.splines);
    	else
	    s = objects.splines;
    else if (shift)
	s = prev_spline(objects.splines, s);

    for (; s != NULL; s = (shift? prev_spline(objects.splines, s): s->next)) {
	if (!active_layer(s->depth))
	    continue;
	if (validspline_in_mask(s)) {
	    n++;
	    *p = NULL;
	    for (*q = s->points; *q != NULL; *p = *q, *q = (*q)->next) {
		if ((abs((*q)->x - x) <= tol) && (abs((*q)->y - y) <= tol))
		    return True;
	    }
	}
    }
    return False;
}

Boolean
next_compound_point_found(x, y, tol, p, q, shift)
    int		    x, y, tol, *p, *q, shift;
/* dirty trick - p and q are called with type `F_point' */
{
    if (!compound_in_mask())
	return False;
    if (c == NULL)
	if (shift)
	    c = last_compound(objects.compounds);
    	else
	    c = objects.compounds;
    else if (shift)
	c = prev_compound(objects.compounds, c);

    for (; c != NULL; c = (shift? prev_compound(objects.compounds, c): c->next), n++) {
	if (!any_active_in_compound(c))
		continue;
	if (abs(c->nwcorner.x - x) <= tol &&
	    abs(c->nwcorner.y - y) <= tol) {
	    *p = c->nwcorner.x;
	    *q = c->nwcorner.y;
	    return True;
	}
	if (abs(c->nwcorner.x - x) <= tol &&
	    abs(c->secorner.y - y) <= tol) {
	    *p = c->nwcorner.x;
	    *q = c->secorner.y;
	    return True;
	}
	if (abs(c->secorner.x - x) <= tol &&
	    abs(c->nwcorner.y - y) <= tol) {
	    *p = c->secorner.x;
	    *q = c->nwcorner.y;
	    return True;
	}
	if (abs(c->secorner.x - x) <= tol &&
	    abs(c->secorner.y - y) <= tol) {
	    *p = c->secorner.x;
	    *q = c->secorner.y;
	    return True;
	}
    }
    return False;
}

void
init_searchproc_left(handlerproc)
    int		    (*handlerproc) ();

{
    handlerproc_left = handlerproc;
}

void
init_searchproc_middle(handlerproc)
    int		    (*handlerproc) ();

{
    handlerproc_middle = handlerproc;
}

void
init_searchproc_right(handlerproc)
    int		    (*handlerproc) ();

{
    handlerproc_right = handlerproc;
}

void
do_point_search(x, y, shift)
    int		    x, y;
    unsigned int    shift;	/* Shift Key Status from XEvent */
{
    F_point	   *px, *py;
    char	    found = 0;

    px = &point1;
    py = &point2;
    init_search();
    for (n = 0; n < objectcount;) {
	switch (type) {
	case O_ELLIPSE:
	    /* dirty trick - px returns point_num */
	    found = next_ellipse_point_found(x, y, TOLERANCE, &px, shift);
	    break;
	case O_POLYLINE:
	    found = next_line_point_found(x, y, TOLERANCE, &px, &py, shift);
	    break;
	case O_SPLINE:
	    found = next_spline_point_found(x, y, TOLERANCE, &px, &py, shift);
	    break;
	case O_ARC:
	    /* dirty trick - px returns point_num */
	    found = next_arc_point_found(x, y, TOLERANCE, &px, shift);
	    break;
	case O_COMPOUND:
	    found = next_compound_point_found(x, y, TOLERANCE, &px, &py, shift);
	    break;
	}
	if (found) {
	    if (shift)
		show_objecthighlight();
	    break;
	}
	switch (type) {
	case O_ELLIPSE:
	    type = O_POLYLINE;
	    l = NULL;
	    break;
	case O_POLYLINE:
	    type = O_SPLINE;
	    s = NULL;
	    break;
	case O_SPLINE:
	    type = O_ARC;
	    a = NULL;
	    break;
	case O_ARC:
	    type = O_COMPOUND;
	    c = NULL;
	    break;
	case O_COMPOUND:
	    type = O_ELLIPSE;
	    e = NULL;
	    break;
	}
    }
    if (!found) {
	csr_x = x;
	csr_y = y;
	type = -1;
	show_objecthighlight();
    } else if (shift) {
	show_objecthighlight();
    } else if (manipulate) {
	erase_objecthighlight();
	switch (type) {
	  case O_ELLIPSE:
	    manipulate(e, type, x, y, px, py);
	    break;
	  case O_POLYLINE:
	    manipulate(l, type, x, y, px, py);
	    break;
	  case O_SPLINE:
	    manipulate(s, type, x, y, px, py);
	    break;
	  case O_ARC:
	    manipulate(a, type, x, y, px, py);
	    break;
	  case O_COMPOUND:
	    manipulate(c, type, x, y, px, py);
	    break;
	}
    }
}

void
point_search_left(x, y, shift)
    int		    x, y;
    unsigned int    shift;	/* Shift Key Status from XEvent */
{
    manipulate = handlerproc_left;
    do_point_search(x, y, shift);
}

void
point_search_middle(x, y, shift)
    int		    x, y;
    unsigned int    shift;	/* Shift Key Status from XEvent */
{
    manipulate = handlerproc_middle;
    do_point_search(x, y, shift);
}

void
point_search_right(x, y, shift)
    int		    x, y;
    unsigned int    shift;	/* Shift Key Status from XEvent */
{
    manipulate = handlerproc_right;
    do_point_search(x, y, shift);
}

F_text	       *
text_search(x, y, posn)
    int		    x, y, *posn;
{
    F_text	   *t;

    for (t = objects.texts; t != NULL; t = t->next) {
	if (active_layer(t->depth) && in_text_bound(t, x, y, posn, False))
		return(t);
    }
    return (NULL);
}

/* return true if (x,y) is in the text rectangle by rotating the point (x,y) 
   around the text base point by it's negative angle and seeing if that is
   in the rectangle.
   Additionally, set posn to the pixel position of the mouse from the beginning
   of the string
   Call with extra = True to consider small distance on either end of string as
   "inside" the bounds.  This is used by the text select tracker (track_text_select).
 */

Boolean
in_text_bound(t, x, y, posn, extra)
    F_text	   *t;
    int		    x,y,*posn;
    Boolean	    extra;
{
    double	    cost, sint;
    int		    xo,yo, xr,yr;
    int		    x0, x1,y1, x2,y2;
    int		    l, h;

    cost = cos((double) -t->angle);
    sint = sin((double) -t->angle);
    xo = t->base_x;
    yo = t->base_y;

    /* rotate the point (x,y) about (xo,yo) giving (xr,yr) */
    xr = xo + (x-xo)*cost - (yo-y)*sint;
    yr = yo - (yo-y)*cost - (x-xo)*sint;
    /* now see if that point is in the text bounds of the unrotated text */
    l = text_length(t);
    /* add 5 char widths to length if extra wanted */
    if (extra)
	l += 5*char_width(t->fontstruct);
    h = t->ascent+t->descent;
    x1 = t->base_x;
    /* use x0 for left bound comparison but use x1 for char position in string of x,y */
    x0 = x1;
    /* subtract 4 char widths before start of string */
    if (extra)
	x0 -= 4*char_width(t->fontstruct);
    y1 = t->base_y+t->descent;
    if (t->type == T_CENTER_JUSTIFIED) {
	x2 = x1 + l/2;
	x1 = x1 - l/2;
	x0 = x0 - l/2;
	y2 = y1 - h;
    }
    else if (t->type == T_RIGHT_JUSTIFIED) {
	x2 = x1;
	x1 = x1 - l;
	x0 = x0 - l;
	y2 = y1 - h;
    }
    else {
	x2 = x1 + l;
	y2 = y1 - h;
    }
    if (xr >= x0 && xr <= x2 && yr <= y1 && yr >= y2) {
	/* return the pixel position from the beginning of the string */
	*posn = xr-x1;
	if (*posn < 0)
	    *posn = 0;
	else if (*posn > text_length(t))
	    *posn = text_length(t);
	return True;
    }
    return False;
}

F_compound     *
compound_search(x, y, tolerance, px, py)
    int		    x, y, tolerance, *px, *py;
{
    F_compound	   *c;
    float	    tol2;

    tol2 = tolerance * tolerance;

    for (c = objects.compounds; c != NULL; c = c->next) {
	if (close_to_vector(c->nwcorner.x, c->nwcorner.y, c->nwcorner.x,
			    c->secorner.y, x, y, tolerance, tol2, px, py))
	    return (c);
	if (close_to_vector(c->secorner.x, c->secorner.y, c->nwcorner.x,
			    c->secorner.y, x, y, tolerance, tol2, px, py))
	    return (c);
	if (close_to_vector(c->secorner.x, c->secorner.y, c->secorner.x,
			    c->nwcorner.y, x, y, tolerance, tol2, px, py))
	    return (c);
	if (close_to_vector(c->nwcorner.x, c->nwcorner.y, c->secorner.x,
			    c->nwcorner.y, x, y, tolerance, tol2, px, py))
	    return (c);
    }
    return (NULL);
}

F_compound     *
compound_point_search(x, y, tol, cx, cy, fx, fy)
    int		    x, y, tol, *cx, *cy, *fx, *fy;
{
    F_compound	   *c;

    for (c = objects.compounds; c != NULL; c = c->next) {
	if (abs(c->nwcorner.x - x) <= tol &&
	    abs(c->nwcorner.y - y) <= tol) {
	    *cx = c->nwcorner.x;
	    *cy = c->nwcorner.y;
	    *fx = c->secorner.x;
	    *fy = c->secorner.y;
	    return (c);
	}
	if (abs(c->nwcorner.x - x) <= tol &&
	    abs(c->secorner.y - y) <= tol) {
	    *cx = c->nwcorner.x;
	    *cy = c->secorner.y;
	    *fx = c->secorner.x;
	    *fy = c->nwcorner.y;
	    return (c);
	}
	if (abs(c->secorner.x - x) <= tol &&
	    abs(c->nwcorner.y - y) <= tol) {
	    *cx = c->secorner.x;
	    *cy = c->nwcorner.y;
	    *fx = c->nwcorner.x;
	    *fy = c->secorner.y;
	    return (c);
	}
	if (abs(c->secorner.x - x) <= tol &&
	    abs(c->secorner.y - y) <= tol) {
	    *cx = c->secorner.x;
	    *cy = c->secorner.y;
	    *fx = c->nwcorner.x;
	    *fy = c->nwcorner.y;
	    return (c);
	}
    }
    return (NULL);
}



F_spline   *
get_spline_point(x, y, p, q)
    int		    x, y;
    F_point	  **p, **q;
{
    F_spline *spline;
    spline = last_spline(objects.splines);
    for (; spline != NULL; spline = prev_spline(objects.splines, spline))
	if (validspline_in_mask(spline)) {
	    n++;
	    *p = NULL;
	    for (*q = spline->points; *q != NULL; *p = *q, *q = (*q)->next) {
		if ((abs((*q)->x - x) <= TOLERANCE) && 
		    (abs((*q)->y - y) <= TOLERANCE))
		    return spline;
	    }
	}
    return (NULL);
}

