aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/geo_ops.c
diff options
context:
space:
mode:
authorMarc G. Fournier <scrappy@hub.org>1997-04-22 17:35:09 +0000
committerMarc G. Fournier <scrappy@hub.org>1997-04-22 17:35:09 +0000
commit9e2a87b62db87fc4175b00dabfd26293a2d072fa (patch)
tree431dc2f390475e033dc2cfda38fddb6a79e4abb2 /src/backend/utils/adt/geo_ops.c
parent051b4210e3cb3f3a9ec7cd5ab4503b48f279ab48 (diff)
downloadpostgresql-9e2a87b62db87fc4175b00dabfd26293a2d072fa.tar.gz
postgresql-9e2a87b62db87fc4175b00dabfd26293a2d072fa.zip
Major patch from Thomas Lockhart <Thomas.G.Lockhart@jpl.nasa.gov>
OK, here are a passel of patches for the geometric data types. These add a "circle" data type, new operators and functions for the existing data types, and change the default formats for some of the existing types to make them consistant with each other. Current formatting conventions (e.g. compatible with v6.0 to allow dump/reload) are supported, but the new conventions should be an improvement and we can eventually drop the old conventions entirely. For example, there are two kinds of paths (connected line segments), open and closed, and the old format was '(1,2,1,2,3,4)' for a closed path with two points (1,2) and (3,4) '(0,2,1,2,3,4)' for an open path with two points (1,2) and (3,4) Pretty arcane, huh? The new format for paths is '((1,2),(3,4))' for a closed path with two points (1,2) and (3,4) '[(1,2),(3,4)]' for an open path with two points (1,2) and (3,4) For polygons, the old convention is '(0,4,2,0,4,3)' for a triangle with points at (0,0),(4,4), and (2,3) and the new convention is '((0,0),(4,4),(2,3))' for a triangle with points at (0,0),(4,4), and (2,3) Other data types which are also represented as lists of points (e.g. boxes, line segments, and polygons) have similar representations (they surround each point with parens). For v6.1, any format which can be interpreted as the old style format is decoded as such; we can remove that backwards compatibility but ugly convention for v7.0. This will allow dump/reloads from v6.0. These include some updates to the regression test files to change the test for creating a data type from "circle" to "widget" to keep the test from trashing the new builtin circle type.
Diffstat (limited to 'src/backend/utils/adt/geo_ops.c')
-rw-r--r--src/backend/utils/adt/geo_ops.c2359
1 files changed, 1815 insertions, 544 deletions
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 32d8d6358ec..4dacd09f1c6 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -1,32 +1,247 @@
/*-------------------------------------------------------------------------
*
- * geo-ops.c--
+ * geo_ops.c--
* 2D geometric operations
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/geo_ops.c,v 1.2 1997/03/14 23:20:15 scrappy Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/geo_ops.c,v 1.3 1997/04/22 17:31:32 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include <math.h>
-#include <float.h> /* faked on sunos */
+#include <float.h>
#include <stdio.h> /* for sprintf proto, etc. */
+#include <stdlib.h> /* for strtod, etc. */
#include <string.h>
+#include <ctype.h>
#include "postgres.h"
#include "utils/geo_decls.h"
#include "utils/palloc.h"
+#define OLD_FORMAT_IN 1
+#define OLD_FORMAT_OUT 0
+
+/*
+ * Delimiters for input and output strings.
+ * LDELIM, RDELIM, and DELIM are left, right, and separator delimiters, respectively.
+ * LDELIM_EP, RDELIM_EP are left and right delimiters for paths with endpoints.
+ */
+
#define LDELIM '('
#define RDELIM ')'
#define DELIM ','
-#define BOXNARGS 4
-#define LSEGNARGS 4
-#define POINTNARGS 2
+#define LDELIM_EP '['
+#define RDELIM_EP ']'
+#define LDELIM_C '<'
+#define RDELIM_C '>'
+
+/* Maximum number of output digits printed */
+#define P_MAXDIG DBL_DIG
+#define P_MAXLEN (2*(P_MAXDIG+7)+1)
+
+static int digits8 = P_MAXDIG;
+
+int geo_precision(int digits);
+
+int geo_precision(int digits)
+{
+ if (digits > P_MAXDIG) {
+ digits8 = P_MAXDIG;
+ } else if (digits > 0) {
+ digits8 = digits;
+ };
+ return digits8;
+}
+
+/*
+ * Geometric data types are composed of points.
+ * This code tries to support a common format throughout the data types,
+ * to allow for more predictable usage and data type conversion.
+ * The fundamental unit is the point. Other units are line segments,
+ * open paths, boxes, closed paths, and polygons (which should be considered
+ * non-intersecting closed paths).
+ *
+ * Data representation is as follows:
+ * point: (x,y)
+ * line segment: [(x1,y1),(x2,y2)]
+ * box: (x1,y1),(x2,y2)
+ * open path: [(x1,y1),...,(xn,yn)]
+ * closed path: ((x1,y1),...,(xn,yn))
+ * polygon: ((x1,y1),...,(xn,yn))
+ *
+ * For boxes, the points are opposite corners with the first point at the top right.
+ * For closed paths and polygons, the points should be reordered to allow
+ * fast and correct equality comparisons.
+ *
+ * XXX perhaps points in complex shapes should be reordered internally
+ * to allow faster internal operations, but should keep track of input order
+ * and restore that order for text output - tgl 97/01/16
+ */
+
+int pair_decode(char *str, float8 *x, float8 *y, char **s);
+int pair_encode(float8 x, float8 y, char *str);
+int pair_count(char *s, char delim);
+int path_decode(int opentype, int npts, char *str, int *isopen, char **ss, Point *p);
+
+char *path_encode( bool closed, int npts, Point *pt);
+
+int pair_decode(char *str, float8 *x, float8 *y, char **s)
+{
+ int has_delim;
+ char *cp;
+
+ if (!PointerIsValid((char *)str))
+ return(FALSE);
+
+ while (isspace( *str)) str++;
+ if ((has_delim = (*str == LDELIM))) str++;
+
+ while (isspace( *str)) str++;
+ *x = strtod( str, &cp);
+ if (cp <= str) return(FALSE);
+ while (isspace( *cp)) cp++;
+ if (*cp++ != DELIM) return(FALSE);
+ while (isspace( *cp)) cp++;
+ *y = strtod( cp, &str);
+ if (str <= cp) return(FALSE);
+ while (isspace( *str)) str++;
+ if (has_delim) {
+ if (*str != RDELIM) return(FALSE);
+ str++;
+ while (isspace( *str)) str++;
+ };
+ if (s != NULL) *s = str;
+
+ return(TRUE);
+}
+
+int pair_encode(float8 x, float8 y, char *str)
+{
+ (void) sprintf(str, "%.*g,%.*g", digits8, x, digits8, y);
+ return(TRUE);
+}
+
+int path_decode(int opentype, int npts, char *str, int *isopen, char **ss, Point *p)
+{
+ int depth = 0;
+ char *s, *cp;
+ int i;
+
+ s = str;
+ while (isspace( *s)) s++;
+ if ((*isopen = (*s == LDELIM_EP))) {
+ /* no open delimiter allowed? */
+ if (! opentype) return(FALSE);
+ depth++;
+ s++;
+ while (isspace( *s)) s++;
+
+ } else if (*s == LDELIM) {
+ cp = (s+1);
+ while (isspace( *cp)) cp++;
+ if (*cp == LDELIM) {
+ /* nested delimiters with only one point? */
+ if (npts <= 1) return(FALSE);
+ depth++;
+ s = cp;
+ } else if (strrchr( s, LDELIM) == s) {
+ depth++;
+ s = cp;
+ };
+ };
+
+ for (i = 0; i < npts; i++) {
+ if (! pair_decode( s, &(p->x), &(p->y), &s))
+ return(FALSE);
+
+ if (*s == DELIM) s++;
+ p++;
+ };
+
+ while (depth > 0) {
+ if ((*s == RDELIM)
+ || ((*s == RDELIM_EP) && (*isopen) && (depth == 1))) {
+ depth--;
+ s++;
+ while (isspace( *s)) s++;
+ } else {
+ return(FALSE);
+ };
+ };
+ *ss = s;
+
+ return(TRUE);
+} /* path_decode() */
+
+char *path_encode( bool closed, int npts, Point *pt)
+{
+ char *result;
+
+ char *cp;
+ int i;
+
+ if (!PointerIsValid(result = (char *)PALLOC(npts*(P_MAXLEN+3)+2)))
+ elog(WARN, "Memory allocation failed, can't output path", NULL);
+
+ cp = result;
+ switch (closed) {
+ case TRUE:
+ *cp++ = LDELIM;
+ break;
+ case FALSE:
+ *cp++ = LDELIM_EP;
+ break;
+ default:
+ break;
+ };
+
+ for (i = 0; i < npts; i++) {
+ *cp++ = LDELIM;
+ if (! pair_encode( pt->x, pt->y, cp))
+ elog (WARN, "Unable to format path", NULL);
+ cp += strlen(cp);
+ *cp++ = RDELIM;
+ *cp++ = DELIM;
+ pt++;
+ };
+ cp--;
+ switch (closed) {
+ case TRUE:
+ *cp++ = RDELIM;
+ break;
+ case FALSE:
+ *cp++ = RDELIM_EP;
+ break;
+ default:
+ break;
+ };
+ *cp = '\0';
+
+ return(result);
+} /* path_encode() */
+
+/*-------------------------------------------------------------
+ * pair_count - count the number of points
+ * allow the following notation:
+ * '((1,2),(3,4))'
+ * '(1,3,2,4)'
+ * require an odd number of delim characters in the string
+ *-------------------------------------------------------------*/
+int pair_count(char *s, char delim)
+{
+ int ndelim = 0;
+
+ while ((s = strchr( s, delim)) != NULL) {
+ ndelim++;
+ s++;
+ };
+ return((ndelim % 2)? ((ndelim+1)/2): -1);
+}
/***********************************************************************
**
@@ -40,57 +255,76 @@
/* box_in - convert a string to internal form.
*
- * str: input string "(f8, f8, f8, f8)"
+ * External format: (two corners of box)
+ * "(f8, f8), (f8, f8)"
+ * also supports the older style "(f8, f8, f8, f8)"
*/
BOX *box_in(char *str)
{
- double tmp;
- char *p, *coord[BOXNARGS];
- int i;
- BOX *result;
-
- if (str == NULL)
- elog (WARN," Bad (null) box external representation");
-
- if ((p = (char *)strchr(str, LDELIM)) == (char *)NULL)
+ BOX *box;
+
+ int isopen;
+ char *s;
+ double x, y;
+
+ if (!PointerIsValid((char *)str))
+ elog (WARN," Bad (null) box external representation",NULL);
+
+ if (!PointerIsValid(box = PALLOCTYPE(BOX)))
+ elog(WARN, "Memory allocation failed, can't input box '%s'",str);
+
+ if ((! path_decode(FALSE, 2, str, &isopen, &s, &(box->high)))
+ || (*s != '\0'))
elog (WARN, "Bad box external representation '%s'",str);
- for (i = 0, p = str; *p && i < BOXNARGS && *p != RDELIM; p++)
- if (*p == DELIM || (*p == LDELIM && !i))
- coord[i++] = p + 1;
- if (i < BOXNARGS - 1)
- elog (WARN, "Bad box external representation '%s'", str);
- result = PALLOCTYPE(BOX);
- result->xh = atof(coord[0]);
- result->yh = atof(coord[1]);
- result->xl = atof(coord[2]);
- result->yl = atof(coord[3]);
- if (result->xh < result->xl) {
- tmp = result->xh;
- result->xh = result->xl;
- result->xl = tmp;
- }
- if (result->yh < result->yl) {
- tmp = result->yh;
- result->yh = result->yl;
- result->yl = tmp;
- }
-
- return(result);
+
+ /* reorder corners if necessary... */
+ if (box->high.x < box->low.x) {
+ x = box->high.x;
+ box->high.x = box->low.x;
+ box->low.x = x;
+ };
+ if (box->high.y < box->low.y) {
+ y = box->high.y;
+ box->high.y = box->low.y;
+ box->low.y = y;
+ };
+
+ return(box);
}
/* box_out - convert a box to external form.
*/
char *box_out(BOX *box)
{
- char *result;
-
- if (box == NULL)
+#if OLD_FORMAT_OUT
+ char *result;
+
+ char *cp;
+#endif
+
+ if (!PointerIsValid((char *)box))
return(NULL);
- result = (char *)PALLOC(80);
- (void) sprintf(result, "(%G,%G,%G,%G)",
- box->xh, box->yh, box->xl, box->yl);
-
- return(result);
+
+#if OLD_FORMAT_OUT
+ if (!PointerIsValid(result = (char *)PALLOC(2*(P_MAXLEN+1)+2)))
+ elog(WARN, "Memory allocation failed, can't output box", NULL);
+
+ cp = result;
+ *cp++ = LDELIM;
+ if (! pair_encode( box->high.x, box->high.y, cp))
+ elog (WARN, "Unable to format box", NULL);
+ cp += strlen(cp);
+ *cp++ = DELIM;
+ if (! pair_encode( box->low.x, box->low.y, cp))
+ elog (WARN, "Unable to format box", NULL);
+ cp += strlen(cp);
+ *cp++ = RDELIM;
+ *cp = '\0';
+
+ return( result);
+#else
+ return( path_encode( -1, 2, (Point *) &(box->high)));
+#endif
}
@@ -109,22 +343,20 @@ BOX *box_construct(double x1, double x2, double y1, double y2)
*/
BOX *box_fill(BOX *result, double x1, double x2, double y1, double y2)
{
- double tmp;
-
- result->xh = x1;
- result->xl = x2;
- result->yh = y1;
- result->yl = y2;
- if (result->xh < result->xl) {
- tmp = result->xh;
- result->xh = result->xl;
- result->xl = tmp;
- }
- if (result->yh < result->yl) {
- tmp = result->yh;
- result->yh = result->yl;
- result->yl = tmp;
- }
+ if (x1 > x2) {
+ result->high.x = x1;
+ result->low.x = x2;
+ } else {
+ result->high.x = x2;
+ result->low.x = x1;
+ };
+ if (y1 > y2) {
+ result->high.y = y1;
+ result->low.y = y2;
+ } else {
+ result->high.y = y2;
+ result->low.y = y1;
+ };
return(result);
}
@@ -150,20 +382,20 @@ BOX *box_copy(BOX *box)
/* box_same - are two boxes identical?
*/
-long box_same(BOX *box1, BOX *box2)
+bool box_same(BOX *box1, BOX *box2)
{
- return((box1->xh == box2->xh && box1->xl == box2->xl) &&
- (box1->yh == box2->yh && box1->yl == box2->yl));
+ return((FPeq(box1->high.x,box2->high.x) && FPeq(box1->low.x,box2->low.x)) &&
+ (FPeq(box1->high.y,box2->high.y) && FPeq(box1->low.y,box2->low.y)));
}
/* box_overlap - does box1 overlap box2?
*/
-long box_overlap(BOX *box1, BOX *box2)
+bool box_overlap(BOX *box1, BOX *box2)
{
- return(((box1->xh >= box2->xh && box1->xl <= box2->xh) ||
- (box2->xh >= box1->xh && box2->xl <= box1->xh)) &&
- ((box1->yh >= box2->yh && box1->yl <= box2->yh) ||
- (box2->yh >= box1->yh && box2->yl <= box1->yh)) );
+ return(((FPge(box1->high.x,box2->high.x) && FPle(box1->low.x,box2->high.x)) ||
+ (FPge(box2->high.x,box1->high.x) && FPle(box2->low.x,box1->high.x))) &&
+ ((FPge(box1->high.y,box2->high.y) && FPle(box1->low.y,box2->high.y)) ||
+ (FPge(box2->high.y,box1->high.y) && FPle(box2->low.y,box1->high.y))) );
}
/* box_overleft - is the right edge of box1 to the left of
@@ -172,23 +404,23 @@ long box_overlap(BOX *box1, BOX *box2)
* This is "less than or equal" for the end of a time range,
* when time ranges are stored as rectangles.
*/
-long box_overleft(BOX *box1, BOX *box2)
+bool box_overleft(BOX *box1, BOX *box2)
{
- return(box1->xh <= box2->xh);
+ return(FPle(box1->high.x,box2->high.x));
}
/* box_left - is box1 strictly left of box2?
*/
-long box_left(BOX *box1, BOX *box2)
+bool box_left(BOX *box1, BOX *box2)
{
- return(box1->xh < box2->xl);
+ return(FPlt(box1->high.x,box2->low.x));
}
/* box_right - is box1 strictly right of box2?
*/
-long box_right(BOX *box1, BOX *box2)
+bool box_right(BOX *box1, BOX *box2)
{
- return(box1->xl > box2->xh);
+ return(FPgt(box1->low.x,box2->high.x));
}
/* box_overright - is the left edge of box1 to the right of
@@ -197,66 +429,66 @@ long box_right(BOX *box1, BOX *box2)
* This is "greater than or equal" for time ranges, when time ranges
* are stored as rectangles.
*/
-long box_overright(BOX *box1, BOX *box2)
+bool box_overright(BOX *box1, BOX *box2)
{
- return(box1->xl >= box2->xl);
+ return(box1->low.x >= box2->low.x);
}
/* box_contained - is box1 contained by box2?
*/
-long box_contained(BOX *box1, BOX *box2)
+bool box_contained(BOX *box1, BOX *box2)
{
- return((box1->xh <= box2->xh && box1->xl >= box2->xl &&
- box1->yh <= box2->yh && box1->yl >= box2->yl));
+ return((FPle(box1->high.x,box2->high.x) && FPge(box1->low.x,box2->low.x)) &&
+ (FPle(box1->high.y,box2->high.y) && FPge(box1->low.y,box2->low.y)));
}
/* box_contain - does box1 contain box2?
*/
-long box_contain(BOX *box1, BOX *box2)
+bool box_contain(BOX *box1, BOX *box2)
{
- return((box1->xh >= box2->xh && box1->xl <= box2->xl &&
- box1->yh >= box2->yh && box1->yl <= box2->yl));
+ return((FPge(box1->high.x,box2->high.x) && FPle(box1->low.x,box2->low.x) &&
+ FPge(box1->high.y,box2->high.y) && FPle(box1->low.y,box2->low.y)));
}
/* box_positionop -
- * is box1 entirely {above, below } box2?
+ * is box1 entirely {above,below} box2?
*/
-long box_below(BOX *box1, BOX *box2)
+bool box_below(BOX *box1, BOX *box2)
{
- return( box1->yh <= box2->yl );
+ return( FPle(box1->high.y,box2->low.y) );
}
-long box_above(BOX *box1, BOX *box2)
+bool box_above(BOX *box1, BOX *box2)
{
- return( box1->yl >= box2->yh );
+ return( FPge(box1->low.y,box2->high.y) );
}
/* box_relop - is area(box1) relop area(box2), within
* our accuracy constraint?
*/
-long box_lt(BOX *box1, BOX *box2)
+bool box_lt(BOX *box1, BOX *box2)
{
return( FPlt(box_ar(box1), box_ar(box2)) );
}
-long box_gt(BOX *box1, BOX *box2)
+bool box_gt(BOX *box1, BOX *box2)
{
return( FPgt(box_ar(box1), box_ar(box2)) );
}
-long box_eq(BOX *box1, BOX *box2)
+bool box_eq(BOX *box1, BOX *box2)
{
return( FPeq(box_ar(box1), box_ar(box2)) );
}
-long box_le(BOX *box1, BOX *box2)
+bool box_le(BOX *box1, BOX *box2)
{
return( FPle(box_ar(box1), box_ar(box2)) );
}
-long box_ge(BOX *box1, BOX *box2)
+bool box_ge(BOX *box1, BOX *box2)
{
return( FPge(box_ar(box1), box_ar(box2)) );
}
@@ -292,7 +524,7 @@ double *box_length(BOX *box)
double *result;
result = PALLOCTYPE(double);
- *result = box->xh - box->xl;
+ *result = box->high.x - box->low.x;
return(result);
}
@@ -306,7 +538,7 @@ double *box_height(BOX *box)
double *result;
result = PALLOCTYPE(double);
- *result = box->yh - box->yl;
+ *result = box->high.y - box->low.y;
return(result);
}
@@ -318,7 +550,7 @@ double *box_height(BOX *box)
double *box_distance(BOX *box1, BOX *box2)
{
double *result;
- Point *box_center(), *a, *b;
+ Point *a, *b;
result = PALLOCTYPE(double);
a = box_center(box1);
@@ -338,8 +570,8 @@ Point *box_center(BOX *box)
Point *result;
result = PALLOCTYPE(Point);
- result->x = (box->xh + box->xl) / 2.0;
- result->y = (box->yh + box->yl) / 2.0;
+ result->x = (box->high.x + box->low.x) / 2.0;
+ result->y = (box->high.y + box->low.y) / 2.0;
return(result);
}
@@ -358,7 +590,7 @@ double box_ar(BOX *box)
*/
double box_ln(BOX *box)
{
- return( box->xh - box->xl );
+ return( box->high.x - box->low.x );
}
@@ -367,7 +599,7 @@ double box_ln(BOX *box)
*/
double box_ht(BOX *box)
{
- return( box->yh - box->yl );
+ return( box->high.y - box->low.y );
}
@@ -377,8 +609,7 @@ double box_ht(BOX *box)
double box_dt(BOX *box1, BOX *box2)
{
double result;
- Point *box_center(),
- *a, *b;
+ Point *a, *b;
a = box_center(box1);
b = box_center(box2);
@@ -400,15 +631,14 @@ double box_dt(BOX *box1, BOX *box2)
BOX *box_intersect(BOX *box1, BOX *box2)
{
BOX *result;
- long box_overlap();
-
+
if (! box_overlap(box1,box2))
return(NULL);
result = PALLOCTYPE(BOX);
- result->xh = Min(box1->xh, box2->xh);
- result->xl = Max(box1->xl, box2->xl);
- result->yh = Min(box1->yh, box2->yh);
- result->yl = Max(box1->yl, box2->yl);
+ result->high.x = Min(box1->high.x, box2->high.x);
+ result->low.x = Max(box1->low.x, box2->low.x);
+ result->high.y = Min(box1->high.y, box2->high.y);
+ result->low.y = Max(box1->low.y, box2->low.y);
return(result);
}
@@ -423,10 +653,10 @@ LSEG *box_diagonal(BOX *box)
{
Point p1, p2;
- p1.x = box->xh;
- p1.y = box->yh;
- p2.x = box->xl;
- p2.y = box->yl;
+ p1.x = box->high.x;
+ p1.y = box->high.y;
+ p2.x = box->low.x;
+ p2.y = box->low.y;
return( lseg_construct( &p1, &p2 ) );
}
@@ -486,17 +716,17 @@ line_construct_pp(Point *pt1, Point *pt2)
* Relative position routines.
*---------------------------------------------------------*/
-long line_intersect(LINE *l1, LINE *l2)
+bool line_intersect(LINE *l1, LINE *l2)
{
return( ! line_parallel(l1, l2) );
}
-long line_parallel(LINE *l1, LINE *l2)
+bool line_parallel(LINE *l1, LINE *l2)
{
return( FPeq(l1->m, l2->m) );
}
-long line_perp(LINE *l1, LINE *l2)
+bool line_perp(LINE *l1, LINE *l2)
{
if (l1->m)
return( FPeq(l2->m / l1->m, -1.0) );
@@ -505,18 +735,18 @@ long line_perp(LINE *l1, LINE *l2)
return(1); /* both 0.0 */
}
-long line_vertical(LINE *line)
+bool line_vertical(LINE *line)
{
return( FPeq(line->A, -1.0) && FPzero(line->B) );
}
-long line_horizontal(LINE *line)
+bool line_horizontal(LINE *line)
{
return( FPzero(line->m) );
}
-long line_eq(LINE *l1, LINE *l2)
+bool line_eq(LINE *l1, LINE *l2)
{
double k;
@@ -589,88 +819,116 @@ line_interpt(LINE *l1, LINE *l2)
**
***********************************************************************/
-#define PATHALLOCSIZE(N) \
- (long) ((unsigned) (sizeof(PATH) + \
- (((N)-1) > 0 ? ((N)-1) : 0) \
- * sizeof(Point)))
-
/*----------------------------------------------------------
* String to path / path to string conversion.
* External format:
+ * "((xcoord, ycoord),... )"
+ * "[(xcoord, ycoord),... ]"
+ * "(xcoord, ycoord),... "
+ * "[xcoord, ycoord,... ]"
+ * Also support older format:
* "(closed, npts, xcoord, ycoord,... )"
*---------------------------------------------------------*/
PATH *path_in(char *str)
{
- double coord;
- long field[2];
- char *s;
- int ct, i;
- PATH *result;
- long pathsize;
-
- if (str == NULL)
+ PATH *path;
+
+ int isopen;
+ char *s;
+ int npts;
+ int size;
+#if OLD_FORMAT_IN
+ int oldstyle = FALSE;
+ double x, y;
+#endif
+
+ if (!PointerIsValid((char *)str))
elog(WARN, "Bad (null) path external representation");
-
- /* read the path header information */
- for (i = 0, s = str; *s && i < 2 && *s != RDELIM; ++s)
- if (*s == DELIM || (*s == LDELIM && !i))
- field[i++] = atol(s + 1);
- if (i < 1)
- elog(WARN, "Bad path external representation '%s'", str);
- pathsize = PATHALLOCSIZE(field[1]);
- result = (PATH *)palloc(pathsize);
- result->length = pathsize;
- result->closed = field[0];
- result->npts = field[1];
-
- /* read the path points */
-
- ct = result->npts * 2; /* two coords for every point */
- for (i = 0;
- *s && i < ct && *s != RDELIM;
- ++s) {
- if (*s == ',') {
- coord = atof(s + 1);
- if (i % 2)
- (result->p[i/2]).y = coord;
- else
- (result->p[i/2]).x = coord;
- ++i;
- }
- }
- if (i % 2 || i < --ct) {
- PFREE(result);
+
+ if ((npts = pair_count(str, ',')) <= 0)
elog(WARN, "Bad path external representation '%s'", str);
- }
-
- return(result);
+
+#if OLD_FORMAT_IN
+ s = str;
+ while (isspace( *s)) s++;
+ /* identify old style format as having only one left delimiter in string... */
+ oldstyle = ((*s == LDELIM) && (strrchr( s, LDELIM) == s));
+
+ /* old-style format? then first two fields are closed flag and point count... */
+ if (oldstyle) {
+ s++;
+ if ((! pair_decode( s, &x, &y, &s)) || (*s++ != DELIM)
+ || ((x != 0) && (x != 1)) || (y <= 0))
+ elog (WARN, "Bad path external representation '%s'",str);
+ isopen = (x == 0);
+ npts = y;
+ };
+#endif
+
+ size = offsetof(PATH, p[0]) + (sizeof(path->p[0]) * npts);
+ if (!PointerIsValid(path = PALLOC(size)))
+ elog(WARN, "Memory allocation failed, can't input path '%s'",str);
+
+ path->size = size;
+ path->npts = npts;
+ if (oldstyle) path->closed = (! isopen);
+
+#if OLD_FORMAT_IN
+ if ((! path_decode(TRUE, npts, s, &isopen, &s, &(path->p[0])))
+ || ! (oldstyle? (*s++ == RDELIM): (*s == '\0')))
+#else
+ if ((! path_decode(TRUE, npts, s, &isopen, &s, &(path->p[0])))
+ || (*s != '\0'))
+#endif
+ elog (WARN, "Bad path external representation '%s'",str);
+
+#if OLD_FORMAT_IN
+ if (oldstyle) {
+ while (isspace( *s)) s++;
+ if (*s != '\0')
+ elog (WARN, "Bad path external representation '%s'",str);
+ };
+#endif
+
+ if (! oldstyle) path->closed = (! isopen);
+
+ return(path);
}
char *path_out(PATH *path)
{
- char buf[BUFSIZ + 20000], *result, *s;
- int i;
- char tmp[64];
-
- if (path == NULL)
- return(NULL);
- (void) sprintf(buf,"%c%d,%d", LDELIM,
- path->closed, path->npts);
- s = buf + strlen(buf);
- for (i = 0; i < path->npts; ++i) {
- (void) sprintf(tmp, ",%G,%G",
- path->p[i].x, path->p[i].y);
- (void) strcpy(s, tmp);
- s += strlen(tmp);
- }
- *s++ = RDELIM;
- *s = '\0';
- result = (char *)PALLOC(strlen(buf) + 1);
- (void) strcpy(result, buf);
-
+#if OLD_FORMAT_OUT
+ int i;
+ char *result, *cp;
+#endif
+
+ if (!PointerIsValid((char *)path))
+ return NULL;
+
+#if OLD_FORMAT_OUT
+ if (!PointerIsValid(result = (char *)PALLOC(path->npts*(P_MAXLEN+3)+2)))
+ elog(WARN, "Memory allocation failed, can't output path", NULL);
+
+ cp = result;
+ *cp++ = LDELIM;
+ if (! pair_encode( path->closed, path->npts, cp))
+ elog (WARN, "Unable to format path", NULL);
+ cp += strlen(cp);
+
+ for (i=0; i<path->npts; i++) {
+ *cp++ = DELIM;
+ if (! pair_encode( path->p[i].x, path->p[i].y, cp))
+ elog (WARN, "Unable to format path", NULL);
+ cp += strlen(cp);
+ };
+ *cp++ = RDELIM;
+ *cp = '\0';
return(result);
+#else
+ return( path_encode( path->closed, path->npts, (Point *) &(path->p[0])));
+#endif
}
@@ -682,55 +940,133 @@ char *path_out(PATH *path)
* Better relops and access methods coming soon.
*---------------------------------------------------------*/
-long path_n_lt(PATH *p1, PATH *p2)
+bool path_n_lt(PATH *p1, PATH *p2)
{
return( (p1->npts < p2->npts ) );
}
-long path_n_gt(PATH *p1, PATH *p2)
+bool path_n_gt(PATH *p1, PATH *p2)
{
return( (p1->npts > p2->npts ) );
}
-long path_n_eq(PATH *p1, PATH *p2)
+bool path_n_eq(PATH *p1, PATH *p2)
{
return( (p1->npts == p2->npts) );
}
-long path_n_le(PATH *p1, PATH *p2)
+bool path_n_le(PATH *p1, PATH *p2)
{
return( (p1->npts <= p2->npts ) );
}
-long path_n_ge(PATH *p1, PATH *p2)
+bool path_n_ge(PATH *p1, PATH *p2)
{
return( (p1->npts >= p2->npts ) );
}
+
+/*----------------------------------------------------------
+ * Conversion operators.
+ *---------------------------------------------------------*/
+
+PATH *path_copy(PATH *path);
+
+bool
+path_isclosed( PATH *path)
+{
+ if (!PointerIsValid((char *)path))
+ return FALSE;
+
+ return(path->closed);
+} /* path_isclosed() */
+
+bool
+path_isopen( PATH *path)
+{
+ if (!PointerIsValid((char *)path))
+ return FALSE;
+
+ return(! path->closed);
+} /* path_isopen() */
+
+
+int4
+path_npoints( PATH *path)
+{
+ if (!PointerIsValid((char *)path))
+ return 0;
+
+ return(path->npts);
+} /* path_npoints() */
+
+PATH *
+path_close(PATH *path)
+{
+ PATH *result;
+
+ if (PointerIsValid((char *)result = path_copy(path)))
+ result->closed = TRUE;
+
+ return(result);
+} /* path_close() */
+
+PATH *
+path_open(PATH *path)
+{
+ PATH *result;
+
+ if (PointerIsValid((char *)result = path_copy(path)))
+ result->closed = FALSE;
+
+ return(result);
+} /* path_open() */
+
+
+PATH *
+path_copy(PATH *path)
+{
+ PATH *result;
+ int size;
+
+ if (!PointerIsValid((char *)path))
+ return NULL;
+
+ size = offsetof(PATH, p[0]) + (sizeof(path->p[0]) * path->npts);
+ if (!PointerIsValid(result = PALLOC(size)))
+ elog(WARN, "Memory allocation failed, can't copy path",NULL);
+
+ memmove((char *) result, (char *) path, size);
+ return(result);
+} /* path_copy() */
+
+
/* path_inter -
* Does p1 intersect p2 at any point?
* Use bounding boxes for a quick (O(n)) check, then do a
* O(n^2) iterative edge check.
*/
-long path_inter(PATH *p1, PATH *p2)
+bool path_inter(PATH *p1, PATH *p2)
{
BOX b1, b2;
int i, j;
LSEG seg1, seg2;
- b1.xh = b1.yh = b2.xh = b2.yh = (double)DBL_MAX;
- b1.xl = b1.yl = b2.xl = b2.yl = -(double)DBL_MAX;
- for (i = 0; i < p1->npts; ++i) {
- b1.xh = Max(p1->p[i].x, b1.xh);
- b1.yh = Max(p1->p[i].y, b1.yh);
- b1.xl = Min(p1->p[i].x, b1.xl);
- b1.yl = Min(p1->p[i].y, b1.yl);
+ b1.high.x = b1.low.x = p1->p[0].x;
+ b1.high.y = b1.low.y = p1->p[0].y;
+ for (i = 1; i < p1->npts; i++) {
+ b1.high.x = Max(p1->p[i].x, b1.high.x);
+ b1.high.y = Max(p1->p[i].y, b1.high.y);
+ b1.low.x = Min(p1->p[i].x, b1.low.x);
+ b1.low.y = Min(p1->p[i].y, b1.low.y);
}
- for (i = 0; i < p2->npts; ++i) {
- b2.xh = Max(p2->p[i].x, b2.xh);
- b2.yh = Max(p2->p[i].y, b2.yh);
- b2.xl = Min(p2->p[i].x, b2.xl);
- b2.yl = Min(p2->p[i].y, b2.yl);
+ b2.high.x = b2.low.x = p2->p[0].x;
+ b2.high.y = b2.low.y = p2->p[0].y;
+ for (i = 1; i < p2->npts; i++) {
+ b2.high.x = Max(p2->p[i].x, b2.high.x);
+ b2.high.y = Max(p2->p[i].y, b2.high.y);
+ b2.low.x = Min(p2->p[i].x, b2.low.x);
+ b2.low.y = Min(p2->p[i].y, b2.low.y);
}
if (! box_overlap(&b1, &b2))
return(0);
@@ -753,25 +1089,31 @@ long path_inter(PATH *p1, PATH *p2)
two paths, and finds the min distance between any two lsegs */
double *path_distance(PATH *p1, PATH *p2)
{
- double *min, *tmp;
+ double *min = NULL, *tmp;
int i,j;
LSEG seg1, seg2;
-
+
+/*
statlseg_construct(&seg1, &p1->p[0], &p1->p[1]);
statlseg_construct(&seg2, &p2->p[0], &p2->p[1]);
min = lseg_distance(&seg1, &seg2);
-
+*/
+
for (i = 0; i < p1->npts - 1; i++)
for (j = 0; j < p2->npts - 1; j++)
{
statlseg_construct(&seg1, &p1->p[i], &p1->p[i+1]);
statlseg_construct(&seg2, &p2->p[j], &p2->p[j+1]);
- if (*min < *(tmp = lseg_distance(&seg1, &seg2)))
- *min = *tmp;
- PFREE(tmp);
+ tmp = lseg_distance(&seg1, &seg2);
+ if ((min == NULL) || (*min < *tmp)) {
+ if (min != NULL) PFREE(min);
+ min = tmp;
+ } else {
+ PFREE(tmp);
+ };
}
-
+
return(min);
}
@@ -782,12 +1124,12 @@ double *path_distance(PATH *p1, PATH *p2)
double *path_length(PATH *path)
{
- double *result;
+ double *result;
int ct, i;
result = PALLOCTYPE(double);
ct = path->npts - 1;
- for (i = 0; i < ct; ++i)
+ for (i = 0; i < ct; i++)
*result += point_dt(&path->p[i], &path->p[i+1]);
return(result);
@@ -797,11 +1139,11 @@ double *path_length(PATH *path)
double path_ln(PATH *path)
{
- double result;
+ double result;
int ct, i;
ct = path->npts - 1;
- for (result = i = 0; i < ct; ++i)
+ for (result = i = 0; i < ct; i++)
result += point_dt(&path->p[i], &path->p[i+1]);
return(result);
@@ -814,52 +1156,49 @@ double path_ln(PATH *path)
/*----------------------------------------------------------
* String to point, point to string conversion.
- * External form: "(x, y)"
+ * External format:
+ * "(x,y)"
+ * "x,y"
*---------------------------------------------------------*/
-Point *point_in(char *str)
+Point *
+point_in(char *str)
{
- char *coord[POINTNARGS], *p, *r;
- int i;
- Point *result;
+ Point *point;
+
+ double x, y;
+ char *s;
- if (str == NULL)
+ if (str == NULL) {
elog(WARN, "Bad (null) point external representation");
-
- if ((p = (char *)strchr(str, LDELIM)) == (char *)NULL)
- elog (WARN, "Bad point external representation '%s'",str);
- for (i = 0, p++; *p && i < POINTNARGS-1 && *p != RDELIM; p = r+1)
- if ((r = (char *)strchr(p, DELIM)) == (char *)NULL)
- elog (WARN, "Bad point external representation '%s'",str);
- else
- coord[i++] = p;
- if ((r = (char *)strchr(p, RDELIM)) == (char *)NULL)
- elog (WARN, "Bad point external representation '%s'",str);
- coord[i++] = p;
-
- if (i < POINTNARGS - 1)
- elog(WARN, "Bad point external representation '%s'",str);
- result = PALLOCTYPE(Point);
- result->x = atof(coord[0]);
- result->y = atof(coord[1]);
- return(result);
-}
+ return NULL;
+ }
+
+ if (! pair_decode( str, &x, &y, &s) || (strlen(s) > 0))
+ elog (WARN, "Bad point external representation '%s'",str);
+
+ if (!PointerIsValid(point = PALLOCTYPE(Point)))
+ elog (WARN, "Unable to allocate point storage for '%s'",str);
-char *point_out(Point *pt)
+ point->x = x;
+ point->y = y;
+
+ return(point);
+} /* point_in() */
+
+char *
+point_out(Point *pt)
{
- char *result;
-
- if (pt == NULL)
+ if (!PointerIsValid((char *)pt))
return(NULL);
- result = (char *)PALLOC(40);
- (void) sprintf(result, "(%G,%G)", pt->x, pt->y);
- return(result);
-}
+
+ return( path_encode( -1, 1, pt));
+} /* point_out() */
Point *point_construct(double x, double y)
{
- Point *result;
+ Point *result;
result = PALLOCTYPE(Point);
result->x = x;
@@ -870,7 +1209,7 @@ Point *point_construct(double x, double y)
Point *point_copy(Point *pt)
{
- Point *result;
+ Point *result;
result = PALLOCTYPE(Point);
result->x = pt->x;
@@ -888,37 +1227,37 @@ Point *point_copy(Point *pt)
* EPSILON = 0.0).
*---------------------------------------------------------*/
-long point_left(Point *pt1, Point *pt2)
+bool point_left(Point *pt1, Point *pt2)
{
return( FPlt(pt1->x, pt2->x) );
}
-long point_right(Point *pt1, Point *pt2)
+bool point_right(Point *pt1, Point *pt2)
{
return( FPgt(pt1->x, pt2->x) );
}
-long point_above(Point *pt1, Point *pt2)
+bool point_above(Point *pt1, Point *pt2)
{
return( FPgt(pt1->y, pt2->y) );
}
-long point_below(Point *pt1, Point *pt2)
+bool point_below(Point *pt1, Point *pt2)
{
return( FPlt(pt1->y, pt2->y) );
}
-long point_vert(Point *pt1, Point *pt2)
+bool point_vert(Point *pt1, Point *pt2)
{
return( FPeq( pt1->x, pt2->x ) );
}
-long point_horiz(Point *pt1, Point *pt2)
+bool point_horiz(Point *pt1, Point *pt2)
{
return( FPeq( pt1->y, pt2->y ) );
}
-long point_eq(Point *pt1, Point *pt2)
+bool point_eq(Point *pt1, Point *pt2)
{
return( point_horiz(pt1, pt2) && point_vert(pt1, pt2) );
}
@@ -927,9 +1266,9 @@ long point_eq(Point *pt1, Point *pt2)
* "Arithmetic" operators on points.
*---------------------------------------------------------*/
-long pointdist(Point *p1, Point *p2)
+int32 pointdist(Point *p1, Point *p2)
{
- long result;
+ int32 result;
result = point_dt(p1, p2);
return(result);
@@ -937,7 +1276,7 @@ long pointdist(Point *p1, Point *p2)
double *point_distance(Point *pt1, Point *pt2)
{
- double *result;
+ double *result;
result = PALLOCTYPE(double);
*result = HYPOT( pt1->x - pt2->x, pt1->y - pt2->y );
@@ -952,7 +1291,7 @@ double point_dt(Point *pt1, Point *pt2)
double *point_slope(Point *pt1, Point *pt2)
{
- double *result;
+ double *result;
result = PALLOCTYPE(double);
if (point_vert(pt1, pt2))
@@ -970,6 +1309,7 @@ double point_sl(Point *pt1, Point *pt2)
: (pt1->y - pt2->y) / (pt1->x - pt2->x) );
}
+
/***********************************************************************
**
** Routines for 2D line segments.
@@ -978,46 +1318,42 @@ double point_sl(Point *pt1, Point *pt2)
/*----------------------------------------------------------
* String to lseg, lseg to string conversion.
- * External form: "(id, info, x1, y1, x2, y2)"
+ * External forms: "[(x1, y1), (x2, y2)]"
+ * "(x1, y1), (x2, y2)"
+ * "x1, y1, x2, y2"
+ * closed form ok "((x1, y1), (x2, y2))"
+ * (old form) "(x1, y1, x2, y2)"
*---------------------------------------------------------*/
LSEG *lseg_in(char *str)
{
- char *coord[LSEGNARGS], *p;
- int i;
- LSEG *result;
-
- if (str == NULL)
- elog (WARN," Bad (null) box external representation");
-
- if ((p = (char *)strchr(str, LDELIM)) == (char *)NULL)
+ LSEG *lseg;
+
+ int isopen;
+ char *s;
+
+ if (!PointerIsValid((char *)str))
+ elog (WARN," Bad (null) lseg external representation",NULL);
+
+ if (!PointerIsValid(lseg = PALLOCTYPE(LSEG)))
+ elog(WARN, "Memory allocation failed, can't input lseg '%s'",str);
+
+ if ((! path_decode(TRUE, 2, str, &isopen, &s, &(lseg->p[0])))
+ || (*s != '\0'))
elog (WARN, "Bad lseg external representation '%s'",str);
- for (i = 0, p = str; *p && i < LSEGNARGS && *p != RDELIM; p++)
- if (*p == DELIM || (*p == LDELIM && !i))
- coord[i++] = p + 1;
- if (i < LSEGNARGS - 1)
- elog (WARN, "Bad lseg external representation '%s'", str);
- result = PALLOCTYPE(LSEG);
- result->p[0].x = atof(coord[0]);
- result->p[0].y = atof(coord[1]);
- result->p[1].x = atof(coord[2]);
- result->p[1].y = atof(coord[3]);
- result->m = point_sl(&result->p[0], &result->p[1]);
+
+ lseg->m = point_sl(&lseg->p[0], &lseg->p[1]);
- return(result);
+ return(lseg);
}
char *lseg_out(LSEG *ls)
{
- char *result;
-
- if (ls == NULL)
+ if (!PointerIsValid((char *)ls))
return(NULL);
- result = (char *)PALLOC(80);
- (void) sprintf(result, "(%G,%G,%G,%G)",
- ls->p[0].x, ls->p[0].y, ls->p[1].x, ls->p[1].y);
- return(result);
+
+ return( path_encode( FALSE, 2, (Point *) &(ls->p[0])));
}
@@ -1033,6 +1369,7 @@ LSEG *lseg_construct(Point *pt1, Point *pt2)
result->p[0].y = pt1->y;
result->p[1].x = pt2->x;
result->p[1].y = pt2->y;
+
result->m = point_sl(pt1, pt2);
return(result);
@@ -1045,6 +1382,7 @@ void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
lseg->p[0].y = pt1->y;
lseg->p[1].x = pt2->x;
lseg->p[1].y = pt2->y;
+
lseg->m = point_sl(pt1, pt2);
}
@@ -1056,29 +1394,29 @@ void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
** find intersection of the two lines, and see if it falls on
** both segments.
*/
-long lseg_intersect(LSEG *l1, LSEG *l2)
+bool lseg_intersect(LSEG *l1, LSEG *l2)
{
LINE *ln;
Point *interpt;
- long retval;
+ bool retval;
ln = line_construct_pp(&l2->p[0], &l2->p[1]);
interpt = interpt_sl(l1, ln);
if (interpt != NULL && on_ps(interpt, l2)) /* interpt on l1 and l2 */
- retval = 1;
- else retval = 0;
+ retval = TRUE;
+ else retval = FALSE;
if (interpt != NULL) PFREE(interpt);
PFREE(ln);
return(retval);
}
-long lseg_parallel(LSEG *l1, LSEG *l2)
+bool lseg_parallel(LSEG *l1, LSEG *l2)
{
return( FPeq(l1->m, l2->m) );
}
-long lseg_perp(LSEG *l1, LSEG *l2)
+bool lseg_perp(LSEG *l1, LSEG *l2)
{
if (! FPzero(l1->m))
return( FPeq(l2->m / l1->m, -1.0) );
@@ -1087,18 +1425,18 @@ long lseg_perp(LSEG *l1, LSEG *l2)
return(0); /* both 0.0 */
}
-long lseg_vertical(LSEG *lseg)
+bool lseg_vertical(LSEG *lseg)
{
return( FPeq(lseg->p[0].x, lseg->p[1].x) );
}
-long lseg_horizontal(LSEG *lseg)
+bool lseg_horizontal(LSEG *lseg)
{
return( FPeq(lseg->p[0].y, lseg->p[1].y) );
}
-long lseg_eq(LSEG *l1, LSEG *l2)
+bool lseg_eq(LSEG *l1, LSEG *l2)
{
return( FPeq(l1->p[0].x, l2->p[0].x) &&
FPeq(l1->p[1].y, l2->p[1].y) &&
@@ -1118,27 +1456,11 @@ long lseg_eq(LSEG *l1, LSEG *l2)
*/
double *lseg_distance(LSEG *l1, LSEG *l2)
{
- double *d, *result;
+ double *result;
result = PALLOCTYPE(double);
- if (lseg_intersect(l1, l2)) {
- *result = 0.0;
- return(result);
- }
- *result = (double)DBL_MAX;
- d = dist_ps(&l1->p[0], l2);
- *result = Min(*result, *d);
- PFREE(d);
- d = dist_ps(&l1->p[1], l2);
- *result = Min(*result, *d);
- PFREE(d);
- d = dist_ps(&l2->p[0], l1);
- *result = Min(*result, *d);
- PFREE(d);
- d = dist_ps(&l2->p[1], l1);
- *result = Min(*result, *d);
- PFREE(d);
-
+ *result = lseg_dt( l1, l2);
+
return(result);
}
@@ -1149,9 +1471,9 @@ double lseg_dt(LSEG *l1, LSEG *l2)
if (lseg_intersect(l1, l2))
return(0.0);
- result = (double)DBL_MAX;
+
d = dist_ps(&l1->p[0], l2);
- result = Min(result, *d);
+ result = *d;
PFREE(d);
d = dist_ps(&l1->p[1], l2);
result = Min(result, *d);
@@ -1265,21 +1587,23 @@ double *dist_ppth(Point *pt, PATH *path)
LSEG lseg;
switch (path->npts) {
+ /* no points in path? then result is undefined... */
case 0:
- result = PALLOCTYPE(double);
- *result = Abs((double) DBL_MAX); /* +infinity */
+ result = NULL;
break;
+ /* one point in path? then get distance between two points... */
case 1:
result = point_distance(pt, &path->p[0]);
break;
default:
+ /* make sure the path makes sense... */
+ Assert(path->npts > 1);
/*
* the distance from a point to a path is the smallest distance
* from the point to any of its constituent segments.
*/
- Assert(path->npts > 1);
result = PALLOCTYPE(double);
- for (i = 0; i < path->npts - 1; ++i) {
+ for (i = 0; i < path->npts - 1; i++) {
statlseg_construct(&lseg, &path->p[i], &path->p[i+1]);
tmp = dist_ps(pt, &lseg);
if (i == 0 || *tmp < *result)
@@ -1298,21 +1622,30 @@ double *dist_pb(Point *pt, BOX *box)
tmp = close_pb(pt, box);
result = point_distance(tmp, pt);
-
PFREE(tmp);
+
return(result);
}
double *dist_sl(LSEG *lseg, LINE *line)
{
- double *result;
-
+ double *result, *d2;
+
if (inter_sl(lseg, line)) {
result = PALLOCTYPE(double);
*result = 0.0;
- } else /* parallel */
+
+ } else {
result = dist_pl(&lseg->p[0], line);
+ d2 = dist_pl(&lseg->p[1], line);
+ if (*d2 > *result) {
+ PFREE( result);
+ result = d2;
+ } else {
+ PFREE( d2);
+ };
+ };
return(result);
}
@@ -1502,7 +1835,7 @@ Point *close_lb(LINE *line, BOX *box)
/* on_pl -
* Does the point satisfy the equation?
*/
-long on_pl(Point *pt, LINE *line)
+bool on_pl(Point *pt, LINE *line)
{
return( FPzero(line->A * pt->x + line->B * pt->y + line->C) );
}
@@ -1511,16 +1844,16 @@ long on_pl(Point *pt, LINE *line)
/* on_ps -
* Determine colinearity by detecting a triangle inequality.
*/
-long on_ps(Point *pt, LSEG *lseg)
+bool on_ps(Point *pt, LSEG *lseg)
{
return( FPeq (point_dt(pt, &lseg->p[0]) + point_dt(pt, &lseg->p[1]),
point_dt(&lseg->p[0], &lseg->p[1])) );
}
-long on_pb(Point *pt, BOX *box)
+bool on_pb(Point *pt, BOX *box)
{
- return( pt->x <= box->xh && pt->x >= box->xl &&
- pt->y <= box->yh && pt->y >= box->yl );
+ return( pt->x <= box->high.x && pt->x >= box->low.x &&
+ pt->y <= box->high.y && pt->y >= box->low.y );
}
/* on_ppath -
@@ -1535,7 +1868,7 @@ long on_pb(Point *pt, BOX *box)
*/
#define NEXT(A) ((A+1) % path->npts) /* cyclic "i+1" */
-long on_ppath(Point *pt, PATH *path)
+bool on_ppath(Point *pt, PATH *path)
{
int above, next, /* is the seg above the ray? */
inter, /* # of times path crosses ray */
@@ -1604,12 +1937,12 @@ long on_ppath(Point *pt, PATH *path)
}
-long on_sl(LSEG *lseg, LINE *line)
+bool on_sl(LSEG *lseg, LINE *line)
{
return( on_pl(&lseg->p[0], line) && on_pl(&lseg->p[1], line) );
}
-long on_sb(LSEG *lseg, BOX *box)
+bool on_sb(LSEG *lseg, BOX *box)
{
return( on_pb(&lseg->p[0], box) && on_pb(&lseg->p[1], box) );
}
@@ -1619,7 +1952,7 @@ long on_sb(LSEG *lseg, BOX *box)
* Whether one object intersects another.
*-------------------------------------------------------------------*/
-long inter_sl(LSEG *lseg, LINE *line)
+bool inter_sl(LSEG *lseg, LINE *line)
{
Point *tmp;
@@ -1631,207 +1964,195 @@ long inter_sl(LSEG *lseg, LINE *line)
return(0);
}
-long inter_sb(LSEG *lseg, BOX *box)
+/* XXX segment and box should be able to intersect; tgl - 97/01/09 */
+
+bool inter_sb(LSEG *lseg, BOX *box)
{
return(0);
}
-long inter_lb(LINE *line, BOX *box)
+/* XXX line and box should be able to intersect; tgl - 97/01/09 */
+
+bool inter_lb(LINE *line, BOX *box)
{
return(0);
}
/*------------------------------------------------------------------
* The following routines define a data type and operator class for
- * POLYGONS .... Part of which (the polygon's bounding box is built on
+ * POLYGONS .... Part of which (the polygon's bounding box) is built on
* top of the BOX data type.
*
* make_bound_box - create the bounding box for the input polygon
*------------------------------------------------------------------*/
-/* Maximum number of output digits printed */
-#define P_MAXDIG 12
-
/*---------------------------------------------------------------------
* Make the smallest bounding box for the given polygon.
*---------------------------------------------------------------------*/
void make_bound_box(POLYGON *poly)
{
+ int i;
double x1,y1,x2,y2;
- int npts = poly->npts;
-
- if (npts > 0) {
- x1 = poly_min((double *)poly->pts, npts);
- x2 = poly_max((double *)poly->pts, npts);
- y1 = poly_min(((double *)poly->pts)+npts, npts),
- y2 = poly_max(((double *)poly->pts)+npts, npts);
+
+ if (poly->npts > 0) {
+ x2 = x1 = poly->p[0].x;
+ y2 = y1 = poly->p[0].y;
+ for (i = 1; i < poly->npts; i++) {
+ if (poly->p[i].x < x1) x1 = poly->p[i].x;
+ if (poly->p[i].x > x2) x2 = poly->p[i].x;
+ if (poly->p[i].y < y1) y1 = poly->p[i].y;
+ if (poly->p[i].y > y2) y2 = poly->p[i].y;
+ };
+
box_fill(&(poly->boundbox), x1, x2, y1, y2);
- }
+ } else {
+ elog (WARN, "Unable to create bounding box for empty polygon", NULL);
+ };
}
/*------------------------------------------------------------------
- * polygon_in - read in the polygon from a string specification
- * the string is of the form "(f8,f8,f8,f8,...,f8)"
+ * poly_in - read in the polygon from a string specification
+ *
+ * External format:
+ * "((x0,y0),...,(xn,yn))"
+ * "x0,y0,...,xn,yn"
+ * also supports the older style "(x1,...,xn,y1,...yn)"
*------------------------------------------------------------------*/
-POLYGON *poly_in(char *s)
+POLYGON *poly_in(char *str)
{
POLYGON *poly;
- long points;
- double *xp, *yp, strtod();
- int i, size;
-
- if((points = poly_pt_count(s, ',')) < 0)
- elog(WARN, "Bad polygon external representation '%s'", s);
-
- size = offsetof(POLYGON, pts[0]) + 2 * sizeof(double) * points;
- poly = (POLYGON *) PALLOC(size);
+ int npts;
+ int size;
+ int isopen;
+
+#if OLD_FORMAT_IN
+ char *s;
+ int oldstyle;
+ int oddcount;
+ int i;
+ double x1, x2;
+#endif
+
+ if (!PointerIsValid((char *)str))
+ elog (WARN," Bad (null) polygon external representation");
+
+ if ((npts = pair_count(str, ',')) <= 0)
+ elog(WARN, "Bad polygon external representation '%s'", str);
+
+ size = offsetof(POLYGON, p[0]) + (sizeof(poly->p[0]) * npts);
+ if (!PointerIsValid(poly = (POLYGON *) PALLOC(size)))
+ elog(WARN, "Memory allocation failed, can't input polygon '%s'",str);
+
memset((char *) poly, 0, size); /* zero any holes */
-
- if (!PointerIsValid(poly))
- elog(WARN, "Memory allocation failed, can't input polygon");
-
- poly->npts = points;
poly->size = size;
-
- /* Store all x coords followed by all y coords */
- xp = (double *) &(poly->pts[0]);
- yp = (double *) (poly->pts + points*sizeof(double));
-
- s++; /* skip LDELIM */
-
- for (i=0; i<points; i++,xp++,yp++)
- {
- *xp = strtod(s, &s);
- s++; /* skip delimiter */
- *yp = strtod(s, &s);
- s++; /* skip delimiter */
- }
+ poly->npts = npts;
+
+#if OLD_FORMAT_IN
+ s = str;
+ while (isspace( *s)) s++;
+ /* identify old style format as having only one left delimiter in string... */
+ oldstyle = ((*s == LDELIM) && (strrchr( s, LDELIM) == s));
+
+ if (oldstyle) {
+ s++;
+ while (isspace( *s)) s++;
+
+ for (i=0; i<npts/2; i++) {
+ if (! pair_decode( s, &x1, &x2, &s))
+ elog (WARN, "Bad polygon external representation '%s'",str);
+
+ if (*s == DELIM) s++;
+ poly->p[i*2].x = x1;
+ poly->p[i*2+1].x = x2;
+ };
+ oddcount = (npts % 2);
+ if (oddcount) {
+ if (! pair_decode( s, &x1, &x2, &s))
+ elog (WARN, "Bad polygon external representation '%s'",str);
+
+ if (*s == DELIM) s++;
+ poly->p[npts-1].x = x1;
+ poly->p[0].y = x2;
+ };
+ for (i=0; i<npts/2; i++) {
+ if (! pair_decode( s, &x1, &x2, &s))
+ elog (WARN, "Bad polygon external representation '%s'",str);
+
+ if (*s == DELIM) s++;
+ poly->p[i*2+oddcount].y = x1;
+ poly->p[i*2+1+oddcount].y = x2;
+ };
+
+ if (*s == RDELIM) {
+ s++;
+ while (isspace( *s)) s++;
+ if (*s != '\0')
+ elog(WARN, "Bad polygon external representation '%s'", str);
+
+ } else {
+ elog(WARN, "Bad polygon external representation '%s'", str);
+ };
+
+ } else {
+#endif
+ if ((! path_decode(FALSE, npts, str, &isopen, &s, &(poly->p[0])))
+ || (*s != '\0'))
+ elog (WARN, "Bad polygon external representation '%s'",str);
+
+#if OLD_FORMAT_IN
+ };
+#endif;
+
make_bound_box(poly);
- return (poly);
-}
-/*-------------------------------------------------------------
- * poly_pt_count - count the number of points specified in the
- * polygon.
- *-------------------------------------------------------------*/
-long poly_pt_count(char *s, char delim)
-{
- long total = 0;
-
- if (*s++ != LDELIM) /* no left delimeter */
- return (long) -1;
-
- while (*s && (*s != RDELIM))
- {
- while (*s && (*s != delim))
- s++;
- total++; /* found one */
- if (*s)
- s++; /* bump s past the delimiter */
- }
-
- /* if there was no right delimiter OR an odd number of points */
-
- if ((*(s-1) != RDELIM) || ((total%2) != 0))
- return (long) -1;
-
- return (total/2);
-}
+ return( poly);
+} /* poly_in() */
/*---------------------------------------------------------------
* poly_out - convert internal POLYGON representation to the
- * character string format "(f8,f8,f8,f8,...f8)"
+ * character string format "((f8,f8),...,(f8,f8))"
+ * also support old format "(f8,f8,...,f8,f8)"
*---------------------------------------------------------------*/
char *poly_out(POLYGON *poly)
{
+#if OLD_FORMAT_OUT
int i;
- double *xp, *yp;
- char *output, *outptr;
-
- /*-----------------------------------------------------
- * Get enough space for "(f8,f8,f8,f8,...,f8)"
- * which P_MAXDIG+1 for each coordinate plus 2
- * for parens and 1 for the null
- *-----------------------------------------------------*/
- output = (char *)PALLOC(2*(P_MAXDIG+1)*poly->npts + 3);
- outptr = output;
-
- if (!output)
- elog(WARN, "Memory allocation failed, can't output polygon");
-
- *outptr++ = LDELIM;
-
- xp = (double *) poly->pts;
- yp = (double *) (poly->pts + (poly->npts * sizeof(double)));
-
- sprintf(outptr, "%*g,%*g", P_MAXDIG, *xp++, P_MAXDIG, *yp++);
- outptr += (2*P_MAXDIG + 1);
-
- for (i=1; i<poly->npts; i++,xp++,yp++)
- {
- sprintf(outptr, ",%*g,%*g", P_MAXDIG, *xp, P_MAXDIG, *yp);
- outptr += 2*(P_MAXDIG + 1);
- }
- *outptr++ = RDELIM;
- *outptr = '\0';
- return (output);
-}
-
-/*-------------------------------------------------------
- * Find the largest coordinate out of n coordinates
- *-------------------------------------------------------*/
-double poly_max(double *coords, int ncoords)
-{
- double max;
-
- max = *coords++;
- ncoords--;
- while (ncoords--)
- {
- if (*coords > max)
- max = *coords;
- coords++;
- }
- return max;
+ char *result, *cp;
+#endif
+
+ if (!PointerIsValid((char *)poly))
+ return NULL;
+
+#if OLD_FORMAT_OUT
+ if (!PointerIsValid(result = (char *)PALLOC(poly->npts*(P_MAXLEN+3)+2)))
+ elog(WARN, "Memory allocation failed, can't output polygon", NULL);
+
+ cp = result;
+ *cp++ = LDELIM;
+
+ for (i=0; i<poly->npts; i++) {
+ if (! pair_encode( poly->p[i].x, poly->p[i].y, cp))
+ elog (WARN, "Unable to format polygon", NULL);
+ cp += strlen(cp);
+ *cp++ = DELIM;
+ };
+ *(cp-1) = RDELIM;
+ *cp = '\0';
+ return(result);
+#else
+ return( path_encode( TRUE, poly->npts, &(poly->p[0])));
+#endif
}
-/*-------------------------------------------------------
- * Find the smallest coordinate out of n coordinates
- *-------------------------------------------------------*/
-double poly_min(double *coords, int ncoords)
-{
- double min;
-
- min = *coords++;
- ncoords--;
- while (ncoords--)
- {
- if (*coords < min)
- min = *coords;
- coords++;
- }
- return min;
-}
/*-------------------------------------------------------
* Is polygon A strictly left of polygon B? i.e. is
* the right most point of A left of the left most point
* of B?
*-------------------------------------------------------*/
-long poly_left(POLYGON *polya, POLYGON *polyb)
+bool poly_left(POLYGON *polya, POLYGON *polyb)
{
- double right, left;
-
- if (polya->npts > 0)
- right = poly_max((double *)polya->pts, polya->npts);
- else
- right = polya->boundbox.xh;
- if (polyb->npts > 0)
- left = poly_min((double *)polyb->pts, polyb->npts);
- else
- left = polyb->boundbox.xl;
-
- return (right < left);
+ return (polya->boundbox.high.x < polyb->boundbox.low.x);
}
/*-------------------------------------------------------
@@ -1839,20 +2160,9 @@ long poly_left(POLYGON *polya, POLYGON *polyb)
* the left most point of A left of the right most point
* of B?
*-------------------------------------------------------*/
-long poly_overleft(POLYGON *polya, POLYGON *polyb)
+bool poly_overleft(POLYGON *polya, POLYGON *polyb)
{
- double left, right;
-
- if (polya->npts > 0)
- left = poly_min((double *)polya->pts, polya->npts);
- else
- left = polya->boundbox.xl;
- if (polyb->npts > 0)
- right = poly_max((double *)polyb->pts, polyb->npts);
- else
- right = polyb->boundbox.xh;
-
- return (left <= right);
+ return (polya->boundbox.low.x <= polyb->boundbox.high.x);
}
/*-------------------------------------------------------
@@ -1860,20 +2170,9 @@ long poly_overleft(POLYGON *polya, POLYGON *polyb)
* the left most point of A right of the right most point
* of B?
*-------------------------------------------------------*/
-long poly_right(POLYGON *polya, POLYGON *polyb)
+bool poly_right(POLYGON *polya, POLYGON *polyb)
{
- double right, left;
-
- if (polya->npts > 0)
- left = poly_min((double *)polya->pts, polya->npts);
- else
- left = polya->boundbox.xl;
- if (polyb->npts > 0)
- right = poly_max((double *)polyb->pts, polyb->npts);
- else
- right = polyb->boundbox.xh;
-
- return (left > right);
+ return( polya->boundbox.low.x > polyb->boundbox.high.x);
}
/*-------------------------------------------------------
@@ -1881,50 +2180,34 @@ long poly_right(POLYGON *polya, POLYGON *polyb)
* the right most point of A right of the left most point
* of B?
*-------------------------------------------------------*/
-long poly_overright(POLYGON *polya, POLYGON *polyb)
+bool poly_overright(POLYGON *polya, POLYGON *polyb)
{
- double right, left;
-
- if (polya->npts > 0)
- right = poly_max((double *)polya->pts, polya->npts);
- else
- right = polya->boundbox.xh;
- if (polyb->npts > 0)
- left = poly_min((double *)polyb->pts, polyb->npts);
- else
- left = polyb->boundbox.xl;
-
- return (right > left);
+ return( polya->boundbox.high.x > polyb->boundbox.low.x);
}
/*-------------------------------------------------------
* Is polygon A the same as polygon B? i.e. are all the
* points the same?
*-------------------------------------------------------*/
-long poly_same(POLYGON *polya, POLYGON *polyb)
+bool poly_same(POLYGON *polya, POLYGON *polyb)
{
int i;
- double *axp, *bxp; /* point to x coordinates for a and b */
-
if (polya->npts != polyb->npts)
- return 0;
-
- axp = (double *)polya->pts;
- bxp = (double *)polyb->pts;
-
- for (i=0; i<polya->npts; axp++, bxp++, i++)
- {
- if (*axp != *bxp)
- return 0;
- }
- return 1;
+ return FALSE;
+
+ for (i = 0; i < polya->npts; i++) {
+ if ((polya->p[i].x != polyb->p[i].x)
+ || (polya->p[i].y != polyb->p[i].y))
+ return FALSE;
+ };
+ return TRUE;
}
/*-----------------------------------------------------------------
* Determine if polygon A overlaps polygon B by determining if
* their bounding boxes overlap.
*-----------------------------------------------------------------*/
-long poly_overlap(POLYGON *polya, POLYGON *polyb)
+bool poly_overlap(POLYGON *polya, POLYGON *polyb)
{
return box_overlap(&(polya->boundbox), &(polyb->boundbox));
}
@@ -1933,7 +2216,7 @@ long poly_overlap(POLYGON *polya, POLYGON *polyb)
* Determine if polygon A contains polygon B by determining if A's
* bounding box contains B's bounding box.
*-----------------------------------------------------------------*/
-long poly_contain(POLYGON *polya, POLYGON *polyb)
+bool poly_contain(POLYGON *polya, POLYGON *polyb)
{
return box_contain(&(polya->boundbox), &(polyb->boundbox));
}
@@ -1942,7 +2225,995 @@ long poly_contain(POLYGON *polya, POLYGON *polyb)
* Determine if polygon A is contained by polygon B by determining
* if A's bounding box is contained by B's bounding box.
*-----------------------------------------------------------------*/
-long poly_contained(POLYGON *polya, POLYGON *polyb)
+bool poly_contained(POLYGON *polya, POLYGON *polyb)
{
return box_contained(&(polya->boundbox), &(polyb->boundbox));
}
+
+
+/***********************************************************************
+ **
+ ** Routines for 2D points.
+ **
+ ***********************************************************************/
+
+Point *
+point(float8 *x, float8 *y)
+{
+ if (! (PointerIsValid(x) && PointerIsValid(y)))
+ return(NULL);
+
+ return(point_construct(*x, *y));
+} /* point() */
+
+
+Point *
+point_add(Point *p1, Point *p2)
+{
+ Point *result;
+
+ if (! (PointerIsValid(p1) && PointerIsValid(p2)))
+ return(NULL);
+
+ if (!PointerIsValid(result = PALLOCTYPE(Point)))
+ elog(WARN, "Memory allocation failed, can't add points",NULL);
+
+ result->x = (p1->x + p2->x);
+ result->y = (p1->y + p2->y);
+
+ return(result);
+} /* point_add() */
+
+Point *
+point_sub(Point *p1, Point *p2)
+{
+ Point *result;
+
+ if (! (PointerIsValid(p1) && PointerIsValid(p2)))
+ return(NULL);
+
+ if (!PointerIsValid(result = PALLOCTYPE(Point)))
+ elog(WARN, "Memory allocation failed, can't add points",NULL);
+
+ result->x = (p1->x - p2->x);
+ result->y = (p1->y - p2->y);
+
+ return(result);
+} /* point_sub() */
+
+Point *
+point_mul(Point *p1, Point *p2)
+{
+ Point *result;
+
+ if (! (PointerIsValid(p1) && PointerIsValid(p2)))
+ return(NULL);
+
+ if (!PointerIsValid(result = PALLOCTYPE(Point)))
+ elog(WARN, "Memory allocation failed, can't multiply points",NULL);
+
+ result->x = (p1->x*p2->x) - (p1->y*p2->y);
+ result->y = (p1->x*p2->y) + (p1->y*p2->x);
+
+ return(result);
+} /* point_mul() */
+
+Point *
+point_div(Point *p1, Point *p2)
+{
+ Point *result;
+ double div;
+
+ if (! (PointerIsValid(p1) && PointerIsValid(p2)))
+ return(NULL);
+
+ if (!PointerIsValid(result = PALLOCTYPE(Point)))
+ elog(WARN, "Memory allocation failed, can't multiply path",NULL);
+
+ div = (p2->x*p2->x) + (p2->y*p2->y);
+
+ result->x = ((p1->x*p2->x) + (p1->y*p2->y)) / div;
+ result->y = ((p2->x*p1->y) - (p2->y*p1->x)) / div;
+
+ return(result);
+} /* point_div() */
+
+
+/***********************************************************************
+ **
+ ** Routines for 2D boxes.
+ **
+ ***********************************************************************/
+
+BOX *
+box(Point *p1, Point *p2)
+{
+ BOX *result;
+
+ if (! (PointerIsValid(p1) && PointerIsValid(p2)))
+ return(NULL);
+
+ result = box_construct( p1->x, p2->x, p1->y, p2->y);
+
+ return(result);
+} /* box() */
+
+BOX *
+box_add(BOX *box, Point *p)
+{
+ BOX *result;
+
+ if (! (PointerIsValid(box) && PointerIsValid(p)))
+ return(NULL);
+
+ result = box_construct( (box->high.x + p->x), (box->low.x + p->x),
+ (box->high.y + p->y), (box->low.y + p->y));
+
+ return(result);
+} /* box_add() */
+
+BOX *
+box_sub(BOX *box, Point *p)
+{
+ BOX *result;
+
+ if (! (PointerIsValid(box) && PointerIsValid(p)))
+ return(NULL);
+
+ result = box_construct( (box->high.x - p->x), (box->low.x - p->x),
+ (box->high.y - p->y), (box->low.y - p->y));
+
+ return(result);
+} /* box_sub() */
+
+BOX *
+box_mul(BOX *box, Point *p)
+{
+ BOX *result;
+ Point *high, *low;
+
+ if (! (PointerIsValid(box) && PointerIsValid(p)))
+ return(NULL);
+
+ high = point_mul( &box->high, p);
+ low = point_mul( &box->low, p);
+
+ result = box_construct( high->x, low->x, high->y, low->y);
+ PFREE( high);
+ PFREE( low);
+
+ return(result);
+} /* box_mul() */
+
+BOX *
+box_div(BOX *box, Point *p)
+{
+ BOX *result;
+ Point *high, *low;
+
+ if (! (PointerIsValid(box) && PointerIsValid(p)))
+ return(NULL);
+
+ high = point_div( &box->high, p);
+ low = point_div( &box->low, p);
+
+ result = box_construct( high->x, low->x, high->y, low->y);
+ PFREE( high);
+ PFREE( low);
+
+ return(result);
+} /* box_div() */
+
+
+/***********************************************************************
+ **
+ ** Routines for 2D lines.
+ ** Lines are not intended to be used as ADTs per se,
+ ** but their ops are useful tools for other ADT ops. Thus,
+ ** there are few relops.
+ **
+ ***********************************************************************/
+
+
+/***********************************************************************
+ **
+ ** Routines for 2D paths.
+ **
+ ***********************************************************************/
+
+POLYGON *path_poly(PATH *path);
+
+/* path_add()
+ * Concatenate two paths (only if they are both open).
+ */
+PATH *
+path_add(PATH *p1, PATH *p2)
+{
+ PATH *result;
+ int size;
+ int i;
+
+ if (! (PointerIsValid(p1) && PointerIsValid(p2))
+ || p1->closed || p2->closed)
+ return(NULL);
+
+ size = offsetof(PATH, p[0]) + (sizeof(p1->p[0]) * (p1->npts+p2->npts));
+ if (!PointerIsValid(result = PALLOC(size)))
+ elog(WARN, "Memory allocation failed, can't add paths",NULL);
+
+ result->size = size;
+ result->npts = (p1->npts+p2->npts);
+ result->closed = p1->closed;
+
+ for (i=0; i<p1->npts; i++) {
+ result->p[i].x = p1->p[i].x;
+ result->p[i].y = p1->p[i].y;
+ };
+ for (i=0; i<p2->npts; i++) {
+ result->p[i+p1->npts].x = p2->p[i].x;
+ result->p[i+p1->npts].y = p2->p[i].y;
+ };
+
+ return(result);
+} /* path_add() */
+
+/* path_add_pt()
+ * Translation operator.
+ */
+PATH *
+path_add_pt(PATH *path, Point *point)
+{
+ PATH *result;
+ int i;
+
+ if (! (PointerIsValid(path) && PointerIsValid(point)))
+ return(NULL);
+
+ if (! PointerIsValid(result = path_copy(path)))
+ elog(WARN, "Memory allocation failed, can't add path",NULL);
+
+ for (i=0; i<path->npts; i++) {
+ result->p[i].x += point->x;
+ result->p[i].y += point->y;
+ };
+
+ return(result);
+} /* path_add_pt() */
+
+PATH *
+path_sub_pt(PATH *path, Point *point)
+{
+ PATH *result;
+ int i;
+
+ if (! (PointerIsValid(path) && PointerIsValid(point)))
+ return(NULL);
+
+ if (! PointerIsValid(result = path_copy(path)))
+ elog(WARN, "Memory allocation failed, can't subtract path",NULL);
+
+ for (i=0; i<path->npts; i++) {
+ result->p[i].x -= point->x;
+ result->p[i].y -= point->y;
+ };
+
+ return(result);
+} /* path_sub_pt() */
+
+
+/* path_mul_pt()
+ * Rotation and scaling operators.
+ */
+PATH *
+path_mul_pt(PATH *path, Point *point)
+{
+ PATH *result;
+ Point *p;
+ int i;
+
+ if (! (PointerIsValid(path) && PointerIsValid(point)))
+ return(NULL);
+
+ if (! PointerIsValid(result = path_copy(path)))
+ elog(WARN, "Memory allocation failed, can't multiply path",NULL);
+
+ for (i=0; i<path->npts; i++) {
+ p = point_mul( &path->p[i], point);
+ result->p[i].x = p->x;
+ result->p[i].y = p->y;
+ PFREE(p);
+ };
+
+ return(result);
+} /* path_mul_pt() */
+
+PATH *
+path_div_pt(PATH *path, Point *point)
+{
+ PATH *result;
+ Point *p;
+ int i;
+
+ if (! (PointerIsValid(path) && PointerIsValid(point)))
+ return(NULL);
+
+ if (! PointerIsValid(result = path_copy(path)))
+ elog(WARN, "Memory allocation failed, can't divide path",NULL);
+
+ for (i=0; i<path->npts; i++) {
+ p = point_div( &path->p[i], point);
+ result->p[i].x = p->x;
+ result->p[i].y = p->y;
+ PFREE(p);
+ };
+
+ return(result);
+} /* path_div_pt() */
+
+
+POLYGON *path_poly(PATH *path)
+{
+ POLYGON *poly;
+ int size;
+ int i;
+
+ if (!PointerIsValid(path))
+ return(NULL);
+
+ if (!path->closed)
+ elog(WARN, "Open path cannot be converted to polygon",NULL);
+
+ size = offsetof(POLYGON, p[0]) + (sizeof(poly->p[0]) * path->npts);
+ if (!PointerIsValid(poly = PALLOC(size)))
+ elog(WARN, "Memory allocation failed, can't convert path to polygon",NULL);
+
+ poly->size = size;
+ poly->npts = path->npts;
+
+ for (i=0; i<path->npts; i++) {
+ poly->p[i].x = path->p[i].x;
+ poly->p[i].y = path->p[i].y;
+ };
+
+ make_bound_box(poly);
+
+ return(poly);
+} /* path_polygon() */
+
+
+/***********************************************************************
+ **
+ ** Routines for 2D polygons.
+ **
+ ***********************************************************************/
+
+int4
+poly_npoints( POLYGON *poly)
+{
+ if (!PointerIsValid(poly))
+ return(0);
+
+ return(poly->npts);
+} /* poly_npoints() */
+
+BOX *
+poly_box(POLYGON *poly)
+{
+ BOX *box;
+
+ if (!PointerIsValid(poly) || (poly->npts < 1))
+ return(NULL);
+
+ box = box_copy( &poly->boundbox);
+
+ return(box);
+} /* poly_box() */
+
+POLYGON *
+box_poly(BOX *box)
+{
+ POLYGON *poly;
+ int size;
+
+ if (!PointerIsValid(box))
+ return(NULL);
+
+ size = offsetof(POLYGON, p[0]) + (sizeof(poly->p[0]) * 4);
+ if (!PointerIsValid(poly = PALLOC(size)))
+ elog(WARN, "Memory allocation failed, can't convert box to polygon",NULL);
+
+ poly->size = size;
+ poly->npts = 4;
+
+ poly->p[0].x = box->low.x;
+ poly->p[0].y = box->low.y;
+ poly->p[1].x = box->low.x;
+ poly->p[1].y = box->high.y;
+ poly->p[2].x = box->high.x;
+ poly->p[2].y = box->high.y;
+ poly->p[3].x = box->high.x;
+ poly->p[3].y = box->low.y;
+
+ box_fill( &poly->boundbox, box->high.x, box->low.x, box->high.y, box->low.y);
+
+ return(poly);
+} /* box_poly() */
+
+PATH *
+poly_path(POLYGON *poly)
+{
+ PATH *path;
+ int size;
+ int i;
+
+ if (!PointerIsValid(poly) || (poly->npts < 0))
+ return(NULL);
+
+ size = offsetof(PATH, p[0]) + (sizeof(path->p[0]) * poly->npts);
+ if (!PointerIsValid(path = PALLOC(size)))
+ elog(WARN, "Memory allocation failed, can't convert polygon to path",NULL);
+
+ path->size = size;
+ path->npts = poly->npts;
+ path->closed = TRUE;
+
+ for (i=0; i<poly->npts; i++) {
+ path->p[i].x = poly->p[i].x;
+ path->p[i].y = poly->p[i].y;
+ };
+
+ return(path);
+} /* poly_path() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * circle.c--
+ * 2D geometric operations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/geo_ops.c,v 1.3 1997/04/22 17:31:32 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PI
+#define PI 3.1415926536
+#endif
+
+int single_decode(char *str, float8 *x, char **ss);
+int single_encode(float8 x, char *str);
+
+int single_decode(char *str, float8 *x, char **s)
+{
+ char *cp;
+
+ if (!PointerIsValid(str))
+ return(FALSE);
+
+ while (isspace( *str)) str++;
+ *x = strtod( str, &cp);
+#ifdef GEODEBUG
+fprintf( stderr, "single_decode- (%x) try decoding %s to %g\n", (cp-str), str, *x);
+#endif
+ if (cp <= str) return(FALSE);
+ while (isspace( *cp)) cp++;
+
+ if (s != NULL) *s = cp;
+
+ return(TRUE);
+}
+
+int single_encode(float8 x, char *str)
+{
+ (void) sprintf(str, "%.*g", digits8, x);
+ return(TRUE);
+}
+
+
+/***********************************************************************
+ **
+ ** Routines for circles.
+ **
+ ***********************************************************************/
+
+/*----------------------------------------------------------
+ * Formatting and conversion routines.
+ *---------------------------------------------------------*/
+
+/* circle_in - convert a string to internal form.
+ *
+ * External format: (center and radius of circle)
+ * "((f8,f8)<f8>)"
+ * also supports quick entry style "(f8,f8,f8)"
+ */
+CIRCLE *circle_in(char *str)
+{
+ CIRCLE *circle;
+
+ char *s, *cp;
+ int depth = 0;
+
+ if (!PointerIsValid(str))
+ elog (WARN," Bad (null) circle external representation",NULL);
+
+ if (!PointerIsValid(circle = PALLOCTYPE(CIRCLE)))
+ elog(WARN, "Memory allocation failed, can't input circle '%s'",str);
+
+ s = str;
+ while (isspace( *s)) s++;
+ if ((*s == LDELIM_C) || (*s == LDELIM)) {
+ depth++;
+ cp = (s+1);
+ while (isspace( *cp)) cp++;
+ if (*cp == LDELIM) {
+ s = cp;
+ };
+ };
+
+ if (! pair_decode( s, &circle->center.x, &circle->center.y, &s))
+ elog (WARN, "Bad circle external representation '%s'",str);
+
+ if (*s == DELIM) s++;
+ while (isspace( *s)) s++;
+
+ if (! single_decode( s, &circle->radius, &s))
+ elog (WARN, "Bad circle external representation '%s'",str);
+
+ while (depth > 0) {
+ if ((*s == RDELIM)
+ || ((*s == RDELIM_C) && (depth == 1))) {
+ depth--;
+ s++;
+ while (isspace( *s)) s++;
+ } else {
+ elog (WARN, "Bad circle external representation '%s'",str);
+ };
+ };
+
+ if (*s != '\0')
+ elog (WARN, "Bad circle external representation '%s'",str);
+
+ return(circle);
+} /* circle_in() */
+
+/* circle_out - convert a circle to external form.
+ */
+char *circle_out(CIRCLE *circle)
+{
+ char *result;
+ char *cp;
+
+ if (!PointerIsValid(circle))
+ return(NULL);
+
+ if (!PointerIsValid(result = (char *)PALLOC(3*(P_MAXLEN+1)+3)))
+ elog(WARN, "Memory allocation failed, can't output circle", NULL);
+
+ cp = result;
+ *cp++ = LDELIM_C;
+ *cp++ = LDELIM;
+ if (! pair_encode( circle->center.x, circle->center.y, cp))
+ elog (WARN, "Unable to format circle", NULL);
+
+ cp += strlen(cp);
+ *cp++ = RDELIM;
+ *cp++ = DELIM;
+ if (! single_encode( circle->radius, cp))
+ elog (WARN, "Unable to format circle", NULL);
+
+ cp += strlen(cp);
+ *cp++ = RDELIM_C;
+ *cp = '\0';
+
+ return(result);
+} /* circle_out() */
+
+
+/*----------------------------------------------------------
+ * Relational operators for CIRCLEs.
+ * <, >, <=, >=, and == are based on circle area.
+ *---------------------------------------------------------*/
+
+/* circles identical?
+ */
+bool circle_same(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPeq(circle1->radius,circle2->radius)
+ && FPeq(circle1->center.x,circle2->center.x)
+ && FPeq(circle1->center.y,circle2->center.y));
+}
+
+/* circle_overlap - does circle1 overlap circle2?
+ */
+bool circle_overlap(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPle(point_dt(&circle1->center,&circle2->center),(circle1->radius+circle2->radius)));
+}
+
+/* circle_overleft - is the right edge of circle1 to the left of
+ * the right edge of circle2?
+ */
+bool circle_overleft(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPle((circle1->center.x+circle1->radius),(circle2->center.x+circle2->radius)));
+}
+
+/* circle_left - is circle1 strictly left of circle2?
+ */
+bool circle_left(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPle((circle1->center.x+circle1->radius),(circle2->center.x-circle2->radius)));
+}
+
+/* circle_right - is circle1 strictly right of circle2?
+ */
+bool circle_right(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPge((circle1->center.x-circle1->radius),(circle2->center.x+circle2->radius)));
+}
+
+/* circle_overright - is the left edge of circle1 to the right of
+ * the left edge of circle2?
+ */
+bool circle_overright(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPge((circle1->center.x-circle1->radius),(circle2->center.x-circle2->radius)));
+}
+
+/* circle_contained - is circle1 contained by circle2?
+ */
+bool circle_contained(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPle((point_dt(&circle1->center,&circle2->center)+circle1->radius),circle2->radius));
+}
+
+/* circle_contain - does circle1 contain circle2?
+ */
+bool circle_contain(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPle((point_dt(&circle1->center,&circle2->center)+circle2->radius),circle1->radius));
+}
+
+
+/* circle_positionop -
+ * is circle1 entirely {above,below} circle2?
+ */
+bool circle_below(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPle((circle1->center.y+circle1->radius),(circle2->center.y-circle2->radius)));
+}
+
+bool circle_above(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPge((circle1->center.y-circle1->radius),(circle2->center.y+circle2->radius)));
+}
+
+
+/* circle_relop - is area(circle1) relop area(circle2), within
+ * our accuracy constraint?
+ */
+bool circle_eq(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPeq(circle_ar(circle1), circle_ar(circle2)) );
+} /* circle_eq() */
+
+bool circle_ne(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( !circle_eq(circle1, circle2));
+} /* circle_ne() */
+
+bool circle_lt(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPlt(circle_ar(circle1), circle_ar(circle2)) );
+} /* circle_lt() */
+
+bool circle_gt(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPgt(circle_ar(circle1), circle_ar(circle2)) );
+} /* circle_gt() */
+
+bool circle_le(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPle(circle_ar(circle1), circle_ar(circle2)) );
+} /* circle_le() */
+
+bool circle_ge(CIRCLE *circle1, CIRCLE *circle2)
+{
+ return( FPge(circle_ar(circle1), circle_ar(circle2)) );
+} /* circle_ge() */
+
+
+/*----------------------------------------------------------
+ * "Arithmetic" operators on circles.
+ * circle_foo returns foo as an object (pointer) that
+ can be passed between languages.
+ * circle_xx is an internal routine which returns the
+ * actual value.
+ *---------------------------------------------------------*/
+
+CIRCLE *circle_copy(CIRCLE *circle);
+
+CIRCLE *
+circle_copy(CIRCLE *circle)
+{
+ CIRCLE *result;
+
+ if (!PointerIsValid(circle))
+ return NULL;
+
+ if (!PointerIsValid(result = PALLOCTYPE(CIRCLE)))
+ elog(WARN, "Memory allocation failed, can't copy circle",NULL);
+
+ memmove((char *) result, (char *) circle, sizeof(CIRCLE));
+ return(result);
+} /* circle_copy() */
+
+
+/* circle_add_pt()
+ * Translation operator.
+ */
+CIRCLE *
+circle_add_pt(CIRCLE *circle, Point *point)
+{
+ CIRCLE *result;
+
+ if (!PointerIsValid(circle) && !PointerIsValid(point))
+ return(NULL);
+
+ if (! PointerIsValid(result = circle_copy(circle)))
+ elog(WARN, "Memory allocation failed, can't add circle",NULL);
+
+ result->center.x += point->x;
+ result->center.y += point->y;
+
+ return(result);
+} /* circle_add_pt() */
+
+CIRCLE *
+circle_sub_pt(CIRCLE *circle, Point *point)
+{
+ CIRCLE *result;
+
+ if (!PointerIsValid(circle) && !PointerIsValid(point))
+ return(NULL);
+
+ if (! PointerIsValid(result = circle_copy(circle)))
+ elog(WARN, "Memory allocation failed, can't subtract circle",NULL);
+
+ result->center.x -= point->x;
+ result->center.y -= point->y;
+
+ return(result);
+} /* circle_sub_pt() */
+
+
+/* circle_mul_pt()
+ * Rotation and scaling operators.
+ */
+CIRCLE *
+circle_mul_pt(CIRCLE *circle, Point *point)
+{
+ CIRCLE *result;
+ Point *p;
+
+ if (!PointerIsValid(circle) && !PointerIsValid(point))
+ return(NULL);
+
+ if (! PointerIsValid(result = circle_copy(circle)))
+ elog(WARN, "Memory allocation failed, can't multiply circle",NULL);
+
+ p = point_mul( &circle->center, point);
+ result->center.x = p->x;
+ result->center.y = p->y;
+ PFREE(p);
+ result->radius *= HYPOT( point->x, point->y);
+
+ return(result);
+} /* circle_mul_pt() */
+
+CIRCLE *
+circle_div_pt(CIRCLE *circle, Point *point)
+{
+ CIRCLE *result;
+ Point *p;
+
+ if (!PointerIsValid(circle) && !PointerIsValid(point))
+ return(NULL);
+
+ if (! PointerIsValid(result = circle_copy(circle)))
+ elog(WARN, "Memory allocation failed, can't add circle",NULL);
+
+ p = point_div( &circle->center, point);
+ result->center.x = p->x;
+ result->center.y = p->y;
+ PFREE(p);
+ result->radius /= HYPOT( point->x, point->y);
+
+ return(result);
+} /* circle_div_pt() */
+
+
+/* circle_area - returns the area of the circle.
+ */
+double *circle_area(CIRCLE *circle)
+{
+ double *result;
+
+ result = PALLOCTYPE(double);
+ *result = circle_ar(circle);
+
+ return(result);
+}
+
+
+/* circle_diameter - returns the diameter of the circle.
+ */
+double *circle_diameter(CIRCLE *circle)
+{
+ double *result;
+
+ result = PALLOCTYPE(double);
+ *result = (2*circle->radius);
+
+ return(result);
+}
+
+
+/* circle_radius - returns the radius of the circle.
+ */
+double *circle_radius(CIRCLE *circle)
+{
+ double *result;
+
+ result = PALLOCTYPE(double);
+ *result = circle->radius;
+
+ return(result);
+}
+
+
+/* circle_distance - returns the distance between the
+ * center points of two circlees.
+ */
+double *circle_distance(CIRCLE *circle1, CIRCLE *circle2)
+{
+ double *result;
+
+ result = PALLOCTYPE(double);
+ *result = point_dt(&circle1->center,&circle2->center);
+
+ return(result);
+}
+
+
+/* circle_center - returns the center point of the circle.
+ */
+Point *circle_center(CIRCLE *circle)
+{
+ Point *result;
+
+ result = PALLOCTYPE(Point);
+ result->x = circle->center.x;
+ result->y = circle->center.y;
+
+ return(result);
+}
+
+
+/* circle_ar - returns the area of the circle.
+ */
+double circle_ar(CIRCLE *circle)
+{
+ return(PI*(circle->radius*circle->radius));
+}
+
+
+/* circle_dt - returns the distance between the
+ * center points of two circlees.
+ */
+double circle_dt(CIRCLE *circle1, CIRCLE *circle2)
+{
+ double result;
+
+ result = point_dt(&circle1->center,&circle2->center);
+
+ return(result);
+}
+
+
+/*----------------------------------------------------------
+ * Conversion operators.
+ *---------------------------------------------------------*/
+
+CIRCLE *circle(Point *center, float8 *radius)
+{
+ CIRCLE *result;
+
+ if (! (PointerIsValid(center) && PointerIsValid(radius)))
+ return(NULL);
+
+ if (!PointerIsValid(result = PALLOCTYPE(CIRCLE)))
+ elog(WARN, "Memory allocation failed, can't convert point to circle",NULL);
+
+ result->center.x = center->x;
+ result->center.y = center->y;
+ result->radius = *radius;
+
+ return(result);
+}
+
+POLYGON *circle_poly(int npts, CIRCLE *circle)
+{
+ POLYGON *poly;
+ int size;
+ int i;
+ double angle;
+
+ if (!PointerIsValid(circle))
+ return(NULL);
+
+ if (FPzero(circle->radius) || (npts <= 2))
+ elog (WARN, "Unable to convert circle to polygon", NULL);
+
+ size = offsetof(POLYGON, p[0]) + (sizeof(poly->p[0]) * npts);
+ if (!PointerIsValid(poly = (POLYGON *) PALLOC(size)))
+ elog(WARN, "Memory allocation failed, can't convert circle to polygon",NULL);
+
+ memset((char *) poly, 0, size); /* zero any holes */
+ poly->size = size;
+ poly->npts = npts;
+
+ for (i=0;i<npts;i++) {
+ angle = i*(2*PI/npts);
+ poly->p[i].x = circle->center.x - (circle->radius*cos(angle));
+ poly->p[i].y = circle->center.y + (circle->radius*sin(angle));
+ };
+
+ make_bound_box(poly);
+
+ return(poly);
+}
+
+/* poly_circle - convert polygon to circle
+ *
+ * XXX This algorithm should use weighted means of line segments
+ * rather than straight average values of points - tgl 97/01/21.
+ */
+CIRCLE *poly_circle(POLYGON *poly)
+{
+ CIRCLE *circle;
+ int i;
+
+ if (!PointerIsValid(poly))
+ return(NULL);
+
+ if (poly->npts <= 2)
+ elog (WARN, "Unable to convert polygon to circle", NULL);
+
+ if (!PointerIsValid(circle = PALLOCTYPE(CIRCLE)))
+ elog(WARN, "Memory allocation failed, can't convert polygon to circle",NULL);
+
+ circle->center.x = 0;
+ circle->center.y = 0;
+ circle->radius = 0;
+
+ for (i=0;i<poly->npts;i++) {
+ circle->center.x += poly->p[i].x;
+ circle->center.y += poly->p[i].y;
+ };
+ circle->center.x /= poly->npts;
+ circle->center.y /= poly->npts;
+
+ for (i=0;i<poly->npts;i++) {
+ circle->radius += point_dt( &poly->p[i], &circle->center);
+ };
+ circle->radius /= poly->npts;
+
+ if (FPzero(circle->radius))
+ elog (WARN, "Unable to convert polygon to circle", NULL);
+
+ return(circle);
+}