/*-------------------------------------------------------------------------
*
* geo_ops . c
* 2 D geometric operations
*
* This module implements the geometric functions and operators . The
* geometric types are ( from simple to more complicated ) :
*
* - point
* - line
* - line segment
* - box
* - circle
* - polygon
*
* Portions Copyright ( c ) 1996 - 2025 , PostgreSQL Global Development Group
* Portions Copyright ( c ) 1994 , Regents of the University of California
*
*
* IDENTIFICATION
* src / backend / utils / adt / geo_ops . c
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres.h"
#include <math.h>
#include <limits.h>
#include <float .h>
#include <ctype.h>
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/miscnodes.h"
#include "utils/float.h"
#include "utils/fmgrprotos.h"
#include "utils/geo_decls.h"
#include "varatt.h"
/*
* * Type constructors have this form :
* void type_construct ( Type * result , . . . ) ;
*
* * Operators commonly have signatures such as
* void type1_operator_type2 ( Type * result , Type1 * obj1 , Type2 * obj2 ) ;
*
* Common operators are :
* * Intersection point :
* bool type1_interpt_type2 ( Point * result , Type1 * obj1 , Type2 * obj2 ) ;
* Return whether the two objects intersect . If * result is not NULL ,
* it is set to the intersection point .
*
* * Containment :
* bool type1_contain_type2 ( Type1 * obj1 , Type2 * obj2 ) ;
* Return whether obj1 contains obj2 .
* bool type1_contain_type2 ( Type1 * contains_obj , Type1 * contained_obj ) ;
* Return whether obj1 contains obj2 ( used when types are the same )
*
* * Distance of closest point in or on obj1 to obj2 :
* float8 type1_closept_type2 ( Point * result , Type1 * obj1 , Type2 * obj2 ) ;
* Returns the shortest distance between two objects . If * result is not
* NULL , it is set to the closest point in or on obj1 to obj2 .
*
* These functions may be used to implement multiple SQL - level operators . For
* example , determining whether two lines are parallel is done by checking
* whether they don ' t intersect .
*/
/*
* Internal routines
*/
enum path_delim
{
PATH_NONE, PATH_OPEN, PATH_CLOSED
};
/* Routines for points */
static inline void point_construct(Point *result, float8 x, float8 y);
static inline void point_add_point(Point *result, Point *pt1, Point *pt2);
static inline void point_sub_point(Point *result, Point *pt1, Point *pt2);
static inline void point_mul_point(Point *result, Point *pt1, Point *pt2);
static inline void point_div_point(Point *result, Point *pt1, Point *pt2);
static inline bool point_eq_point(Point *pt1, Point *pt2);
static inline float8 point_dt(Point *pt1, Point *pt2);
static inline float8 point_sl(Point *pt1, Point *pt2);
static int point_inside(Point *p, int npts, Point *plist);
/* Routines for lines */
static inline void line_construct(LINE *result, Point *pt, float8 m);
static inline float8 line_sl(LINE *line);
static inline float8 line_invsl(LINE *line);
static bool line_interpt_line(Point *result, LINE *l1, LINE *l2);
static bool line_contain_point(LINE *line, Point *point);
static float8 line_closept_point(Point *result, LINE *line, Point *point);
/* Routines for line segments */
static inline void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
static inline float8 lseg_sl(LSEG *lseg);
static inline float8 lseg_invsl(LSEG *lseg);
static bool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line);
static bool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2);
static int lseg_crossing(float8 x, float8 y, float8 prev_x, float8 prev_y);
static bool lseg_contain_point(LSEG *lseg, Point *pt);
static float8 lseg_closept_point(Point *result, LSEG *lseg, Point *pt);
static float8 lseg_closept_line(Point *result, LSEG *lseg, LINE *line);
static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
/* Routines for boxes */
static inline void box_construct(BOX *result, Point *pt1, Point *pt2);
static void box_cn(Point *center, BOX *box);
static bool box_ov(BOX *box1, BOX *box2);
static float8 box_ar(BOX *box);
static float8 box_ht(BOX *box);
static float8 box_wd(BOX *box);
static bool box_contain_point(BOX *box, Point *point);
static bool box_contain_box(BOX *contains_box, BOX *contained_box);
static bool box_contain_lseg(BOX *box, LSEG *lseg);
static bool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg);
static float8 box_closept_point(Point *result, BOX *box, Point *pt);
static float8 box_closept_lseg(Point *result, BOX *box, LSEG *lseg);
/* Routines for circles */
static float8 circle_ar(CIRCLE *circle);
/* Routines for polygons */
static void make_bound_box(POLYGON *poly);
static void poly_to_circle(CIRCLE *result, POLYGON *poly);
static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
static bool plist_same(int npts, Point *p1, Point *p2);
static float8 dist_ppoly_internal(Point *pt, POLYGON *poly);
/* Routines for encoding and decoding */
static bool single_decode(char *num, float8 *x, char **endptr_p,
const char *type_name, const char *orig_string,
Node *escontext);
static void single_encode(float8 x, StringInfo str);
static bool pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
const char *type_name, const char *orig_string,
Node *escontext);
static void pair_encode(float8 x, float8 y, StringInfo str);
static int pair_count(char *s, char delim);
static bool path_decode(char *str, bool opentype, int npts, Point *p,
bool *isopen, char **endptr_p,
const char *type_name, const char *orig_string,
Node *escontext);
static char *path_encode(enum path_delim path_delim, int npts, Point *pt);
/*
* 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 LDELIM_EP '['
#define RDELIM_EP ']'
#define LDELIM_C '<'
#define RDELIM_C '>'
#define LDELIM_L '{'
#define RDELIM_L '}'
/*
* 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
*/
static bool
single_decode(char *num, float8 *x, char **endptr_p,
const char *type_name, const char *orig_string,
Node *escontext)
{
*x = float8in_internal(num, endptr_p, type_name, orig_string, escontext);
return (!SOFT_ERROR_OCCURRED(escontext));
} /* single_decode() */
static void
single_encode(float8 x, StringInfo str)
{
char *xstr = float8out_internal(x);
appendStringInfoString(str, xstr);
pfree(xstr);
} /* single_encode() */
static bool
pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
const char *type_name, const char *orig_string,
Node *escontext)
{
bool has_delim;
while (isspace((unsigned char ) *str))
str++;
if ((has_delim = (*str == LDELIM)))
str++;
if (!single_decode(str, x, &str, type_name, orig_string, escontext))
return false ;
if (*str++ != DELIM)
goto fail;
if (!single_decode(str, y, &str, type_name, orig_string, escontext))
return false ;
if (has_delim)
{
if (*str++ != RDELIM)
goto fail;
while (isspace((unsigned char ) *str))
str++;
}
/* report stopping point if wanted, else complain if not end of string */
if (endptr_p)
*endptr_p = str;
else if (*str != '\0' )
goto fail;
return true ;
fail:
ereturn(escontext, false ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \" %s\"" ,
type_name, orig_string)));
}
static void
pair_encode(float8 x, float8 y, StringInfo str)
{
char *xstr = float8out_internal(x);
char *ystr = float8out_internal(y);
appendStringInfo(str, "%s,%s" , xstr, ystr);
pfree(xstr);
pfree(ystr);
}
static bool
path_decode(char *str, bool opentype, int npts, Point *p,
bool *isopen, char **endptr_p,
const char *type_name, const char *orig_string,
Node *escontext)
{
int depth = 0 ;
char *cp;
int i;
while (isspace((unsigned char ) *str))
str++;
if ((*isopen = (*str == LDELIM_EP)))
{
/* no open delimiter allowed? */
if (!opentype)
goto fail;
depth++;
str++;
}
else if (*str == LDELIM)
{
cp = (str + 1 );
while (isspace((unsigned char ) *cp))
cp++;
if (*cp == LDELIM)
{
depth++;
str = cp;
}
else if (strrchr(str, LDELIM) == str)
{
depth++;
str = cp;
}
}
for (i = 0 ; i < npts; i++)
{
if (!pair_decode(str, &(p->x), &(p->y), &str, type_name, orig_string,
escontext))
return false ;
if (*str == DELIM)
str++;
p++;
}
while (depth > 0 )
{
if (*str == RDELIM || (*str == RDELIM_EP && *isopen && depth == 1 ))
{
depth--;
str++;
while (isspace((unsigned char ) *str))
str++;
}
else
goto fail;
}
/* report stopping point if wanted, else complain if not end of string */
if (endptr_p)
*endptr_p = str;
else if (*str != '\0' )
goto fail;
return true ;
fail:
ereturn(escontext, false ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \" %s\"" ,
type_name, orig_string)));
} /* path_decode() */
static char *
path_encode(enum path_delim path_delim, int npts, Point *pt)
{
StringInfoData str;
int i;
initStringInfo(&str);
switch (path_delim)
{
case PATH_CLOSED:
appendStringInfoChar(&str, LDELIM);
break ;
case PATH_OPEN:
appendStringInfoChar(&str, LDELIM_EP);
break ;
case PATH_NONE:
break ;
}
for (i = 0 ; i < npts; i++)
{
if (i > 0 )
appendStringInfoChar(&str, DELIM);
appendStringInfoChar(&str, LDELIM);
pair_encode(pt->x, pt->y, &str);
appendStringInfoChar(&str, RDELIM);
pt++;
}
switch (path_delim)
{
case PATH_CLOSED:
appendStringInfoChar(&str, RDELIM);
break ;
case PATH_OPEN:
appendStringInfoChar(&str, RDELIM_EP);
break ;
case PATH_NONE:
break ;
}
return str.data;
} /* 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
*-------------------------------------------------------------*/
static 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 ;
}
/***********************************************************************
* *
* * Routines for two - dimensional boxes .
* *
***********************************************************************/
/*----------------------------------------------------------
* Formatting and conversion routines .
*---------------------------------------------------------*/
/* box_in - convert a string to internal form.
*
* External format : ( two corners of box )
* " ( f8 , f8 ) , ( f8 , f8 ) "
* also supports the older style " ( f8 , f8 , f8 , f8 ) "
*/
Datum
box_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0 );
Node *escontext = fcinfo->context;
BOX *box = (BOX *) palloc(sizeof (BOX));
bool isopen;
float8 x,
y;
if (!path_decode(str, false , 2 , &(box->high), &isopen, NULL, "box" , str,
escontext))
PG_RETURN_NULL();
/* reorder corners if necessary... */
if (float8_lt(box->high.x, box->low.x))
{
x = box->high.x;
box->high.x = box->low.x;
box->low.x = x;
}
if (float8_lt(box->high.y, box->low.y))
{
y = box->high.y;
box->high.y = box->low.y;
box->low.y = y;
}
PG_RETURN_BOX_P(box);
}
/* box_out - convert a box to external form.
*/
Datum
box_out(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
PG_RETURN_CSTRING(path_encode(PATH_NONE, 2 , &(box->high)));
}
/*
* box_recv - converts external binary format to box
*/
Datum
box_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0 );
BOX *box;
float8 x,
y;
box = (BOX *) palloc(sizeof (BOX));
box->high.x = pq_getmsgfloat8(buf);
box->high.y = pq_getmsgfloat8(buf);
box->low.x = pq_getmsgfloat8(buf);
box->low.y = pq_getmsgfloat8(buf);
/* reorder corners if necessary... */
if (float8_lt(box->high.x, box->low.x))
{
x = box->high.x;
box->high.x = box->low.x;
box->low.x = x;
}
if (float8_lt(box->high.y, box->low.y))
{
y = box->high.y;
box->high.y = box->low.y;
box->low.y = y;
}
PG_RETURN_BOX_P(box);
}
/*
* box_send - converts box to binary format
*/
Datum
box_send(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
StringInfoData buf;
pq_begintypsend(&buf);
pq_sendfloat8(&buf, box->high.x);
pq_sendfloat8(&buf, box->high.y);
pq_sendfloat8(&buf, box->low.x);
pq_sendfloat8(&buf, box->low.y);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/* box_construct - fill in a new box.
*/
static inline void
box_construct(BOX *result, Point *pt1, Point *pt2)
{
if (float8_gt(pt1->x, pt2->x))
{
result->high.x = pt1->x;
result->low.x = pt2->x;
}
else
{
result->high.x = pt2->x;
result->low.x = pt1->x;
}
if (float8_gt(pt1->y, pt2->y))
{
result->high.y = pt1->y;
result->low.y = pt2->y;
}
else
{
result->high.y = pt2->y;
result->low.y = pt1->y;
}
}
/*----------------------------------------------------------
* Relational operators for BOXes .
* < , > , < = , > = , and = = are based on box area .
*---------------------------------------------------------*/
/* box_same - are two boxes identical?
*/
Datum
box_same(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(point_eq_point(&box1->high, &box2->high) &&
point_eq_point(&box1->low, &box2->low));
}
/* box_overlap - does box1 overlap box2?
*/
Datum
box_overlap(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(box_ov(box1, box2));
}
static bool
box_ov(BOX *box1, BOX *box2)
{
return (FPle(box1->low.x, box2->high.x) &&
FPle(box2->low.x, box1->high.x) &&
FPle(box1->low.y, box2->high.y) &&
FPle(box2->low.y, box1->high.y));
}
/* box_left - is box1 strictly left of box2?
*/
Datum
box_left(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPlt(box1->high.x, box2->low.x));
}
/* box_overleft - is the right edge of box1 at or left of
* the right edge of box2 ?
*
* This is " less than or equal " for the end of a time range ,
* when time ranges are stored as rectangles .
*/
Datum
box_overleft(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x));
}
/* box_right - is box1 strictly right of box2?
*/
Datum
box_right(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPgt(box1->low.x, box2->high.x));
}
/* box_overright - is the left edge of box1 at or right of
* the left edge of box2 ?
*
* This is " greater than or equal " for time ranges , when time ranges
* are stored as rectangles .
*/
Datum
box_overright(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPge(box1->low.x, box2->low.x));
}
/* box_below - is box1 strictly below box2?
*/
Datum
box_below(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPlt(box1->high.y, box2->low.y));
}
/* box_overbelow - is the upper edge of box1 at or below
* the upper edge of box2 ?
*/
Datum
box_overbelow(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPle(box1->high.y, box2->high.y));
}
/* box_above - is box1 strictly above box2?
*/
Datum
box_above(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPgt(box1->low.y, box2->high.y));
}
/* box_overabove - is the lower edge of box1 at or above
* the lower edge of box2 ?
*/
Datum
box_overabove(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPge(box1->low.y, box2->low.y));
}
/* box_contained - is box1 contained by box2?
*/
Datum
box_contained(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(box_contain_box(box2, box1));
}
/* box_contain - does box1 contain box2?
*/
Datum
box_contain(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(box_contain_box(box1, box2));
}
/*
* Check whether the second box is in the first box or on its border
*/
static bool
box_contain_box(BOX *contains_box, BOX *contained_box)
{
return FPge(contains_box->high.x, contained_box->high.x) &&
FPle(contains_box->low.x, contained_box->low.x) &&
FPge(contains_box->high.y, contained_box->high.y) &&
FPle(contains_box->low.y, contained_box->low.y);
}
/* box_positionop -
* is box1 entirely { above , below } box2 ?
*
* box_below_eq and box_above_eq are obsolete versions that ( probably
* erroneously ) accept the equal - boundaries case . Since these are not
* in sync with the box_left and box_right code , they are deprecated and
* not supported in the PG 8 . 1 rtree operator class extension .
*/
Datum
box_below_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPle(box1->high.y, box2->low.y));
}
Datum
box_above_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPge(box1->low.y, box2->high.y));
}
/* box_relop - is area(box1) relop area(box2), within
* our accuracy constraint ?
*/
Datum
box_lt(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPlt(box_ar(box1), box_ar(box2)));
}
Datum
box_gt(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPgt(box_ar(box1), box_ar(box2)));
}
Datum
box_eq(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPeq(box_ar(box1), box_ar(box2)));
}
Datum
box_le(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPle(box_ar(box1), box_ar(box2)));
}
Datum
box_ge(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(FPge(box_ar(box1), box_ar(box2)));
}
/*----------------------------------------------------------
* " Arithmetic " operators on boxes .
*---------------------------------------------------------*/
/* box_area - returns the area of the box.
*/
Datum
box_area(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
PG_RETURN_FLOAT8(box_ar(box));
}
/* box_width - returns the width of the box
* ( horizontal magnitude ) .
*/
Datum
box_width(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
PG_RETURN_FLOAT8(box_wd(box));
}
/* box_height - returns the height of the box
* ( vertical magnitude ) .
*/
Datum
box_height(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
PG_RETURN_FLOAT8(box_ht(box));
}
/* box_distance - returns the distance between the
* center points of two boxes .
*/
Datum
box_distance(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
Point a,
b;
box_cn(&a, box1);
box_cn(&b, box2);
PG_RETURN_FLOAT8(point_dt(&a, &b));
}
/* box_center - returns the center point of the box.
*/
Datum
box_center(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
Point *result = (Point *) palloc(sizeof (Point));
box_cn(result, box);
PG_RETURN_POINT_P(result);
}
/* box_ar - returns the area of the box.
*/
static float8
box_ar(BOX *box)
{
return float8_mul(box_wd(box), box_ht(box));
}
/* box_cn - stores the centerpoint of the box into *center.
*/
static void
box_cn(Point *center, BOX *box)
{
center->x = float8_div(float8_pl(box->high.x, box->low.x), 2 .0 );
center->y = float8_div(float8_pl(box->high.y, box->low.y), 2 .0 );
}
/* box_wd - returns the width (length) of the box
* ( horizontal magnitude ) .
*/
static float8
box_wd(BOX *box)
{
return float8_mi(box->high.x, box->low.x);
}
/* box_ht - returns the height of the box
* ( vertical magnitude ) .
*/
static float8
box_ht(BOX *box)
{
return float8_mi(box->high.y, box->low.y);
}
/*----------------------------------------------------------
* Funky operations .
*---------------------------------------------------------*/
/* box_intersect -
* returns the overlapping portion of two boxes ,
* or NULL if they do not intersect .
*/
Datum
box_intersect(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 );
BOX *box2 = PG_GETARG_BOX_P(1 );
BOX *result;
if (!box_ov(box1, box2))
PG_RETURN_NULL();
result = (BOX *) palloc(sizeof (BOX));
result->high.x = float8_min(box1->high.x, box2->high.x);
result->low.x = float8_max(box1->low.x, box2->low.x);
result->high.y = float8_min(box1->high.y, box2->high.y);
result->low.y = float8_max(box1->low.y, box2->low.y);
PG_RETURN_BOX_P(result);
}
/* box_diagonal -
* returns a line segment which happens to be the
* positive - slope diagonal of " box " .
*/
Datum
box_diagonal(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
LSEG *result = (LSEG *) palloc(sizeof (LSEG));
statlseg_construct(result, &box->high, &box->low);
PG_RETURN_LSEG_P(result);
}
/***********************************************************************
* *
* * Routines for 2 D lines .
* *
***********************************************************************/
static bool
line_decode(char *s, const char *str, LINE *line, Node *escontext)
{
/* s was already advanced over leading '{' */
if (!single_decode(s, &line->A, &s, "line" , str, escontext))
return false ;
if (*s++ != DELIM)
goto fail;
if (!single_decode(s, &line->B, &s, "line" , str, escontext))
return false ;
if (*s++ != DELIM)
goto fail;
if (!single_decode(s, &line->C, &s, "line" , str, escontext))
return false ;
if (*s++ != RDELIM_L)
goto fail;
while (isspace((unsigned char ) *s))
s++;
if (*s != '\0' )
goto fail;
return true ;
fail:
ereturn(escontext, false ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \" %s\"" ,
"line" , str)));
}
Datum
line_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0 );
Node *escontext = fcinfo->context;
LINE *line = (LINE *) palloc(sizeof (LINE));
LSEG lseg;
bool isopen;
char *s;
s = str;
while (isspace((unsigned char ) *s))
s++;
if (*s == LDELIM_L)
{
if (!line_decode(s + 1 , str, line, escontext))
PG_RETURN_NULL();
if (FPzero(line->A) && FPzero(line->B))
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid line specification: A and B cannot both be zero" )));
}
else
{
if (!path_decode(s, true , 2 , &lseg.p[0 ], &isopen, NULL, "line" , str,
escontext))
PG_RETURN_NULL();
if (point_eq_point(&lseg.p[0 ], &lseg.p[1 ]))
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid line specification: must be two distinct points" )));
/*
* XXX lseg_sl ( ) and line_construct ( ) can throw overflow / underflow
* errors . Eventually we should allow those to be soft , but the
* notational pain seems to outweigh the value for now .
*/
line_construct(line, &lseg.p[0 ], lseg_sl(&lseg));
}
PG_RETURN_LINE_P(line);
}
Datum
line_out(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0 );
char *astr = float8out_internal(line->A);
char *bstr = float8out_internal(line->B);
char *cstr = float8out_internal(line->C);
PG_RETURN_CSTRING(psprintf("%c%s%c%s%c%s%c" , LDELIM_L, astr, DELIM, bstr,
DELIM, cstr, RDELIM_L));
}
/*
* line_recv - converts external binary format to line
*/
Datum
line_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0 );
LINE *line;
line = (LINE *) palloc(sizeof (LINE));
line->A = pq_getmsgfloat8(buf);
line->B = pq_getmsgfloat8(buf);
line->C = pq_getmsgfloat8(buf);
if (FPzero(line->A) && FPzero(line->B))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid line specification: A and B cannot both be zero" )));
PG_RETURN_LINE_P(line);
}
/*
* line_send - converts line to binary format
*/
Datum
line_send(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0 );
StringInfoData buf;
pq_begintypsend(&buf);
pq_sendfloat8(&buf, line->A);
pq_sendfloat8(&buf, line->B);
pq_sendfloat8(&buf, line->C);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*----------------------------------------------------------
* Conversion routines from one line formula to internal .
* Internal form : Ax + By + C = 0
*---------------------------------------------------------*/
/*
* Fill already - allocated LINE struct from the point and the slope
*/
static inline void
line_construct(LINE *result, Point *pt, float8 m)
{
if (isinf(m))
{
/* vertical - use "x = C" */
result->A = -1 .0 ;
result->B = 0 .0 ;
result->C = pt->x;
}
else if (m == 0 )
{
/* horizontal - use "y = C" */
result->A = 0 .0 ;
result->B = -1 .0 ;
result->C = pt->y;
}
else
{
/* use "mx - y + yinter = 0" */
result->A = m;
result->B = -1 .0 ;
result->C = float8_mi(pt->y, float8_mul(m, pt->x));
/* on some platforms, the preceding expression tends to produce -0 */
if (result->C == 0 .0 )
result->C = 0 .0 ;
}
}
/* line_construct_pp()
* two points
*/
Datum
line_construct_pp(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
LINE *result = (LINE *) palloc(sizeof (LINE));
if (point_eq_point(pt1, pt2))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid line specification: must be two distinct points" )));
line_construct(result, pt1, point_sl(pt1, pt2));
PG_RETURN_LINE_P(result);
}
/*----------------------------------------------------------
* Relative position routines .
*---------------------------------------------------------*/
Datum
line_intersect(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0 );
LINE *l2 = PG_GETARG_LINE_P(1 );
PG_RETURN_BOOL(line_interpt_line(NULL, l1, l2));
}
Datum
line_parallel(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0 );
LINE *l2 = PG_GETARG_LINE_P(1 );
PG_RETURN_BOOL(!line_interpt_line(NULL, l1, l2));
}
Datum
line_perp(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0 );
LINE *l2 = PG_GETARG_LINE_P(1 );
if (FPzero(l1->A))
PG_RETURN_BOOL(FPzero(l2->B));
if (FPzero(l2->A))
PG_RETURN_BOOL(FPzero(l1->B));
if (FPzero(l1->B))
PG_RETURN_BOOL(FPzero(l2->A));
if (FPzero(l2->B))
PG_RETURN_BOOL(FPzero(l1->A));
PG_RETURN_BOOL(FPeq(float8_div(float8_mul(l1->A, l2->A),
float8_mul(l1->B, l2->B)), -1 .0 ));
}
Datum
line_vertical(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0 );
PG_RETURN_BOOL(FPzero(line->B));
}
Datum
line_horizontal(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0 );
PG_RETURN_BOOL(FPzero(line->A));
}
/*
* Check whether the two lines are the same
*/
Datum
line_eq(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0 );
LINE *l2 = PG_GETARG_LINE_P(1 );
float8 ratio;
/* If any NaNs are involved, insist on exact equality */
if (unlikely(isnan(l1->A) || isnan(l1->B) || isnan(l1->C) ||
isnan(l2->A) || isnan(l2->B) || isnan(l2->C)))
{
PG_RETURN_BOOL(float8_eq(l1->A, l2->A) &&
float8_eq(l1->B, l2->B) &&
float8_eq(l1->C, l2->C));
}
/* Otherwise, lines whose parameters are proportional are the same */
if (!FPzero(l2->A))
ratio = float8_div(l1->A, l2->A);
else if (!FPzero(l2->B))
ratio = float8_div(l1->B, l2->B);
else if (!FPzero(l2->C))
ratio = float8_div(l1->C, l2->C);
else
ratio = 1 .0 ;
PG_RETURN_BOOL(FPeq(l1->A, float8_mul(ratio, l2->A)) &&
FPeq(l1->B, float8_mul(ratio, l2->B)) &&
FPeq(l1->C, float8_mul(ratio, l2->C)));
}
/*----------------------------------------------------------
* Line arithmetic routines .
*---------------------------------------------------------*/
/*
* Return slope of the line
*/
static inline float8
line_sl(LINE *line)
{
if (FPzero(line->A))
return 0 .0 ;
if (FPzero(line->B))
return get_float8_infinity();
return float8_div(line->A, -line->B);
}
/*
* Return inverse slope of the line
*/
static inline float8
line_invsl(LINE *line)
{
if (FPzero(line->A))
return get_float8_infinity();
if (FPzero(line->B))
return 0 .0 ;
return float8_div(line->B, line->A);
}
/* line_distance()
* Distance between two lines .
*/
Datum
line_distance(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0 );
LINE *l2 = PG_GETARG_LINE_P(1 );
float8 ratio;
if (line_interpt_line(NULL, l1, l2)) /* intersecting? */
PG_RETURN_FLOAT8(0 .0 );
if (!FPzero(l1->A) && !isnan(l1->A) && !FPzero(l2->A) && !isnan(l2->A))
ratio = float8_div(l1->A, l2->A);
else if (!FPzero(l1->B) && !isnan(l1->B) && !FPzero(l2->B) && !isnan(l2->B))
ratio = float8_div(l1->B, l2->B);
else
ratio = 1 .0 ;
PG_RETURN_FLOAT8(float8_div(fabs(float8_mi(l1->C,
float8_mul(ratio, l2->C))),
HYPOT(l1->A, l1->B)));
}
/* line_interpt()
* Point where two lines l1 , l2 intersect ( if any )
*/
Datum
line_interpt(PG_FUNCTION_ARGS)
{
LINE *l1 = PG_GETARG_LINE_P(0 );
LINE *l2 = PG_GETARG_LINE_P(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
if (!line_interpt_line(result, l1, l2))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Internal version of line_interpt
*
* Return whether two lines intersect . If * result is not NULL , it is set to
* the intersection point .
*
* NOTE : If the lines are identical then we will find they are parallel
* and report " no intersection " . This is a little weird , but since
* there ' s no * unique * intersection , maybe it ' s appropriate behavior .
*
* If the lines have NaN constants , we will return true , and the intersection
* point would have NaN coordinates . We shouldn ' t return false in this case
* because that would mean the lines are parallel .
*/
static bool
line_interpt_line(Point *result, LINE *l1, LINE *l2)
{
float8 x,
y;
if (!FPzero(l1->B))
{
if (FPeq(l2->A, float8_mul(l1->A, float8_div(l2->B, l1->B))))
return false ;
x = float8_div(float8_mi(float8_mul(l1->B, l2->C),
float8_mul(l2->B, l1->C)),
float8_mi(float8_mul(l1->A, l2->B),
float8_mul(l2->A, l1->B)));
y = float8_div(-float8_pl(float8_mul(l1->A, x), l1->C), l1->B);
}
else if (!FPzero(l2->B))
{
if (FPeq(l1->A, float8_mul(l2->A, float8_div(l1->B, l2->B))))
return false ;
x = float8_div(float8_mi(float8_mul(l2->B, l1->C),
float8_mul(l1->B, l2->C)),
float8_mi(float8_mul(l2->A, l1->B),
float8_mul(l1->A, l2->B)));
y = float8_div(-float8_pl(float8_mul(l2->A, x), l2->C), l2->B);
}
else
return false ;
/* On some platforms, the preceding expressions tend to produce -0. */
if (x == 0 .0 )
x = 0 .0 ;
if (y == 0 .0 )
y = 0 .0 ;
if (result != NULL)
point_construct(result, x, y);
return true ;
}
/***********************************************************************
* *
* * Routines for 2 D paths ( sequences of line segments , also
* * called ` polylines ' ) .
* *
* * This is not a general package for geometric paths ,
* * which of course include polygons ; the emphasis here
* * is on ( for example ) usefulness in wire layout .
* *
***********************************************************************/
/*----------------------------------------------------------
* 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 , . . . ) "
*---------------------------------------------------------*/
Datum
path_area(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0 );
float8 area = 0 .0 ;
int i,
j;
if (!path->closed)
PG_RETURN_NULL();
for (i = 0 ; i < path->npts; i++)
{
j = (i + 1 ) % path->npts;
area = float8_pl(area, float8_mul(path->p[i].x, path->p[j].y));
area = float8_mi(area, float8_mul(path->p[i].y, path->p[j].x));
}
PG_RETURN_FLOAT8(float8_div(fabs(area), 2 .0 ));
}
Datum
path_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0 );
Node *escontext = fcinfo->context;
PATH *path;
bool isopen;
char *s;
int npts;
int size;
int base_size;
int depth = 0 ;
if ((npts = pair_count(str, ',' )) <= 0 )
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \" %s\"" ,
"path" , str)));
s = str;
while (isspace((unsigned char ) *s))
s++;
/* skip single leading paren */
if ((*s == LDELIM) && (strrchr(s, LDELIM) == s))
{
s++;
depth++;
}
base_size = sizeof (path->p[0 ]) * npts;
size = offsetof(PATH, p) + base_size;
/* Check for integer overflow */
if (base_size / npts != sizeof (path->p[0 ]) || size <= base_size)
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested" )));
path = (PATH *) palloc(size);
SET_VARSIZE(path, size);
path->npts = npts;
if (!path_decode(s, true , npts, &(path->p[0 ]), &isopen, &s, "path" , str,
escontext))
PG_RETURN_NULL();
if (depth >= 1 )
{
if (*s++ != RDELIM)
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \" %s\"" ,
"path" , str)));
while (isspace((unsigned char ) *s))
s++;
}
if (*s != '\0' )
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \" %s\"" ,
"path" , str)));
path->closed = (!isopen);
/* prevent instability in unused pad bytes */
path->dummy = 0 ;
PG_RETURN_PATH_P(path);
}
Datum
path_out(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0 );
PG_RETURN_CSTRING(path_encode(path->closed ? PATH_CLOSED : PATH_OPEN, path->npts, path->p));
}
/*
* path_recv - converts external binary format to path
*
* External representation is closed flag ( a boolean byte ) , int32 number
* of points , and the points .
*/
Datum
path_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0 );
PATH *path;
int closed;
int32 npts;
int32 i;
int size;
closed = pq_getmsgbyte(buf);
npts = pq_getmsgint(buf, sizeof (int32));
if (npts <= 0 || npts >= (int32) ((INT_MAX - offsetof(PATH, p)) / sizeof (Point)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid number of points in external \" path\" value" )));
size = offsetof(PATH, p) + sizeof (path->p[0 ]) * npts;
path = (PATH *) palloc(size);
SET_VARSIZE(path, size);
path->npts = npts;
path->closed = (closed ? 1 : 0 );
/* prevent instability in unused pad bytes */
path->dummy = 0 ;
for (i = 0 ; i < npts; i++)
{
path->p[i].x = pq_getmsgfloat8(buf);
path->p[i].y = pq_getmsgfloat8(buf);
}
PG_RETURN_PATH_P(path);
}
/*
* path_send - converts path to binary format
*/
Datum
path_send(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0 );
StringInfoData buf;
int32 i;
pq_begintypsend(&buf);
pq_sendbyte(&buf, path->closed ? 1 : 0 );
pq_sendint32(&buf, path->npts);
for (i = 0 ; i < path->npts; i++)
{
pq_sendfloat8(&buf, path->p[i].x);
pq_sendfloat8(&buf, path->p[i].y);
}
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*----------------------------------------------------------
* Relational operators .
* These are based on the path cardinality ,
* as stupid as that sounds .
*
* Better relops and access methods coming soon .
*---------------------------------------------------------*/
Datum
path_n_lt(PG_FUNCTION_ARGS)
{
PATH *p1 = PG_GETARG_PATH_P(0 );
PATH *p2 = PG_GETARG_PATH_P(1 );
PG_RETURN_BOOL(p1->npts < p2->npts);
}
Datum
path_n_gt(PG_FUNCTION_ARGS)
{
PATH *p1 = PG_GETARG_PATH_P(0 );
PATH *p2 = PG_GETARG_PATH_P(1 );
PG_RETURN_BOOL(p1->npts > p2->npts);
}
Datum
path_n_eq(PG_FUNCTION_ARGS)
{
PATH *p1 = PG_GETARG_PATH_P(0 );
PATH *p2 = PG_GETARG_PATH_P(1 );
PG_RETURN_BOOL(p1->npts == p2->npts);
}
Datum
path_n_le(PG_FUNCTION_ARGS)
{
PATH *p1 = PG_GETARG_PATH_P(0 );
PATH *p2 = PG_GETARG_PATH_P(1 );
PG_RETURN_BOOL(p1->npts <= p2->npts);
}
Datum
path_n_ge(PG_FUNCTION_ARGS)
{
PATH *p1 = PG_GETARG_PATH_P(0 );
PATH *p2 = PG_GETARG_PATH_P(1 );
PG_RETURN_BOOL(p1->npts >= p2->npts);
}
/*----------------------------------------------------------
* Conversion operators .
*---------------------------------------------------------*/
Datum
path_isclosed(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0 );
PG_RETURN_BOOL(path->closed);
}
Datum
path_isopen(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0 );
PG_RETURN_BOOL(!path->closed);
}
Datum
path_npoints(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0 );
PG_RETURN_INT32(path->npts);
}
Datum
path_close(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0 );
path->closed = true ;
PG_RETURN_PATH_P(path);
}
Datum
path_open(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0 );
path->closed = false ;
PG_RETURN_PATH_P(path);
}
/* 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 .
*/
Datum
path_inter(PG_FUNCTION_ARGS)
{
PATH *p1 = PG_GETARG_PATH_P(0 );
PATH *p2 = PG_GETARG_PATH_P(1 );
BOX b1,
b2;
int i,
j;
LSEG seg1,
seg2;
Assert(p1->npts > 0 && p2->npts > 0 );
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 = float8_max(p1->p[i].x, b1.high.x);
b1.high.y = float8_max(p1->p[i].y, b1.high.y);
b1.low.x = float8_min(p1->p[i].x, b1.low.x);
b1.low.y = float8_min(p1->p[i].y, b1.low.y);
}
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 = float8_max(p2->p[i].x, b2.high.x);
b2.high.y = float8_max(p2->p[i].y, b2.high.y);
b2.low.x = float8_min(p2->p[i].x, b2.low.x);
b2.low.y = float8_min(p2->p[i].y, b2.low.y);
}
if (!box_ov(&b1, &b2))
PG_RETURN_BOOL(false );
/* pairwise check lseg intersections */
for (i = 0 ; i < p1->npts; i++)
{
int iprev;
if (i > 0 )
iprev = i - 1 ;
else
{
if (!p1->closed)
continue ;
iprev = p1->npts - 1 ; /* include the closure segment */
}
for (j = 0 ; j < p2->npts; j++)
{
int jprev;
if (j > 0 )
jprev = j - 1 ;
else
{
if (!p2->closed)
continue ;
jprev = p2->npts - 1 ; /* include the closure segment */
}
statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
if (lseg_interpt_lseg(NULL, &seg1, &seg2))
PG_RETURN_BOOL(true );
}
}
/* if we dropped through, no two segs intersected */
PG_RETURN_BOOL(false );
}
/* path_distance()
* This essentially does a cartesian product of the lsegs in the
* two paths , and finds the min distance between any two lsegs
*/
Datum
path_distance(PG_FUNCTION_ARGS)
{
PATH *p1 = PG_GETARG_PATH_P(0 );
PATH *p2 = PG_GETARG_PATH_P(1 );
float8 min = 0 .0 ; /* initialize to keep compiler quiet */
bool have_min = false ;
float8 tmp;
int i,
j;
LSEG seg1,
seg2;
for (i = 0 ; i < p1->npts; i++)
{
int iprev;
if (i > 0 )
iprev = i - 1 ;
else
{
if (!p1->closed)
continue ;
iprev = p1->npts - 1 ; /* include the closure segment */
}
for (j = 0 ; j < p2->npts; j++)
{
int jprev;
if (j > 0 )
jprev = j - 1 ;
else
{
if (!p2->closed)
continue ;
jprev = p2->npts - 1 ; /* include the closure segment */
}
statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
tmp = lseg_closept_lseg(NULL, &seg1, &seg2);
if (!have_min || float8_lt(tmp, min))
{
min = tmp;
have_min = true ;
}
}
}
if (!have_min)
PG_RETURN_NULL();
PG_RETURN_FLOAT8(min);
}
/*----------------------------------------------------------
* " Arithmetic " operations .
*---------------------------------------------------------*/
Datum
path_length(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0 );
float8 result = 0 .0 ;
int i;
for (i = 0 ; i < path->npts; i++)
{
int iprev;
if (i > 0 )
iprev = i - 1 ;
else
{
if (!path->closed)
continue ;
iprev = path->npts - 1 ; /* include the closure segment */
}
result = float8_pl(result, point_dt(&path->p[iprev], &path->p[i]));
}
PG_RETURN_FLOAT8(result);
}
/***********************************************************************
* *
* * Routines for 2 D points .
* *
***********************************************************************/
/*----------------------------------------------------------
* String to point , point to string conversion .
* External format :
* " ( x , y ) "
* " x , y "
*---------------------------------------------------------*/
Datum
point_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0 );
Point *point = (Point *) palloc(sizeof (Point));
/* Ignore failure from pair_decode, since our return value won't matter */
pair_decode(str, &point->x, &point->y, NULL, "point" , str, fcinfo->context);
PG_RETURN_POINT_P(point);
}
Datum
point_out(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
PG_RETURN_CSTRING(path_encode(PATH_NONE, 1 , pt));
}
/*
* point_recv - converts external binary format to point
*/
Datum
point_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0 );
Point *point;
point = (Point *) palloc(sizeof (Point));
point->x = pq_getmsgfloat8(buf);
point->y = pq_getmsgfloat8(buf);
PG_RETURN_POINT_P(point);
}
/*
* point_send - converts point to binary format
*/
Datum
point_send(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
StringInfoData buf;
pq_begintypsend(&buf);
pq_sendfloat8(&buf, pt->x);
pq_sendfloat8(&buf, pt->y);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
* Initialize a point
*/
static inline void
point_construct(Point *result, float8 x, float8 y)
{
result->x = x;
result->y = y;
}
/*----------------------------------------------------------
* Relational operators for Points .
* Since we do have a sense of coordinates being
* " equal " to a given accuracy ( point_vert , point_horiz ) ,
* the other ops must preserve that sense . This means
* that results may , strictly speaking , be a lie ( unless
* EPSILON = 0 . 0 ) .
*---------------------------------------------------------*/
Datum
point_left(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
PG_RETURN_BOOL(FPlt(pt1->x, pt2->x));
}
Datum
point_right(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
PG_RETURN_BOOL(FPgt(pt1->x, pt2->x));
}
Datum
point_above(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
PG_RETURN_BOOL(FPgt(pt1->y, pt2->y));
}
Datum
point_below(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
PG_RETURN_BOOL(FPlt(pt1->y, pt2->y));
}
Datum
point_vert(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
PG_RETURN_BOOL(FPeq(pt1->x, pt2->x));
}
Datum
point_horiz(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
PG_RETURN_BOOL(FPeq(pt1->y, pt2->y));
}
Datum
point_eq(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
PG_RETURN_BOOL(point_eq_point(pt1, pt2));
}
Datum
point_ne(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
PG_RETURN_BOOL(!point_eq_point(pt1, pt2));
}
/*
* Check whether the two points are the same
*/
static inline bool
point_eq_point(Point *pt1, Point *pt2)
{
/* If any NaNs are involved, insist on exact equality */
if (unlikely(isnan(pt1->x) || isnan(pt1->y) ||
isnan(pt2->x) || isnan(pt2->y)))
return (float8_eq(pt1->x, pt2->x) && float8_eq(pt1->y, pt2->y));
return (FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y));
}
/*----------------------------------------------------------
* " Arithmetic " operators on points .
*---------------------------------------------------------*/
Datum
point_distance(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
PG_RETURN_FLOAT8(point_dt(pt1, pt2));
}
static inline float8
point_dt(Point *pt1, Point *pt2)
{
return HYPOT(float8_mi(pt1->x, pt2->x), float8_mi(pt1->y, pt2->y));
}
Datum
point_slope(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
PG_RETURN_FLOAT8(point_sl(pt1, pt2));
}
/*
* Return slope of two points
*
* Note that this function returns Inf when the points are the same .
*/
static inline float8
point_sl(Point *pt1, Point *pt2)
{
if (FPeq(pt1->x, pt2->x))
return get_float8_infinity();
if (FPeq(pt1->y, pt2->y))
return 0 .0 ;
return float8_div(float8_mi(pt1->y, pt2->y), float8_mi(pt1->x, pt2->x));
}
/*
* Return inverse slope of two points
*
* Note that this function returns 0 . 0 when the points are the same .
*/
static inline float8
point_invsl(Point *pt1, Point *pt2)
{
if (FPeq(pt1->x, pt2->x))
return 0 .0 ;
if (FPeq(pt1->y, pt2->y))
return get_float8_infinity();
return float8_div(float8_mi(pt1->x, pt2->x), float8_mi(pt2->y, pt1->y));
}
/***********************************************************************
* *
* * Routines for 2 D line segments .
* *
***********************************************************************/
/*----------------------------------------------------------
* String to lseg , lseg to string conversion .
* 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 ) "
*---------------------------------------------------------*/
Datum
lseg_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0 );
Node *escontext = fcinfo->context;
LSEG *lseg = (LSEG *) palloc(sizeof (LSEG));
bool isopen;
if (!path_decode(str, true , 2 , &lseg->p[0 ], &isopen, NULL, "lseg" , str,
escontext))
PG_RETURN_NULL();
PG_RETURN_LSEG_P(lseg);
}
Datum
lseg_out(PG_FUNCTION_ARGS)
{
LSEG *ls = PG_GETARG_LSEG_P(0 );
PG_RETURN_CSTRING(path_encode(PATH_OPEN, 2 , &ls->p[0 ]));
}
/*
* lseg_recv - converts external binary format to lseg
*/
Datum
lseg_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0 );
LSEG *lseg;
lseg = (LSEG *) palloc(sizeof (LSEG));
lseg->p[0 ].x = pq_getmsgfloat8(buf);
lseg->p[0 ].y = pq_getmsgfloat8(buf);
lseg->p[1 ].x = pq_getmsgfloat8(buf);
lseg->p[1 ].y = pq_getmsgfloat8(buf);
PG_RETURN_LSEG_P(lseg);
}
/*
* lseg_send - converts lseg to binary format
*/
Datum
lseg_send(PG_FUNCTION_ARGS)
{
LSEG *ls = PG_GETARG_LSEG_P(0 );
StringInfoData buf;
pq_begintypsend(&buf);
pq_sendfloat8(&buf, ls->p[0 ].x);
pq_sendfloat8(&buf, ls->p[0 ].y);
pq_sendfloat8(&buf, ls->p[1 ].x);
pq_sendfloat8(&buf, ls->p[1 ].y);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/* lseg_construct -
* form a LSEG from two Points .
*/
Datum
lseg_construct(PG_FUNCTION_ARGS)
{
Point *pt1 = PG_GETARG_POINT_P(0 );
Point *pt2 = PG_GETARG_POINT_P(1 );
LSEG *result = (LSEG *) palloc(sizeof (LSEG));
statlseg_construct(result, pt1, pt2);
PG_RETURN_LSEG_P(result);
}
/* like lseg_construct, but assume space already allocated */
static inline void
statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
{
lseg->p[0 ].x = pt1->x;
lseg->p[0 ].y = pt1->y;
lseg->p[1 ].x = pt2->x;
lseg->p[1 ].y = pt2->y;
}
/*
* Return slope of the line segment
*/
static inline float8
lseg_sl(LSEG *lseg)
{
return point_sl(&lseg->p[0 ], &lseg->p[1 ]);
}
/*
* Return inverse slope of the line segment
*/
static inline float8
lseg_invsl(LSEG *lseg)
{
return point_invsl(&lseg->p[0 ], &lseg->p[1 ]);
}
Datum
lseg_length(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
PG_RETURN_FLOAT8(point_dt(&lseg->p[0 ], &lseg->p[1 ]));
}
/*----------------------------------------------------------
* Relative position routines .
*---------------------------------------------------------*/
/*
* * find intersection of the two lines , and see if it falls on
* * both segments .
*/
Datum
lseg_intersect(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
PG_RETURN_BOOL(lseg_interpt_lseg(NULL, l1, l2));
}
Datum
lseg_parallel(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
PG_RETURN_BOOL(FPeq(lseg_sl(l1), lseg_sl(l2)));
}
/*
* Determine if two line segments are perpendicular .
*/
Datum
lseg_perp(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
PG_RETURN_BOOL(FPeq(lseg_sl(l1), lseg_invsl(l2)));
}
Datum
lseg_vertical(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
PG_RETURN_BOOL(FPeq(lseg->p[0 ].x, lseg->p[1 ].x));
}
Datum
lseg_horizontal(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
PG_RETURN_BOOL(FPeq(lseg->p[0 ].y, lseg->p[1 ].y));
}
Datum
lseg_eq(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
PG_RETURN_BOOL(point_eq_point(&l1->p[0 ], &l2->p[0 ]) &&
point_eq_point(&l1->p[1 ], &l2->p[1 ]));
}
Datum
lseg_ne(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
PG_RETURN_BOOL(!point_eq_point(&l1->p[0 ], &l2->p[0 ]) ||
!point_eq_point(&l1->p[1 ], &l2->p[1 ]));
}
Datum
lseg_lt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
PG_RETURN_BOOL(FPlt(point_dt(&l1->p[0 ], &l1->p[1 ]),
point_dt(&l2->p[0 ], &l2->p[1 ])));
}
Datum
lseg_le(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
PG_RETURN_BOOL(FPle(point_dt(&l1->p[0 ], &l1->p[1 ]),
point_dt(&l2->p[0 ], &l2->p[1 ])));
}
Datum
lseg_gt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
PG_RETURN_BOOL(FPgt(point_dt(&l1->p[0 ], &l1->p[1 ]),
point_dt(&l2->p[0 ], &l2->p[1 ])));
}
Datum
lseg_ge(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
PG_RETURN_BOOL(FPge(point_dt(&l1->p[0 ], &l1->p[1 ]),
point_dt(&l2->p[0 ], &l2->p[1 ])));
}
/*----------------------------------------------------------
* Line arithmetic routines .
*---------------------------------------------------------*/
/* lseg_distance -
* If two segments don ' t intersect , then the closest
* point will be from one of the endpoints to the other
* segment .
*/
Datum
lseg_distance(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
PG_RETURN_FLOAT8(lseg_closept_lseg(NULL, l1, l2));
}
Datum
lseg_center(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
Point *result;
result = (Point *) palloc(sizeof (Point));
result->x = float8_div(float8_pl(lseg->p[0 ].x, lseg->p[1 ].x), 2 .0 );
result->y = float8_div(float8_pl(lseg->p[0 ].y, lseg->p[1 ].y), 2 .0 );
PG_RETURN_POINT_P(result);
}
/*
* Return whether the two segments intersect . If * result is not NULL ,
* it is set to the intersection point .
*
* This function is almost perfectly symmetric , even though it doesn ' t look
* like it . See lseg_interpt_line ( ) for the other half of it .
*/
static bool
lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2)
{
Point interpt;
LINE tmp;
line_construct(&tmp, &l2->p[0 ], lseg_sl(l2));
if (!lseg_interpt_line(&interpt, l1, &tmp))
return false ;
/*
* If the line intersection point isn ' t within l2 , there is no valid
* segment intersection point at all .
*/
if (!lseg_contain_point(l2, &interpt))
return false ;
if (result != NULL)
*result = interpt;
return true ;
}
Datum
lseg_interpt(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
if (!lseg_interpt_lseg(result, l1, l2))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/***********************************************************************
* *
* * Routines for position comparisons of differently - typed
* * 2 D objects .
* *
***********************************************************************/
/*---------------------------------------------------------------------
* dist_
* Minimum distance from one object to another .
*-------------------------------------------------------------------*/
/*
* Distance from a point to a line
*/
Datum
dist_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
LINE *line = PG_GETARG_LINE_P(1 );
PG_RETURN_FLOAT8(line_closept_point(NULL, line, pt));
}
/*
* Distance from a line to a point
*/
Datum
dist_lp(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0 );
Point *pt = PG_GETARG_POINT_P(1 );
PG_RETURN_FLOAT8(line_closept_point(NULL, line, pt));
}
/*
* Distance from a point to a lseg
*/
Datum
dist_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
LSEG *lseg = PG_GETARG_LSEG_P(1 );
PG_RETURN_FLOAT8(lseg_closept_point(NULL, lseg, pt));
}
/*
* Distance from a lseg to a point
*/
Datum
dist_sp(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
Point *pt = PG_GETARG_POINT_P(1 );
PG_RETURN_FLOAT8(lseg_closept_point(NULL, lseg, pt));
}
static float8
dist_ppath_internal(Point *pt, PATH *path)
{
float8 result = 0 .0 ; /* keep compiler quiet */
bool have_min = false ;
float8 tmp;
int i;
LSEG lseg;
Assert(path->npts > 0 );
/*
* The distance from a point to a path is the smallest distance from the
* point to any of its constituent segments .
*/
for (i = 0 ; i < path->npts; i++)
{
int iprev;
if (i > 0 )
iprev = i - 1 ;
else
{
if (!path->closed)
continue ;
iprev = path->npts - 1 ; /* Include the closure segment */
}
statlseg_construct(&lseg, &path->p[iprev], &path->p[i]);
tmp = lseg_closept_point(NULL, &lseg, pt);
if (!have_min || float8_lt(tmp, result))
{
result = tmp;
have_min = true ;
}
}
return result;
}
/*
* Distance from a point to a path
*/
Datum
dist_ppath(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
PATH *path = PG_GETARG_PATH_P(1 );
PG_RETURN_FLOAT8(dist_ppath_internal(pt, path));
}
/*
* Distance from a path to a point
*/
Datum
dist_pathp(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0 );
Point *pt = PG_GETARG_POINT_P(1 );
PG_RETURN_FLOAT8(dist_ppath_internal(pt, path));
}
/*
* Distance from a point to a box
*/
Datum
dist_pb(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
BOX *box = PG_GETARG_BOX_P(1 );
PG_RETURN_FLOAT8(box_closept_point(NULL, box, pt));
}
/*
* Distance from a box to a point
*/
Datum
dist_bp(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
Point *pt = PG_GETARG_POINT_P(1 );
PG_RETURN_FLOAT8(box_closept_point(NULL, box, pt));
}
/*
* Distance from a lseg to a line
*/
Datum
dist_sl(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
LINE *line = PG_GETARG_LINE_P(1 );
PG_RETURN_FLOAT8(lseg_closept_line(NULL, lseg, line));
}
/*
* Distance from a line to a lseg
*/
Datum
dist_ls(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0 );
LSEG *lseg = PG_GETARG_LSEG_P(1 );
PG_RETURN_FLOAT8(lseg_closept_line(NULL, lseg, line));
}
/*
* Distance from a lseg to a box
*/
Datum
dist_sb(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
BOX *box = PG_GETARG_BOX_P(1 );
PG_RETURN_FLOAT8(box_closept_lseg(NULL, box, lseg));
}
/*
* Distance from a box to a lseg
*/
Datum
dist_bs(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
LSEG *lseg = PG_GETARG_LSEG_P(1 );
PG_RETURN_FLOAT8(box_closept_lseg(NULL, box, lseg));
}
static float8
dist_cpoly_internal(CIRCLE *circle, POLYGON *poly)
{
float8 result;
/* calculate distance to center, and subtract radius */
result = float8_mi(dist_ppoly_internal(&circle->center, poly),
circle->radius);
if (result < 0 .0 )
result = 0 .0 ;
return result;
}
/*
* Distance from a circle to a polygon
*/
Datum
dist_cpoly(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
POLYGON *poly = PG_GETARG_POLYGON_P(1 );
PG_RETURN_FLOAT8(dist_cpoly_internal(circle, poly));
}
/*
* Distance from a polygon to a circle
*/
Datum
dist_polyc(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0 );
CIRCLE *circle = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_FLOAT8(dist_cpoly_internal(circle, poly));
}
/*
* Distance from a point to a polygon
*/
Datum
dist_ppoly(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0 );
POLYGON *poly = PG_GETARG_POLYGON_P(1 );
PG_RETURN_FLOAT8(dist_ppoly_internal(point, poly));
}
Datum
dist_polyp(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0 );
Point *point = PG_GETARG_POINT_P(1 );
PG_RETURN_FLOAT8(dist_ppoly_internal(point, poly));
}
static float8
dist_ppoly_internal(Point *pt, POLYGON *poly)
{
float8 result;
float8 d;
int i;
LSEG seg;
if (point_inside(pt, poly->npts, poly->p) != 0 )
return 0 .0 ;
/* initialize distance with segment between first and last points */
seg.p[0 ].x = poly->p[0 ].x;
seg.p[0 ].y = poly->p[0 ].y;
seg.p[1 ].x = poly->p[poly->npts - 1 ].x;
seg.p[1 ].y = poly->p[poly->npts - 1 ].y;
result = lseg_closept_point(NULL, &seg, pt);
/* check distances for other segments */
for (i = 0 ; i < poly->npts - 1 ; i++)
{
seg.p[0 ].x = poly->p[i].x;
seg.p[0 ].y = poly->p[i].y;
seg.p[1 ].x = poly->p[i + 1 ].x;
seg.p[1 ].y = poly->p[i + 1 ].y;
d = lseg_closept_point(NULL, &seg, pt);
if (float8_lt(d, result))
result = d;
}
return result;
}
/*---------------------------------------------------------------------
* interpt_
* Intersection point of objects .
* We choose to ignore the " point " of intersection between
* lines and boxes , since there are typically two .
*-------------------------------------------------------------------*/
/*
* Return whether the line segment intersect with the line . If * result is not
* NULL , it is set to the intersection point .
*/
static bool
lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
{
Point interpt;
LINE tmp;
/*
* First , we promote the line segment to a line , because we know how to
* find the intersection point of two lines . If they don ' t have an
* intersection point , we are done .
*/
line_construct(&tmp, &lseg->p[0 ], lseg_sl(lseg));
if (!line_interpt_line(&interpt, &tmp, line))
return false ;
/*
* Then , we check whether the intersection point is actually on the line
* segment .
*/
if (!lseg_contain_point(lseg, &interpt))
return false ;
if (result != NULL)
{
/*
* If there is an intersection , then check explicitly for matching
* endpoints since there may be rounding effects with annoying LSB
* residue .
*/
if (point_eq_point(&lseg->p[0 ], &interpt))
*result = lseg->p[0 ];
else if (point_eq_point(&lseg->p[1 ], &interpt))
*result = lseg->p[1 ];
else
*result = interpt;
}
return true ;
}
/*---------------------------------------------------------------------
* close_
* Point of closest proximity between objects .
*-------------------------------------------------------------------*/
/*
* If * result is not NULL , it is set to the intersection point of a
* perpendicular of the line through the point . Returns the distance
* of those two points .
*/
static float8
line_closept_point(Point *result, LINE *line, Point *point)
{
Point closept;
LINE tmp;
/*
* We drop a perpendicular to find the intersection point . Ordinarily we
* should always find it , but that can fail in the presence of NaN
* coordinates , and perhaps even from simple roundoff issues .
*/
line_construct(&tmp, point, line_invsl(line));
if (!line_interpt_line(&closept, &tmp, line))
{
if (result != NULL)
*result = *point;
return get_float8_nan();
}
if (result != NULL)
*result = closept;
return point_dt(&closept, point);
}
Datum
close_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
LINE *line = PG_GETARG_LINE_P(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
if (isnan(line_closept_point(result, line, pt)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on line segment to specified point .
*
* If * result is not NULL , set it to the closest point on the line segment
* to the point . Returns the distance of the two points .
*/
static float8
lseg_closept_point(Point *result, LSEG *lseg, Point *pt)
{
Point closept;
LINE tmp;
/*
* To find the closest point , we draw a perpendicular line from the point
* to the line segment .
*/
line_construct(&tmp, pt, point_invsl(&lseg->p[0 ], &lseg->p[1 ]));
lseg_closept_line(&closept, lseg, &tmp);
if (result != NULL)
*result = closept;
return point_dt(&closept, pt);
}
Datum
close_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
LSEG *lseg = PG_GETARG_LSEG_P(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
if (isnan(lseg_closept_point(result, lseg, pt)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on line segment to line segment
*/
static float8
lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg)
{
Point point;
float8 dist,
d;
/* First, we handle the case when the line segments are intersecting. */
if (lseg_interpt_lseg(result, on_lseg, to_lseg))
return 0 .0 ;
/*
* Then , we find the closest points from the endpoints of the second line
* segment , and keep the closest one .
*/
dist = lseg_closept_point(result, on_lseg, &to_lseg->p[0 ]);
d = lseg_closept_point(&point, on_lseg, &to_lseg->p[1 ]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = point;
}
/* The closest point can still be one of the endpoints, so we test them. */
d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[0 ]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = on_lseg->p[0 ];
}
d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[1 ]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = on_lseg->p[1 ];
}
return dist;
}
Datum
close_lseg(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0 );
LSEG *l2 = PG_GETARG_LSEG_P(1 );
Point *result;
if (lseg_sl(l1) == lseg_sl(l2))
PG_RETURN_NULL();
result = (Point *) palloc(sizeof (Point));
if (isnan(lseg_closept_lseg(result, l2, l1)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on or in box to specified point .
*
* If * result is not NULL , set it to the closest point on the box to the
* given point , and return the distance of the two points .
*/
static float8
box_closept_point(Point *result, BOX *box, Point *pt)
{
float8 dist,
d;
Point point,
closept;
LSEG lseg;
if (box_contain_point(box, pt))
{
if (result != NULL)
*result = *pt;
return 0 .0 ;
}
/* pairwise check lseg distances */
point.x = box->low.x;
point.y = box->high.y;
statlseg_construct(&lseg, &box->low, &point);
dist = lseg_closept_point(result, &lseg, pt);
statlseg_construct(&lseg, &box->high, &point);
d = lseg_closept_point(&closept, &lseg, pt);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = closept;
}
point.x = box->high.x;
point.y = box->low.y;
statlseg_construct(&lseg, &box->low, &point);
d = lseg_closept_point(&closept, &lseg, pt);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = closept;
}
statlseg_construct(&lseg, &box->high, &point);
d = lseg_closept_point(&closept, &lseg, pt);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = closept;
}
return dist;
}
Datum
close_pb(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
BOX *box = PG_GETARG_BOX_P(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
if (isnan(box_closept_point(result, box, pt)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on line segment to line .
*
* Return the distance between the line and the closest point of the line
* segment to the line . If * result is not NULL , set it to that point .
*
* NOTE : When the lines are parallel , endpoints of one of the line segment
* are FPeq ( ) , in presence of NaN or Infinite coordinates , or perhaps =
* even because of simple roundoff issues , there may not be a single closest
* point . We are likely to set the result to the second endpoint in these
* cases .
*/
static float8
lseg_closept_line(Point *result, LSEG *lseg, LINE *line)
{
float8 dist1,
dist2;
if (lseg_interpt_line(result, lseg, line))
return 0 .0 ;
dist1 = line_closept_point(NULL, line, &lseg->p[0 ]);
dist2 = line_closept_point(NULL, line, &lseg->p[1 ]);
if (dist1 < dist2)
{
if (result != NULL)
*result = lseg->p[0 ];
return dist1;
}
else
{
if (result != NULL)
*result = lseg->p[1 ];
return dist2;
}
}
Datum
close_ls(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0 );
LSEG *lseg = PG_GETARG_LSEG_P(1 );
Point *result;
if (lseg_sl(lseg) == line_sl(line))
PG_RETURN_NULL();
result = (Point *) palloc(sizeof (Point));
if (isnan(lseg_closept_line(result, lseg, line)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on or in box to line segment .
*
* Returns the distance between the closest point on or in the box to
* the line segment . If * result is not NULL , it is set to that point .
*/
static float8
box_closept_lseg(Point *result, BOX *box, LSEG *lseg)
{
float8 dist,
d;
Point point,
closept;
LSEG bseg;
if (box_interpt_lseg(result, box, lseg))
return 0 .0 ;
/* pairwise check lseg distances */
point.x = box->low.x;
point.y = box->high.y;
statlseg_construct(&bseg, &box->low, &point);
dist = lseg_closept_lseg(result, &bseg, lseg);
statlseg_construct(&bseg, &box->high, &point);
d = lseg_closept_lseg(&closept, &bseg, lseg);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = closept;
}
point.x = box->high.x;
point.y = box->low.y;
statlseg_construct(&bseg, &box->low, &point);
d = lseg_closept_lseg(&closept, &bseg, lseg);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = closept;
}
statlseg_construct(&bseg, &box->high, &point);
d = lseg_closept_lseg(&closept, &bseg, lseg);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = closept;
}
return dist;
}
Datum
close_sb(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
BOX *box = PG_GETARG_BOX_P(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
if (isnan(box_closept_lseg(result, box, lseg)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*---------------------------------------------------------------------
* on_
* Whether one object lies completely within another .
*-------------------------------------------------------------------*/
/*
* Does the point satisfy the equation ?
*/
static bool
line_contain_point(LINE *line, Point *point)
{
return FPzero(float8_pl(float8_pl(float8_mul(line->A, point->x),
float8_mul(line->B, point->y)),
line->C));
}
Datum
on_pl(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
LINE *line = PG_GETARG_LINE_P(1 );
PG_RETURN_BOOL(line_contain_point(line, pt));
}
/*
* Determine colinearity by detecting a triangle inequality .
* This algorithm seems to behave nicely even with lsb residues - tgl 1997 - 07 - 09
*/
static bool
lseg_contain_point(LSEG *lseg, Point *pt)
{
return FPeq(point_dt(pt, &lseg->p[0 ]) +
point_dt(pt, &lseg->p[1 ]),
point_dt(&lseg->p[0 ], &lseg->p[1 ]));
}
Datum
on_ps(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
LSEG *lseg = PG_GETARG_LSEG_P(1 );
PG_RETURN_BOOL(lseg_contain_point(lseg, pt));
}
/*
* Check whether the point is in the box or on its border
*/
static bool
box_contain_point(BOX *box, Point *point)
{
return box->high.x >= point->x && box->low.x <= point->x &&
box->high.y >= point->y && box->low.y <= point->y;
}
Datum
on_pb(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
BOX *box = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(box_contain_point(box, pt));
}
Datum
box_contain_pt(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
Point *pt = PG_GETARG_POINT_P(1 );
PG_RETURN_BOOL(box_contain_point(box, pt));
}
/* on_ppath -
* Whether a point lies within ( on ) a polyline .
* If open , we have to ( groan ) check each segment .
* ( uses same algorithm as for point intersecting segment - tgl 1997 - 07 - 09 )
* If closed , we use the old O ( n ) ray method for point - in - polygon .
* The ray is horizontal , from pt out to the right .
* Each segment that crosses the ray counts as an
* intersection ; note that an endpoint or edge may touch
* but not cross .
* ( we can do p - in - p in lg ( n ) , but it takes preprocessing )
*/
Datum
on_ppath(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
PATH *path = PG_GETARG_PATH_P(1 );
int i,
n;
float8 a,
b;
/*-- OPEN --*/
if (!path->closed)
{
n = path->npts - 1 ;
a = point_dt(pt, &path->p[0 ]);
for (i = 0 ; i < n; i++)
{
b = point_dt(pt, &path->p[i + 1 ]);
if (FPeq(float8_pl(a, b), point_dt(&path->p[i], &path->p[i + 1 ])))
PG_RETURN_BOOL(true );
a = b;
}
PG_RETURN_BOOL(false );
}
/*-- CLOSED --*/
PG_RETURN_BOOL(point_inside(pt, path->npts, path->p) != 0 );
}
/*
* Check whether the line segment is on the line or close enough
*
* It is , if both of its points are on the line or close enough .
*/
Datum
on_sl(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
LINE *line = PG_GETARG_LINE_P(1 );
PG_RETURN_BOOL(line_contain_point(line, &lseg->p[0 ]) &&
line_contain_point(line, &lseg->p[1 ]));
}
/*
* Check whether the line segment is in the box or on its border
*
* It is , if both of its points are in the box or on its border .
*/
static bool
box_contain_lseg(BOX *box, LSEG *lseg)
{
return box_contain_point(box, &lseg->p[0 ]) &&
box_contain_point(box, &lseg->p[1 ]);
}
Datum
on_sb(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
BOX *box = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(box_contain_lseg(box, lseg));
}
/*---------------------------------------------------------------------
* inter_
* Whether one object intersects another .
*-------------------------------------------------------------------*/
Datum
inter_sl(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
LINE *line = PG_GETARG_LINE_P(1 );
PG_RETURN_BOOL(lseg_interpt_line(NULL, lseg, line));
}
/*
* Do line segment and box intersect ?
*
* Segment completely inside box counts as intersection .
* If you want only segments crossing box boundaries ,
* try converting box to path first .
*
* This function also sets the * result to the closest point on the line
* segment to the center of the box when they overlap and the result is
* not NULL . It is somewhat arbitrary , but maybe the best we can do as
* there are typically two points they intersect .
*
* Optimize for non - intersection by checking for box intersection first .
* - thomas 1998 - 01 - 30
*/
static bool
box_interpt_lseg(Point *result, BOX *box, LSEG *lseg)
{
BOX lbox;
LSEG bseg;
Point point;
lbox.low.x = float8_min(lseg->p[0 ].x, lseg->p[1 ].x);
lbox.low.y = float8_min(lseg->p[0 ].y, lseg->p[1 ].y);
lbox.high.x = float8_max(lseg->p[0 ].x, lseg->p[1 ].x);
lbox.high.y = float8_max(lseg->p[0 ].y, lseg->p[1 ].y);
/* nothing close to overlap? then not going to intersect */
if (!box_ov(&lbox, box))
return false ;
if (result != NULL)
{
box_cn(&point, box);
lseg_closept_point(result, lseg, &point);
}
/* an endpoint of segment is inside box? then clearly intersects */
if (box_contain_point(box, &lseg->p[0 ]) ||
box_contain_point(box, &lseg->p[1 ]))
return true ;
/* pairwise check lseg intersections */
point.x = box->low.x;
point.y = box->high.y;
statlseg_construct(&bseg, &box->low, &point);
if (lseg_interpt_lseg(NULL, &bseg, lseg))
return true ;
statlseg_construct(&bseg, &box->high, &point);
if (lseg_interpt_lseg(NULL, &bseg, lseg))
return true ;
point.x = box->high.x;
point.y = box->low.y;
statlseg_construct(&bseg, &box->low, &point);
if (lseg_interpt_lseg(NULL, &bseg, lseg))
return true ;
statlseg_construct(&bseg, &box->high, &point);
if (lseg_interpt_lseg(NULL, &bseg, lseg))
return true ;
/* if we dropped through, no two segs intersected */
return false ;
}
Datum
inter_sb(PG_FUNCTION_ARGS)
{
LSEG *lseg = PG_GETARG_LSEG_P(0 );
BOX *box = PG_GETARG_BOX_P(1 );
PG_RETURN_BOOL(box_interpt_lseg(NULL, box, lseg));
}
/* inter_lb()
* Do line and box intersect ?
*/
Datum
inter_lb(PG_FUNCTION_ARGS)
{
LINE *line = PG_GETARG_LINE_P(0 );
BOX *box = PG_GETARG_BOX_P(1 );
LSEG bseg;
Point p1,
p2;
/* pairwise check lseg intersections */
p1.x = box->low.x;
p1.y = box->low.y;
p2.x = box->low.x;
p2.y = box->high.y;
statlseg_construct(&bseg, &p1, &p2);
if (lseg_interpt_line(NULL, &bseg, line))
PG_RETURN_BOOL(true );
p1.x = box->high.x;
p1.y = box->high.y;
statlseg_construct(&bseg, &p1, &p2);
if (lseg_interpt_line(NULL, &bseg, line))
PG_RETURN_BOOL(true );
p2.x = box->high.x;
p2.y = box->low.y;
statlseg_construct(&bseg, &p1, &p2);
if (lseg_interpt_line(NULL, &bseg, line))
PG_RETURN_BOOL(true );
p1.x = box->low.x;
p1.y = box->low.y;
statlseg_construct(&bseg, &p1, &p2);
if (lseg_interpt_line(NULL, &bseg, line))
PG_RETURN_BOOL(true );
/* if we dropped through, no intersection */
PG_RETURN_BOOL(false );
}
/*------------------------------------------------------------------
* The following routines define a data type and operator class for
* 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
*------------------------------------------------------------------*/
/*---------------------------------------------------------------------
* Make the smallest bounding box for the given polygon .
*---------------------------------------------------------------------*/
static void
make_bound_box(POLYGON *poly)
{
int i;
float8 x1,
y1,
x2,
y2;
Assert(poly->npts > 0 );
x1 = x2 = poly->p[0 ].x;
y2 = y1 = poly->p[0 ].y;
for (i = 1 ; i < poly->npts; i++)
{
if (float8_lt(poly->p[i].x, x1))
x1 = poly->p[i].x;
if (float8_gt(poly->p[i].x, x2))
x2 = poly->p[i].x;
if (float8_lt(poly->p[i].y, y1))
y1 = poly->p[i].y;
if (float8_gt(poly->p[i].y, y2))
y2 = poly->p[i].y;
}
poly->boundbox.low.x = x1;
poly->boundbox.high.x = x2;
poly->boundbox.low.y = y1;
poly->boundbox.high.y = y2;
}
/*------------------------------------------------------------------
* 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 ) "
*------------------------------------------------------------------*/
Datum
poly_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0 );
Node *escontext = fcinfo->context;
POLYGON *poly;
int npts;
int size;
int base_size;
bool isopen;
if ((npts = pair_count(str, ',' )) <= 0 )
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \" %s\"" ,
"polygon" , str)));
base_size = sizeof (poly->p[0 ]) * npts;
size = offsetof(POLYGON, p) + base_size;
/* Check for integer overflow */
if (base_size / npts != sizeof (poly->p[0 ]) || size <= base_size)
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested" )));
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
poly->npts = npts;
if (!path_decode(str, false , npts, &(poly->p[0 ]), &isopen, NULL, "polygon" ,
str, escontext))
PG_RETURN_NULL();
make_bound_box(poly);
PG_RETURN_POLYGON_P(poly);
}
/*---------------------------------------------------------------
* poly_out - convert internal POLYGON representation to the
* character string format " ( ( f8 , f8 ) , . . . , ( f8 , f8 ) ) "
*---------------------------------------------------------------*/
Datum
poly_out(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0 );
PG_RETURN_CSTRING(path_encode(PATH_CLOSED, poly->npts, poly->p));
}
/*
* poly_recv - converts external binary format to polygon
*
* External representation is int32 number of points , and the points .
* We recompute the bounding box on read , instead of trusting it to
* be valid . ( Checking it would take just as long , so may as well
* omit it from external representation . )
*/
Datum
poly_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0 );
POLYGON *poly;
int32 npts;
int32 i;
int size;
npts = pq_getmsgint(buf, sizeof (int32));
if (npts <= 0 || npts >= (int32) ((INT_MAX - offsetof(POLYGON, p)) / sizeof (Point)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid number of points in external \" polygon\" value" )));
size = offsetof(POLYGON, p) + sizeof (poly->p[0 ]) * npts;
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
poly->npts = npts;
for (i = 0 ; i < npts; i++)
{
poly->p[i].x = pq_getmsgfloat8(buf);
poly->p[i].y = pq_getmsgfloat8(buf);
}
make_bound_box(poly);
PG_RETURN_POLYGON_P(poly);
}
/*
* poly_send - converts polygon to binary format
*/
Datum
poly_send(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0 );
StringInfoData buf;
int32 i;
pq_begintypsend(&buf);
pq_sendint32(&buf, poly->npts);
for (i = 0 ; i < poly->npts; i++)
{
pq_sendfloat8(&buf, poly->p[i].x);
pq_sendfloat8(&buf, poly->p[i].y);
}
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*-------------------------------------------------------
* 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 ?
*-------------------------------------------------------*/
Datum
poly_left(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
result = polya->boundbox.high.x < polyb->boundbox.low.x;
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*-------------------------------------------------------
* Is polygon A overlapping or left of polygon B ? i . e . is
* the right most point of A at or left of the right most point
* of B ?
*-------------------------------------------------------*/
Datum
poly_overleft(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
result = polya->boundbox.high.x <= polyb->boundbox.high.x;
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*-------------------------------------------------------
* Is polygon A strictly right of polygon B ? i . e . is
* the left most point of A right of the right most point
* of B ?
*-------------------------------------------------------*/
Datum
poly_right(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
result = polya->boundbox.low.x > polyb->boundbox.high.x;
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*-------------------------------------------------------
* Is polygon A overlapping or right of polygon B ? i . e . is
* the left most point of A at or right of the left most point
* of B ?
*-------------------------------------------------------*/
Datum
poly_overright(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
result = polya->boundbox.low.x >= polyb->boundbox.low.x;
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*-------------------------------------------------------
* Is polygon A strictly below polygon B ? i . e . is
* the upper most point of A below the lower most point
* of B ?
*-------------------------------------------------------*/
Datum
poly_below(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
result = polya->boundbox.high.y < polyb->boundbox.low.y;
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*-------------------------------------------------------
* Is polygon A overlapping or below polygon B ? i . e . is
* the upper most point of A at or below the upper most point
* of B ?
*-------------------------------------------------------*/
Datum
poly_overbelow(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
result = polya->boundbox.high.y <= polyb->boundbox.high.y;
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*-------------------------------------------------------
* Is polygon A strictly above polygon B ? i . e . is
* the lower most point of A above the upper most point
* of B ?
*-------------------------------------------------------*/
Datum
poly_above(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
result = polya->boundbox.low.y > polyb->boundbox.high.y;
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*-------------------------------------------------------
* Is polygon A overlapping or above polygon B ? i . e . is
* the lower most point of A at or above the lower most point
* of B ?
*-------------------------------------------------------*/
Datum
poly_overabove(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
result = polya->boundbox.low.y >= polyb->boundbox.low.y;
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*-------------------------------------------------------
* Is polygon A the same as polygon B ? i . e . are all the
* points the same ?
* Check all points for matches in both forward and reverse
* direction since polygons are non - directional and are
* closed shapes .
*-------------------------------------------------------*/
Datum
poly_same(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
if (polya->npts != polyb->npts)
result = false ;
else
result = plist_same(polya->npts, polya->p, polyb->p);
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*-----------------------------------------------------------------
* Determine if polygon A overlaps polygon B
*-----------------------------------------------------------------*/
static bool
poly_overlap_internal(POLYGON *polya, POLYGON *polyb)
{
bool result;
Assert(polya->npts > 0 && polyb->npts > 0 );
/* Quick check by bounding box */
result = box_ov(&polya->boundbox, &polyb->boundbox);
/*
* Brute - force algorithm - try to find intersected edges , if so then
* polygons are overlapped else check is one polygon inside other or not
* by testing single point of them .
*/
if (result)
{
int ia,
ib;
LSEG sa,
sb;
/* Init first of polya's edge with last point */
sa.p[0 ] = polya->p[polya->npts - 1 ];
result = false ;
for (ia = 0 ; ia < polya->npts && !result; ia++)
{
/* Second point of polya's edge is a current one */
sa.p[1 ] = polya->p[ia];
/* Init first of polyb's edge with last point */
sb.p[0 ] = polyb->p[polyb->npts - 1 ];
for (ib = 0 ; ib < polyb->npts && !result; ib++)
{
sb.p[1 ] = polyb->p[ib];
result = lseg_interpt_lseg(NULL, &sa, &sb);
sb.p[0 ] = sb.p[1 ];
}
/*
* move current endpoint to the first point of next edge
*/
sa.p[0 ] = sa.p[1 ];
}
if (!result)
{
result = (point_inside(polya->p, polyb->npts, polyb->p) ||
point_inside(polyb->p, polya->npts, polya->p));
}
}
return result;
}
Datum
poly_overlap(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
result = poly_overlap_internal(polya, polyb);
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*
* Tests special kind of segment for in / out of polygon .
* Special kind means :
* - point a should be on segment s
* - segment ( a , b ) should not be contained by s
* Returns true if :
* - segment ( a , b ) is collinear to s and ( a , b ) is in polygon
* - segment ( a , b ) s not collinear to s . Note : that doesn ' t
* mean that segment is in polygon !
*/
static bool
touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
{
/* point a is on s, b is not */
LSEG t;
t.p[0 ] = *a;
t.p[1 ] = *b;
if (point_eq_point(a, s->p))
{
if (lseg_contain_point(&t, s->p + 1 ))
return lseg_inside_poly(b, s->p + 1 , poly, start);
}
else if (point_eq_point(a, s->p + 1 ))
{
if (lseg_contain_point(&t, s->p))
return lseg_inside_poly(b, s->p, poly, start);
}
else if (lseg_contain_point(&t, s->p))
{
return lseg_inside_poly(b, s->p, poly, start);
}
else if (lseg_contain_point(&t, s->p + 1 ))
{
return lseg_inside_poly(b, s->p + 1 , poly, start);
}
return true ; /* may be not true, but that will check later */
}
/*
* Returns true if segment ( a , b ) is in polygon , option
* start is used for optimization - function checks
* polygon ' s edges starting from start
*/
static bool
lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
{
LSEG s,
t;
int i;
bool res = true ,
intersection = false ;
/* since this function recurses, it could be driven to stack overflow */
check_stack_depth();
t.p[0 ] = *a;
t.p[1 ] = *b;
s.p[0 ] = poly->p[(start == 0 ) ? (poly->npts - 1 ) : (start - 1 )];
for (i = start; i < poly->npts && res; i++)
{
Point interpt;
CHECK_FOR_INTERRUPTS();
s.p[1 ] = poly->p[i];
if (lseg_contain_point(&s, t.p))
{
if (lseg_contain_point(&s, t.p + 1 ))
return true ; /* t is contained by s */
/* Y-cross */
res = touched_lseg_inside_poly(t.p, t.p + 1 , &s, poly, i + 1 );
}
else if (lseg_contain_point(&s, t.p + 1 ))
{
/* Y-cross */
res = touched_lseg_inside_poly(t.p + 1 , t.p, &s, poly, i + 1 );
}
else if (lseg_interpt_lseg(&interpt, &t, &s))
{
/*
* segments are X - crossing , go to check each subsegment
*/
intersection = true ;
res = lseg_inside_poly(t.p, &interpt, poly, i + 1 );
if (res)
res = lseg_inside_poly(t.p + 1 , &interpt, poly, i + 1 );
}
s.p[0 ] = s.p[1 ];
}
if (res && !intersection)
{
Point p;
/*
* if X - intersection wasn ' t found , then check central point of tested
* segment . In opposite case we already check all subsegments
*/
p.x = float8_div(float8_pl(t.p[0 ].x, t.p[1 ].x), 2 .0 );
p.y = float8_div(float8_pl(t.p[0 ].y, t.p[1 ].y), 2 .0 );
res = point_inside(&p, poly->npts, poly->p);
}
return res;
}
/*
* Check whether the first polygon contains the second
*/
static bool
poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly)
{
int i;
LSEG s;
Assert(contains_poly->npts > 0 && contained_poly->npts > 0 );
/*
* Quick check to see if contained ' s bounding box is contained in
* contains ' bb .
*/
if (!box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox))
return false ;
s.p[0 ] = contained_poly->p[contained_poly->npts - 1 ];
for (i = 0 ; i < contained_poly->npts; i++)
{
s.p[1 ] = contained_poly->p[i];
if (!lseg_inside_poly(s.p, s.p + 1 , contains_poly, 0 ))
return false ;
s.p[0 ] = s.p[1 ];
}
return true ;
}
Datum
poly_contain(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
result = poly_contain_poly(polya, polyb);
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
/*-----------------------------------------------------------------
* Determine if polygon A is contained by polygon B
*-----------------------------------------------------------------*/
Datum
poly_contained(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
bool result;
/* Just switch the arguments and pass it off to poly_contain */
result = poly_contain_poly(polyb, polya);
/*
* Avoid leaking memory for toasted inputs . . . needed for rtree indexes
*/
PG_FREE_IF_COPY(polya, 0 );
PG_FREE_IF_COPY(polyb, 1 );
PG_RETURN_BOOL(result);
}
Datum
poly_contain_pt(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0 );
Point *p = PG_GETARG_POINT_P(1 );
PG_RETURN_BOOL(point_inside(p, poly->npts, poly->p) != 0 );
}
Datum
pt_contained_poly(PG_FUNCTION_ARGS)
{
Point *p = PG_GETARG_POINT_P(0 );
POLYGON *poly = PG_GETARG_POLYGON_P(1 );
PG_RETURN_BOOL(point_inside(p, poly->npts, poly->p) != 0 );
}
Datum
poly_distance(PG_FUNCTION_ARGS)
{
POLYGON *polya = PG_GETARG_POLYGON_P(0 );
POLYGON *polyb = PG_GETARG_POLYGON_P(1 );
float8 min = 0 .0 ; /* initialize to keep compiler quiet */
bool have_min = false ;
float8 tmp;
int i,
j;
LSEG seg1,
seg2;
/*
* Distance is zero if polygons overlap . We must check this because the
* path distance will not give the right answer if one poly is entirely
* within the other .
*/
if (poly_overlap_internal(polya, polyb))
PG_RETURN_FLOAT8(0 .0 );
/*
* When they don ' t overlap , the distance calculation is identical to that
* for closed paths ( i . e . , we needn ' t care about the fact that polygons
* include their contained areas ) . See path_distance ( ) .
*/
for (i = 0 ; i < polya->npts; i++)
{
int iprev;
if (i > 0 )
iprev = i - 1 ;
else
iprev = polya->npts - 1 ;
for (j = 0 ; j < polyb->npts; j++)
{
int jprev;
if (j > 0 )
jprev = j - 1 ;
else
jprev = polyb->npts - 1 ;
statlseg_construct(&seg1, &polya->p[iprev], &polya->p[i]);
statlseg_construct(&seg2, &polyb->p[jprev], &polyb->p[j]);
tmp = lseg_closept_lseg(NULL, &seg1, &seg2);
if (!have_min || float8_lt(tmp, min))
{
min = tmp;
have_min = true ;
}
}
}
if (!have_min)
PG_RETURN_NULL();
PG_RETURN_FLOAT8(min);
}
/***********************************************************************
* *
* * Routines for 2 D points .
* *
***********************************************************************/
Datum
construct_point(PG_FUNCTION_ARGS)
{
float8 x = PG_GETARG_FLOAT8(0 );
float8 y = PG_GETARG_FLOAT8(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
point_construct(result, x, y);
PG_RETURN_POINT_P(result);
}
static inline void
point_add_point(Point *result, Point *pt1, Point *pt2)
{
point_construct(result,
float8_pl(pt1->x, pt2->x),
float8_pl(pt1->y, pt2->y));
}
Datum
point_add(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0 );
Point *p2 = PG_GETARG_POINT_P(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
point_add_point(result, p1, p2);
PG_RETURN_POINT_P(result);
}
static inline void
point_sub_point(Point *result, Point *pt1, Point *pt2)
{
point_construct(result,
float8_mi(pt1->x, pt2->x),
float8_mi(pt1->y, pt2->y));
}
Datum
point_sub(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0 );
Point *p2 = PG_GETARG_POINT_P(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
point_sub_point(result, p1, p2);
PG_RETURN_POINT_P(result);
}
static inline void
point_mul_point(Point *result, Point *pt1, Point *pt2)
{
point_construct(result,
float8_mi(float8_mul(pt1->x, pt2->x),
float8_mul(pt1->y, pt2->y)),
float8_pl(float8_mul(pt1->x, pt2->y),
float8_mul(pt1->y, pt2->x)));
}
Datum
point_mul(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0 );
Point *p2 = PG_GETARG_POINT_P(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
point_mul_point(result, p1, p2);
PG_RETURN_POINT_P(result);
}
static inline void
point_div_point(Point *result, Point *pt1, Point *pt2)
{
float8 div;
div = float8_pl(float8_mul(pt2->x, pt2->x), float8_mul(pt2->y, pt2->y));
point_construct(result,
float8_div(float8_pl(float8_mul(pt1->x, pt2->x),
float8_mul(pt1->y, pt2->y)), div),
float8_div(float8_mi(float8_mul(pt1->y, pt2->x),
float8_mul(pt1->x, pt2->y)), div));
}
Datum
point_div(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0 );
Point *p2 = PG_GETARG_POINT_P(1 );
Point *result;
result = (Point *) palloc(sizeof (Point));
point_div_point(result, p1, p2);
PG_RETURN_POINT_P(result);
}
/***********************************************************************
* *
* * Routines for 2 D boxes .
* *
***********************************************************************/
Datum
points_box(PG_FUNCTION_ARGS)
{
Point *p1 = PG_GETARG_POINT_P(0 );
Point *p2 = PG_GETARG_POINT_P(1 );
BOX *result;
result = (BOX *) palloc(sizeof (BOX));
box_construct(result, p1, p2);
PG_RETURN_BOX_P(result);
}
Datum
box_add(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
Point *p = PG_GETARG_POINT_P(1 );
BOX *result;
result = (BOX *) palloc(sizeof (BOX));
point_add_point(&result->high, &box->high, p);
point_add_point(&result->low, &box->low, p);
PG_RETURN_BOX_P(result);
}
Datum
box_sub(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
Point *p = PG_GETARG_POINT_P(1 );
BOX *result;
result = (BOX *) palloc(sizeof (BOX));
point_sub_point(&result->high, &box->high, p);
point_sub_point(&result->low, &box->low, p);
PG_RETURN_BOX_P(result);
}
Datum
box_mul(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
Point *p = PG_GETARG_POINT_P(1 );
BOX *result;
Point high,
low;
result = (BOX *) palloc(sizeof (BOX));
point_mul_point(&high, &box->high, p);
point_mul_point(&low, &box->low, p);
box_construct(result, &high, &low);
PG_RETURN_BOX_P(result);
}
Datum
box_div(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
Point *p = PG_GETARG_POINT_P(1 );
BOX *result;
Point high,
low;
result = (BOX *) palloc(sizeof (BOX));
point_div_point(&high, &box->high, p);
point_div_point(&low, &box->low, p);
box_construct(result, &high, &low);
PG_RETURN_BOX_P(result);
}
/*
* Convert point to empty box
*/
Datum
point_box(PG_FUNCTION_ARGS)
{
Point *pt = PG_GETARG_POINT_P(0 );
BOX *box;
box = (BOX *) palloc(sizeof (BOX));
box->high.x = pt->x;
box->low.x = pt->x;
box->high.y = pt->y;
box->low.y = pt->y;
PG_RETURN_BOX_P(box);
}
/*
* Smallest bounding box that includes both of the given boxes
*/
Datum
boxes_bound_box(PG_FUNCTION_ARGS)
{
BOX *box1 = PG_GETARG_BOX_P(0 ),
*box2 = PG_GETARG_BOX_P(1 ),
*container;
container = (BOX *) palloc(sizeof (BOX));
container->high.x = float8_max(box1->high.x, box2->high.x);
container->low.x = float8_min(box1->low.x, box2->low.x);
container->high.y = float8_max(box1->high.y, box2->high.y);
container->low.y = float8_min(box1->low.y, box2->low.y);
PG_RETURN_BOX_P(container);
}
/***********************************************************************
* *
* * Routines for 2 D paths .
* *
***********************************************************************/
/* path_add()
* Concatenate two paths ( only if they are both open ) .
*/
Datum
path_add(PG_FUNCTION_ARGS)
{
PATH *p1 = PG_GETARG_PATH_P(0 );
PATH *p2 = PG_GETARG_PATH_P(1 );
PATH *result;
int size,
base_size;
int i;
if (p1->closed || p2->closed)
PG_RETURN_NULL();
base_size = sizeof (p1->p[0 ]) * (p1->npts + p2->npts);
size = offsetof(PATH, p) + base_size;
/* Check for integer overflow */
if (base_size / sizeof (p1->p[0 ]) != (p1->npts + p2->npts) ||
size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested" )));
result = (PATH *) palloc(size);
SET_VARSIZE(result, size);
result->npts = (p1->npts + p2->npts);
result->closed = p1->closed;
/* prevent instability in unused pad bytes */
result->dummy = 0 ;
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;
}
PG_RETURN_PATH_P(result);
}
/* path_add_pt()
* Translation operators .
*/
Datum
path_add_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0 );
Point *point = PG_GETARG_POINT_P(1 );
int i;
for (i = 0 ; i < path->npts; i++)
point_add_point(&path->p[i], &path->p[i], point);
PG_RETURN_PATH_P(path);
}
Datum
path_sub_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0 );
Point *point = PG_GETARG_POINT_P(1 );
int i;
for (i = 0 ; i < path->npts; i++)
point_sub_point(&path->p[i], &path->p[i], point);
PG_RETURN_PATH_P(path);
}
/* path_mul_pt()
* Rotation and scaling operators .
*/
Datum
path_mul_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0 );
Point *point = PG_GETARG_POINT_P(1 );
int i;
for (i = 0 ; i < path->npts; i++)
point_mul_point(&path->p[i], &path->p[i], point);
PG_RETURN_PATH_P(path);
}
Datum
path_div_pt(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P_COPY(0 );
Point *point = PG_GETARG_POINT_P(1 );
int i;
for (i = 0 ; i < path->npts; i++)
point_div_point(&path->p[i], &path->p[i], point);
PG_RETURN_PATH_P(path);
}
Datum
path_poly(PG_FUNCTION_ARGS)
{
PATH *path = PG_GETARG_PATH_P(0 );
POLYGON *poly;
int size;
int i;
/* This is not very consistent --- other similar cases return NULL ... */
if (!path->closed)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("open path cannot be converted to polygon" )));
/*
* Never overflows : the old size fit in MaxAllocSize , and the new size is
* just a small constant larger .
*/
size = offsetof(POLYGON, p) + sizeof (poly->p[0 ]) * path->npts;
poly = (POLYGON *) palloc(size);
SET_VARSIZE(poly, 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);
PG_RETURN_POLYGON_P(poly);
}
/***********************************************************************
* *
* * Routines for 2 D polygons .
* *
***********************************************************************/
Datum
poly_npoints(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0 );
PG_RETURN_INT32(poly->npts);
}
Datum
poly_center(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0 );
Point *result;
CIRCLE circle;
result = (Point *) palloc(sizeof (Point));
poly_to_circle(&circle, poly);
*result = circle.center;
PG_RETURN_POINT_P(result);
}
Datum
poly_box(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0 );
BOX *box;
box = (BOX *) palloc(sizeof (BOX));
*box = poly->boundbox;
PG_RETURN_BOX_P(box);
}
/* box_poly()
* Convert a box to a polygon .
*/
Datum
box_poly(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
POLYGON *poly;
int size;
/* map four corners of the box to a polygon */
size = offsetof(POLYGON, p) + sizeof (poly->p[0 ]) * 4 ;
poly = (POLYGON *) palloc(size);
SET_VARSIZE(poly, 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_construct(&poly->boundbox, &box->high, &box->low);
PG_RETURN_POLYGON_P(poly);
}
Datum
poly_path(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0 );
PATH *path;
int size;
int i;
/*
* Never overflows : the old size fit in MaxAllocSize , and the new size is
* smaller by a small constant .
*/
size = offsetof(PATH, p) + sizeof (path->p[0 ]) * poly->npts;
path = (PATH *) palloc(size);
SET_VARSIZE(path, size);
path->npts = poly->npts;
path->closed = true ;
/* prevent instability in unused pad bytes */
path->dummy = 0 ;
for (i = 0 ; i < poly->npts; i++)
{
path->p[i].x = poly->p[i].x;
path->p[i].y = poly->p[i].y;
}
PG_RETURN_PATH_P(path);
}
/***********************************************************************
* *
* * 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 "
*/
Datum
circle_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0 );
Node *escontext = fcinfo->context;
CIRCLE *circle = (CIRCLE *) palloc(sizeof (CIRCLE));
char *s,
*cp;
int depth = 0 ;
s = str;
while (isspace((unsigned char ) *s))
s++;
if (*s == LDELIM_C)
depth++, s++;
else if (*s == LDELIM)
{
/* If there are two left parens, consume the first one */
cp = (s + 1 );
while (isspace((unsigned char ) *cp))
cp++;
if (*cp == LDELIM)
depth++, s = cp;
}
/* pair_decode will consume parens around the pair, if any */
if (!pair_decode(s, &circle->center.x, &circle->center.y, &s, "circle" , str,
escontext))
PG_RETURN_NULL();
if (*s == DELIM)
s++;
if (!single_decode(s, &circle->radius, &s, "circle" , str, escontext))
PG_RETURN_NULL();
/* We have to accept NaN. */
if (circle->radius < 0 .0 )
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \" %s\"" ,
"circle" , str)));
while (depth > 0 )
{
if ((*s == RDELIM) || ((*s == RDELIM_C) && (depth == 1 )))
{
depth--;
s++;
while (isspace((unsigned char ) *s))
s++;
}
else
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \" %s\"" ,
"circle" , str)));
}
if (*s != '\0' )
ereturn(escontext, (Datum) 0 ,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \" %s\"" ,
"circle" , str)));
PG_RETURN_CIRCLE_P(circle);
}
/* circle_out - convert a circle to external form.
*/
Datum
circle_out(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
StringInfoData str;
initStringInfo(&str);
appendStringInfoChar(&str, LDELIM_C);
appendStringInfoChar(&str, LDELIM);
pair_encode(circle->center.x, circle->center.y, &str);
appendStringInfoChar(&str, RDELIM);
appendStringInfoChar(&str, DELIM);
single_encode(circle->radius, &str);
appendStringInfoChar(&str, RDELIM_C);
PG_RETURN_CSTRING(str.data);
}
/*
* circle_recv - converts external binary format to circle
*/
Datum
circle_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0 );
CIRCLE *circle;
circle = (CIRCLE *) palloc(sizeof (CIRCLE));
circle->center.x = pq_getmsgfloat8(buf);
circle->center.y = pq_getmsgfloat8(buf);
circle->radius = pq_getmsgfloat8(buf);
/* We have to accept NaN. */
if (circle->radius < 0 .0 )
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid radius in external \" circle\" value" )));
PG_RETURN_CIRCLE_P(circle);
}
/*
* circle_send - converts circle to binary format
*/
Datum
circle_send(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
StringInfoData buf;
pq_begintypsend(&buf);
pq_sendfloat8(&buf, circle->center.x);
pq_sendfloat8(&buf, circle->center.y);
pq_sendfloat8(&buf, circle->radius);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*----------------------------------------------------------
* Relational operators for CIRCLEs .
* < , > , < = , > = , and = = are based on circle area .
*---------------------------------------------------------*/
/* circles identical?
*
* We consider NaNs values to be equal to each other to let those circles
* to be found .
*/
Datum
circle_same(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(((isnan(circle1->radius) && isnan(circle2->radius)) ||
FPeq(circle1->radius, circle2->radius)) &&
point_eq_point(&circle1->center, &circle2->center));
}
/* circle_overlap - does circle1 overlap circle2?
*/
Datum
circle_overlap(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center),
float8_pl(circle1->radius, circle2->radius)));
}
/* circle_overleft - is the right edge of circle1 at or left of
* the right edge of circle2 ?
*/
Datum
circle_overleft(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPle(float8_pl(circle1->center.x, circle1->radius),
float8_pl(circle2->center.x, circle2->radius)));
}
/* circle_left - is circle1 strictly left of circle2?
*/
Datum
circle_left(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPlt(float8_pl(circle1->center.x, circle1->radius),
float8_mi(circle2->center.x, circle2->radius)));
}
/* circle_right - is circle1 strictly right of circle2?
*/
Datum
circle_right(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPgt(float8_mi(circle1->center.x, circle1->radius),
float8_pl(circle2->center.x, circle2->radius)));
}
/* circle_overright - is the left edge of circle1 at or right of
* the left edge of circle2 ?
*/
Datum
circle_overright(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPge(float8_mi(circle1->center.x, circle1->radius),
float8_mi(circle2->center.x, circle2->radius)));
}
/* circle_contained - is circle1 contained by circle2?
*/
Datum
circle_contained(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center),
float8_mi(circle2->radius, circle1->radius)));
}
/* circle_contain - does circle1 contain circle2?
*/
Datum
circle_contain(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center),
float8_mi(circle1->radius, circle2->radius)));
}
/* circle_below - is circle1 strictly below circle2?
*/
Datum
circle_below(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPlt(float8_pl(circle1->center.y, circle1->radius),
float8_mi(circle2->center.y, circle2->radius)));
}
/* circle_above - is circle1 strictly above circle2?
*/
Datum
circle_above(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPgt(float8_mi(circle1->center.y, circle1->radius),
float8_pl(circle2->center.y, circle2->radius)));
}
/* circle_overbelow - is the upper edge of circle1 at or below
* the upper edge of circle2 ?
*/
Datum
circle_overbelow(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPle(float8_pl(circle1->center.y, circle1->radius),
float8_pl(circle2->center.y, circle2->radius)));
}
/* circle_overabove - is the lower edge of circle1 at or above
* the lower edge of circle2 ?
*/
Datum
circle_overabove(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPge(float8_mi(circle1->center.y, circle1->radius),
float8_mi(circle2->center.y, circle2->radius)));
}
/* circle_relop - is area(circle1) relop area(circle2), within
* our accuracy constraint ?
*/
Datum
circle_eq(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPeq(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_ne(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPne(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_lt(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPlt(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_gt(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPgt(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_le(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPle(circle_ar(circle1), circle_ar(circle2)));
}
Datum
circle_ge(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
PG_RETURN_BOOL(FPge(circle_ar(circle1), circle_ar(circle2)));
}
/*----------------------------------------------------------
* " Arithmetic " operators on circles .
*---------------------------------------------------------*/
/* circle_add_pt()
* Translation operator .
*/
Datum
circle_add_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
Point *point = PG_GETARG_POINT_P(1 );
CIRCLE *result;
result = (CIRCLE *) palloc(sizeof (CIRCLE));
point_add_point(&result->center, &circle->center, point);
result->radius = circle->radius;
PG_RETURN_CIRCLE_P(result);
}
Datum
circle_sub_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
Point *point = PG_GETARG_POINT_P(1 );
CIRCLE *result;
result = (CIRCLE *) palloc(sizeof (CIRCLE));
point_sub_point(&result->center, &circle->center, point);
result->radius = circle->radius;
PG_RETURN_CIRCLE_P(result);
}
/* circle_mul_pt()
* Rotation and scaling operators .
*/
Datum
circle_mul_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
Point *point = PG_GETARG_POINT_P(1 );
CIRCLE *result;
result = (CIRCLE *) palloc(sizeof (CIRCLE));
point_mul_point(&result->center, &circle->center, point);
result->radius = float8_mul(circle->radius, HYPOT(point->x, point->y));
PG_RETURN_CIRCLE_P(result);
}
Datum
circle_div_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
Point *point = PG_GETARG_POINT_P(1 );
CIRCLE *result;
result = (CIRCLE *) palloc(sizeof (CIRCLE));
point_div_point(&result->center, &circle->center, point);
result->radius = float8_div(circle->radius, HYPOT(point->x, point->y));
PG_RETURN_CIRCLE_P(result);
}
/* circle_area - returns the area of the circle.
*/
Datum
circle_area(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
PG_RETURN_FLOAT8(circle_ar(circle));
}
/* circle_diameter - returns the diameter of the circle.
*/
Datum
circle_diameter(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
PG_RETURN_FLOAT8(float8_mul(circle->radius, 2 .0 ));
}
/* circle_radius - returns the radius of the circle.
*/
Datum
circle_radius(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
PG_RETURN_FLOAT8(circle->radius);
}
/* circle_distance - returns the distance between
* two circles .
*/
Datum
circle_distance(PG_FUNCTION_ARGS)
{
CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0 );
CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1 );
float8 result;
result = float8_mi(point_dt(&circle1->center, &circle2->center),
float8_pl(circle1->radius, circle2->radius));
if (result < 0 .0 )
result = 0 .0 ;
PG_RETURN_FLOAT8(result);
}
Datum
circle_contain_pt(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
Point *point = PG_GETARG_POINT_P(1 );
float8 d;
d = point_dt(&circle->center, point);
PG_RETURN_BOOL(d <= circle->radius);
}
Datum
pt_contained_circle(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0 );
CIRCLE *circle = PG_GETARG_CIRCLE_P(1 );
float8 d;
d = point_dt(&circle->center, point);
PG_RETURN_BOOL(d <= circle->radius);
}
/* dist_pc - returns the distance between
* a point and a circle .
*/
Datum
dist_pc(PG_FUNCTION_ARGS)
{
Point *point = PG_GETARG_POINT_P(0 );
CIRCLE *circle = PG_GETARG_CIRCLE_P(1 );
float8 result;
result = float8_mi(point_dt(point, &circle->center),
circle->radius);
if (result < 0 .0 )
result = 0 .0 ;
PG_RETURN_FLOAT8(result);
}
/*
* Distance from a circle to a point
*/
Datum
dist_cpoint(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
Point *point = PG_GETARG_POINT_P(1 );
float8 result;
result = float8_mi(point_dt(point, &circle->center), circle->radius);
if (result < 0 .0 )
result = 0 .0 ;
PG_RETURN_FLOAT8(result);
}
/* circle_center - returns the center point of the circle.
*/
Datum
circle_center(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
Point *result;
result = (Point *) palloc(sizeof (Point));
result->x = circle->center.x;
result->y = circle->center.y;
PG_RETURN_POINT_P(result);
}
/* circle_ar - returns the area of the circle.
*/
static float8
circle_ar(CIRCLE *circle)
{
return float8_mul(float8_mul(circle->radius, circle->radius), M_PI);
}
/*----------------------------------------------------------
* Conversion operators .
*---------------------------------------------------------*/
Datum
cr_circle(PG_FUNCTION_ARGS)
{
Point *center = PG_GETARG_POINT_P(0 );
float8 radius = PG_GETARG_FLOAT8(1 );
CIRCLE *result;
result = (CIRCLE *) palloc(sizeof (CIRCLE));
result->center.x = center->x;
result->center.y = center->y;
result->radius = radius;
PG_RETURN_CIRCLE_P(result);
}
Datum
circle_box(PG_FUNCTION_ARGS)
{
CIRCLE *circle = PG_GETARG_CIRCLE_P(0 );
BOX *box;
float8 delta;
box = (BOX *) palloc(sizeof (BOX));
delta = float8_div(circle->radius, sqrt(2 .0 ));
box->high.x = float8_pl(circle->center.x, delta);
box->low.x = float8_mi(circle->center.x, delta);
box->high.y = float8_pl(circle->center.y, delta);
box->low.y = float8_mi(circle->center.y, delta);
PG_RETURN_BOX_P(box);
}
/* box_circle()
* Convert a box to a circle .
*/
Datum
box_circle(PG_FUNCTION_ARGS)
{
BOX *box = PG_GETARG_BOX_P(0 );
CIRCLE *circle;
circle = (CIRCLE *) palloc(sizeof (CIRCLE));
circle->center.x = float8_div(float8_pl(box->high.x, box->low.x), 2 .0 );
circle->center.y = float8_div(float8_pl(box->high.y, box->low.y), 2 .0 );
circle->radius = point_dt(&circle->center, &box->high);
PG_RETURN_CIRCLE_P(circle);
}
Datum
circle_poly(PG_FUNCTION_ARGS)
{
int32 npts = PG_GETARG_INT32(0 );
CIRCLE *circle = PG_GETARG_CIRCLE_P(1 );
POLYGON *poly;
int base_size,
size;
int i;
float8 angle;
float8 anglestep;
if (FPzero(circle->radius))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert circle with radius zero to polygon" )));
if (npts < 2 )
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("must request at least 2 points" )));
base_size = sizeof (poly->p[0 ]) * npts;
size = offsetof(POLYGON, p) + base_size;
/* Check for integer overflow */
if (base_size / npts != sizeof (poly->p[0 ]) || size <= base_size)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many points requested" )));
poly = (POLYGON *) palloc0(size); /* zero any holes */
SET_VARSIZE(poly, size);
poly->npts = npts;
anglestep = float8_div(2 .0 * M_PI, npts);
for (i = 0 ; i < npts; i++)
{
angle = float8_mul(anglestep, i);
poly->p[i].x = float8_mi(circle->center.x,
float8_mul(circle->radius, cos(angle)));
poly->p[i].y = float8_pl(circle->center.y,
float8_mul(circle->radius, sin(angle)));
}
make_bound_box(poly);
PG_RETURN_POLYGON_P(poly);
}
/*
* Convert polygon to circle
*
* The result must be preallocated .
*
* XXX This algorithm should use weighted means of line segments
* rather than straight average values of points - tgl 97 / 01 / 21 .
*/
static void
poly_to_circle(CIRCLE *result, POLYGON *poly)
{
int i;
Assert(poly->npts > 0 );
result->center.x = 0 ;
result->center.y = 0 ;
result->radius = 0 ;
for (i = 0 ; i < poly->npts; i++)
point_add_point(&result->center, &result->center, &poly->p[i]);
result->center.x = float8_div(result->center.x, poly->npts);
result->center.y = float8_div(result->center.y, poly->npts);
for (i = 0 ; i < poly->npts; i++)
result->radius = float8_pl(result->radius,
point_dt(&poly->p[i], &result->center));
result->radius = float8_div(result->radius, poly->npts);
}
Datum
poly_circle(PG_FUNCTION_ARGS)
{
POLYGON *poly = PG_GETARG_POLYGON_P(0 );
CIRCLE *result;
result = (CIRCLE *) palloc(sizeof (CIRCLE));
poly_to_circle(result, poly);
PG_RETURN_CIRCLE_P(result);
}
/***********************************************************************
* *
* * Private routines for multiple types .
* *
***********************************************************************/
/*
* Test to see if the point is inside the polygon , returns 1 / 0 , or 2 if
* the point is on the polygon .
* Code adapted but not copied from integer - based routines in WN : A
* Server for the HTTP
* version 1 . 15 . 1 , file wn / image . c
* http : //hopf.math.northwestern.edu/index.html
* Description of algorithm : http : //www.linuxjournal.com/article/2197
* http : //www.linuxjournal.com/article/2029
*/
#define POINT_ON_POLYGON INT_MAX
static int
point_inside(Point *p, int npts, Point *plist)
{
float8 x0,
y0;
float8 prev_x,
prev_y;
int i = 0 ;
float8 x,
y;
int cross,
total_cross = 0 ;
Assert(npts > 0 );
/* compute first polygon point relative to single point */
x0 = float8_mi(plist[0 ].x, p->x);
y0 = float8_mi(plist[0 ].y, p->y);
prev_x = x0;
prev_y = y0;
/* loop over polygon points and aggregate total_cross */
for (i = 1 ; i < npts; i++)
{
/* compute next polygon point relative to single point */
x = float8_mi(plist[i].x, p->x);
y = float8_mi(plist[i].y, p->y);
/* compute previous to current point crossing */
if ((cross = lseg_crossing(x, y, prev_x, prev_y)) == POINT_ON_POLYGON)
return 2 ;
total_cross += cross;
prev_x = x;
prev_y = y;
}
/* now do the first point */
if ((cross = lseg_crossing(x0, y0, prev_x, prev_y)) == POINT_ON_POLYGON)
return 2 ;
total_cross += cross;
if (total_cross != 0 )
return 1 ;
return 0 ;
}
/* lseg_crossing()
* Returns + / - 2 if line segment crosses the positive X - axis in a + / - direction .
* Returns + / - 1 if one point is on the positive X - axis .
* Returns 0 if both points are on the positive X - axis , or there is no crossing .
* Returns POINT_ON_POLYGON if the segment contains ( 0 , 0 ) .
* Wow , that is one confusing API , but it is used above , and when summed ,
* can tell is if a point is in a polygon .
*/
static int
lseg_crossing(float8 x, float8 y, float8 prev_x, float8 prev_y)
{
float8 z;
int y_sign;
if (FPzero(y))
{ /* y == 0, on X axis */
if (FPzero(x)) /* (x,y) is (0,0)? */
return POINT_ON_POLYGON;
else if (FPgt(x, 0 ))
{ /* x > 0 */
if (FPzero(prev_y)) /* y and prev_y are zero */
/* prev_x > 0? */
return FPgt(prev_x, 0 .0 ) ? 0 : POINT_ON_POLYGON;
return FPlt(prev_y, 0 .0 ) ? 1 : -1 ;
}
else
{ /* x < 0, x not on positive X axis */
if (FPzero(prev_y))
/* prev_x < 0? */
return FPlt(prev_x, 0 .0 ) ? 0 : POINT_ON_POLYGON;
return 0 ;
}
}
else
{ /* y != 0 */
/* compute y crossing direction from previous point */
y_sign = FPgt(y, 0 .0 ) ? 1 : -1 ;
if (FPzero(prev_y))
/* previous point was on X axis, so new point is either off or on */
return FPlt(prev_x, 0 .0 ) ? 0 : y_sign;
else if ((y_sign < 0 && FPlt(prev_y, 0 .0 )) ||
(y_sign > 0 && FPgt(prev_y, 0 .0 )))
/* both above or below X axis */
return 0 ; /* same sign */
else
{ /* y and prev_y cross X-axis */
if (FPge(x, 0 .0 ) && FPgt(prev_x, 0 .0 ))
/* both non-negative so cross positive X-axis */
return 2 * y_sign;
if (FPlt(x, 0 .0 ) && FPle(prev_x, 0 .0 ))
/* both non-positive so do not cross positive X-axis */
return 0 ;
/* x and y cross axes, see URL above point_inside() */
z = float8_mi(float8_mul(float8_mi(x, prev_x), y),
float8_mul(float8_mi(y, prev_y), x));
if (FPzero(z))
return POINT_ON_POLYGON;
if ((y_sign < 0 && FPlt(z, 0 .0 )) ||
(y_sign > 0 && FPgt(z, 0 .0 )))
return 0 ;
return 2 * y_sign;
}
}
}
static bool
plist_same(int npts, Point *p1, Point *p2)
{
int i,
ii,
j;
/* find match for first point */
for (i = 0 ; i < npts; i++)
{
if (point_eq_point(&p2[i], &p1[0 ]))
{
/* match found? then look forward through remaining points */
for (ii = 1 , j = i + 1 ; ii < npts; ii++, j++)
{
if (j >= npts)
j = 0 ;
if (!point_eq_point(&p2[j], &p1[ii]))
break ;
}
if (ii == npts)
return true ;
/* match not found forwards? then look backwards */
for (ii = 1 , j = i - 1 ; ii < npts; ii++, j--)
{
if (j < 0 )
j = (npts - 1 );
if (!point_eq_point(&p2[j], &p1[ii]))
break ;
}
if (ii == npts)
return true ;
}
}
return false ;
}
/*-------------------------------------------------------------------------
* Determine the hypotenuse .
*
* If required , x and y are swapped to make x the larger number . The
* traditional formula of x ^ 2 + y ^ 2 is rearranged to factor x outside the
* sqrt . This allows computation of the hypotenuse for significantly
* larger values , and with a higher precision than when using the naive
* formula . In particular , this cannot overflow unless the final result
* would be out - of - range .
*
* sqrt ( x ^ 2 + y ^ 2 ) = sqrt ( x ^ 2 ( 1 + y ^ 2 / x ^ 2 ) )
* = x * sqrt ( 1 + y ^ 2 / x ^ 2 )
* = x * sqrt ( 1 + y / x * y / x )
*
* It is expected that this routine will eventually be replaced with the
* C99 hypot ( ) function .
*
* This implementation conforms to IEEE Std 1003 . 1 and GLIBC , in that the
* case of hypot ( inf , nan ) results in INF , and not NAN .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
float8
pg_hypot(float8 x, float8 y)
{
float8 yx,
result;
/* Handle INF and NaN properly */
if (isinf(x) || isinf(y))
return get_float8_infinity();
if (isnan(x) || isnan(y))
return get_float8_nan();
/* Else, drop any minus signs */
x = fabs(x);
y = fabs(y);
/* Swap x and y if needed to make x the larger one */
if (x < y)
{
float8 temp = x;
x = y;
y = temp;
}
/*
* If y is zero , the hypotenuse is x . This test saves a few cycles in
* such cases , but more importantly it also protects against
* divide - by - zero errors , since now x > = y .
*/
if (y == 0 .0 )
return x;
/* Determine the hypotenuse */
yx = y / x;
result = x * sqrt(1 .0 + (yx * yx));
if (unlikely(isinf(result)))
float_overflow_error();
if (unlikely(result == 0 .0 ))
float_underflow_error();
return result;
}
Messung V0.5 in Prozent C=94 H=93 G=93
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.147Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-08)
¤
*Eine klare Vorstellung vom Zielzustand