Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * geo_ops.c
4 : * 2D geometric operations
5 : *
6 : * This module implements the geometric functions and operators. The
7 : * geometric types are (from simple to more complicated):
8 : *
9 : * - point
10 : * - line
11 : * - line segment
12 : * - box
13 : * - circle
14 : * - polygon
15 : *
16 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
17 : * Portions Copyright (c) 1994, Regents of the University of California
18 : *
19 : *
20 : * IDENTIFICATION
21 : * src/backend/utils/adt/geo_ops.c
22 : *
23 : *-------------------------------------------------------------------------
24 : */
25 : #include "postgres.h"
26 :
27 : #include <math.h>
28 : #include <limits.h>
29 : #include <float.h>
30 : #include <ctype.h>
31 :
32 : #include "libpq/pqformat.h"
33 : #include "miscadmin.h"
34 : #include "nodes/miscnodes.h"
35 : #include "utils/float.h"
36 : #include "utils/fmgrprotos.h"
37 : #include "utils/geo_decls.h"
38 : #include "varatt.h"
39 :
40 : /*
41 : * * Type constructors have this form:
42 : * void type_construct(Type *result, ...);
43 : *
44 : * * Operators commonly have signatures such as
45 : * void type1_operator_type2(Type *result, Type1 *obj1, Type2 *obj2);
46 : *
47 : * Common operators are:
48 : * * Intersection point:
49 : * bool type1_interpt_type2(Point *result, Type1 *obj1, Type2 *obj2);
50 : * Return whether the two objects intersect. If *result is not NULL,
51 : * it is set to the intersection point.
52 : *
53 : * * Containment:
54 : * bool type1_contain_type2(Type1 *obj1, Type2 *obj2);
55 : * Return whether obj1 contains obj2.
56 : * bool type1_contain_type2(Type1 *contains_obj, Type1 *contained_obj);
57 : * Return whether obj1 contains obj2 (used when types are the same)
58 : *
59 : * * Distance of closest point in or on obj1 to obj2:
60 : * float8 type1_closept_type2(Point *result, Type1 *obj1, Type2 *obj2);
61 : * Returns the shortest distance between two objects. If *result is not
62 : * NULL, it is set to the closest point in or on obj1 to obj2.
63 : *
64 : * These functions may be used to implement multiple SQL-level operators. For
65 : * example, determining whether two lines are parallel is done by checking
66 : * whether they don't intersect.
67 : */
68 :
69 : /*
70 : * Internal routines
71 : */
72 :
73 : enum path_delim
74 : {
75 : PATH_NONE, PATH_OPEN, PATH_CLOSED
76 : };
77 :
78 : /* Routines for points */
79 : static inline void point_construct(Point *result, float8 x, float8 y);
80 : static inline void point_add_point(Point *result, Point *pt1, Point *pt2, Node *escontext);
81 : static inline void point_sub_point(Point *result, Point *pt1, Point *pt2);
82 : static inline void point_mul_point(Point *result, Point *pt1, Point *pt2);
83 : static inline void point_div_point(Point *result, Point *pt1, Point *pt2);
84 : static inline bool point_eq_point(Point *pt1, Point *pt2);
85 : static inline float8 point_dt(Point *pt1, Point *pt2, Node *escontext);
86 : static inline float8 point_sl(Point *pt1, Point *pt2);
87 : static int point_inside(Point *p, int npts, Point *plist);
88 :
89 : /* Routines for lines */
90 : static inline void line_construct(LINE *result, Point *pt, float8 m);
91 : static inline float8 line_sl(LINE *line);
92 : static inline float8 line_invsl(LINE *line);
93 : static bool line_interpt_line(Point *result, LINE *l1, LINE *l2);
94 : static bool line_contain_point(LINE *line, Point *point);
95 : static float8 line_closept_point(Point *result, LINE *line, Point *point);
96 :
97 : /* Routines for line segments */
98 : static inline void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
99 : static inline float8 lseg_sl(LSEG *lseg);
100 : static inline float8 lseg_invsl(LSEG *lseg);
101 : static bool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line);
102 : static bool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2);
103 : static int lseg_crossing(float8 x, float8 y, float8 prev_x, float8 prev_y);
104 : static bool lseg_contain_point(LSEG *lseg, Point *pt);
105 : static float8 lseg_closept_point(Point *result, LSEG *lseg, Point *pt);
106 : static float8 lseg_closept_line(Point *result, LSEG *lseg, LINE *line);
107 : static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
108 :
109 : /* Routines for boxes */
110 : static inline void box_construct(BOX *result, Point *pt1, Point *pt2);
111 : static void box_cn(Point *center, BOX *box, Node *escontext);
112 : static bool box_ov(BOX *box1, BOX *box2);
113 : static float8 box_ar(BOX *box);
114 : static float8 box_ht(BOX *box);
115 : static float8 box_wd(BOX *box);
116 : static bool box_contain_point(BOX *box, Point *point);
117 : static bool box_contain_box(BOX *contains_box, BOX *contained_box);
118 : static bool box_contain_lseg(BOX *box, LSEG *lseg);
119 : static bool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg);
120 : static float8 box_closept_point(Point *result, BOX *box, Point *pt);
121 : static float8 box_closept_lseg(Point *result, BOX *box, LSEG *lseg);
122 :
123 : /* Routines for circles */
124 : static float8 circle_ar(CIRCLE *circle);
125 :
126 : /* Routines for polygons */
127 : static void make_bound_box(POLYGON *poly);
128 : static POLYGON *circle_poly_internal(int32 npts, const CIRCLE *circle, FunctionCallInfo fcinfo);
129 : static void poly_to_circle(CIRCLE *result, POLYGON *poly, Node *escontext);
130 : static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
131 : static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
132 : static bool plist_same(int npts, Point *p1, Point *p2);
133 : static float8 dist_ppoly_internal(Point *pt, POLYGON *poly);
134 :
135 : /* Routines for encoding and decoding */
136 : static bool single_decode(char *num, float8 *x, char **endptr_p,
137 : const char *type_name, const char *orig_string,
138 : Node *escontext);
139 : static void single_encode(float8 x, StringInfo str);
140 : static bool pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
141 : const char *type_name, const char *orig_string,
142 : Node *escontext);
143 : static void pair_encode(float8 x, float8 y, StringInfo str);
144 : static int pair_count(char *s, char delim);
145 : static bool path_decode(char *str, bool opentype, int npts, Point *p,
146 : bool *isopen, char **endptr_p,
147 : const char *type_name, const char *orig_string,
148 : Node *escontext);
149 : static char *path_encode(enum path_delim path_delim, int npts, Point *pt);
150 :
151 :
152 : /*
153 : * Delimiters for input and output strings.
154 : * LDELIM, RDELIM, and DELIM are left, right, and separator delimiters, respectively.
155 : * LDELIM_EP, RDELIM_EP are left and right delimiters for paths with endpoints.
156 : */
157 :
158 : #define LDELIM '('
159 : #define RDELIM ')'
160 : #define DELIM ','
161 : #define LDELIM_EP '['
162 : #define RDELIM_EP ']'
163 : #define LDELIM_C '<'
164 : #define RDELIM_C '>'
165 : #define LDELIM_L '{'
166 : #define RDELIM_L '}'
167 :
168 :
169 : /*
170 : * Geometric data types are composed of points.
171 : * This code tries to support a common format throughout the data types,
172 : * to allow for more predictable usage and data type conversion.
173 : * The fundamental unit is the point. Other units are line segments,
174 : * open paths, boxes, closed paths, and polygons (which should be considered
175 : * non-intersecting closed paths).
176 : *
177 : * Data representation is as follows:
178 : * point: (x,y)
179 : * line segment: [(x1,y1),(x2,y2)]
180 : * box: (x1,y1),(x2,y2)
181 : * open path: [(x1,y1),...,(xn,yn)]
182 : * closed path: ((x1,y1),...,(xn,yn))
183 : * polygon: ((x1,y1),...,(xn,yn))
184 : *
185 : * For boxes, the points are opposite corners with the first point at the top right.
186 : * For closed paths and polygons, the points should be reordered to allow
187 : * fast and correct equality comparisons.
188 : *
189 : * XXX perhaps points in complex shapes should be reordered internally
190 : * to allow faster internal operations, but should keep track of input order
191 : * and restore that order for text output - tgl 97/01/16
192 : */
193 :
194 : static bool
195 182290 : single_decode(char *num, float8 *x, char **endptr_p,
196 : const char *type_name, const char *orig_string,
197 : Node *escontext)
198 : {
199 182290 : *x = float8in_internal(num, endptr_p, type_name, orig_string, escontext);
200 182254 : return (!SOFT_ERROR_OCCURRED(escontext));
201 : } /* single_decode() */
202 :
203 : static void
204 6104 : single_encode(float8 x, StringInfo str)
205 : {
206 6104 : char *xstr = float8out_internal(x);
207 :
208 6104 : appendStringInfoString(str, xstr);
209 6104 : pfree(xstr);
210 6104 : } /* single_encode() */
211 :
212 : static bool
213 90924 : pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
214 : const char *type_name, const char *orig_string,
215 : Node *escontext)
216 : {
217 : bool has_delim;
218 :
219 90990 : while (isspace((unsigned char) *str))
220 66 : str++;
221 90924 : if ((has_delim = (*str == LDELIM)))
222 64956 : str++;
223 :
224 90924 : if (!single_decode(str, x, &str, type_name, orig_string, escontext))
225 0 : return false;
226 :
227 90900 : if (*str++ != DELIM)
228 36 : goto fail;
229 :
230 90864 : if (!single_decode(str, y, &str, type_name, orig_string, escontext))
231 36 : return false;
232 :
233 90820 : if (has_delim)
234 : {
235 64904 : if (*str++ != RDELIM)
236 12 : goto fail;
237 64934 : while (isspace((unsigned char) *str))
238 42 : str++;
239 : }
240 :
241 : /* report stopping point if wanted, else complain if not end of string */
242 90808 : if (endptr_p)
243 73461 : *endptr_p = str;
244 17347 : else if (*str != '\0')
245 4 : goto fail;
246 90804 : return true;
247 :
248 52 : fail:
249 52 : ereturn(escontext, false,
250 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
251 : errmsg("invalid input syntax for type %s: \"%s\"",
252 : type_name, orig_string)));
253 : }
254 :
255 : static void
256 909275 : pair_encode(float8 x, float8 y, StringInfo str)
257 : {
258 909275 : char *xstr = float8out_internal(x);
259 909275 : char *ystr = float8out_internal(y);
260 :
261 909275 : appendStringInfo(str, "%s,%s", xstr, ystr);
262 909275 : pfree(xstr);
263 909275 : pfree(ystr);
264 909275 : }
265 :
266 : static bool
267 34203 : path_decode(char *str, bool opentype, int npts, Point *p,
268 : bool *isopen, char **endptr_p,
269 : const char *type_name, const char *orig_string,
270 : Node *escontext)
271 : {
272 34203 : int depth = 0;
273 : char *cp;
274 : int i;
275 :
276 34207 : while (isspace((unsigned char) *str))
277 4 : str++;
278 34203 : if ((*isopen = (*str == LDELIM_EP)))
279 : {
280 : /* no open delimiter allowed? */
281 20273 : if (!opentype)
282 4 : goto fail;
283 20269 : depth++;
284 20269 : str++;
285 : }
286 13930 : else if (*str == LDELIM)
287 : {
288 13814 : cp = (str + 1);
289 13824 : while (isspace((unsigned char) *cp))
290 10 : cp++;
291 13814 : if (*cp == LDELIM)
292 : {
293 818 : depth++;
294 818 : str = cp;
295 : }
296 12996 : else if (strrchr(str, LDELIM) == str)
297 : {
298 12706 : depth++;
299 12706 : str = cp;
300 : }
301 : }
302 :
303 107406 : for (i = 0; i < npts; i++)
304 : {
305 73283 : if (!pair_decode(str, &(p->x), &(p->y), &str, type_name, orig_string,
306 : escontext))
307 48 : return false;
308 73207 : if (*str == DELIM)
309 39060 : str++;
310 73207 : p++;
311 : }
312 :
313 67832 : while (depth > 0)
314 : {
315 33737 : if (*str == RDELIM || (*str == RDELIM_EP && *isopen && depth == 1))
316 : {
317 33709 : depth--;
318 33709 : str++;
319 33721 : while (isspace((unsigned char) *str))
320 12 : str++;
321 : }
322 : else
323 28 : goto fail;
324 : }
325 :
326 : /* report stopping point if wanted, else complain if not end of string */
327 34095 : if (endptr_p)
328 20585 : *endptr_p = str;
329 13510 : else if (*str != '\0')
330 4 : goto fail;
331 34091 : return true;
332 :
333 36 : fail:
334 36 : ereturn(escontext, false,
335 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
336 : errmsg("invalid input syntax for type %s: \"%s\"",
337 : type_name, orig_string)));
338 : } /* path_decode() */
339 :
340 : static char *
341 307863 : path_encode(enum path_delim path_delim, int npts, Point *pt)
342 : {
343 : StringInfoData str;
344 : int i;
345 :
346 307863 : initStringInfo(&str);
347 :
348 307863 : switch (path_delim)
349 : {
350 52090 : case PATH_CLOSED:
351 52090 : appendStringInfoChar(&str, LDELIM);
352 52090 : break;
353 32407 : case PATH_OPEN:
354 32407 : appendStringInfoChar(&str, LDELIM_EP);
355 32407 : break;
356 223366 : case PATH_NONE:
357 223366 : break;
358 : }
359 :
360 1211034 : for (i = 0; i < npts; i++)
361 : {
362 903171 : if (i > 0)
363 595308 : appendStringInfoChar(&str, DELIM);
364 903171 : appendStringInfoChar(&str, LDELIM);
365 903171 : pair_encode(pt->x, pt->y, &str);
366 903171 : appendStringInfoChar(&str, RDELIM);
367 903171 : pt++;
368 : }
369 :
370 307863 : switch (path_delim)
371 : {
372 52090 : case PATH_CLOSED:
373 52090 : appendStringInfoChar(&str, RDELIM);
374 52090 : break;
375 32407 : case PATH_OPEN:
376 32407 : appendStringInfoChar(&str, RDELIM_EP);
377 32407 : break;
378 223366 : case PATH_NONE:
379 223366 : break;
380 : }
381 :
382 307863 : return str.data;
383 : } /* path_encode() */
384 :
385 : /*-------------------------------------------------------------
386 : * pair_count - count the number of points
387 : * allow the following notation:
388 : * '((1,2),(3,4))'
389 : * '(1,3,2,4)'
390 : * require an odd number of delim characters in the string
391 : *-------------------------------------------------------------*/
392 : static int
393 20882 : pair_count(char *s, char delim)
394 : {
395 20882 : int ndelim = 0;
396 :
397 93326 : while ((s = strchr(s, delim)) != NULL)
398 : {
399 72444 : ndelim++;
400 72444 : s++;
401 : }
402 20882 : return (ndelim % 2) ? ((ndelim + 1) / 2) : -1;
403 : }
404 :
405 :
406 : /***********************************************************************
407 : **
408 : ** Routines for two-dimensional boxes.
409 : **
410 : ***********************************************************************/
411 :
412 : /*----------------------------------------------------------
413 : * Formatting and conversion routines.
414 : *---------------------------------------------------------*/
415 :
416 : /*
417 : * box_in - convert a string to internal form.
418 : *
419 : * External format: (two corners of box)
420 : * "(f8, f8), (f8, f8)"
421 : * also supports the older style "(f8, f8, f8, f8)"
422 : */
423 : Datum
424 13240 : box_in(PG_FUNCTION_ARGS)
425 : {
426 13240 : char *str = PG_GETARG_CSTRING(0);
427 13240 : Node *escontext = fcinfo->context;
428 13240 : BOX *box = palloc_object(BOX);
429 : bool isopen;
430 : float8 x,
431 : y;
432 :
433 13240 : if (!path_decode(str, false, 2, &(box->high), &isopen, NULL, "box", str,
434 : escontext))
435 16 : PG_RETURN_NULL();
436 :
437 : /* reorder corners if necessary... */
438 13204 : if (float8_lt(box->high.x, box->low.x))
439 : {
440 608 : x = box->high.x;
441 608 : box->high.x = box->low.x;
442 608 : box->low.x = x;
443 : }
444 13204 : if (float8_lt(box->high.y, box->low.y))
445 : {
446 616 : y = box->high.y;
447 616 : box->high.y = box->low.y;
448 616 : box->low.y = y;
449 : }
450 :
451 13204 : PG_RETURN_BOX_P(box);
452 : }
453 :
454 : /*
455 : * box_out - convert a box to external form.
456 : */
457 : Datum
458 89728 : box_out(PG_FUNCTION_ARGS)
459 : {
460 89728 : BOX *box = PG_GETARG_BOX_P(0);
461 :
462 89728 : PG_RETURN_CSTRING(path_encode(PATH_NONE, 2, &(box->high)));
463 : }
464 :
465 : /*
466 : * box_recv - converts external binary format to box
467 : */
468 : Datum
469 0 : box_recv(PG_FUNCTION_ARGS)
470 : {
471 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
472 : BOX *box;
473 : float8 x,
474 : y;
475 :
476 0 : box = palloc_object(BOX);
477 :
478 0 : box->high.x = pq_getmsgfloat8(buf);
479 0 : box->high.y = pq_getmsgfloat8(buf);
480 0 : box->low.x = pq_getmsgfloat8(buf);
481 0 : box->low.y = pq_getmsgfloat8(buf);
482 :
483 : /* reorder corners if necessary... */
484 0 : if (float8_lt(box->high.x, box->low.x))
485 : {
486 0 : x = box->high.x;
487 0 : box->high.x = box->low.x;
488 0 : box->low.x = x;
489 : }
490 0 : if (float8_lt(box->high.y, box->low.y))
491 : {
492 0 : y = box->high.y;
493 0 : box->high.y = box->low.y;
494 0 : box->low.y = y;
495 : }
496 :
497 0 : PG_RETURN_BOX_P(box);
498 : }
499 :
500 : /*
501 : * box_send - converts box to binary format
502 : */
503 : Datum
504 0 : box_send(PG_FUNCTION_ARGS)
505 : {
506 0 : BOX *box = PG_GETARG_BOX_P(0);
507 : StringInfoData buf;
508 :
509 0 : pq_begintypsend(&buf);
510 0 : pq_sendfloat8(&buf, box->high.x);
511 0 : pq_sendfloat8(&buf, box->high.y);
512 0 : pq_sendfloat8(&buf, box->low.x);
513 0 : pq_sendfloat8(&buf, box->low.y);
514 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
515 : }
516 :
517 :
518 : /*
519 : * box_construct - fill in a new box.
520 : */
521 : static inline void
522 173684 : box_construct(BOX *result, Point *pt1, Point *pt2)
523 : {
524 173684 : if (float8_gt(pt1->x, pt2->x))
525 : {
526 12372 : result->high.x = pt1->x;
527 12372 : result->low.x = pt2->x;
528 : }
529 : else
530 : {
531 161312 : result->high.x = pt2->x;
532 161312 : result->low.x = pt1->x;
533 : }
534 173684 : if (float8_gt(pt1->y, pt2->y))
535 : {
536 12386 : result->high.y = pt1->y;
537 12386 : result->low.y = pt2->y;
538 : }
539 : else
540 : {
541 161298 : result->high.y = pt2->y;
542 161298 : result->low.y = pt1->y;
543 : }
544 173684 : }
545 :
546 :
547 : /*----------------------------------------------------------
548 : * Relational operators for BOXes.
549 : * <, >, <=, >=, and == are based on box area.
550 : *---------------------------------------------------------*/
551 :
552 : /*
553 : * box_same - are two boxes identical?
554 : */
555 : Datum
556 9173 : box_same(PG_FUNCTION_ARGS)
557 : {
558 9173 : BOX *box1 = PG_GETARG_BOX_P(0);
559 9173 : BOX *box2 = PG_GETARG_BOX_P(1);
560 :
561 9173 : PG_RETURN_BOOL(point_eq_point(&box1->high, &box2->high) &&
562 : point_eq_point(&box1->low, &box2->low));
563 : }
564 :
565 : /*
566 : * box_overlap - does box1 overlap box2?
567 : */
568 : Datum
569 43156 : box_overlap(PG_FUNCTION_ARGS)
570 : {
571 43156 : BOX *box1 = PG_GETARG_BOX_P(0);
572 43156 : BOX *box2 = PG_GETARG_BOX_P(1);
573 :
574 43156 : PG_RETURN_BOOL(box_ov(box1, box2));
575 : }
576 :
577 : static bool
578 984120 : box_ov(BOX *box1, BOX *box2)
579 : {
580 1486696 : return (FPle(box1->low.x, box2->high.x) &&
581 584524 : FPle(box2->low.x, box1->high.x) &&
582 1568644 : FPle(box1->low.y, box2->high.y) &&
583 65648 : FPle(box2->low.y, box1->high.y));
584 : }
585 :
586 : /*
587 : * box_left - is box1 strictly left of box2?
588 : */
589 : Datum
590 33348 : box_left(PG_FUNCTION_ARGS)
591 : {
592 33348 : BOX *box1 = PG_GETARG_BOX_P(0);
593 33348 : BOX *box2 = PG_GETARG_BOX_P(1);
594 :
595 33348 : PG_RETURN_BOOL(FPlt(box1->high.x, box2->low.x));
596 : }
597 :
598 : /*
599 : * box_overleft - is the right edge of box1 at or left of
600 : * the right edge of box2?
601 : *
602 : * This is "less than or equal" for the end of a time range,
603 : * when time ranges are stored as rectangles.
604 : */
605 : Datum
606 66472 : box_overleft(PG_FUNCTION_ARGS)
607 : {
608 66472 : BOX *box1 = PG_GETARG_BOX_P(0);
609 66472 : BOX *box2 = PG_GETARG_BOX_P(1);
610 :
611 66472 : PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x));
612 : }
613 :
614 : /*
615 : * box_right - is box1 strictly right of box2?
616 : */
617 : Datum
618 87828 : box_right(PG_FUNCTION_ARGS)
619 : {
620 87828 : BOX *box1 = PG_GETARG_BOX_P(0);
621 87828 : BOX *box2 = PG_GETARG_BOX_P(1);
622 :
623 87828 : PG_RETURN_BOOL(FPgt(box1->low.x, box2->high.x));
624 : }
625 :
626 : /*
627 : * box_overright - is the left edge of box1 at or right of
628 : * the left edge of box2?
629 : *
630 : * This is "greater than or equal" for time ranges, when time ranges
631 : * are stored as rectangles.
632 : */
633 : Datum
634 74932 : box_overright(PG_FUNCTION_ARGS)
635 : {
636 74932 : BOX *box1 = PG_GETARG_BOX_P(0);
637 74932 : BOX *box2 = PG_GETARG_BOX_P(1);
638 :
639 74932 : PG_RETURN_BOOL(FPge(box1->low.x, box2->low.x));
640 : }
641 :
642 : /*
643 : * box_below - is box1 strictly below box2?
644 : */
645 : Datum
646 18452 : box_below(PG_FUNCTION_ARGS)
647 : {
648 18452 : BOX *box1 = PG_GETARG_BOX_P(0);
649 18452 : BOX *box2 = PG_GETARG_BOX_P(1);
650 :
651 18452 : PG_RETURN_BOOL(FPlt(box1->high.y, box2->low.y));
652 : }
653 :
654 : /*
655 : * box_overbelow - is the upper edge of box1 at or below
656 : * the upper edge of box2?
657 : */
658 : Datum
659 53624 : box_overbelow(PG_FUNCTION_ARGS)
660 : {
661 53624 : BOX *box1 = PG_GETARG_BOX_P(0);
662 53624 : BOX *box2 = PG_GETARG_BOX_P(1);
663 :
664 53624 : PG_RETURN_BOOL(FPle(box1->high.y, box2->high.y));
665 : }
666 :
667 : /*
668 : * box_above - is box1 strictly above box2?
669 : */
670 : Datum
671 38480 : box_above(PG_FUNCTION_ARGS)
672 : {
673 38480 : BOX *box1 = PG_GETARG_BOX_P(0);
674 38480 : BOX *box2 = PG_GETARG_BOX_P(1);
675 :
676 38480 : PG_RETURN_BOOL(FPgt(box1->low.y, box2->high.y));
677 : }
678 :
679 : /*
680 : * box_overabove - is the lower edge of box1 at or above
681 : * the lower edge of box2?
682 : */
683 : Datum
684 74100 : box_overabove(PG_FUNCTION_ARGS)
685 : {
686 74100 : BOX *box1 = PG_GETARG_BOX_P(0);
687 74100 : BOX *box2 = PG_GETARG_BOX_P(1);
688 :
689 74100 : PG_RETURN_BOOL(FPge(box1->low.y, box2->low.y));
690 : }
691 :
692 : /*
693 : * box_contained - is box1 contained by box2?
694 : */
695 : Datum
696 105528 : box_contained(PG_FUNCTION_ARGS)
697 : {
698 105528 : BOX *box1 = PG_GETARG_BOX_P(0);
699 105528 : BOX *box2 = PG_GETARG_BOX_P(1);
700 :
701 105528 : PG_RETURN_BOOL(box_contain_box(box2, box1));
702 : }
703 :
704 : /*
705 : * box_contain - does box1 contain box2?
706 : */
707 : Datum
708 8168 : box_contain(PG_FUNCTION_ARGS)
709 : {
710 8168 : BOX *box1 = PG_GETARG_BOX_P(0);
711 8168 : BOX *box2 = PG_GETARG_BOX_P(1);
712 :
713 8168 : PG_RETURN_BOOL(box_contain_box(box1, box2));
714 : }
715 :
716 : /*
717 : * Check whether the second box is in the first box or on its border
718 : */
719 : static bool
720 170316 : box_contain_box(BOX *contains_box, BOX *contained_box)
721 : {
722 278368 : return FPge(contains_box->high.x, contained_box->high.x) &&
723 185236 : FPle(contains_box->low.x, contained_box->low.x) &&
724 355552 : FPge(contains_box->high.y, contained_box->high.y) &&
725 64996 : FPle(contains_box->low.y, contained_box->low.y);
726 : }
727 :
728 :
729 : /*
730 : * box_positionop -
731 : * is box1 entirely {above,below} box2?
732 : *
733 : * box_below_eq and box_above_eq are obsolete versions that (probably
734 : * erroneously) accept the equal-boundaries case. Since these are not
735 : * in sync with the box_left and box_right code, they are deprecated and
736 : * not supported in the PG 8.1 rtree operator class extension.
737 : */
738 : Datum
739 100 : box_below_eq(PG_FUNCTION_ARGS)
740 : {
741 100 : BOX *box1 = PG_GETARG_BOX_P(0);
742 100 : BOX *box2 = PG_GETARG_BOX_P(1);
743 :
744 100 : PG_RETURN_BOOL(FPle(box1->high.y, box2->low.y));
745 : }
746 :
747 : Datum
748 100 : box_above_eq(PG_FUNCTION_ARGS)
749 : {
750 100 : BOX *box1 = PG_GETARG_BOX_P(0);
751 100 : BOX *box2 = PG_GETARG_BOX_P(1);
752 :
753 100 : PG_RETURN_BOOL(FPge(box1->low.y, box2->high.y));
754 : }
755 :
756 :
757 : /*
758 : * box_relop - is area(box1) relop area(box2), within
759 : * our accuracy constraint?
760 : */
761 : Datum
762 20 : box_lt(PG_FUNCTION_ARGS)
763 : {
764 20 : BOX *box1 = PG_GETARG_BOX_P(0);
765 20 : BOX *box2 = PG_GETARG_BOX_P(1);
766 :
767 20 : PG_RETURN_BOOL(FPlt(box_ar(box1), box_ar(box2)));
768 : }
769 :
770 : Datum
771 20 : box_gt(PG_FUNCTION_ARGS)
772 : {
773 20 : BOX *box1 = PG_GETARG_BOX_P(0);
774 20 : BOX *box2 = PG_GETARG_BOX_P(1);
775 :
776 20 : PG_RETURN_BOOL(FPgt(box_ar(box1), box_ar(box2)));
777 : }
778 :
779 : Datum
780 20 : box_eq(PG_FUNCTION_ARGS)
781 : {
782 20 : BOX *box1 = PG_GETARG_BOX_P(0);
783 20 : BOX *box2 = PG_GETARG_BOX_P(1);
784 :
785 20 : PG_RETURN_BOOL(FPeq(box_ar(box1), box_ar(box2)));
786 : }
787 :
788 : Datum
789 20 : box_le(PG_FUNCTION_ARGS)
790 : {
791 20 : BOX *box1 = PG_GETARG_BOX_P(0);
792 20 : BOX *box2 = PG_GETARG_BOX_P(1);
793 :
794 20 : PG_RETURN_BOOL(FPle(box_ar(box1), box_ar(box2)));
795 : }
796 :
797 : Datum
798 20 : box_ge(PG_FUNCTION_ARGS)
799 : {
800 20 : BOX *box1 = PG_GETARG_BOX_P(0);
801 20 : BOX *box2 = PG_GETARG_BOX_P(1);
802 :
803 20 : PG_RETURN_BOOL(FPge(box_ar(box1), box_ar(box2)));
804 : }
805 :
806 :
807 : /*----------------------------------------------------------
808 : * "Arithmetic" operators on boxes.
809 : *---------------------------------------------------------*/
810 :
811 : /*
812 : * box_area - returns the area of the box.
813 : */
814 : Datum
815 20 : box_area(PG_FUNCTION_ARGS)
816 : {
817 20 : BOX *box = PG_GETARG_BOX_P(0);
818 :
819 20 : PG_RETURN_FLOAT8(box_ar(box));
820 : }
821 :
822 :
823 : /*
824 : * box_width - returns the width of the box
825 : * (horizontal magnitude).
826 : */
827 : Datum
828 20 : box_width(PG_FUNCTION_ARGS)
829 : {
830 20 : BOX *box = PG_GETARG_BOX_P(0);
831 :
832 20 : PG_RETURN_FLOAT8(box_wd(box));
833 : }
834 :
835 :
836 : /*
837 : * box_height - returns the height of the box
838 : * (vertical magnitude).
839 : */
840 : Datum
841 20 : box_height(PG_FUNCTION_ARGS)
842 : {
843 20 : BOX *box = PG_GETARG_BOX_P(0);
844 :
845 20 : PG_RETURN_FLOAT8(box_ht(box));
846 : }
847 :
848 :
849 : /*
850 : * box_distance - returns the distance between the
851 : * center points of two boxes.
852 : */
853 : Datum
854 100 : box_distance(PG_FUNCTION_ARGS)
855 : {
856 100 : BOX *box1 = PG_GETARG_BOX_P(0);
857 100 : BOX *box2 = PG_GETARG_BOX_P(1);
858 : Point a,
859 : b;
860 :
861 100 : box_cn(&a, box1, NULL);
862 100 : box_cn(&b, box2, NULL);
863 :
864 100 : PG_RETURN_FLOAT8(point_dt(&a, &b, NULL));
865 : }
866 :
867 :
868 : /*
869 : * box_center - returns the center point of the box.
870 : */
871 : Datum
872 60 : box_center(PG_FUNCTION_ARGS)
873 : {
874 60 : BOX *box = PG_GETARG_BOX_P(0);
875 60 : Point *result = palloc_object(Point);
876 :
877 60 : box_cn(result, box, fcinfo->context);
878 60 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
879 0 : PG_RETURN_NULL();
880 :
881 60 : PG_RETURN_POINT_P(result);
882 : }
883 :
884 :
885 : /*
886 : * box_ar - returns the area of the box.
887 : */
888 : static float8
889 220 : box_ar(BOX *box)
890 : {
891 220 : return float8_mul(box_wd(box), box_ht(box));
892 : }
893 :
894 :
895 : /*
896 : * box_cn - stores the centerpoint of the box into *center.
897 : */
898 : static void
899 316 : box_cn(Point *center, BOX *box, Node *escontext)
900 : {
901 : float8 x;
902 : float8 y;
903 :
904 316 : x = float8_pl_safe(box->high.x, box->low.x, escontext);
905 316 : if (SOFT_ERROR_OCCURRED(escontext))
906 0 : return;
907 :
908 316 : center->x = float8_div_safe(x, 2.0, escontext);
909 316 : if (SOFT_ERROR_OCCURRED(escontext))
910 0 : return;
911 :
912 316 : y = float8_pl_safe(box->high.y, box->low.y, escontext);
913 316 : if (SOFT_ERROR_OCCURRED(escontext))
914 0 : return;
915 :
916 316 : center->y = float8_div_safe(y, 2.0, escontext);
917 316 : if (SOFT_ERROR_OCCURRED(escontext))
918 0 : return;
919 : }
920 :
921 : /*
922 : * box_wd - returns the width (length) of the box
923 : * (horizontal magnitude).
924 : */
925 : static float8
926 240 : box_wd(BOX *box)
927 : {
928 240 : return float8_mi(box->high.x, box->low.x);
929 : }
930 :
931 :
932 : /*
933 : * box_ht - returns the height of the box
934 : * (vertical magnitude).
935 : */
936 : static float8
937 240 : box_ht(BOX *box)
938 : {
939 240 : return float8_mi(box->high.y, box->low.y);
940 : }
941 :
942 :
943 : /*----------------------------------------------------------
944 : * Funky operations.
945 : *---------------------------------------------------------*/
946 :
947 : /*
948 : * box_intersect -
949 : * returns the overlapping portion of two boxes,
950 : * or NULL if they do not intersect.
951 : */
952 : Datum
953 100 : box_intersect(PG_FUNCTION_ARGS)
954 : {
955 100 : BOX *box1 = PG_GETARG_BOX_P(0);
956 100 : BOX *box2 = PG_GETARG_BOX_P(1);
957 : BOX *result;
958 :
959 100 : if (!box_ov(box1, box2))
960 56 : PG_RETURN_NULL();
961 :
962 44 : result = palloc_object(BOX);
963 :
964 44 : result->high.x = float8_min(box1->high.x, box2->high.x);
965 44 : result->low.x = float8_max(box1->low.x, box2->low.x);
966 44 : result->high.y = float8_min(box1->high.y, box2->high.y);
967 44 : result->low.y = float8_max(box1->low.y, box2->low.y);
968 :
969 44 : PG_RETURN_BOX_P(result);
970 : }
971 :
972 :
973 : /*
974 : * box_diagonal -
975 : * returns a line segment which happens to be the
976 : * positive-slope diagonal of "box".
977 : */
978 : Datum
979 20 : box_diagonal(PG_FUNCTION_ARGS)
980 : {
981 20 : BOX *box = PG_GETARG_BOX_P(0);
982 20 : LSEG *result = palloc_object(LSEG);
983 :
984 20 : statlseg_construct(result, &box->high, &box->low);
985 :
986 20 : PG_RETURN_LSEG_P(result);
987 : }
988 :
989 : /***********************************************************************
990 : **
991 : ** Routines for 2D lines.
992 : **
993 : ***********************************************************************/
994 :
995 : static bool
996 92 : line_decode(char *s, const char *str, LINE *line, Node *escontext)
997 : {
998 : /* s was already advanced over leading '{' */
999 92 : if (!single_decode(s, &line->A, &s, "line", str, escontext))
1000 0 : return false;
1001 88 : if (*s++ != DELIM)
1002 4 : goto fail;
1003 84 : if (!single_decode(s, &line->B, &s, "line", str, escontext))
1004 0 : return false;
1005 84 : if (*s++ != DELIM)
1006 12 : goto fail;
1007 72 : if (!single_decode(s, &line->C, &s, "line", str, escontext))
1008 16 : return false;
1009 56 : if (*s++ != RDELIM_L)
1010 4 : goto fail;
1011 56 : while (isspace((unsigned char) *s))
1012 4 : s++;
1013 52 : if (*s != '\0')
1014 4 : goto fail;
1015 48 : return true;
1016 :
1017 24 : fail:
1018 24 : ereturn(escontext, false,
1019 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1020 : errmsg("invalid input syntax for type %s: \"%s\"",
1021 : "line", str)));
1022 : }
1023 :
1024 : Datum
1025 140 : line_in(PG_FUNCTION_ARGS)
1026 : {
1027 140 : char *str = PG_GETARG_CSTRING(0);
1028 140 : Node *escontext = fcinfo->context;
1029 140 : LINE *line = palloc_object(LINE);
1030 : LSEG lseg;
1031 : bool isopen;
1032 : char *s;
1033 :
1034 140 : s = str;
1035 144 : while (isspace((unsigned char) *s))
1036 4 : s++;
1037 140 : if (*s == LDELIM_L)
1038 : {
1039 92 : if (!line_decode(s + 1, str, line, escontext))
1040 24 : PG_RETURN_NULL();
1041 48 : if (FPzero(line->A) && FPzero(line->B))
1042 12 : ereturn(escontext, (Datum) 0,
1043 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1044 : errmsg("invalid line specification: A and B cannot both be zero")));
1045 : }
1046 : else
1047 : {
1048 48 : if (!path_decode(s, true, 2, &lseg.p[0], &isopen, NULL, "line", str,
1049 : escontext))
1050 8 : PG_RETURN_NULL();
1051 24 : if (point_eq_point(&lseg.p[0], &lseg.p[1]))
1052 4 : ereturn(escontext, (Datum) 0,
1053 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1054 : errmsg("invalid line specification: must be two distinct points")));
1055 :
1056 : /*
1057 : * XXX lseg_sl() and line_construct() can throw overflow/underflow
1058 : * errors. Eventually we should allow those to be soft, but the
1059 : * notational pain seems to outweigh the value for now.
1060 : */
1061 20 : line_construct(line, &lseg.p[0], lseg_sl(&lseg));
1062 : }
1063 :
1064 56 : PG_RETURN_LINE_P(line);
1065 : }
1066 :
1067 :
1068 : Datum
1069 4628 : line_out(PG_FUNCTION_ARGS)
1070 : {
1071 4628 : LINE *line = PG_GETARG_LINE_P(0);
1072 4628 : char *astr = float8out_internal(line->A);
1073 4628 : char *bstr = float8out_internal(line->B);
1074 4628 : char *cstr = float8out_internal(line->C);
1075 :
1076 4628 : PG_RETURN_CSTRING(psprintf("%c%s%c%s%c%s%c", LDELIM_L, astr, DELIM, bstr,
1077 : DELIM, cstr, RDELIM_L));
1078 : }
1079 :
1080 : /*
1081 : * line_recv - converts external binary format to line
1082 : */
1083 : Datum
1084 0 : line_recv(PG_FUNCTION_ARGS)
1085 : {
1086 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1087 : LINE *line;
1088 :
1089 0 : line = palloc_object(LINE);
1090 :
1091 0 : line->A = pq_getmsgfloat8(buf);
1092 0 : line->B = pq_getmsgfloat8(buf);
1093 0 : line->C = pq_getmsgfloat8(buf);
1094 :
1095 0 : if (FPzero(line->A) && FPzero(line->B))
1096 0 : ereport(ERROR,
1097 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1098 : errmsg("invalid line specification: A and B cannot both be zero")));
1099 :
1100 0 : PG_RETURN_LINE_P(line);
1101 : }
1102 :
1103 : /*
1104 : * line_send - converts line to binary format
1105 : */
1106 : Datum
1107 0 : line_send(PG_FUNCTION_ARGS)
1108 : {
1109 0 : LINE *line = PG_GETARG_LINE_P(0);
1110 : StringInfoData buf;
1111 :
1112 0 : pq_begintypsend(&buf);
1113 0 : pq_sendfloat8(&buf, line->A);
1114 0 : pq_sendfloat8(&buf, line->B);
1115 0 : pq_sendfloat8(&buf, line->C);
1116 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1117 : }
1118 :
1119 :
1120 : /*----------------------------------------------------------
1121 : * Conversion routines from one line formula to internal.
1122 : * Internal form: Ax+By+C=0
1123 : *---------------------------------------------------------*/
1124 :
1125 : /*
1126 : * Fill already-allocated LINE struct from the point and the slope
1127 : */
1128 : static inline void
1129 4363641 : line_construct(LINE *result, Point *pt, float8 m)
1130 : {
1131 4363641 : if (isinf(m))
1132 : {
1133 : /* vertical - use "x = C" */
1134 996465 : result->A = -1.0;
1135 996465 : result->B = 0.0;
1136 996465 : result->C = pt->x;
1137 : }
1138 3367176 : else if (m == 0)
1139 : {
1140 : /* horizontal - use "y = C" */
1141 985612 : result->A = 0.0;
1142 985612 : result->B = -1.0;
1143 985612 : result->C = pt->y;
1144 : }
1145 : else
1146 : {
1147 : /* use "mx - y + yinter = 0" */
1148 2381564 : result->A = m;
1149 2381564 : result->B = -1.0;
1150 2381564 : result->C = float8_mi(pt->y, float8_mul(m, pt->x));
1151 : /* on some platforms, the preceding expression tends to produce -0 */
1152 2381564 : if (result->C == 0.0)
1153 3240 : result->C = 0.0;
1154 : }
1155 4363641 : }
1156 :
1157 : /*
1158 : * line_construct_pp()
1159 : * two points
1160 : */
1161 : Datum
1162 361 : line_construct_pp(PG_FUNCTION_ARGS)
1163 : {
1164 361 : Point *pt1 = PG_GETARG_POINT_P(0);
1165 361 : Point *pt2 = PG_GETARG_POINT_P(1);
1166 361 : LINE *result = palloc_object(LINE);
1167 :
1168 361 : if (point_eq_point(pt1, pt2))
1169 4 : ereport(ERROR,
1170 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1171 : errmsg("invalid line specification: must be two distinct points")));
1172 :
1173 357 : line_construct(result, pt1, point_sl(pt1, pt2));
1174 :
1175 357 : PG_RETURN_LINE_P(result);
1176 : }
1177 :
1178 :
1179 : /*----------------------------------------------------------
1180 : * Relative position routines.
1181 : *---------------------------------------------------------*/
1182 :
1183 : Datum
1184 400 : line_intersect(PG_FUNCTION_ARGS)
1185 : {
1186 400 : LINE *l1 = PG_GETARG_LINE_P(0);
1187 400 : LINE *l2 = PG_GETARG_LINE_P(1);
1188 :
1189 400 : PG_RETURN_BOOL(line_interpt_line(NULL, l1, l2));
1190 : }
1191 :
1192 : Datum
1193 400 : line_parallel(PG_FUNCTION_ARGS)
1194 : {
1195 400 : LINE *l1 = PG_GETARG_LINE_P(0);
1196 400 : LINE *l2 = PG_GETARG_LINE_P(1);
1197 :
1198 400 : PG_RETURN_BOOL(!line_interpt_line(NULL, l1, l2));
1199 : }
1200 :
1201 : Datum
1202 400 : line_perp(PG_FUNCTION_ARGS)
1203 : {
1204 400 : LINE *l1 = PG_GETARG_LINE_P(0);
1205 400 : LINE *l2 = PG_GETARG_LINE_P(1);
1206 :
1207 400 : if (FPzero(l1->A))
1208 120 : PG_RETURN_BOOL(FPzero(l2->B));
1209 280 : if (FPzero(l2->A))
1210 84 : PG_RETURN_BOOL(FPzero(l1->B));
1211 196 : if (FPzero(l1->B))
1212 56 : PG_RETURN_BOOL(FPzero(l2->A));
1213 140 : if (FPzero(l2->B))
1214 40 : PG_RETURN_BOOL(FPzero(l1->A));
1215 :
1216 100 : PG_RETURN_BOOL(FPeq(float8_div(float8_mul(l1->A, l2->A),
1217 : float8_mul(l1->B, l2->B)), -1.0));
1218 : }
1219 :
1220 : Datum
1221 40 : line_vertical(PG_FUNCTION_ARGS)
1222 : {
1223 40 : LINE *line = PG_GETARG_LINE_P(0);
1224 :
1225 40 : PG_RETURN_BOOL(FPzero(line->B));
1226 : }
1227 :
1228 : Datum
1229 40 : line_horizontal(PG_FUNCTION_ARGS)
1230 : {
1231 40 : LINE *line = PG_GETARG_LINE_P(0);
1232 :
1233 40 : PG_RETURN_BOOL(FPzero(line->A));
1234 : }
1235 :
1236 :
1237 : /*
1238 : * Check whether the two lines are the same
1239 : */
1240 : Datum
1241 410 : line_eq(PG_FUNCTION_ARGS)
1242 : {
1243 410 : LINE *l1 = PG_GETARG_LINE_P(0);
1244 410 : LINE *l2 = PG_GETARG_LINE_P(1);
1245 : float8 ratio;
1246 :
1247 : /* If any NaNs are involved, insist on exact equality */
1248 410 : if (unlikely(isnan(l1->A) || isnan(l1->B) || isnan(l1->C) ||
1249 : isnan(l2->A) || isnan(l2->B) || isnan(l2->C)))
1250 : {
1251 154 : PG_RETURN_BOOL(float8_eq(l1->A, l2->A) &&
1252 : float8_eq(l1->B, l2->B) &&
1253 : float8_eq(l1->C, l2->C));
1254 : }
1255 :
1256 : /* Otherwise, lines whose parameters are proportional are the same */
1257 256 : if (!FPzero(l2->A))
1258 160 : ratio = float8_div(l1->A, l2->A);
1259 96 : else if (!FPzero(l2->B))
1260 96 : ratio = float8_div(l1->B, l2->B);
1261 0 : else if (!FPzero(l2->C))
1262 0 : ratio = float8_div(l1->C, l2->C);
1263 : else
1264 0 : ratio = 1.0;
1265 :
1266 256 : PG_RETURN_BOOL(FPeq(l1->A, float8_mul(ratio, l2->A)) &&
1267 : FPeq(l1->B, float8_mul(ratio, l2->B)) &&
1268 : FPeq(l1->C, float8_mul(ratio, l2->C)));
1269 : }
1270 :
1271 :
1272 : /*----------------------------------------------------------
1273 : * Line arithmetic routines.
1274 : *---------------------------------------------------------*/
1275 :
1276 : /*
1277 : * Return slope of the line
1278 : */
1279 : static inline float8
1280 320 : line_sl(LINE *line)
1281 : {
1282 320 : if (FPzero(line->A))
1283 96 : return 0.0;
1284 224 : if (FPzero(line->B))
1285 64 : return get_float8_infinity();
1286 160 : return float8_div(line->A, -line->B);
1287 : }
1288 :
1289 :
1290 : /*
1291 : * Return inverse slope of the line
1292 : */
1293 : static inline float8
1294 1195440 : line_invsl(LINE *line)
1295 : {
1296 1195440 : if (FPzero(line->A))
1297 476880 : return get_float8_infinity();
1298 718560 : if (FPzero(line->B))
1299 463736 : return 0.0;
1300 254824 : return float8_div(line->B, line->A);
1301 : }
1302 :
1303 :
1304 : /*
1305 : * line_distance()
1306 : * Distance between two lines.
1307 : */
1308 : Datum
1309 400 : line_distance(PG_FUNCTION_ARGS)
1310 : {
1311 400 : LINE *l1 = PG_GETARG_LINE_P(0);
1312 400 : LINE *l2 = PG_GETARG_LINE_P(1);
1313 : float8 ratio;
1314 :
1315 400 : if (line_interpt_line(NULL, l1, l2)) /* intersecting? */
1316 336 : PG_RETURN_FLOAT8(0.0);
1317 :
1318 64 : if (!FPzero(l1->A) && !isnan(l1->A) && !FPzero(l2->A) && !isnan(l2->A))
1319 28 : ratio = float8_div(l1->A, l2->A);
1320 36 : else if (!FPzero(l1->B) && !isnan(l1->B) && !FPzero(l2->B) && !isnan(l2->B))
1321 36 : ratio = float8_div(l1->B, l2->B);
1322 : else
1323 0 : ratio = 1.0;
1324 :
1325 64 : PG_RETURN_FLOAT8(float8_div(fabs(float8_mi(l1->C,
1326 : float8_mul(ratio, l2->C))),
1327 : hypot(l1->A, l1->B)));
1328 : }
1329 :
1330 : /*
1331 : * line_interpt()
1332 : * Point where two lines l1, l2 intersect (if any)
1333 : */
1334 : Datum
1335 400 : line_interpt(PG_FUNCTION_ARGS)
1336 : {
1337 400 : LINE *l1 = PG_GETARG_LINE_P(0);
1338 400 : LINE *l2 = PG_GETARG_LINE_P(1);
1339 : Point *result;
1340 :
1341 400 : result = palloc_object(Point);
1342 :
1343 400 : if (!line_interpt_line(result, l1, l2))
1344 64 : PG_RETURN_NULL();
1345 336 : PG_RETURN_POINT_P(result);
1346 : }
1347 :
1348 : /*
1349 : * Internal version of line_interpt
1350 : *
1351 : * Return whether two lines intersect. If *result is not NULL, it is set to
1352 : * the intersection point.
1353 : *
1354 : * NOTE: If the lines are identical then we will find they are parallel
1355 : * and report "no intersection". This is a little weird, but since
1356 : * there's no *unique* intersection, maybe it's appropriate behavior.
1357 : *
1358 : * If the lines have NaN constants, we will return true, and the intersection
1359 : * point would have NaN coordinates. We shouldn't return false in this case
1360 : * because that would mean the lines are parallel.
1361 : */
1362 : static bool
1363 2781900 : line_interpt_line(Point *result, LINE *l1, LINE *l2)
1364 : {
1365 : float8 x,
1366 : y;
1367 :
1368 2781900 : if (!FPzero(l1->B))
1369 : {
1370 2024300 : if (FPeq(l2->A, float8_mul(l1->A, float8_div(l2->B, l1->B))))
1371 6392 : return false;
1372 :
1373 2017908 : x = float8_div(float8_mi(float8_mul(l1->B, l2->C),
1374 : float8_mul(l2->B, l1->C)),
1375 : float8_mi(float8_mul(l1->A, l2->B),
1376 : float8_mul(l2->A, l1->B)));
1377 2017908 : y = float8_div(-float8_pl(float8_mul(l1->A, x), l1->C), l1->B);
1378 : }
1379 757600 : else if (!FPzero(l2->B))
1380 : {
1381 755628 : if (FPeq(l1->A, float8_mul(l2->A, float8_div(l1->B, l2->B))))
1382 0 : return false;
1383 :
1384 755628 : x = float8_div(float8_mi(float8_mul(l2->B, l1->C),
1385 : float8_mul(l1->B, l2->C)),
1386 : float8_mi(float8_mul(l2->A, l1->B),
1387 : float8_mul(l1->A, l2->B)));
1388 755628 : y = float8_div(-float8_pl(float8_mul(l2->A, x), l2->C), l2->B);
1389 : }
1390 : else
1391 1972 : return false;
1392 :
1393 : /* On some platforms, the preceding expressions tend to produce -0. */
1394 2773536 : if (x == 0.0)
1395 81140 : x = 0.0;
1396 2773536 : if (y == 0.0)
1397 79952 : y = 0.0;
1398 :
1399 2773536 : if (result != NULL)
1400 2772528 : point_construct(result, x, y);
1401 :
1402 2773536 : return true;
1403 : }
1404 :
1405 :
1406 : /***********************************************************************
1407 : **
1408 : ** Routines for 2D paths (sequences of line segments, also
1409 : ** called `polylines').
1410 : **
1411 : ** This is not a general package for geometric paths,
1412 : ** which of course include polygons; the emphasis here
1413 : ** is on (for example) usefulness in wire layout.
1414 : **
1415 : ***********************************************************************/
1416 :
1417 : /*----------------------------------------------------------
1418 : * String to path / path to string conversion.
1419 : * External format:
1420 : * "((xcoord, ycoord),... )"
1421 : * "[(xcoord, ycoord),... ]"
1422 : * "(xcoord, ycoord),... "
1423 : * "[xcoord, ycoord,... ]"
1424 : * Also support older format:
1425 : * "(closed, npts, xcoord, ycoord,... )"
1426 : *---------------------------------------------------------*/
1427 :
1428 : Datum
1429 36 : path_area(PG_FUNCTION_ARGS)
1430 : {
1431 36 : PATH *path = PG_GETARG_PATH_P(0);
1432 36 : float8 area = 0.0;
1433 : int i,
1434 : j;
1435 :
1436 36 : if (!path->closed)
1437 16 : PG_RETURN_NULL();
1438 :
1439 56 : for (i = 0; i < path->npts; i++)
1440 : {
1441 36 : j = (i + 1) % path->npts;
1442 36 : area = float8_pl(area, float8_mul(path->p[i].x, path->p[j].y));
1443 36 : area = float8_mi(area, float8_mul(path->p[i].y, path->p[j].x));
1444 : }
1445 :
1446 20 : PG_RETURN_FLOAT8(float8_div(fabs(area), 2.0));
1447 : }
1448 :
1449 :
1450 : Datum
1451 20613 : path_in(PG_FUNCTION_ARGS)
1452 : {
1453 20613 : char *str = PG_GETARG_CSTRING(0);
1454 20613 : Node *escontext = fcinfo->context;
1455 : PATH *path;
1456 : bool isopen;
1457 : char *s;
1458 : int npts;
1459 : int size;
1460 : int base_size;
1461 20613 : int depth = 0;
1462 :
1463 20613 : if ((npts = pair_count(str, ',')) <= 0)
1464 12 : ereturn(escontext, (Datum) 0,
1465 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1466 : errmsg("invalid input syntax for type %s: \"%s\"",
1467 : "path", str)));
1468 :
1469 20601 : s = str;
1470 20609 : while (isspace((unsigned char) *s))
1471 8 : s++;
1472 :
1473 : /* skip single leading paren */
1474 20601 : if ((*s == LDELIM) && (strrchr(s, LDELIM) == s))
1475 : {
1476 16 : s++;
1477 16 : depth++;
1478 : }
1479 :
1480 20601 : base_size = sizeof(path->p[0]) * npts;
1481 20601 : size = offsetof(PATH, p) + base_size;
1482 :
1483 : /* Check for integer overflow */
1484 20601 : if (base_size / npts != sizeof(path->p[0]) || size <= base_size)
1485 0 : ereturn(escontext, (Datum) 0,
1486 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1487 : errmsg("too many points requested")));
1488 :
1489 20601 : path = (PATH *) palloc(size);
1490 :
1491 20601 : SET_VARSIZE(path, size);
1492 20601 : path->npts = npts;
1493 :
1494 20601 : if (!path_decode(s, true, npts, &(path->p[0]), &isopen, &s, "path", str,
1495 : escontext))
1496 8 : PG_RETURN_NULL();
1497 :
1498 20585 : if (depth >= 1)
1499 : {
1500 16 : if (*s++ != RDELIM)
1501 4 : ereturn(escontext, (Datum) 0,
1502 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1503 : errmsg("invalid input syntax for type %s: \"%s\"",
1504 : "path", str)));
1505 16 : while (isspace((unsigned char) *s))
1506 4 : s++;
1507 : }
1508 20581 : if (*s != '\0')
1509 4 : ereturn(escontext, (Datum) 0,
1510 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
1511 : errmsg("invalid input syntax for type %s: \"%s\"",
1512 : "path", str)));
1513 :
1514 20577 : path->closed = (!isopen);
1515 : /* prevent instability in unused pad bytes */
1516 20577 : path->dummy = 0;
1517 :
1518 20577 : PG_RETURN_PATH_P(path);
1519 : }
1520 :
1521 :
1522 : Datum
1523 31871 : path_out(PG_FUNCTION_ARGS)
1524 : {
1525 31871 : PATH *path = PG_GETARG_PATH_P(0);
1526 :
1527 31871 : PG_RETURN_CSTRING(path_encode(path->closed ? PATH_CLOSED : PATH_OPEN, path->npts, path->p));
1528 : }
1529 :
1530 : /*
1531 : * path_recv - converts external binary format to path
1532 : *
1533 : * External representation is closed flag (a boolean byte), int32 number
1534 : * of points, and the points.
1535 : */
1536 : Datum
1537 0 : path_recv(PG_FUNCTION_ARGS)
1538 : {
1539 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1540 : PATH *path;
1541 : int closed;
1542 : int32 npts;
1543 : int32 i;
1544 : int size;
1545 :
1546 0 : closed = pq_getmsgbyte(buf);
1547 0 : npts = pq_getmsgint(buf, sizeof(int32));
1548 0 : if (npts <= 0 || npts >= (int32) ((INT_MAX - offsetof(PATH, p)) / sizeof(Point)))
1549 0 : ereport(ERROR,
1550 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1551 : errmsg("invalid number of points in external \"path\" value")));
1552 :
1553 0 : size = offsetof(PATH, p) + sizeof(path->p[0]) * npts;
1554 0 : path = (PATH *) palloc(size);
1555 :
1556 0 : SET_VARSIZE(path, size);
1557 0 : path->npts = npts;
1558 0 : path->closed = (closed ? 1 : 0);
1559 : /* prevent instability in unused pad bytes */
1560 0 : path->dummy = 0;
1561 :
1562 0 : for (i = 0; i < npts; i++)
1563 : {
1564 0 : path->p[i].x = pq_getmsgfloat8(buf);
1565 0 : path->p[i].y = pq_getmsgfloat8(buf);
1566 : }
1567 :
1568 0 : PG_RETURN_PATH_P(path);
1569 : }
1570 :
1571 : /*
1572 : * path_send - converts path to binary format
1573 : */
1574 : Datum
1575 0 : path_send(PG_FUNCTION_ARGS)
1576 : {
1577 0 : PATH *path = PG_GETARG_PATH_P(0);
1578 : StringInfoData buf;
1579 : int32 i;
1580 :
1581 0 : pq_begintypsend(&buf);
1582 0 : pq_sendbyte(&buf, path->closed ? 1 : 0);
1583 0 : pq_sendint32(&buf, path->npts);
1584 0 : for (i = 0; i < path->npts; i++)
1585 : {
1586 0 : pq_sendfloat8(&buf, path->p[i].x);
1587 0 : pq_sendfloat8(&buf, path->p[i].y);
1588 : }
1589 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1590 : }
1591 :
1592 :
1593 : /*----------------------------------------------------------
1594 : * Relational operators.
1595 : * These are based on the path cardinality,
1596 : * as stupid as that sounds.
1597 : *
1598 : * Better relops and access methods coming soon.
1599 : *---------------------------------------------------------*/
1600 :
1601 : Datum
1602 324 : path_n_lt(PG_FUNCTION_ARGS)
1603 : {
1604 324 : PATH *p1 = PG_GETARG_PATH_P(0);
1605 324 : PATH *p2 = PG_GETARG_PATH_P(1);
1606 :
1607 324 : PG_RETURN_BOOL(p1->npts < p2->npts);
1608 : }
1609 :
1610 : Datum
1611 324 : path_n_gt(PG_FUNCTION_ARGS)
1612 : {
1613 324 : PATH *p1 = PG_GETARG_PATH_P(0);
1614 324 : PATH *p2 = PG_GETARG_PATH_P(1);
1615 :
1616 324 : PG_RETURN_BOOL(p1->npts > p2->npts);
1617 : }
1618 :
1619 : Datum
1620 325 : path_n_eq(PG_FUNCTION_ARGS)
1621 : {
1622 325 : PATH *p1 = PG_GETARG_PATH_P(0);
1623 325 : PATH *p2 = PG_GETARG_PATH_P(1);
1624 :
1625 325 : PG_RETURN_BOOL(p1->npts == p2->npts);
1626 : }
1627 :
1628 : Datum
1629 324 : path_n_le(PG_FUNCTION_ARGS)
1630 : {
1631 324 : PATH *p1 = PG_GETARG_PATH_P(0);
1632 324 : PATH *p2 = PG_GETARG_PATH_P(1);
1633 :
1634 324 : PG_RETURN_BOOL(p1->npts <= p2->npts);
1635 : }
1636 :
1637 : Datum
1638 324 : path_n_ge(PG_FUNCTION_ARGS)
1639 : {
1640 324 : PATH *p1 = PG_GETARG_PATH_P(0);
1641 324 : PATH *p2 = PG_GETARG_PATH_P(1);
1642 :
1643 324 : PG_RETURN_BOOL(p1->npts >= p2->npts);
1644 : }
1645 :
1646 : /*----------------------------------------------------------
1647 : * Conversion operators.
1648 : *---------------------------------------------------------*/
1649 :
1650 : Datum
1651 108 : path_isclosed(PG_FUNCTION_ARGS)
1652 : {
1653 108 : PATH *path = PG_GETARG_PATH_P(0);
1654 :
1655 108 : PG_RETURN_BOOL(path->closed);
1656 : }
1657 :
1658 : Datum
1659 76 : path_isopen(PG_FUNCTION_ARGS)
1660 : {
1661 76 : PATH *path = PG_GETARG_PATH_P(0);
1662 :
1663 76 : PG_RETURN_BOOL(!path->closed);
1664 : }
1665 :
1666 : Datum
1667 3620 : path_npoints(PG_FUNCTION_ARGS)
1668 : {
1669 3620 : PATH *path = PG_GETARG_PATH_P(0);
1670 :
1671 3620 : PG_RETURN_INT32(path->npts);
1672 : }
1673 :
1674 :
1675 : Datum
1676 52 : path_close(PG_FUNCTION_ARGS)
1677 : {
1678 52 : PATH *path = PG_GETARG_PATH_P_COPY(0);
1679 :
1680 52 : path->closed = true;
1681 :
1682 52 : PG_RETURN_PATH_P(path);
1683 : }
1684 :
1685 : Datum
1686 36 : path_open(PG_FUNCTION_ARGS)
1687 : {
1688 36 : PATH *path = PG_GETARG_PATH_P_COPY(0);
1689 :
1690 36 : path->closed = false;
1691 :
1692 36 : PG_RETURN_PATH_P(path);
1693 : }
1694 :
1695 :
1696 : /*
1697 : * path_inter -
1698 : * Does p1 intersect p2 at any point?
1699 : * Use bounding boxes for a quick (O(n)) check, then do a
1700 : * O(n^2) iterative edge check.
1701 : */
1702 : Datum
1703 920612 : path_inter(PG_FUNCTION_ARGS)
1704 : {
1705 920612 : PATH *p1 = PG_GETARG_PATH_P(0);
1706 920612 : PATH *p2 = PG_GETARG_PATH_P(1);
1707 : BOX b1,
1708 : b2;
1709 : int i,
1710 : j;
1711 : LSEG seg1,
1712 : seg2;
1713 :
1714 : Assert(p1->npts > 0 && p2->npts > 0);
1715 :
1716 920612 : b1.high.x = b1.low.x = p1->p[0].x;
1717 920612 : b1.high.y = b1.low.y = p1->p[0].y;
1718 3501400 : for (i = 1; i < p1->npts; i++)
1719 : {
1720 2580788 : b1.high.x = float8_max(p1->p[i].x, b1.high.x);
1721 2580788 : b1.high.y = float8_max(p1->p[i].y, b1.high.y);
1722 2580788 : b1.low.x = float8_min(p1->p[i].x, b1.low.x);
1723 2580788 : b1.low.y = float8_min(p1->p[i].y, b1.low.y);
1724 : }
1725 920612 : b2.high.x = b2.low.x = p2->p[0].x;
1726 920612 : b2.high.y = b2.low.y = p2->p[0].y;
1727 2634216 : for (i = 1; i < p2->npts; i++)
1728 : {
1729 1713604 : b2.high.x = float8_max(p2->p[i].x, b2.high.x);
1730 1713604 : b2.high.y = float8_max(p2->p[i].y, b2.high.y);
1731 1713604 : b2.low.x = float8_min(p2->p[i].x, b2.low.x);
1732 1713604 : b2.low.y = float8_min(p2->p[i].y, b2.low.y);
1733 : }
1734 920612 : if (!box_ov(&b1, &b2))
1735 898280 : PG_RETURN_BOOL(false);
1736 :
1737 : /* pairwise check lseg intersections */
1738 110008 : for (i = 0; i < p1->npts; i++)
1739 : {
1740 : int iprev;
1741 :
1742 92592 : if (i > 0)
1743 70260 : iprev = i - 1;
1744 : else
1745 : {
1746 22332 : if (!p1->closed)
1747 5324 : continue;
1748 17008 : iprev = p1->npts - 1; /* include the closure segment */
1749 : }
1750 :
1751 290444 : for (j = 0; j < p2->npts; j++)
1752 : {
1753 : int jprev;
1754 :
1755 208092 : if (j > 0)
1756 120824 : jprev = j - 1;
1757 : else
1758 : {
1759 87268 : if (!p2->closed)
1760 82492 : continue;
1761 4776 : jprev = p2->npts - 1; /* include the closure segment */
1762 : }
1763 :
1764 125600 : statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
1765 125600 : statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
1766 125600 : if (lseg_interpt_lseg(NULL, &seg1, &seg2))
1767 4916 : PG_RETURN_BOOL(true);
1768 : }
1769 : }
1770 :
1771 : /* if we dropped through, no two segs intersected */
1772 17416 : PG_RETURN_BOOL(false);
1773 : }
1774 :
1775 : /*
1776 : * path_distance()
1777 : * This essentially does a cartesian product of the lsegs in the
1778 : * two paths, and finds the min distance between any two lsegs
1779 : */
1780 : Datum
1781 324 : path_distance(PG_FUNCTION_ARGS)
1782 : {
1783 324 : PATH *p1 = PG_GETARG_PATH_P(0);
1784 324 : PATH *p2 = PG_GETARG_PATH_P(1);
1785 324 : float8 min = 0.0; /* initialize to keep compiler quiet */
1786 324 : bool have_min = false;
1787 : float8 tmp;
1788 : int i,
1789 : j;
1790 : LSEG seg1,
1791 : seg2;
1792 :
1793 1008 : for (i = 0; i < p1->npts; i++)
1794 : {
1795 : int iprev;
1796 :
1797 684 : if (i > 0)
1798 360 : iprev = i - 1;
1799 : else
1800 : {
1801 324 : if (!p1->closed)
1802 144 : continue;
1803 180 : iprev = p1->npts - 1; /* include the closure segment */
1804 : }
1805 :
1806 1680 : for (j = 0; j < p2->npts; j++)
1807 : {
1808 : int jprev;
1809 :
1810 1140 : if (j > 0)
1811 600 : jprev = j - 1;
1812 : else
1813 : {
1814 540 : if (!p2->closed)
1815 240 : continue;
1816 300 : jprev = p2->npts - 1; /* include the closure segment */
1817 : }
1818 :
1819 900 : statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
1820 900 : statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
1821 :
1822 900 : tmp = lseg_closept_lseg(NULL, &seg1, &seg2);
1823 900 : if (!have_min || float8_lt(tmp, min))
1824 : {
1825 388 : min = tmp;
1826 388 : have_min = true;
1827 : }
1828 : }
1829 : }
1830 :
1831 324 : if (!have_min)
1832 0 : PG_RETURN_NULL();
1833 :
1834 324 : PG_RETURN_FLOAT8(min);
1835 : }
1836 :
1837 :
1838 : /*----------------------------------------------------------
1839 : * "Arithmetic" operations.
1840 : *---------------------------------------------------------*/
1841 :
1842 : Datum
1843 36 : path_length(PG_FUNCTION_ARGS)
1844 : {
1845 36 : PATH *path = PG_GETARG_PATH_P(0);
1846 36 : float8 result = 0.0;
1847 : int i;
1848 :
1849 112 : for (i = 0; i < path->npts; i++)
1850 : {
1851 : int iprev;
1852 :
1853 76 : if (i > 0)
1854 40 : iprev = i - 1;
1855 : else
1856 : {
1857 36 : if (!path->closed)
1858 16 : continue;
1859 20 : iprev = path->npts - 1; /* include the closure segment */
1860 : }
1861 :
1862 60 : result = float8_pl(result, point_dt(&path->p[iprev], &path->p[i], NULL));
1863 : }
1864 :
1865 36 : PG_RETURN_FLOAT8(result);
1866 : }
1867 :
1868 : /***********************************************************************
1869 : **
1870 : ** Routines for 2D points.
1871 : **
1872 : ***********************************************************************/
1873 :
1874 : /*----------------------------------------------------------
1875 : * String to point, point to string conversion.
1876 : * External format:
1877 : * "(x,y)"
1878 : * "x,y"
1879 : *---------------------------------------------------------*/
1880 :
1881 : Datum
1882 17371 : point_in(PG_FUNCTION_ARGS)
1883 : {
1884 17371 : char *str = PG_GETARG_CSTRING(0);
1885 17371 : Point *point = palloc_object(Point);
1886 :
1887 : /* Ignore failure from pair_decode, since our return value won't matter */
1888 17371 : pair_decode(str, &point->x, &point->y, NULL, "point", str, fcinfo->context);
1889 17351 : PG_RETURN_POINT_P(point);
1890 : }
1891 :
1892 : Datum
1893 133638 : point_out(PG_FUNCTION_ARGS)
1894 : {
1895 133638 : Point *pt = PG_GETARG_POINT_P(0);
1896 :
1897 133638 : PG_RETURN_CSTRING(path_encode(PATH_NONE, 1, pt));
1898 : }
1899 :
1900 : /*
1901 : * point_recv - converts external binary format to point
1902 : */
1903 : Datum
1904 12 : point_recv(PG_FUNCTION_ARGS)
1905 : {
1906 12 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1907 : Point *point;
1908 :
1909 12 : point = palloc_object(Point);
1910 12 : point->x = pq_getmsgfloat8(buf);
1911 12 : point->y = pq_getmsgfloat8(buf);
1912 12 : PG_RETURN_POINT_P(point);
1913 : }
1914 :
1915 : /*
1916 : * point_send - converts point to binary format
1917 : */
1918 : Datum
1919 12 : point_send(PG_FUNCTION_ARGS)
1920 : {
1921 12 : Point *pt = PG_GETARG_POINT_P(0);
1922 : StringInfoData buf;
1923 :
1924 12 : pq_begintypsend(&buf);
1925 12 : pq_sendfloat8(&buf, pt->x);
1926 12 : pq_sendfloat8(&buf, pt->y);
1927 12 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1928 : }
1929 :
1930 :
1931 : /*
1932 : * Initialize a point
1933 : */
1934 : static inline void
1935 3466983 : point_construct(Point *result, float8 x, float8 y)
1936 : {
1937 3466983 : result->x = x;
1938 3466983 : result->y = y;
1939 3466983 : }
1940 :
1941 :
1942 : /*----------------------------------------------------------
1943 : * Relational operators for Points.
1944 : * Since we do have a sense of coordinates being
1945 : * "equal" to a given accuracy (point_vert, point_horiz),
1946 : * the other ops must preserve that sense. This means
1947 : * that results may, strictly speaking, be a lie (unless
1948 : * EPSILON = 0.0).
1949 : *---------------------------------------------------------*/
1950 :
1951 : Datum
1952 471970 : point_left(PG_FUNCTION_ARGS)
1953 : {
1954 471970 : Point *pt1 = PG_GETARG_POINT_P(0);
1955 471970 : Point *pt2 = PG_GETARG_POINT_P(1);
1956 :
1957 471970 : PG_RETURN_BOOL(FPlt(pt1->x, pt2->x));
1958 : }
1959 :
1960 : Datum
1961 11224719 : point_right(PG_FUNCTION_ARGS)
1962 : {
1963 11224719 : Point *pt1 = PG_GETARG_POINT_P(0);
1964 11224719 : Point *pt2 = PG_GETARG_POINT_P(1);
1965 :
1966 11224719 : PG_RETURN_BOOL(FPgt(pt1->x, pt2->x));
1967 : }
1968 :
1969 : Datum
1970 11383422 : point_above(PG_FUNCTION_ARGS)
1971 : {
1972 11383422 : Point *pt1 = PG_GETARG_POINT_P(0);
1973 11383422 : Point *pt2 = PG_GETARG_POINT_P(1);
1974 :
1975 11383422 : PG_RETURN_BOOL(FPgt(pt1->y, pt2->y));
1976 : }
1977 :
1978 : Datum
1979 835181 : point_below(PG_FUNCTION_ARGS)
1980 : {
1981 835181 : Point *pt1 = PG_GETARG_POINT_P(0);
1982 835181 : Point *pt2 = PG_GETARG_POINT_P(1);
1983 :
1984 835181 : PG_RETURN_BOOL(FPlt(pt1->y, pt2->y));
1985 : }
1986 :
1987 : Datum
1988 332274 : point_vert(PG_FUNCTION_ARGS)
1989 : {
1990 332274 : Point *pt1 = PG_GETARG_POINT_P(0);
1991 332274 : Point *pt2 = PG_GETARG_POINT_P(1);
1992 :
1993 332274 : PG_RETURN_BOOL(FPeq(pt1->x, pt2->x));
1994 : }
1995 :
1996 : Datum
1997 355538 : point_horiz(PG_FUNCTION_ARGS)
1998 : {
1999 355538 : Point *pt1 = PG_GETARG_POINT_P(0);
2000 355538 : Point *pt2 = PG_GETARG_POINT_P(1);
2001 :
2002 355538 : PG_RETURN_BOOL(FPeq(pt1->y, pt2->y));
2003 : }
2004 :
2005 : Datum
2006 53469 : point_eq(PG_FUNCTION_ARGS)
2007 : {
2008 53469 : Point *pt1 = PG_GETARG_POINT_P(0);
2009 53469 : Point *pt2 = PG_GETARG_POINT_P(1);
2010 :
2011 53469 : PG_RETURN_BOOL(point_eq_point(pt1, pt2));
2012 : }
2013 :
2014 : Datum
2015 468 : point_ne(PG_FUNCTION_ARGS)
2016 : {
2017 468 : Point *pt1 = PG_GETARG_POINT_P(0);
2018 468 : Point *pt2 = PG_GETARG_POINT_P(1);
2019 :
2020 468 : PG_RETURN_BOOL(!point_eq_point(pt1, pt2));
2021 : }
2022 :
2023 :
2024 : /*
2025 : * Check whether the two points are the same
2026 : */
2027 : static inline bool
2028 202261 : point_eq_point(Point *pt1, Point *pt2)
2029 : {
2030 : /* If any NaNs are involved, insist on exact equality */
2031 202261 : if (unlikely(isnan(pt1->x) || isnan(pt1->y) ||
2032 : isnan(pt2->x) || isnan(pt2->y)))
2033 284 : return (float8_eq(pt1->x, pt2->x) && float8_eq(pt1->y, pt2->y));
2034 :
2035 201977 : return (FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y));
2036 : }
2037 :
2038 :
2039 : /*----------------------------------------------------------
2040 : * "Arithmetic" operators on points.
2041 : *---------------------------------------------------------*/
2042 :
2043 : Datum
2044 493117 : point_distance(PG_FUNCTION_ARGS)
2045 : {
2046 493117 : Point *pt1 = PG_GETARG_POINT_P(0);
2047 493117 : Point *pt2 = PG_GETARG_POINT_P(1);
2048 :
2049 493117 : PG_RETURN_FLOAT8(point_dt(pt1, pt2, NULL));
2050 : }
2051 :
2052 : static inline float8
2053 10364497 : point_dt(Point *pt1, Point *pt2, Node *escontext)
2054 : {
2055 : float8 x;
2056 : float8 y;
2057 :
2058 10364497 : x = float8_mi_safe(pt1->x, pt2->x, escontext);
2059 10364497 : if (unlikely(SOFT_ERROR_OCCURRED(escontext)))
2060 0 : return 0.0;
2061 :
2062 10364497 : y = float8_mi_safe(pt1->y, pt2->y, escontext);
2063 10364497 : if (unlikely(SOFT_ERROR_OCCURRED(escontext)))
2064 0 : return 0.0;
2065 :
2066 10364497 : return hypot(x, y);
2067 : }
2068 :
2069 : Datum
2070 400 : point_slope(PG_FUNCTION_ARGS)
2071 : {
2072 400 : Point *pt1 = PG_GETARG_POINT_P(0);
2073 400 : Point *pt2 = PG_GETARG_POINT_P(1);
2074 :
2075 400 : PG_RETURN_FLOAT8(point_sl(pt1, pt2));
2076 : }
2077 :
2078 :
2079 : /*
2080 : * Return slope of two points
2081 : *
2082 : * Note that this function returns Inf when the points are the same.
2083 : */
2084 : static inline float8
2085 2566137 : point_sl(Point *pt1, Point *pt2)
2086 : {
2087 2566137 : if (FPeq(pt1->x, pt2->x))
2088 285669 : return get_float8_infinity();
2089 2280468 : if (FPeq(pt1->y, pt2->y))
2090 282996 : return 0.0;
2091 1997472 : return float8_div(float8_mi(pt1->y, pt2->y), float8_mi(pt1->x, pt2->x));
2092 : }
2093 :
2094 :
2095 : /*
2096 : * Return inverse slope of two points
2097 : *
2098 : * Note that this function returns 0.0 when the points are the same.
2099 : */
2100 : static inline float8
2101 604320 : point_invsl(Point *pt1, Point *pt2)
2102 : {
2103 604320 : if (FPeq(pt1->x, pt2->x))
2104 239072 : return 0.0;
2105 365248 : if (FPeq(pt1->y, pt2->y))
2106 234136 : return get_float8_infinity();
2107 131112 : return float8_div(float8_mi(pt1->x, pt2->x), float8_mi(pt2->y, pt1->y));
2108 : }
2109 :
2110 :
2111 : /***********************************************************************
2112 : **
2113 : ** Routines for 2D line segments.
2114 : **
2115 : ***********************************************************************/
2116 :
2117 : /*----------------------------------------------------------
2118 : * String to lseg, lseg to string conversion.
2119 : * External forms: "[(x1, y1), (x2, y2)]"
2120 : * "(x1, y1), (x2, y2)"
2121 : * "x1, y1, x2, y2"
2122 : * closed form ok "((x1, y1), (x2, y2))"
2123 : * (old form) "(x1, y1, x2, y2)"
2124 : *---------------------------------------------------------*/
2125 :
2126 : Datum
2127 69 : lseg_in(PG_FUNCTION_ARGS)
2128 : {
2129 69 : char *str = PG_GETARG_CSTRING(0);
2130 69 : Node *escontext = fcinfo->context;
2131 69 : LSEG *lseg = palloc_object(LSEG);
2132 : bool isopen;
2133 :
2134 69 : if (!path_decode(str, true, 2, &lseg->p[0], &isopen, NULL, "lseg", str,
2135 : escontext))
2136 8 : PG_RETURN_NULL();
2137 :
2138 45 : PG_RETURN_LSEG_P(lseg);
2139 : }
2140 :
2141 :
2142 : Datum
2143 4871 : lseg_out(PG_FUNCTION_ARGS)
2144 : {
2145 4871 : LSEG *ls = PG_GETARG_LSEG_P(0);
2146 :
2147 4871 : PG_RETURN_CSTRING(path_encode(PATH_OPEN, 2, &ls->p[0]));
2148 : }
2149 :
2150 : /*
2151 : * lseg_recv - converts external binary format to lseg
2152 : */
2153 : Datum
2154 0 : lseg_recv(PG_FUNCTION_ARGS)
2155 : {
2156 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
2157 : LSEG *lseg;
2158 :
2159 0 : lseg = palloc_object(LSEG);
2160 :
2161 0 : lseg->p[0].x = pq_getmsgfloat8(buf);
2162 0 : lseg->p[0].y = pq_getmsgfloat8(buf);
2163 0 : lseg->p[1].x = pq_getmsgfloat8(buf);
2164 0 : lseg->p[1].y = pq_getmsgfloat8(buf);
2165 :
2166 0 : PG_RETURN_LSEG_P(lseg);
2167 : }
2168 :
2169 : /*
2170 : * lseg_send - converts lseg to binary format
2171 : */
2172 : Datum
2173 0 : lseg_send(PG_FUNCTION_ARGS)
2174 : {
2175 0 : LSEG *ls = PG_GETARG_LSEG_P(0);
2176 : StringInfoData buf;
2177 :
2178 0 : pq_begintypsend(&buf);
2179 0 : pq_sendfloat8(&buf, ls->p[0].x);
2180 0 : pq_sendfloat8(&buf, ls->p[0].y);
2181 0 : pq_sendfloat8(&buf, ls->p[1].x);
2182 0 : pq_sendfloat8(&buf, ls->p[1].y);
2183 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
2184 : }
2185 :
2186 :
2187 : /*
2188 : * lseg_construct -
2189 : * form a LSEG from two Points.
2190 : */
2191 : Datum
2192 5 : lseg_construct(PG_FUNCTION_ARGS)
2193 : {
2194 5 : Point *pt1 = PG_GETARG_POINT_P(0);
2195 5 : Point *pt2 = PG_GETARG_POINT_P(1);
2196 5 : LSEG *result = palloc_object(LSEG);
2197 :
2198 5 : statlseg_construct(result, pt1, pt2);
2199 :
2200 5 : PG_RETURN_LSEG_P(result);
2201 : }
2202 :
2203 : /* like lseg_construct, but assume space already allocated */
2204 : static inline void
2205 673949 : statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
2206 : {
2207 673949 : lseg->p[0].x = pt1->x;
2208 673949 : lseg->p[0].y = pt1->y;
2209 673949 : lseg->p[1].x = pt2->x;
2210 673949 : lseg->p[1].y = pt2->y;
2211 673949 : }
2212 :
2213 :
2214 : /*
2215 : * Return slope of the line segment
2216 : */
2217 : static inline float8
2218 2565380 : lseg_sl(LSEG *lseg)
2219 : {
2220 2565380 : return point_sl(&lseg->p[0], &lseg->p[1]);
2221 : }
2222 :
2223 :
2224 : /*
2225 : * Return inverse slope of the line segment
2226 : */
2227 : static inline float8
2228 256 : lseg_invsl(LSEG *lseg)
2229 : {
2230 256 : return point_invsl(&lseg->p[0], &lseg->p[1]);
2231 : }
2232 :
2233 :
2234 : Datum
2235 32 : lseg_length(PG_FUNCTION_ARGS)
2236 : {
2237 32 : LSEG *lseg = PG_GETARG_LSEG_P(0);
2238 :
2239 32 : PG_RETURN_FLOAT8(point_dt(&lseg->p[0], &lseg->p[1], NULL));
2240 : }
2241 :
2242 : /*----------------------------------------------------------
2243 : * Relative position routines.
2244 : *---------------------------------------------------------*/
2245 :
2246 : /*
2247 : * find intersection of the two lines, and see if it falls on
2248 : * both segments.
2249 : */
2250 : Datum
2251 16912 : lseg_intersect(PG_FUNCTION_ARGS)
2252 : {
2253 16912 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2254 16912 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2255 :
2256 16912 : PG_RETURN_BOOL(lseg_interpt_lseg(NULL, l1, l2));
2257 : }
2258 :
2259 :
2260 : Datum
2261 256 : lseg_parallel(PG_FUNCTION_ARGS)
2262 : {
2263 256 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2264 256 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2265 :
2266 256 : PG_RETURN_BOOL(FPeq(lseg_sl(l1), lseg_sl(l2)));
2267 : }
2268 :
2269 : /*
2270 : * Determine if two line segments are perpendicular.
2271 : */
2272 : Datum
2273 256 : lseg_perp(PG_FUNCTION_ARGS)
2274 : {
2275 256 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2276 256 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2277 :
2278 256 : PG_RETURN_BOOL(FPeq(lseg_sl(l1), lseg_invsl(l2)));
2279 : }
2280 :
2281 : Datum
2282 32 : lseg_vertical(PG_FUNCTION_ARGS)
2283 : {
2284 32 : LSEG *lseg = PG_GETARG_LSEG_P(0);
2285 :
2286 32 : PG_RETURN_BOOL(FPeq(lseg->p[0].x, lseg->p[1].x));
2287 : }
2288 :
2289 : Datum
2290 32 : lseg_horizontal(PG_FUNCTION_ARGS)
2291 : {
2292 32 : LSEG *lseg = PG_GETARG_LSEG_P(0);
2293 :
2294 32 : PG_RETURN_BOOL(FPeq(lseg->p[0].y, lseg->p[1].y));
2295 : }
2296 :
2297 :
2298 : Datum
2299 257 : lseg_eq(PG_FUNCTION_ARGS)
2300 : {
2301 257 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2302 257 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2303 :
2304 257 : PG_RETURN_BOOL(point_eq_point(&l1->p[0], &l2->p[0]) &&
2305 : point_eq_point(&l1->p[1], &l2->p[1]));
2306 : }
2307 :
2308 : Datum
2309 256 : lseg_ne(PG_FUNCTION_ARGS)
2310 : {
2311 256 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2312 256 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2313 :
2314 256 : PG_RETURN_BOOL(!point_eq_point(&l1->p[0], &l2->p[0]) ||
2315 : !point_eq_point(&l1->p[1], &l2->p[1]));
2316 : }
2317 :
2318 : Datum
2319 256 : lseg_lt(PG_FUNCTION_ARGS)
2320 : {
2321 256 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2322 256 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2323 :
2324 256 : PG_RETURN_BOOL(FPlt(point_dt(&l1->p[0], &l1->p[1], NULL),
2325 : point_dt(&l2->p[0], &l2->p[1], NULL)));
2326 : }
2327 :
2328 : Datum
2329 256 : lseg_le(PG_FUNCTION_ARGS)
2330 : {
2331 256 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2332 256 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2333 :
2334 256 : PG_RETURN_BOOL(FPle(point_dt(&l1->p[0], &l1->p[1], NULL),
2335 : point_dt(&l2->p[0], &l2->p[1], NULL)));
2336 : }
2337 :
2338 : Datum
2339 256 : lseg_gt(PG_FUNCTION_ARGS)
2340 : {
2341 256 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2342 256 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2343 :
2344 256 : PG_RETURN_BOOL(FPgt(point_dt(&l1->p[0], &l1->p[1], NULL),
2345 : point_dt(&l2->p[0], &l2->p[1], NULL)));
2346 : }
2347 :
2348 : Datum
2349 256 : lseg_ge(PG_FUNCTION_ARGS)
2350 : {
2351 256 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2352 256 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2353 :
2354 256 : PG_RETURN_BOOL(FPge(point_dt(&l1->p[0], &l1->p[1], NULL),
2355 : point_dt(&l2->p[0], &l2->p[1], NULL)));
2356 : }
2357 :
2358 :
2359 : /*----------------------------------------------------------
2360 : * Line arithmetic routines.
2361 : *---------------------------------------------------------*/
2362 :
2363 : /*
2364 : * lseg_distance -
2365 : * If two segments don't intersect, then the closest
2366 : * point will be from one of the endpoints to the other
2367 : * segment.
2368 : */
2369 : Datum
2370 256 : lseg_distance(PG_FUNCTION_ARGS)
2371 : {
2372 256 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2373 256 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2374 :
2375 256 : PG_RETURN_FLOAT8(lseg_closept_lseg(NULL, l1, l2));
2376 : }
2377 :
2378 :
2379 : Datum
2380 64 : lseg_center(PG_FUNCTION_ARGS)
2381 : {
2382 64 : LSEG *lseg = PG_GETARG_LSEG_P(0);
2383 : Point *result;
2384 : float8 x;
2385 : float8 y;
2386 :
2387 64 : result = palloc_object(Point);
2388 :
2389 64 : x = float8_pl_safe(lseg->p[0].x, lseg->p[1].x, fcinfo->context);
2390 64 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
2391 0 : goto fail;
2392 :
2393 64 : result->x = float8_div_safe(x, 2.0, fcinfo->context);
2394 64 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
2395 0 : goto fail;
2396 :
2397 64 : y = float8_pl_safe(lseg->p[0].y, lseg->p[1].y, fcinfo->context);
2398 64 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
2399 0 : goto fail;
2400 :
2401 64 : result->y = float8_div_safe(y, 2.0, fcinfo->context);
2402 64 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
2403 0 : goto fail;
2404 :
2405 64 : PG_RETURN_POINT_P(result);
2406 :
2407 0 : fail:
2408 0 : PG_RETURN_NULL();
2409 : }
2410 :
2411 :
2412 : /*
2413 : * Return whether the two segments intersect. If *result is not NULL,
2414 : * it is set to the intersection point.
2415 : *
2416 : * This function is almost perfectly symmetric, even though it doesn't look
2417 : * like it. See lseg_interpt_line() for the other half of it.
2418 : */
2419 : static bool
2420 978900 : lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2)
2421 : {
2422 : Point interpt;
2423 : LINE tmp;
2424 :
2425 978900 : line_construct(&tmp, &l2->p[0], lseg_sl(l2));
2426 978900 : if (!lseg_interpt_line(&interpt, l1, &tmp))
2427 923904 : return false;
2428 :
2429 : /*
2430 : * If the line intersection point isn't within l2, there is no valid
2431 : * segment intersection point at all.
2432 : */
2433 54996 : if (!lseg_contain_point(l2, &interpt))
2434 41188 : return false;
2435 :
2436 13808 : if (result != NULL)
2437 4476 : *result = interpt;
2438 :
2439 13808 : return true;
2440 : }
2441 :
2442 : Datum
2443 3832 : lseg_interpt(PG_FUNCTION_ARGS)
2444 : {
2445 3832 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2446 3832 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2447 : Point *result;
2448 :
2449 3832 : result = palloc_object(Point);
2450 :
2451 3832 : if (!lseg_interpt_lseg(result, l1, l2))
2452 256 : PG_RETURN_NULL();
2453 3576 : PG_RETURN_POINT_P(result);
2454 : }
2455 :
2456 : /***********************************************************************
2457 : **
2458 : ** Routines for position comparisons of differently-typed
2459 : ** 2D objects.
2460 : **
2461 : ***********************************************************************/
2462 :
2463 : /*---------------------------------------------------------------------
2464 : * dist_
2465 : * Minimum distance from one object to another.
2466 : *-------------------------------------------------------------------*/
2467 :
2468 : /*
2469 : * Distance from a point to a line
2470 : */
2471 : Datum
2472 400 : dist_pl(PG_FUNCTION_ARGS)
2473 : {
2474 400 : Point *pt = PG_GETARG_POINT_P(0);
2475 400 : LINE *line = PG_GETARG_LINE_P(1);
2476 :
2477 400 : PG_RETURN_FLOAT8(line_closept_point(NULL, line, pt));
2478 : }
2479 :
2480 : /*
2481 : * Distance from a line to a point
2482 : */
2483 : Datum
2484 400 : dist_lp(PG_FUNCTION_ARGS)
2485 : {
2486 400 : LINE *line = PG_GETARG_LINE_P(0);
2487 400 : Point *pt = PG_GETARG_POINT_P(1);
2488 :
2489 400 : PG_RETURN_FLOAT8(line_closept_point(NULL, line, pt));
2490 : }
2491 :
2492 : /*
2493 : * Distance from a point to a lseg
2494 : */
2495 : Datum
2496 320 : dist_ps(PG_FUNCTION_ARGS)
2497 : {
2498 320 : Point *pt = PG_GETARG_POINT_P(0);
2499 320 : LSEG *lseg = PG_GETARG_LSEG_P(1);
2500 :
2501 320 : PG_RETURN_FLOAT8(lseg_closept_point(NULL, lseg, pt));
2502 : }
2503 :
2504 : /*
2505 : * Distance from a lseg to a point
2506 : */
2507 : Datum
2508 320 : dist_sp(PG_FUNCTION_ARGS)
2509 : {
2510 320 : LSEG *lseg = PG_GETARG_LSEG_P(0);
2511 320 : Point *pt = PG_GETARG_POINT_P(1);
2512 :
2513 320 : PG_RETURN_FLOAT8(lseg_closept_point(NULL, lseg, pt));
2514 : }
2515 :
2516 : static float8
2517 720 : dist_ppath_internal(Point *pt, PATH *path)
2518 : {
2519 720 : float8 result = 0.0; /* keep compiler quiet */
2520 720 : bool have_min = false;
2521 : float8 tmp;
2522 : int i;
2523 : LSEG lseg;
2524 :
2525 : Assert(path->npts > 0);
2526 :
2527 : /*
2528 : * The distance from a point to a path is the smallest distance from the
2529 : * point to any of its constituent segments.
2530 : */
2531 2240 : for (i = 0; i < path->npts; i++)
2532 : {
2533 : int iprev;
2534 :
2535 1520 : if (i > 0)
2536 800 : iprev = i - 1;
2537 : else
2538 : {
2539 720 : if (!path->closed)
2540 320 : continue;
2541 400 : iprev = path->npts - 1; /* Include the closure segment */
2542 : }
2543 :
2544 1200 : statlseg_construct(&lseg, &path->p[iprev], &path->p[i]);
2545 1200 : tmp = lseg_closept_point(NULL, &lseg, pt);
2546 1200 : if (!have_min || float8_lt(tmp, result))
2547 : {
2548 752 : result = tmp;
2549 752 : have_min = true;
2550 : }
2551 : }
2552 :
2553 720 : return result;
2554 : }
2555 :
2556 : /*
2557 : * Distance from a point to a path
2558 : */
2559 : Datum
2560 360 : dist_ppath(PG_FUNCTION_ARGS)
2561 : {
2562 360 : Point *pt = PG_GETARG_POINT_P(0);
2563 360 : PATH *path = PG_GETARG_PATH_P(1);
2564 :
2565 360 : PG_RETURN_FLOAT8(dist_ppath_internal(pt, path));
2566 : }
2567 :
2568 : /*
2569 : * Distance from a path to a point
2570 : */
2571 : Datum
2572 360 : dist_pathp(PG_FUNCTION_ARGS)
2573 : {
2574 360 : PATH *path = PG_GETARG_PATH_P(0);
2575 360 : Point *pt = PG_GETARG_POINT_P(1);
2576 :
2577 360 : PG_RETURN_FLOAT8(dist_ppath_internal(pt, path));
2578 : }
2579 :
2580 : /*
2581 : * Distance from a point to a box
2582 : */
2583 : Datum
2584 284 : dist_pb(PG_FUNCTION_ARGS)
2585 : {
2586 284 : Point *pt = PG_GETARG_POINT_P(0);
2587 284 : BOX *box = PG_GETARG_BOX_P(1);
2588 :
2589 284 : PG_RETURN_FLOAT8(box_closept_point(NULL, box, pt));
2590 : }
2591 :
2592 : /*
2593 : * Distance from a box to a point
2594 : */
2595 : Datum
2596 103508 : dist_bp(PG_FUNCTION_ARGS)
2597 : {
2598 103508 : BOX *box = PG_GETARG_BOX_P(0);
2599 103508 : Point *pt = PG_GETARG_POINT_P(1);
2600 :
2601 103508 : PG_RETURN_FLOAT8(box_closept_point(NULL, box, pt));
2602 : }
2603 :
2604 : /*
2605 : * Distance from a lseg to a line
2606 : */
2607 : Datum
2608 320 : dist_sl(PG_FUNCTION_ARGS)
2609 : {
2610 320 : LSEG *lseg = PG_GETARG_LSEG_P(0);
2611 320 : LINE *line = PG_GETARG_LINE_P(1);
2612 :
2613 320 : PG_RETURN_FLOAT8(lseg_closept_line(NULL, lseg, line));
2614 : }
2615 :
2616 : /*
2617 : * Distance from a line to a lseg
2618 : */
2619 : Datum
2620 320 : dist_ls(PG_FUNCTION_ARGS)
2621 : {
2622 320 : LINE *line = PG_GETARG_LINE_P(0);
2623 320 : LSEG *lseg = PG_GETARG_LSEG_P(1);
2624 :
2625 320 : PG_RETURN_FLOAT8(lseg_closept_line(NULL, lseg, line));
2626 : }
2627 :
2628 : /*
2629 : * Distance from a lseg to a box
2630 : */
2631 : Datum
2632 160 : dist_sb(PG_FUNCTION_ARGS)
2633 : {
2634 160 : LSEG *lseg = PG_GETARG_LSEG_P(0);
2635 160 : BOX *box = PG_GETARG_BOX_P(1);
2636 :
2637 160 : PG_RETURN_FLOAT8(box_closept_lseg(NULL, box, lseg));
2638 : }
2639 :
2640 : /*
2641 : * Distance from a box to a lseg
2642 : */
2643 : Datum
2644 160 : dist_bs(PG_FUNCTION_ARGS)
2645 : {
2646 160 : BOX *box = PG_GETARG_BOX_P(0);
2647 160 : LSEG *lseg = PG_GETARG_LSEG_P(1);
2648 :
2649 160 : PG_RETURN_FLOAT8(box_closept_lseg(NULL, box, lseg));
2650 : }
2651 :
2652 : static float8
2653 224 : dist_cpoly_internal(CIRCLE *circle, POLYGON *poly)
2654 : {
2655 : float8 result;
2656 :
2657 : /* calculate distance to center, and subtract radius */
2658 224 : result = float8_mi(dist_ppoly_internal(&circle->center, poly),
2659 : circle->radius);
2660 224 : if (result < 0.0)
2661 120 : result = 0.0;
2662 :
2663 224 : return result;
2664 : }
2665 :
2666 : /*
2667 : * Distance from a circle to a polygon
2668 : */
2669 : Datum
2670 224 : dist_cpoly(PG_FUNCTION_ARGS)
2671 : {
2672 224 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
2673 224 : POLYGON *poly = PG_GETARG_POLYGON_P(1);
2674 :
2675 224 : PG_RETURN_FLOAT8(dist_cpoly_internal(circle, poly));
2676 : }
2677 :
2678 : /*
2679 : * Distance from a polygon to a circle
2680 : */
2681 : Datum
2682 0 : dist_polyc(PG_FUNCTION_ARGS)
2683 : {
2684 0 : POLYGON *poly = PG_GETARG_POLYGON_P(0);
2685 0 : CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
2686 :
2687 0 : PG_RETURN_FLOAT8(dist_cpoly_internal(circle, poly));
2688 : }
2689 :
2690 : /*
2691 : * Distance from a point to a polygon
2692 : */
2693 : Datum
2694 280 : dist_ppoly(PG_FUNCTION_ARGS)
2695 : {
2696 280 : Point *point = PG_GETARG_POINT_P(0);
2697 280 : POLYGON *poly = PG_GETARG_POLYGON_P(1);
2698 :
2699 280 : PG_RETURN_FLOAT8(dist_ppoly_internal(point, poly));
2700 : }
2701 :
2702 : Datum
2703 22872 : dist_polyp(PG_FUNCTION_ARGS)
2704 : {
2705 22872 : POLYGON *poly = PG_GETARG_POLYGON_P(0);
2706 22872 : Point *point = PG_GETARG_POINT_P(1);
2707 :
2708 22872 : PG_RETURN_FLOAT8(dist_ppoly_internal(point, poly));
2709 : }
2710 :
2711 : static float8
2712 23376 : dist_ppoly_internal(Point *pt, POLYGON *poly)
2713 : {
2714 : float8 result;
2715 : float8 d;
2716 : int i;
2717 : LSEG seg;
2718 :
2719 23376 : if (point_inside(pt, poly->npts, poly->p) != 0)
2720 120 : return 0.0;
2721 :
2722 : /* initialize distance with segment between first and last points */
2723 23256 : seg.p[0].x = poly->p[0].x;
2724 23256 : seg.p[0].y = poly->p[0].y;
2725 23256 : seg.p[1].x = poly->p[poly->npts - 1].x;
2726 23256 : seg.p[1].y = poly->p[poly->npts - 1].y;
2727 23256 : result = lseg_closept_point(NULL, &seg, pt);
2728 :
2729 : /* check distances for other segments */
2730 172008 : for (i = 0; i < poly->npts - 1; i++)
2731 : {
2732 148752 : seg.p[0].x = poly->p[i].x;
2733 148752 : seg.p[0].y = poly->p[i].y;
2734 148752 : seg.p[1].x = poly->p[i + 1].x;
2735 148752 : seg.p[1].y = poly->p[i + 1].y;
2736 148752 : d = lseg_closept_point(NULL, &seg, pt);
2737 148752 : if (float8_lt(d, result))
2738 4392 : result = d;
2739 : }
2740 :
2741 23256 : return result;
2742 : }
2743 :
2744 :
2745 : /*---------------------------------------------------------------------
2746 : * interpt_
2747 : * Intersection point of objects.
2748 : * We choose to ignore the "point" of intersection between
2749 : * lines and boxes, since there are typically two.
2750 : *-------------------------------------------------------------------*/
2751 :
2752 : /*
2753 : * Return whether the line segment intersect with the line. If *result is not
2754 : * NULL, it is set to the intersection point.
2755 : */
2756 : static bool
2757 1584860 : lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
2758 : {
2759 : Point interpt;
2760 : LINE tmp;
2761 :
2762 : /*
2763 : * First, we promote the line segment to a line, because we know how to
2764 : * find the intersection point of two lines. If they don't have an
2765 : * intersection point, we are done.
2766 : */
2767 1584860 : line_construct(&tmp, &lseg->p[0], lseg_sl(lseg));
2768 1584860 : if (!line_interpt_line(&interpt, &tmp, line))
2769 8108 : return false;
2770 :
2771 : /*
2772 : * Then, we check whether the intersection point is actually on the line
2773 : * segment.
2774 : */
2775 1576752 : if (!lseg_contain_point(lseg, &interpt))
2776 1513768 : return false;
2777 62984 : if (result != NULL)
2778 : {
2779 : /*
2780 : * If there is an intersection, then check explicitly for matching
2781 : * endpoints since there may be rounding effects with annoying LSB
2782 : * residue.
2783 : */
2784 62728 : if (point_eq_point(&lseg->p[0], &interpt))
2785 4408 : *result = lseg->p[0];
2786 58320 : else if (point_eq_point(&lseg->p[1], &interpt))
2787 8836 : *result = lseg->p[1];
2788 : else
2789 49484 : *result = interpt;
2790 : }
2791 :
2792 62984 : return true;
2793 : }
2794 :
2795 : /*---------------------------------------------------------------------
2796 : * close_
2797 : * Point of closest proximity between objects.
2798 : *-------------------------------------------------------------------*/
2799 :
2800 : /*
2801 : * If *result is not NULL, it is set to the intersection point of a
2802 : * perpendicular of the line through the point. Returns the distance
2803 : * of those two points.
2804 : */
2805 : static float8
2806 1195440 : line_closept_point(Point *result, LINE *line, Point *point)
2807 : {
2808 : Point closept;
2809 : LINE tmp;
2810 :
2811 : /*
2812 : * We drop a perpendicular to find the intersection point. Ordinarily we
2813 : * should always find it, but that can fail in the presence of NaN
2814 : * coordinates, and perhaps even from simple roundoff issues.
2815 : */
2816 1195440 : line_construct(&tmp, point, line_invsl(line));
2817 1195440 : if (!line_interpt_line(&closept, &tmp, line))
2818 : {
2819 0 : if (result != NULL)
2820 0 : *result = *point;
2821 :
2822 0 : return get_float8_nan();
2823 : }
2824 :
2825 1195440 : if (result != NULL)
2826 400 : *result = closept;
2827 :
2828 1195440 : return point_dt(&closept, point, NULL);
2829 : }
2830 :
2831 : Datum
2832 400 : close_pl(PG_FUNCTION_ARGS)
2833 : {
2834 400 : Point *pt = PG_GETARG_POINT_P(0);
2835 400 : LINE *line = PG_GETARG_LINE_P(1);
2836 : Point *result;
2837 :
2838 400 : result = palloc_object(Point);
2839 :
2840 400 : if (isnan(line_closept_point(result, line, pt)))
2841 144 : PG_RETURN_NULL();
2842 :
2843 256 : PG_RETURN_POINT_P(result);
2844 : }
2845 :
2846 :
2847 : /*
2848 : * Closest point on line segment to specified point.
2849 : *
2850 : * If *result is not NULL, set it to the closest point on the line segment
2851 : * to the point. Returns the distance of the two points.
2852 : */
2853 : static float8
2854 604064 : lseg_closept_point(Point *result, LSEG *lseg, Point *pt)
2855 : {
2856 : Point closept;
2857 : LINE tmp;
2858 :
2859 : /*
2860 : * To find the closest point, we draw a perpendicular line from the point
2861 : * to the line segment.
2862 : */
2863 604064 : line_construct(&tmp, pt, point_invsl(&lseg->p[0], &lseg->p[1]));
2864 604064 : lseg_closept_line(&closept, lseg, &tmp);
2865 :
2866 604064 : if (result != NULL)
2867 317444 : *result = closept;
2868 :
2869 604064 : return point_dt(&closept, pt, NULL);
2870 : }
2871 :
2872 : Datum
2873 320 : close_ps(PG_FUNCTION_ARGS)
2874 : {
2875 320 : Point *pt = PG_GETARG_POINT_P(0);
2876 320 : LSEG *lseg = PG_GETARG_LSEG_P(1);
2877 : Point *result;
2878 :
2879 320 : result = palloc_object(Point);
2880 :
2881 320 : if (isnan(lseg_closept_point(result, lseg, pt)))
2882 64 : PG_RETURN_NULL();
2883 :
2884 256 : PG_RETURN_POINT_P(result);
2885 : }
2886 :
2887 :
2888 : /*
2889 : * Closest point on line segment to line segment
2890 : */
2891 : static float8
2892 3512 : lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg)
2893 : {
2894 : Point point;
2895 : float8 dist,
2896 : d;
2897 :
2898 : /* First, we handle the case when the line segments are intersecting. */
2899 3512 : if (lseg_interpt_lseg(result, on_lseg, to_lseg))
2900 16 : return 0.0;
2901 :
2902 : /*
2903 : * Then, we find the closest points from the endpoints of the second line
2904 : * segment, and keep the closest one.
2905 : */
2906 3496 : dist = lseg_closept_point(result, on_lseg, &to_lseg->p[0]);
2907 3496 : d = lseg_closept_point(&point, on_lseg, &to_lseg->p[1]);
2908 3496 : if (float8_lt(d, dist))
2909 : {
2910 1164 : dist = d;
2911 1164 : if (result != NULL)
2912 544 : *result = point;
2913 : }
2914 :
2915 : /* The closest point can still be one of the endpoints, so we test them. */
2916 3496 : d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[0]);
2917 3496 : if (float8_lt(d, dist))
2918 : {
2919 768 : dist = d;
2920 768 : if (result != NULL)
2921 496 : *result = on_lseg->p[0];
2922 : }
2923 3496 : d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[1]);
2924 3496 : if (float8_lt(d, dist))
2925 : {
2926 296 : dist = d;
2927 296 : if (result != NULL)
2928 168 : *result = on_lseg->p[1];
2929 : }
2930 :
2931 3496 : return dist;
2932 : }
2933 :
2934 : Datum
2935 256 : close_lseg(PG_FUNCTION_ARGS)
2936 : {
2937 256 : LSEG *l1 = PG_GETARG_LSEG_P(0);
2938 256 : LSEG *l2 = PG_GETARG_LSEG_P(1);
2939 : Point *result;
2940 :
2941 256 : if (lseg_sl(l1) == lseg_sl(l2))
2942 52 : PG_RETURN_NULL();
2943 :
2944 204 : result = palloc_object(Point);
2945 :
2946 204 : if (isnan(lseg_closept_lseg(result, l2, l1)))
2947 60 : PG_RETURN_NULL();
2948 :
2949 144 : PG_RETURN_POINT_P(result);
2950 : }
2951 :
2952 :
2953 : /*
2954 : * Closest point on or in box to specified point.
2955 : *
2956 : * If *result is not NULL, set it to the closest point on the box to the
2957 : * given point, and return the distance of the two points.
2958 : */
2959 : static float8
2960 103992 : box_closept_point(Point *result, BOX *box, Point *pt)
2961 : {
2962 : float8 dist,
2963 : d;
2964 : Point point,
2965 : closept;
2966 : LSEG lseg;
2967 :
2968 103992 : if (box_contain_point(box, pt))
2969 : {
2970 28 : if (result != NULL)
2971 4 : *result = *pt;
2972 :
2973 28 : return 0.0;
2974 : }
2975 :
2976 : /* pairwise check lseg distances */
2977 103964 : point.x = box->low.x;
2978 103964 : point.y = box->high.y;
2979 103964 : statlseg_construct(&lseg, &box->low, &point);
2980 103964 : dist = lseg_closept_point(result, &lseg, pt);
2981 :
2982 103964 : statlseg_construct(&lseg, &box->high, &point);
2983 103964 : d = lseg_closept_point(&closept, &lseg, pt);
2984 103964 : if (float8_lt(d, dist))
2985 : {
2986 4956 : dist = d;
2987 4956 : if (result != NULL)
2988 36 : *result = closept;
2989 : }
2990 :
2991 103964 : point.x = box->high.x;
2992 103964 : point.y = box->low.y;
2993 103964 : statlseg_construct(&lseg, &box->low, &point);
2994 103964 : d = lseg_closept_point(&closept, &lseg, pt);
2995 103964 : if (float8_lt(d, dist))
2996 : {
2997 5292 : dist = d;
2998 5292 : if (result != NULL)
2999 4 : *result = closept;
3000 : }
3001 :
3002 103964 : statlseg_construct(&lseg, &box->high, &point);
3003 103964 : d = lseg_closept_point(&closept, &lseg, pt);
3004 103964 : if (float8_lt(d, dist))
3005 : {
3006 24 : dist = d;
3007 24 : if (result != NULL)
3008 8 : *result = closept;
3009 : }
3010 :
3011 103964 : return dist;
3012 : }
3013 :
3014 : Datum
3015 200 : close_pb(PG_FUNCTION_ARGS)
3016 : {
3017 200 : Point *pt = PG_GETARG_POINT_P(0);
3018 200 : BOX *box = PG_GETARG_BOX_P(1);
3019 : Point *result;
3020 :
3021 200 : result = palloc_object(Point);
3022 :
3023 200 : if (isnan(box_closept_point(result, box, pt)))
3024 20 : PG_RETURN_NULL();
3025 :
3026 180 : PG_RETURN_POINT_P(result);
3027 : }
3028 :
3029 : /*
3030 : * Closest point on line segment to line.
3031 : *
3032 : * Return the distance between the line and the closest point of the line
3033 : * segment to the line. If *result is not NULL, set it to that point.
3034 : *
3035 : * NOTE: When the lines are parallel, endpoints of one of the line segment
3036 : * are FPeq(), in presence of NaN or Infinite coordinates, or perhaps =
3037 : * even because of simple roundoff issues, there may not be a single closest
3038 : * point. We are likely to set the result to the second endpoint in these
3039 : * cases.
3040 : */
3041 : static float8
3042 604988 : lseg_closept_line(Point *result, LSEG *lseg, LINE *line)
3043 : {
3044 : float8 dist1,
3045 : dist2;
3046 :
3047 604988 : if (lseg_interpt_line(result, lseg, line))
3048 7868 : return 0.0;
3049 :
3050 597120 : dist1 = line_closept_point(NULL, line, &lseg->p[0]);
3051 597120 : dist2 = line_closept_point(NULL, line, &lseg->p[1]);
3052 :
3053 597120 : if (dist1 < dist2)
3054 : {
3055 317140 : if (result != NULL)
3056 317004 : *result = lseg->p[0];
3057 :
3058 317140 : return dist1;
3059 : }
3060 : else
3061 : {
3062 279980 : if (result != NULL)
3063 279612 : *result = lseg->p[1];
3064 :
3065 279980 : return dist2;
3066 : }
3067 : }
3068 :
3069 : Datum
3070 320 : close_ls(PG_FUNCTION_ARGS)
3071 : {
3072 320 : LINE *line = PG_GETARG_LINE_P(0);
3073 320 : LSEG *lseg = PG_GETARG_LSEG_P(1);
3074 : Point *result;
3075 :
3076 320 : if (lseg_sl(lseg) == line_sl(line))
3077 36 : PG_RETURN_NULL();
3078 :
3079 284 : result = palloc_object(Point);
3080 :
3081 284 : if (isnan(lseg_closept_line(result, lseg, line)))
3082 96 : PG_RETURN_NULL();
3083 :
3084 188 : PG_RETURN_POINT_P(result);
3085 : }
3086 :
3087 :
3088 : /*
3089 : * Closest point on or in box to line segment.
3090 : *
3091 : * Returns the distance between the closest point on or in the box to
3092 : * the line segment. If *result is not NULL, it is set to that point.
3093 : */
3094 : static float8
3095 480 : box_closept_lseg(Point *result, BOX *box, LSEG *lseg)
3096 : {
3097 : float8 dist,
3098 : d;
3099 : Point point,
3100 : closept;
3101 : LSEG bseg;
3102 :
3103 480 : if (box_interpt_lseg(result, box, lseg))
3104 96 : return 0.0;
3105 :
3106 : /* pairwise check lseg distances */
3107 384 : point.x = box->low.x;
3108 384 : point.y = box->high.y;
3109 384 : statlseg_construct(&bseg, &box->low, &point);
3110 384 : dist = lseg_closept_lseg(result, &bseg, lseg);
3111 :
3112 384 : statlseg_construct(&bseg, &box->high, &point);
3113 384 : d = lseg_closept_lseg(&closept, &bseg, lseg);
3114 384 : if (float8_lt(d, dist))
3115 : {
3116 96 : dist = d;
3117 96 : if (result != NULL)
3118 32 : *result = closept;
3119 : }
3120 :
3121 384 : point.x = box->high.x;
3122 384 : point.y = box->low.y;
3123 384 : statlseg_construct(&bseg, &box->low, &point);
3124 384 : d = lseg_closept_lseg(&closept, &bseg, lseg);
3125 384 : if (float8_lt(d, dist))
3126 : {
3127 12 : dist = d;
3128 12 : if (result != NULL)
3129 4 : *result = closept;
3130 : }
3131 :
3132 384 : statlseg_construct(&bseg, &box->high, &point);
3133 384 : d = lseg_closept_lseg(&closept, &bseg, lseg);
3134 384 : if (float8_lt(d, dist))
3135 : {
3136 12 : dist = d;
3137 12 : if (result != NULL)
3138 4 : *result = closept;
3139 : }
3140 :
3141 384 : return dist;
3142 : }
3143 :
3144 : Datum
3145 160 : close_sb(PG_FUNCTION_ARGS)
3146 : {
3147 160 : LSEG *lseg = PG_GETARG_LSEG_P(0);
3148 160 : BOX *box = PG_GETARG_BOX_P(1);
3149 : Point *result;
3150 :
3151 160 : result = palloc_object(Point);
3152 :
3153 160 : if (isnan(box_closept_lseg(result, box, lseg)))
3154 20 : PG_RETURN_NULL();
3155 :
3156 140 : PG_RETURN_POINT_P(result);
3157 : }
3158 :
3159 :
3160 : /*---------------------------------------------------------------------
3161 : * on_
3162 : * Whether one object lies completely within another.
3163 : *-------------------------------------------------------------------*/
3164 :
3165 : /*
3166 : * Does the point satisfy the equation?
3167 : */
3168 : static bool
3169 736 : line_contain_point(LINE *line, Point *point)
3170 : {
3171 736 : return FPzero(float8_pl(float8_pl(float8_mul(line->A, point->x),
3172 : float8_mul(line->B, point->y)),
3173 : line->C));
3174 : }
3175 :
3176 : Datum
3177 400 : on_pl(PG_FUNCTION_ARGS)
3178 : {
3179 400 : Point *pt = PG_GETARG_POINT_P(0);
3180 400 : LINE *line = PG_GETARG_LINE_P(1);
3181 :
3182 400 : PG_RETURN_BOOL(line_contain_point(line, pt));
3183 : }
3184 :
3185 :
3186 : /*
3187 : * Determine colinearity by detecting a triangle inequality.
3188 : * This algorithm seems to behave nicely even with lsb residues - tgl 1997-07-09
3189 : */
3190 : static bool
3191 2676572 : lseg_contain_point(LSEG *lseg, Point *pt)
3192 : {
3193 5353144 : return FPeq(point_dt(pt, &lseg->p[0], NULL) +
3194 2676572 : point_dt(pt, &lseg->p[1], NULL),
3195 : point_dt(&lseg->p[0], &lseg->p[1], NULL));
3196 : }
3197 :
3198 : Datum
3199 320 : on_ps(PG_FUNCTION_ARGS)
3200 : {
3201 320 : Point *pt = PG_GETARG_POINT_P(0);
3202 320 : LSEG *lseg = PG_GETARG_LSEG_P(1);
3203 :
3204 320 : PG_RETURN_BOOL(lseg_contain_point(lseg, pt));
3205 : }
3206 :
3207 :
3208 : /*
3209 : * Check whether the point is in the box or on its border
3210 : */
3211 : static bool
3212 291856 : box_contain_point(BOX *box, Point *point)
3213 : {
3214 169188 : return box->high.x >= point->x && box->low.x <= point->x &&
3215 461044 : box->high.y >= point->y && box->low.y <= point->y;
3216 : }
3217 :
3218 : Datum
3219 92168 : on_pb(PG_FUNCTION_ARGS)
3220 : {
3221 92168 : Point *pt = PG_GETARG_POINT_P(0);
3222 92168 : BOX *box = PG_GETARG_BOX_P(1);
3223 :
3224 92168 : PG_RETURN_BOOL(box_contain_point(box, pt));
3225 : }
3226 :
3227 : Datum
3228 95124 : box_contain_pt(PG_FUNCTION_ARGS)
3229 : {
3230 95124 : BOX *box = PG_GETARG_BOX_P(0);
3231 95124 : Point *pt = PG_GETARG_POINT_P(1);
3232 :
3233 95124 : PG_RETURN_BOOL(box_contain_point(box, pt));
3234 : }
3235 :
3236 : /*
3237 : * on_ppath -
3238 : * Whether a point lies within (on) a polyline.
3239 : * If open, we have to (groan) check each segment.
3240 : * (uses same algorithm as for point intersecting segment - tgl 1997-07-09)
3241 : * If closed, we use the old O(n) ray method for point-in-polygon.
3242 : * The ray is horizontal, from pt out to the right.
3243 : * Each segment that crosses the ray counts as an
3244 : * intersection; note that an endpoint or edge may touch
3245 : * but not cross.
3246 : * (we can do p-in-p in lg(n), but it takes preprocessing)
3247 : */
3248 : Datum
3249 400 : on_ppath(PG_FUNCTION_ARGS)
3250 : {
3251 400 : Point *pt = PG_GETARG_POINT_P(0);
3252 400 : PATH *path = PG_GETARG_PATH_P(1);
3253 : int i,
3254 : n;
3255 : float8 a,
3256 : b;
3257 :
3258 : /*-- OPEN --*/
3259 400 : if (!path->closed)
3260 : {
3261 200 : n = path->npts - 1;
3262 200 : a = point_dt(pt, &path->p[0], NULL);
3263 472 : for (i = 0; i < n; i++)
3264 : {
3265 292 : b = point_dt(pt, &path->p[i + 1], NULL);
3266 292 : if (FPeq(float8_pl(a, b), point_dt(&path->p[i], &path->p[i + 1], NULL)))
3267 20 : PG_RETURN_BOOL(true);
3268 272 : a = b;
3269 : }
3270 180 : PG_RETURN_BOOL(false);
3271 : }
3272 :
3273 : /*-- CLOSED --*/
3274 200 : PG_RETURN_BOOL(point_inside(pt, path->npts, path->p) != 0);
3275 : }
3276 :
3277 :
3278 : /*
3279 : * Check whether the line segment is on the line or close enough
3280 : *
3281 : * It is, if both of its points are on the line or close enough.
3282 : */
3283 : Datum
3284 320 : on_sl(PG_FUNCTION_ARGS)
3285 : {
3286 320 : LSEG *lseg = PG_GETARG_LSEG_P(0);
3287 320 : LINE *line = PG_GETARG_LINE_P(1);
3288 :
3289 320 : PG_RETURN_BOOL(line_contain_point(line, &lseg->p[0]) &&
3290 : line_contain_point(line, &lseg->p[1]));
3291 : }
3292 :
3293 :
3294 : /*
3295 : * Check whether the line segment is in the box or on its border
3296 : *
3297 : * It is, if both of its points are in the box or on its border.
3298 : */
3299 : static bool
3300 160 : box_contain_lseg(BOX *box, LSEG *lseg)
3301 : {
3302 172 : return box_contain_point(box, &lseg->p[0]) &&
3303 12 : box_contain_point(box, &lseg->p[1]);
3304 : }
3305 :
3306 : Datum
3307 160 : on_sb(PG_FUNCTION_ARGS)
3308 : {
3309 160 : LSEG *lseg = PG_GETARG_LSEG_P(0);
3310 160 : BOX *box = PG_GETARG_BOX_P(1);
3311 :
3312 160 : PG_RETURN_BOOL(box_contain_lseg(box, lseg));
3313 : }
3314 :
3315 : /*---------------------------------------------------------------------
3316 : * inter_
3317 : * Whether one object intersects another.
3318 : *-------------------------------------------------------------------*/
3319 :
3320 : Datum
3321 320 : inter_sl(PG_FUNCTION_ARGS)
3322 : {
3323 320 : LSEG *lseg = PG_GETARG_LSEG_P(0);
3324 320 : LINE *line = PG_GETARG_LINE_P(1);
3325 :
3326 320 : PG_RETURN_BOOL(lseg_interpt_line(NULL, lseg, line));
3327 : }
3328 :
3329 :
3330 : /*
3331 : * Do line segment and box intersect?
3332 : *
3333 : * Segment completely inside box counts as intersection.
3334 : * If you want only segments crossing box boundaries,
3335 : * try converting box to path first.
3336 : *
3337 : * This function also sets the *result to the closest point on the line
3338 : * segment to the center of the box when they overlap and the result is
3339 : * not NULL. It is somewhat arbitrary, but maybe the best we can do as
3340 : * there are typically two points they intersect.
3341 : *
3342 : * Optimize for non-intersection by checking for box intersection first.
3343 : * - thomas 1998-01-30
3344 : */
3345 : static bool
3346 640 : box_interpt_lseg(Point *result, BOX *box, LSEG *lseg)
3347 : {
3348 : BOX lbox;
3349 : LSEG bseg;
3350 : Point point;
3351 :
3352 640 : lbox.low.x = float8_min(lseg->p[0].x, lseg->p[1].x);
3353 640 : lbox.low.y = float8_min(lseg->p[0].y, lseg->p[1].y);
3354 640 : lbox.high.x = float8_max(lseg->p[0].x, lseg->p[1].x);
3355 640 : lbox.high.y = float8_max(lseg->p[0].y, lseg->p[1].y);
3356 :
3357 : /* nothing close to overlap? then not going to intersect */
3358 640 : if (!box_ov(&lbox, box))
3359 416 : return false;
3360 :
3361 224 : if (result != NULL)
3362 : {
3363 56 : box_cn(&point, box, NULL);
3364 56 : lseg_closept_point(result, lseg, &point);
3365 : }
3366 :
3367 : /* an endpoint of segment is inside box? then clearly intersects */
3368 400 : if (box_contain_point(box, &lseg->p[0]) ||
3369 176 : box_contain_point(box, &lseg->p[1]))
3370 64 : return true;
3371 :
3372 : /* pairwise check lseg intersections */
3373 160 : point.x = box->low.x;
3374 160 : point.y = box->high.y;
3375 160 : statlseg_construct(&bseg, &box->low, &point);
3376 160 : if (lseg_interpt_lseg(NULL, &bseg, lseg))
3377 64 : return true;
3378 :
3379 96 : statlseg_construct(&bseg, &box->high, &point);
3380 96 : if (lseg_interpt_lseg(NULL, &bseg, lseg))
3381 0 : return true;
3382 :
3383 96 : point.x = box->high.x;
3384 96 : point.y = box->low.y;
3385 96 : statlseg_construct(&bseg, &box->low, &point);
3386 96 : if (lseg_interpt_lseg(NULL, &bseg, lseg))
3387 0 : return true;
3388 :
3389 96 : statlseg_construct(&bseg, &box->high, &point);
3390 96 : if (lseg_interpt_lseg(NULL, &bseg, lseg))
3391 0 : return true;
3392 :
3393 : /* if we dropped through, no two segs intersected */
3394 96 : return false;
3395 : }
3396 :
3397 : Datum
3398 160 : inter_sb(PG_FUNCTION_ARGS)
3399 : {
3400 160 : LSEG *lseg = PG_GETARG_LSEG_P(0);
3401 160 : BOX *box = PG_GETARG_BOX_P(1);
3402 :
3403 160 : PG_RETURN_BOOL(box_interpt_lseg(NULL, box, lseg));
3404 : }
3405 :
3406 :
3407 : /*
3408 : * inter_lb()
3409 : * Do line and box intersect?
3410 : */
3411 : Datum
3412 200 : inter_lb(PG_FUNCTION_ARGS)
3413 : {
3414 200 : LINE *line = PG_GETARG_LINE_P(0);
3415 200 : BOX *box = PG_GETARG_BOX_P(1);
3416 : LSEG bseg;
3417 : Point p1,
3418 : p2;
3419 :
3420 : /* pairwise check lseg intersections */
3421 200 : p1.x = box->low.x;
3422 200 : p1.y = box->low.y;
3423 200 : p2.x = box->low.x;
3424 200 : p2.y = box->high.y;
3425 200 : statlseg_construct(&bseg, &p1, &p2);
3426 200 : if (lseg_interpt_line(NULL, &bseg, line))
3427 44 : PG_RETURN_BOOL(true);
3428 156 : p1.x = box->high.x;
3429 156 : p1.y = box->high.y;
3430 156 : statlseg_construct(&bseg, &p1, &p2);
3431 156 : if (lseg_interpt_line(NULL, &bseg, line))
3432 8 : PG_RETURN_BOOL(true);
3433 148 : p2.x = box->high.x;
3434 148 : p2.y = box->low.y;
3435 148 : statlseg_construct(&bseg, &p1, &p2);
3436 148 : if (lseg_interpt_line(NULL, &bseg, line))
3437 0 : PG_RETURN_BOOL(true);
3438 148 : p1.x = box->low.x;
3439 148 : p1.y = box->low.y;
3440 148 : statlseg_construct(&bseg, &p1, &p2);
3441 148 : if (lseg_interpt_line(NULL, &bseg, line))
3442 0 : PG_RETURN_BOOL(true);
3443 :
3444 : /* if we dropped through, no intersection */
3445 148 : PG_RETURN_BOOL(false);
3446 : }
3447 :
3448 : /*------------------------------------------------------------------
3449 : * The following routines define a data type and operator class for
3450 : * POLYGONS .... Part of which (the polygon's bounding box) is built on
3451 : * top of the BOX data type.
3452 : *
3453 : * make_bound_box - create the bounding box for the input polygon
3454 : *------------------------------------------------------------------*/
3455 :
3456 : /*---------------------------------------------------------------------
3457 : * Make the smallest bounding box for the given polygon.
3458 : *---------------------------------------------------------------------*/
3459 : static void
3460 40338 : make_bound_box(POLYGON *poly)
3461 : {
3462 : int i;
3463 : float8 x1,
3464 : y1,
3465 : x2,
3466 : y2;
3467 :
3468 : Assert(poly->npts > 0);
3469 :
3470 40338 : x1 = x2 = poly->p[0].x;
3471 40338 : y2 = y1 = poly->p[0].y;
3472 481378 : for (i = 1; i < poly->npts; i++)
3473 : {
3474 441040 : if (float8_lt(poly->p[i].x, x1))
3475 44 : x1 = poly->p[i].x;
3476 441040 : if (float8_gt(poly->p[i].x, x2))
3477 240707 : x2 = poly->p[i].x;
3478 441040 : if (float8_lt(poly->p[i].y, y1))
3479 120267 : y1 = poly->p[i].y;
3480 441040 : if (float8_gt(poly->p[i].y, y2))
3481 120404 : y2 = poly->p[i].y;
3482 : }
3483 :
3484 40338 : poly->boundbox.low.x = x1;
3485 40338 : poly->boundbox.high.x = x2;
3486 40338 : poly->boundbox.low.y = y1;
3487 40338 : poly->boundbox.high.y = y2;
3488 40338 : }
3489 :
3490 : /*------------------------------------------------------------------
3491 : * poly_in - read in the polygon from a string specification
3492 : *
3493 : * External format:
3494 : * "((x0,y0),...,(xn,yn))"
3495 : * "x0,y0,...,xn,yn"
3496 : * also supports the older style "(x1,...,xn,y1,...yn)"
3497 : *------------------------------------------------------------------*/
3498 : Datum
3499 269 : poly_in(PG_FUNCTION_ARGS)
3500 : {
3501 269 : char *str = PG_GETARG_CSTRING(0);
3502 269 : Node *escontext = fcinfo->context;
3503 : POLYGON *poly;
3504 : int npts;
3505 : int size;
3506 : int base_size;
3507 : bool isopen;
3508 :
3509 269 : if ((npts = pair_count(str, ',')) <= 0)
3510 24 : ereturn(escontext, (Datum) 0,
3511 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
3512 : errmsg("invalid input syntax for type %s: \"%s\"",
3513 : "polygon", str)));
3514 :
3515 245 : base_size = sizeof(poly->p[0]) * npts;
3516 245 : size = offsetof(POLYGON, p) + base_size;
3517 :
3518 : /* Check for integer overflow */
3519 245 : if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
3520 0 : ereturn(escontext, (Datum) 0,
3521 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3522 : errmsg("too many points requested")));
3523 :
3524 245 : poly = (POLYGON *) palloc0(size); /* zero any holes */
3525 :
3526 245 : SET_VARSIZE(poly, size);
3527 245 : poly->npts = npts;
3528 :
3529 245 : if (!path_decode(str, false, npts, &(poly->p[0]), &isopen, NULL, "polygon",
3530 : str, escontext))
3531 8 : PG_RETURN_NULL();
3532 :
3533 233 : make_bound_box(poly);
3534 :
3535 233 : PG_RETURN_POLYGON_P(poly);
3536 : }
3537 :
3538 : /*---------------------------------------------------------------
3539 : * poly_out - convert internal POLYGON representation to the
3540 : * character string format "((f8,f8),...,(f8,f8))"
3541 : *---------------------------------------------------------------*/
3542 : Datum
3543 47755 : poly_out(PG_FUNCTION_ARGS)
3544 : {
3545 47755 : POLYGON *poly = PG_GETARG_POLYGON_P(0);
3546 :
3547 47755 : PG_RETURN_CSTRING(path_encode(PATH_CLOSED, poly->npts, poly->p));
3548 : }
3549 :
3550 : /*
3551 : * poly_recv - converts external binary format to polygon
3552 : *
3553 : * External representation is int32 number of points, and the points.
3554 : * We recompute the bounding box on read, instead of trusting it to
3555 : * be valid. (Checking it would take just as long, so may as well
3556 : * omit it from external representation.)
3557 : */
3558 : Datum
3559 0 : poly_recv(PG_FUNCTION_ARGS)
3560 : {
3561 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
3562 : POLYGON *poly;
3563 : int32 npts;
3564 : int32 i;
3565 : int size;
3566 :
3567 0 : npts = pq_getmsgint(buf, sizeof(int32));
3568 0 : if (npts <= 0 || npts >= (int32) ((INT_MAX - offsetof(POLYGON, p)) / sizeof(Point)))
3569 0 : ereport(ERROR,
3570 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
3571 : errmsg("invalid number of points in external \"polygon\" value")));
3572 :
3573 0 : size = offsetof(POLYGON, p) + sizeof(poly->p[0]) * npts;
3574 0 : poly = (POLYGON *) palloc0(size); /* zero any holes */
3575 :
3576 0 : SET_VARSIZE(poly, size);
3577 0 : poly->npts = npts;
3578 :
3579 0 : for (i = 0; i < npts; i++)
3580 : {
3581 0 : poly->p[i].x = pq_getmsgfloat8(buf);
3582 0 : poly->p[i].y = pq_getmsgfloat8(buf);
3583 : }
3584 :
3585 0 : make_bound_box(poly);
3586 :
3587 0 : PG_RETURN_POLYGON_P(poly);
3588 : }
3589 :
3590 : /*
3591 : * poly_send - converts polygon to binary format
3592 : */
3593 : Datum
3594 0 : poly_send(PG_FUNCTION_ARGS)
3595 : {
3596 0 : POLYGON *poly = PG_GETARG_POLYGON_P(0);
3597 : StringInfoData buf;
3598 : int32 i;
3599 :
3600 0 : pq_begintypsend(&buf);
3601 0 : pq_sendint32(&buf, poly->npts);
3602 0 : for (i = 0; i < poly->npts; i++)
3603 : {
3604 0 : pq_sendfloat8(&buf, poly->p[i].x);
3605 0 : pq_sendfloat8(&buf, poly->p[i].y);
3606 : }
3607 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
3608 : }
3609 :
3610 :
3611 : /*-------------------------------------------------------
3612 : * Is polygon A strictly left of polygon B? i.e. is
3613 : * the right most point of A left of the left most point
3614 : * of B?
3615 : *-------------------------------------------------------*/
3616 : Datum
3617 196 : poly_left(PG_FUNCTION_ARGS)
3618 : {
3619 196 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
3620 196 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
3621 : bool result;
3622 :
3623 196 : result = polya->boundbox.high.x < polyb->boundbox.low.x;
3624 :
3625 : /*
3626 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
3627 : */
3628 196 : PG_FREE_IF_COPY(polya, 0);
3629 196 : PG_FREE_IF_COPY(polyb, 1);
3630 :
3631 196 : PG_RETURN_BOOL(result);
3632 : }
3633 :
3634 : /*-------------------------------------------------------
3635 : * Is polygon A overlapping or left of polygon B? i.e. is
3636 : * the right most point of A at or left of the right most point
3637 : * of B?
3638 : *-------------------------------------------------------*/
3639 : Datum
3640 196 : poly_overleft(PG_FUNCTION_ARGS)
3641 : {
3642 196 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
3643 196 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
3644 : bool result;
3645 :
3646 196 : result = polya->boundbox.high.x <= polyb->boundbox.high.x;
3647 :
3648 : /*
3649 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
3650 : */
3651 196 : PG_FREE_IF_COPY(polya, 0);
3652 196 : PG_FREE_IF_COPY(polyb, 1);
3653 :
3654 196 : PG_RETURN_BOOL(result);
3655 : }
3656 :
3657 : /*-------------------------------------------------------
3658 : * Is polygon A strictly right of polygon B? i.e. is
3659 : * the left most point of A right of the right most point
3660 : * of B?
3661 : *-------------------------------------------------------*/
3662 : Datum
3663 196 : poly_right(PG_FUNCTION_ARGS)
3664 : {
3665 196 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
3666 196 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
3667 : bool result;
3668 :
3669 196 : result = polya->boundbox.low.x > polyb->boundbox.high.x;
3670 :
3671 : /*
3672 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
3673 : */
3674 196 : PG_FREE_IF_COPY(polya, 0);
3675 196 : PG_FREE_IF_COPY(polyb, 1);
3676 :
3677 196 : PG_RETURN_BOOL(result);
3678 : }
3679 :
3680 : /*-------------------------------------------------------
3681 : * Is polygon A overlapping or right of polygon B? i.e. is
3682 : * the left most point of A at or right of the left most point
3683 : * of B?
3684 : *-------------------------------------------------------*/
3685 : Datum
3686 196 : poly_overright(PG_FUNCTION_ARGS)
3687 : {
3688 196 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
3689 196 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
3690 : bool result;
3691 :
3692 196 : result = polya->boundbox.low.x >= polyb->boundbox.low.x;
3693 :
3694 : /*
3695 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
3696 : */
3697 196 : PG_FREE_IF_COPY(polya, 0);
3698 196 : PG_FREE_IF_COPY(polyb, 1);
3699 :
3700 196 : PG_RETURN_BOOL(result);
3701 : }
3702 :
3703 : /*-------------------------------------------------------
3704 : * Is polygon A strictly below polygon B? i.e. is
3705 : * the upper most point of A below the lower most point
3706 : * of B?
3707 : *-------------------------------------------------------*/
3708 : Datum
3709 196 : poly_below(PG_FUNCTION_ARGS)
3710 : {
3711 196 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
3712 196 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
3713 : bool result;
3714 :
3715 196 : result = polya->boundbox.high.y < polyb->boundbox.low.y;
3716 :
3717 : /*
3718 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
3719 : */
3720 196 : PG_FREE_IF_COPY(polya, 0);
3721 196 : PG_FREE_IF_COPY(polyb, 1);
3722 :
3723 196 : PG_RETURN_BOOL(result);
3724 : }
3725 :
3726 : /*-------------------------------------------------------
3727 : * Is polygon A overlapping or below polygon B? i.e. is
3728 : * the upper most point of A at or below the upper most point
3729 : * of B?
3730 : *-------------------------------------------------------*/
3731 : Datum
3732 196 : poly_overbelow(PG_FUNCTION_ARGS)
3733 : {
3734 196 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
3735 196 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
3736 : bool result;
3737 :
3738 196 : result = polya->boundbox.high.y <= polyb->boundbox.high.y;
3739 :
3740 : /*
3741 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
3742 : */
3743 196 : PG_FREE_IF_COPY(polya, 0);
3744 196 : PG_FREE_IF_COPY(polyb, 1);
3745 :
3746 196 : PG_RETURN_BOOL(result);
3747 : }
3748 :
3749 : /*-------------------------------------------------------
3750 : * Is polygon A strictly above polygon B? i.e. is
3751 : * the lower most point of A above the upper most point
3752 : * of B?
3753 : *-------------------------------------------------------*/
3754 : Datum
3755 196 : poly_above(PG_FUNCTION_ARGS)
3756 : {
3757 196 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
3758 196 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
3759 : bool result;
3760 :
3761 196 : result = polya->boundbox.low.y > polyb->boundbox.high.y;
3762 :
3763 : /*
3764 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
3765 : */
3766 196 : PG_FREE_IF_COPY(polya, 0);
3767 196 : PG_FREE_IF_COPY(polyb, 1);
3768 :
3769 196 : PG_RETURN_BOOL(result);
3770 : }
3771 :
3772 : /*-------------------------------------------------------
3773 : * Is polygon A overlapping or above polygon B? i.e. is
3774 : * the lower most point of A at or above the lower most point
3775 : * of B?
3776 : *-------------------------------------------------------*/
3777 : Datum
3778 196 : poly_overabove(PG_FUNCTION_ARGS)
3779 : {
3780 196 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
3781 196 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
3782 : bool result;
3783 :
3784 196 : result = polya->boundbox.low.y >= polyb->boundbox.low.y;
3785 :
3786 : /*
3787 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
3788 : */
3789 196 : PG_FREE_IF_COPY(polya, 0);
3790 196 : PG_FREE_IF_COPY(polyb, 1);
3791 :
3792 196 : PG_RETURN_BOOL(result);
3793 : }
3794 :
3795 :
3796 : /*-------------------------------------------------------
3797 : * Is polygon A the same as polygon B? i.e. are all the
3798 : * points the same?
3799 : * Check all points for matches in both forward and reverse
3800 : * direction since polygons are non-directional and are
3801 : * closed shapes.
3802 : *-------------------------------------------------------*/
3803 : Datum
3804 4197 : poly_same(PG_FUNCTION_ARGS)
3805 : {
3806 4197 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
3807 4197 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
3808 : bool result;
3809 :
3810 4197 : if (polya->npts != polyb->npts)
3811 136 : result = false;
3812 : else
3813 4061 : result = plist_same(polya->npts, polya->p, polyb->p);
3814 :
3815 : /*
3816 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
3817 : */
3818 4197 : PG_FREE_IF_COPY(polya, 0);
3819 4197 : PG_FREE_IF_COPY(polyb, 1);
3820 :
3821 4197 : PG_RETURN_BOOL(result);
3822 : }
3823 :
3824 : /*-----------------------------------------------------------------
3825 : * Determine if polygon A overlaps polygon B
3826 : *-----------------------------------------------------------------*/
3827 : static bool
3828 19612 : poly_overlap_internal(POLYGON *polya, POLYGON *polyb)
3829 : {
3830 : bool result;
3831 :
3832 : Assert(polya->npts > 0 && polyb->npts > 0);
3833 :
3834 : /* Quick check by bounding box */
3835 19612 : result = box_ov(&polya->boundbox, &polyb->boundbox);
3836 :
3837 : /*
3838 : * Brute-force algorithm - try to find intersected edges, if so then
3839 : * polygons are overlapped else check is one polygon inside other or not
3840 : * by testing single point of them.
3841 : */
3842 19612 : if (result)
3843 : {
3844 : int ia,
3845 : ib;
3846 : LSEG sa,
3847 : sb;
3848 :
3849 : /* Init first of polya's edge with last point */
3850 7076 : sa.p[0] = polya->p[polya->npts - 1];
3851 7076 : result = false;
3852 :
3853 84364 : for (ia = 0; ia < polya->npts && !result; ia++)
3854 : {
3855 : /* Second point of polya's edge is a current one */
3856 77288 : sa.p[1] = polya->p[ia];
3857 :
3858 : /* Init first of polyb's edge with last point */
3859 77288 : sb.p[0] = polyb->p[polyb->npts - 1];
3860 :
3861 384676 : for (ib = 0; ib < polyb->npts && !result; ib++)
3862 : {
3863 307388 : sb.p[1] = polyb->p[ib];
3864 307388 : result = lseg_interpt_lseg(NULL, &sa, &sb);
3865 307388 : sb.p[0] = sb.p[1];
3866 : }
3867 :
3868 : /*
3869 : * move current endpoint to the first point of next edge
3870 : */
3871 77288 : sa.p[0] = sa.p[1];
3872 : }
3873 :
3874 7076 : if (!result)
3875 : {
3876 9268 : result = (point_inside(polya->p, polyb->npts, polyb->p) ||
3877 2952 : point_inside(polyb->p, polya->npts, polya->p));
3878 : }
3879 : }
3880 :
3881 19612 : return result;
3882 : }
3883 :
3884 : Datum
3885 19416 : poly_overlap(PG_FUNCTION_ARGS)
3886 : {
3887 19416 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
3888 19416 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
3889 : bool result;
3890 :
3891 19416 : result = poly_overlap_internal(polya, polyb);
3892 :
3893 : /*
3894 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
3895 : */
3896 19416 : PG_FREE_IF_COPY(polya, 0);
3897 19416 : PG_FREE_IF_COPY(polyb, 1);
3898 :
3899 19416 : PG_RETURN_BOOL(result);
3900 : }
3901 :
3902 : /*
3903 : * Tests special kind of segment for in/out of polygon.
3904 : * Special kind means:
3905 : * - point a should be on segment s
3906 : * - segment (a,b) should not be contained by s
3907 : * Returns true if:
3908 : * - segment (a,b) is collinear to s and (a,b) is in polygon
3909 : * - segment (a,b) s not collinear to s. Note: that doesn't
3910 : * mean that segment is in polygon!
3911 : */
3912 :
3913 : static bool
3914 404 : touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
3915 : {
3916 : /* point a is on s, b is not */
3917 : LSEG t;
3918 :
3919 404 : t.p[0] = *a;
3920 404 : t.p[1] = *b;
3921 :
3922 404 : if (point_eq_point(a, s->p))
3923 : {
3924 48 : if (lseg_contain_point(&t, s->p + 1))
3925 0 : return lseg_inside_poly(b, s->p + 1, poly, start);
3926 : }
3927 356 : else if (point_eq_point(a, s->p + 1))
3928 : {
3929 104 : if (lseg_contain_point(&t, s->p))
3930 0 : return lseg_inside_poly(b, s->p, poly, start);
3931 : }
3932 252 : else if (lseg_contain_point(&t, s->p))
3933 : {
3934 0 : return lseg_inside_poly(b, s->p, poly, start);
3935 : }
3936 252 : else if (lseg_contain_point(&t, s->p + 1))
3937 : {
3938 0 : return lseg_inside_poly(b, s->p + 1, poly, start);
3939 : }
3940 :
3941 404 : return true; /* may be not true, but that will check later */
3942 : }
3943 :
3944 : /*
3945 : * Returns true if segment (a,b) is in polygon, option
3946 : * start is used for optimization - function checks
3947 : * polygon's edges starting from start
3948 : */
3949 : static bool
3950 132184 : lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
3951 : {
3952 : LSEG s,
3953 : t;
3954 : int i;
3955 132184 : bool res = true,
3956 132184 : intersection = false;
3957 :
3958 : /* since this function recurses, it could be driven to stack overflow */
3959 132184 : check_stack_depth();
3960 :
3961 132184 : t.p[0] = *a;
3962 132184 : t.p[1] = *b;
3963 132184 : s.p[0] = poly->p[(start == 0) ? (poly->npts - 1) : (start - 1)];
3964 :
3965 653796 : for (i = start; i < poly->npts && res; i++)
3966 : {
3967 : Point interpt;
3968 :
3969 521924 : CHECK_FOR_INTERRUPTS();
3970 :
3971 521924 : s.p[1] = poly->p[i];
3972 :
3973 521924 : if (lseg_contain_point(&s, t.p))
3974 : {
3975 524 : if (lseg_contain_point(&s, t.p + 1))
3976 312 : return true; /* t is contained by s */
3977 :
3978 : /* Y-cross */
3979 212 : res = touched_lseg_inside_poly(t.p, t.p + 1, &s, poly, i + 1);
3980 : }
3981 521400 : else if (lseg_contain_point(&s, t.p + 1))
3982 : {
3983 : /* Y-cross */
3984 192 : res = touched_lseg_inside_poly(t.p + 1, t.p, &s, poly, i + 1);
3985 : }
3986 521208 : else if (lseg_interpt_lseg(&interpt, &t, &s))
3987 : {
3988 : /*
3989 : * segments are X-crossing, go to check each subsegment
3990 : */
3991 :
3992 900 : intersection = true;
3993 900 : res = lseg_inside_poly(t.p, &interpt, poly, i + 1);
3994 900 : if (res)
3995 772 : res = lseg_inside_poly(t.p + 1, &interpt, poly, i + 1);
3996 : }
3997 :
3998 521612 : s.p[0] = s.p[1];
3999 : }
4000 :
4001 131872 : if (res && !intersection)
4002 : {
4003 : Point p;
4004 :
4005 : /*
4006 : * if X-intersection wasn't found, then check central point of tested
4007 : * segment. In opposite case we already check all subsegments
4008 : */
4009 130972 : p.x = float8_div(float8_pl(t.p[0].x, t.p[1].x), 2.0);
4010 130972 : p.y = float8_div(float8_pl(t.p[0].y, t.p[1].y), 2.0);
4011 :
4012 130972 : res = point_inside(&p, poly->npts, poly->p);
4013 : }
4014 :
4015 131872 : return res;
4016 : }
4017 :
4018 : /*
4019 : * Check whether the first polygon contains the second
4020 : */
4021 : static bool
4022 56620 : poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly)
4023 : {
4024 : int i;
4025 : LSEG s;
4026 :
4027 : Assert(contains_poly->npts > 0 && contained_poly->npts > 0);
4028 :
4029 : /*
4030 : * Quick check to see if contained's bounding box is contained in
4031 : * contains' bb.
4032 : */
4033 56620 : if (!box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox))
4034 38228 : return false;
4035 :
4036 18392 : s.p[0] = contained_poly->p[contained_poly->npts - 1];
4037 :
4038 140592 : for (i = 0; i < contained_poly->npts; i++)
4039 : {
4040 130512 : s.p[1] = contained_poly->p[i];
4041 130512 : if (!lseg_inside_poly(s.p, s.p + 1, contains_poly, 0))
4042 8312 : return false;
4043 122200 : s.p[0] = s.p[1];
4044 : }
4045 :
4046 10080 : return true;
4047 : }
4048 :
4049 : Datum
4050 256 : poly_contain(PG_FUNCTION_ARGS)
4051 : {
4052 256 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
4053 256 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
4054 : bool result;
4055 :
4056 256 : result = poly_contain_poly(polya, polyb);
4057 :
4058 : /*
4059 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
4060 : */
4061 256 : PG_FREE_IF_COPY(polya, 0);
4062 256 : PG_FREE_IF_COPY(polyb, 1);
4063 :
4064 256 : PG_RETURN_BOOL(result);
4065 : }
4066 :
4067 :
4068 : /*-----------------------------------------------------------------
4069 : * Determine if polygon A is contained by polygon B
4070 : *-----------------------------------------------------------------*/
4071 : Datum
4072 56364 : poly_contained(PG_FUNCTION_ARGS)
4073 : {
4074 56364 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
4075 56364 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
4076 : bool result;
4077 :
4078 : /* Just switch the arguments and pass it off to poly_contain */
4079 56364 : result = poly_contain_poly(polyb, polya);
4080 :
4081 : /*
4082 : * Avoid leaking memory for toasted inputs ... needed for rtree indexes
4083 : */
4084 56364 : PG_FREE_IF_COPY(polya, 0);
4085 56364 : PG_FREE_IF_COPY(polyb, 1);
4086 :
4087 56364 : PG_RETURN_BOOL(result);
4088 : }
4089 :
4090 :
4091 : Datum
4092 296 : poly_contain_pt(PG_FUNCTION_ARGS)
4093 : {
4094 296 : POLYGON *poly = PG_GETARG_POLYGON_P(0);
4095 296 : Point *p = PG_GETARG_POINT_P(1);
4096 :
4097 296 : PG_RETURN_BOOL(point_inside(p, poly->npts, poly->p) != 0);
4098 : }
4099 :
4100 : Datum
4101 321 : pt_contained_poly(PG_FUNCTION_ARGS)
4102 : {
4103 321 : Point *p = PG_GETARG_POINT_P(0);
4104 321 : POLYGON *poly = PG_GETARG_POLYGON_P(1);
4105 :
4106 321 : PG_RETURN_BOOL(point_inside(p, poly->npts, poly->p) != 0);
4107 : }
4108 :
4109 :
4110 : Datum
4111 196 : poly_distance(PG_FUNCTION_ARGS)
4112 : {
4113 196 : POLYGON *polya = PG_GETARG_POLYGON_P(0);
4114 196 : POLYGON *polyb = PG_GETARG_POLYGON_P(1);
4115 196 : float8 min = 0.0; /* initialize to keep compiler quiet */
4116 196 : bool have_min = false;
4117 : float8 tmp;
4118 : int i,
4119 : j;
4120 : LSEG seg1,
4121 : seg2;
4122 :
4123 : /*
4124 : * Distance is zero if polygons overlap. We must check this because the
4125 : * path distance will not give the right answer if one poly is entirely
4126 : * within the other.
4127 : */
4128 196 : if (poly_overlap_internal(polya, polyb))
4129 100 : PG_RETURN_FLOAT8(0.0);
4130 :
4131 : /*
4132 : * When they don't overlap, the distance calculation is identical to that
4133 : * for closed paths (i.e., we needn't care about the fact that polygons
4134 : * include their contained areas). See path_distance().
4135 : */
4136 352 : for (i = 0; i < polya->npts; i++)
4137 : {
4138 : int iprev;
4139 :
4140 256 : if (i > 0)
4141 160 : iprev = i - 1;
4142 : else
4143 96 : iprev = polya->npts - 1;
4144 :
4145 872 : for (j = 0; j < polyb->npts; j++)
4146 : {
4147 : int jprev;
4148 :
4149 616 : if (j > 0)
4150 360 : jprev = j - 1;
4151 : else
4152 256 : jprev = polyb->npts - 1;
4153 :
4154 616 : statlseg_construct(&seg1, &polya->p[iprev], &polya->p[i]);
4155 616 : statlseg_construct(&seg2, &polyb->p[jprev], &polyb->p[j]);
4156 :
4157 616 : tmp = lseg_closept_lseg(NULL, &seg1, &seg2);
4158 616 : if (!have_min || float8_lt(tmp, min))
4159 : {
4160 128 : min = tmp;
4161 128 : have_min = true;
4162 : }
4163 : }
4164 : }
4165 :
4166 96 : if (!have_min)
4167 0 : PG_RETURN_NULL();
4168 :
4169 96 : PG_RETURN_FLOAT8(min);
4170 : }
4171 :
4172 :
4173 : /***********************************************************************
4174 : **
4175 : ** Routines for 2D points.
4176 : **
4177 : ***********************************************************************/
4178 :
4179 : Datum
4180 688675 : construct_point(PG_FUNCTION_ARGS)
4181 : {
4182 688675 : float8 x = PG_GETARG_FLOAT8(0);
4183 688675 : float8 y = PG_GETARG_FLOAT8(1);
4184 : Point *result;
4185 :
4186 688675 : result = palloc_object(Point);
4187 :
4188 688675 : point_construct(result, x, y);
4189 :
4190 688675 : PG_RETURN_POINT_P(result);
4191 : }
4192 :
4193 :
4194 : static inline void
4195 2048 : point_add_point(Point *result, Point *pt1, Point *pt2, Node *escontext)
4196 : {
4197 : float8 x;
4198 : float8 y;
4199 :
4200 2048 : x = float8_pl_safe(pt1->x, pt2->x, escontext);
4201 2048 : if (SOFT_ERROR_OCCURRED(escontext))
4202 0 : return;
4203 :
4204 2048 : y = float8_pl_safe(pt1->y, pt2->y, escontext);
4205 2048 : if (SOFT_ERROR_OCCURRED(escontext))
4206 0 : return;
4207 :
4208 2048 : point_construct(result, x, y);
4209 : }
4210 :
4211 : Datum
4212 400 : point_add(PG_FUNCTION_ARGS)
4213 : {
4214 400 : Point *p1 = PG_GETARG_POINT_P(0);
4215 400 : Point *p2 = PG_GETARG_POINT_P(1);
4216 : Point *result;
4217 :
4218 400 : result = palloc_object(Point);
4219 :
4220 400 : point_add_point(result, p1, p2, NULL);
4221 :
4222 400 : PG_RETURN_POINT_P(result);
4223 : }
4224 :
4225 :
4226 : static inline void
4227 1880 : point_sub_point(Point *result, Point *pt1, Point *pt2)
4228 : {
4229 1880 : point_construct(result,
4230 : float8_mi(pt1->x, pt2->x),
4231 : float8_mi(pt1->y, pt2->y));
4232 1880 : }
4233 :
4234 : Datum
4235 400 : point_sub(PG_FUNCTION_ARGS)
4236 : {
4237 400 : Point *p1 = PG_GETARG_POINT_P(0);
4238 400 : Point *p2 = PG_GETARG_POINT_P(1);
4239 : Point *result;
4240 :
4241 400 : result = palloc_object(Point);
4242 :
4243 400 : point_sub_point(result, p1, p2);
4244 :
4245 400 : PG_RETURN_POINT_P(result);
4246 : }
4247 :
4248 :
4249 : static inline void
4250 1480 : point_mul_point(Point *result, Point *pt1, Point *pt2)
4251 : {
4252 1480 : point_construct(result,
4253 : float8_mi(float8_mul(pt1->x, pt2->x),
4254 : float8_mul(pt1->y, pt2->y)),
4255 : float8_pl(float8_mul(pt1->x, pt2->y),
4256 : float8_mul(pt1->y, pt2->x)));
4257 1476 : }
4258 :
4259 : Datum
4260 200 : point_mul(PG_FUNCTION_ARGS)
4261 : {
4262 200 : Point *p1 = PG_GETARG_POINT_P(0);
4263 200 : Point *p2 = PG_GETARG_POINT_P(1);
4264 : Point *result;
4265 :
4266 200 : result = palloc_object(Point);
4267 :
4268 200 : point_mul_point(result, p1, p2);
4269 :
4270 196 : PG_RETURN_POINT_P(result);
4271 : }
4272 :
4273 :
4274 : static inline void
4275 396 : point_div_point(Point *result, Point *pt1, Point *pt2)
4276 : {
4277 : float8 div;
4278 :
4279 396 : div = float8_pl(float8_mul(pt2->x, pt2->x), float8_mul(pt2->y, pt2->y));
4280 :
4281 388 : point_construct(result,
4282 : float8_div(float8_pl(float8_mul(pt1->x, pt2->x),
4283 : float8_mul(pt1->y, pt2->y)), div),
4284 : float8_div(float8_mi(float8_mul(pt1->y, pt2->x),
4285 : float8_mul(pt1->x, pt2->y)), div));
4286 376 : }
4287 :
4288 : Datum
4289 88 : point_div(PG_FUNCTION_ARGS)
4290 : {
4291 88 : Point *p1 = PG_GETARG_POINT_P(0);
4292 88 : Point *p2 = PG_GETARG_POINT_P(1);
4293 : Point *result;
4294 :
4295 88 : result = palloc_object(Point);
4296 :
4297 88 : point_div_point(result, p1, p2);
4298 :
4299 80 : PG_RETURN_POINT_P(result);
4300 : }
4301 :
4302 :
4303 : /***********************************************************************
4304 : **
4305 : ** Routines for 2D boxes.
4306 : **
4307 : ***********************************************************************/
4308 :
4309 : Datum
4310 161124 : points_box(PG_FUNCTION_ARGS)
4311 : {
4312 161124 : Point *p1 = PG_GETARG_POINT_P(0);
4313 161124 : Point *p2 = PG_GETARG_POINT_P(1);
4314 : BOX *result;
4315 :
4316 161124 : result = palloc_object(BOX);
4317 :
4318 161124 : box_construct(result, p1, p2);
4319 :
4320 161124 : PG_RETURN_BOX_P(result);
4321 : }
4322 :
4323 : Datum
4324 200 : box_add(PG_FUNCTION_ARGS)
4325 : {
4326 200 : BOX *box = PG_GETARG_BOX_P(0);
4327 200 : Point *p = PG_GETARG_POINT_P(1);
4328 : BOX *result;
4329 :
4330 200 : result = palloc_object(BOX);
4331 :
4332 200 : point_add_point(&result->high, &box->high, p, NULL);
4333 200 : point_add_point(&result->low, &box->low, p, NULL);
4334 :
4335 200 : PG_RETURN_BOX_P(result);
4336 : }
4337 :
4338 : Datum
4339 200 : box_sub(PG_FUNCTION_ARGS)
4340 : {
4341 200 : BOX *box = PG_GETARG_BOX_P(0);
4342 200 : Point *p = PG_GETARG_POINT_P(1);
4343 : BOX *result;
4344 :
4345 200 : result = palloc_object(BOX);
4346 :
4347 200 : point_sub_point(&result->high, &box->high, p);
4348 200 : point_sub_point(&result->low, &box->low, p);
4349 :
4350 200 : PG_RETURN_BOX_P(result);
4351 : }
4352 :
4353 : Datum
4354 100 : box_mul(PG_FUNCTION_ARGS)
4355 : {
4356 100 : BOX *box = PG_GETARG_BOX_P(0);
4357 100 : Point *p = PG_GETARG_POINT_P(1);
4358 : BOX *result;
4359 : Point high,
4360 : low;
4361 :
4362 100 : result = palloc_object(BOX);
4363 :
4364 100 : point_mul_point(&high, &box->high, p);
4365 100 : point_mul_point(&low, &box->low, p);
4366 :
4367 100 : box_construct(result, &high, &low);
4368 :
4369 100 : PG_RETURN_BOX_P(result);
4370 : }
4371 :
4372 : Datum
4373 40 : box_div(PG_FUNCTION_ARGS)
4374 : {
4375 40 : BOX *box = PG_GETARG_BOX_P(0);
4376 40 : Point *p = PG_GETARG_POINT_P(1);
4377 : BOX *result;
4378 : Point high,
4379 : low;
4380 :
4381 40 : result = palloc_object(BOX);
4382 :
4383 40 : point_div_point(&high, &box->high, p);
4384 40 : point_div_point(&low, &box->low, p);
4385 :
4386 40 : box_construct(result, &high, &low);
4387 :
4388 40 : PG_RETURN_BOX_P(result);
4389 : }
4390 :
4391 : /*
4392 : * Convert point to empty box
4393 : */
4394 : Datum
4395 244 : point_box(PG_FUNCTION_ARGS)
4396 : {
4397 244 : Point *pt = PG_GETARG_POINT_P(0);
4398 : BOX *box;
4399 :
4400 244 : box = palloc_object(BOX);
4401 :
4402 244 : box->high.x = pt->x;
4403 244 : box->low.x = pt->x;
4404 244 : box->high.y = pt->y;
4405 244 : box->low.y = pt->y;
4406 :
4407 244 : PG_RETURN_BOX_P(box);
4408 : }
4409 :
4410 : /*
4411 : * Smallest bounding box that includes both of the given boxes
4412 : */
4413 : Datum
4414 100 : boxes_bound_box(PG_FUNCTION_ARGS)
4415 : {
4416 100 : BOX *box1 = PG_GETARG_BOX_P(0),
4417 100 : *box2 = PG_GETARG_BOX_P(1),
4418 : *container;
4419 :
4420 100 : container = palloc_object(BOX);
4421 :
4422 100 : container->high.x = float8_max(box1->high.x, box2->high.x);
4423 100 : container->low.x = float8_min(box1->low.x, box2->low.x);
4424 100 : container->high.y = float8_max(box1->high.y, box2->high.y);
4425 100 : container->low.y = float8_min(box1->low.y, box2->low.y);
4426 :
4427 100 : PG_RETURN_BOX_P(container);
4428 : }
4429 :
4430 :
4431 : /***********************************************************************
4432 : **
4433 : ** Routines for 2D paths.
4434 : **
4435 : ***********************************************************************/
4436 :
4437 : /*
4438 : * path_add()
4439 : * Concatenate two paths (only if they are both open).
4440 : */
4441 : Datum
4442 324 : path_add(PG_FUNCTION_ARGS)
4443 : {
4444 324 : PATH *p1 = PG_GETARG_PATH_P(0);
4445 324 : PATH *p2 = PG_GETARG_PATH_P(1);
4446 : PATH *result;
4447 : int size,
4448 : base_size;
4449 : int i;
4450 :
4451 324 : if (p1->closed || p2->closed)
4452 260 : PG_RETURN_NULL();
4453 :
4454 64 : base_size = sizeof(p1->p[0]) * (p1->npts + p2->npts);
4455 64 : size = offsetof(PATH, p) + base_size;
4456 :
4457 : /* Check for integer overflow */
4458 64 : if (base_size / sizeof(p1->p[0]) != (p1->npts + p2->npts) ||
4459 : size <= base_size)
4460 0 : ereport(ERROR,
4461 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
4462 : errmsg("too many points requested")));
4463 :
4464 64 : result = (PATH *) palloc(size);
4465 :
4466 64 : SET_VARSIZE(result, size);
4467 64 : result->npts = (p1->npts + p2->npts);
4468 64 : result->closed = p1->closed;
4469 : /* prevent instability in unused pad bytes */
4470 64 : result->dummy = 0;
4471 :
4472 224 : for (i = 0; i < p1->npts; i++)
4473 : {
4474 160 : result->p[i].x = p1->p[i].x;
4475 160 : result->p[i].y = p1->p[i].y;
4476 : }
4477 224 : for (i = 0; i < p2->npts; i++)
4478 : {
4479 160 : result->p[i + p1->npts].x = p2->p[i].x;
4480 160 : result->p[i + p1->npts].y = p2->p[i].y;
4481 : }
4482 :
4483 64 : PG_RETURN_PATH_P(result);
4484 : }
4485 :
4486 : /*
4487 : * path_add_pt()
4488 : * Translation operators.
4489 : */
4490 : Datum
4491 360 : path_add_pt(PG_FUNCTION_ARGS)
4492 : {
4493 360 : PATH *path = PG_GETARG_PATH_P_COPY(0);
4494 360 : Point *point = PG_GETARG_POINT_P(1);
4495 : int i;
4496 :
4497 1120 : for (i = 0; i < path->npts; i++)
4498 760 : point_add_point(&path->p[i], &path->p[i], point, NULL);
4499 :
4500 360 : PG_RETURN_PATH_P(path);
4501 : }
4502 :
4503 : Datum
4504 360 : path_sub_pt(PG_FUNCTION_ARGS)
4505 : {
4506 360 : PATH *path = PG_GETARG_PATH_P_COPY(0);
4507 360 : Point *point = PG_GETARG_POINT_P(1);
4508 : int i;
4509 :
4510 1120 : for (i = 0; i < path->npts; i++)
4511 760 : point_sub_point(&path->p[i], &path->p[i], point);
4512 :
4513 360 : PG_RETURN_PATH_P(path);
4514 : }
4515 :
4516 : /*
4517 : * path_mul_pt()
4518 : * Rotation and scaling operators.
4519 : */
4520 : Datum
4521 360 : path_mul_pt(PG_FUNCTION_ARGS)
4522 : {
4523 360 : PATH *path = PG_GETARG_PATH_P_COPY(0);
4524 360 : Point *point = PG_GETARG_POINT_P(1);
4525 : int i;
4526 :
4527 1120 : for (i = 0; i < path->npts; i++)
4528 760 : point_mul_point(&path->p[i], &path->p[i], point);
4529 :
4530 360 : PG_RETURN_PATH_P(path);
4531 : }
4532 :
4533 : Datum
4534 76 : path_div_pt(PG_FUNCTION_ARGS)
4535 : {
4536 76 : PATH *path = PG_GETARG_PATH_P_COPY(0);
4537 76 : Point *point = PG_GETARG_POINT_P(1);
4538 : int i;
4539 :
4540 228 : for (i = 0; i < path->npts; i++)
4541 156 : point_div_point(&path->p[i], &path->p[i], point);
4542 :
4543 72 : PG_RETURN_PATH_P(path);
4544 : }
4545 :
4546 :
4547 : Datum
4548 60 : path_poly(PG_FUNCTION_ARGS)
4549 : {
4550 60 : PATH *path = PG_GETARG_PATH_P(0);
4551 : POLYGON *poly;
4552 : int size;
4553 : int i;
4554 :
4555 : /* This is not very consistent --- other similar cases return NULL ... */
4556 60 : if (!path->closed)
4557 4 : ereturn(fcinfo->context, (Datum) 0,
4558 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4559 : errmsg("open path cannot be converted to polygon")));
4560 :
4561 : /*
4562 : * Never overflows: the old size fit in MaxAllocSize, and the new size is
4563 : * just a small constant larger.
4564 : */
4565 56 : size = offsetof(POLYGON, p) + sizeof(poly->p[0]) * path->npts;
4566 56 : poly = (POLYGON *) palloc(size);
4567 :
4568 56 : SET_VARSIZE(poly, size);
4569 56 : poly->npts = path->npts;
4570 :
4571 168 : for (i = 0; i < path->npts; i++)
4572 : {
4573 112 : poly->p[i].x = path->p[i].x;
4574 112 : poly->p[i].y = path->p[i].y;
4575 : }
4576 :
4577 56 : make_bound_box(poly);
4578 :
4579 56 : PG_RETURN_POLYGON_P(poly);
4580 : }
4581 :
4582 :
4583 : /***********************************************************************
4584 : **
4585 : ** Routines for 2D polygons.
4586 : **
4587 : ***********************************************************************/
4588 :
4589 : Datum
4590 84 : poly_npoints(PG_FUNCTION_ARGS)
4591 : {
4592 84 : POLYGON *poly = PG_GETARG_POLYGON_P(0);
4593 :
4594 84 : PG_RETURN_INT32(poly->npts);
4595 : }
4596 :
4597 :
4598 : Datum
4599 28 : poly_center(PG_FUNCTION_ARGS)
4600 : {
4601 28 : POLYGON *poly = PG_GETARG_POLYGON_P(0);
4602 : Point *result;
4603 : CIRCLE circle;
4604 :
4605 28 : result = palloc_object(Point);
4606 :
4607 28 : poly_to_circle(&circle, poly, fcinfo->context);
4608 28 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
4609 0 : PG_RETURN_NULL();
4610 :
4611 28 : *result = circle.center;
4612 :
4613 28 : PG_RETURN_POINT_P(result);
4614 : }
4615 :
4616 :
4617 : Datum
4618 28 : poly_box(PG_FUNCTION_ARGS)
4619 : {
4620 28 : POLYGON *poly = PG_GETARG_POLYGON_P(0);
4621 : BOX *box;
4622 :
4623 28 : box = palloc_object(BOX);
4624 28 : *box = poly->boundbox;
4625 :
4626 28 : PG_RETURN_BOX_P(box);
4627 : }
4628 :
4629 :
4630 : /*
4631 : * box_poly()
4632 : * Convert a box to a polygon.
4633 : */
4634 : Datum
4635 12420 : box_poly(PG_FUNCTION_ARGS)
4636 : {
4637 12420 : BOX *box = PG_GETARG_BOX_P(0);
4638 : POLYGON *poly;
4639 : int size;
4640 :
4641 : /* map four corners of the box to a polygon */
4642 12420 : size = offsetof(POLYGON, p) + sizeof(poly->p[0]) * 4;
4643 12420 : poly = (POLYGON *) palloc(size);
4644 :
4645 12420 : SET_VARSIZE(poly, size);
4646 12420 : poly->npts = 4;
4647 :
4648 12420 : poly->p[0].x = box->low.x;
4649 12420 : poly->p[0].y = box->low.y;
4650 12420 : poly->p[1].x = box->low.x;
4651 12420 : poly->p[1].y = box->high.y;
4652 12420 : poly->p[2].x = box->high.x;
4653 12420 : poly->p[2].y = box->high.y;
4654 12420 : poly->p[3].x = box->high.x;
4655 12420 : poly->p[3].y = box->low.y;
4656 :
4657 12420 : box_construct(&poly->boundbox, &box->high, &box->low);
4658 :
4659 12420 : PG_RETURN_POLYGON_P(poly);
4660 : }
4661 :
4662 :
4663 : Datum
4664 28 : poly_path(PG_FUNCTION_ARGS)
4665 : {
4666 28 : POLYGON *poly = PG_GETARG_POLYGON_P(0);
4667 : PATH *path;
4668 : int size;
4669 : int i;
4670 :
4671 : /*
4672 : * Never overflows: the old size fit in MaxAllocSize, and the new size is
4673 : * smaller by a small constant.
4674 : */
4675 28 : size = offsetof(PATH, p) + sizeof(path->p[0]) * poly->npts;
4676 28 : path = (PATH *) palloc(size);
4677 :
4678 28 : SET_VARSIZE(path, size);
4679 28 : path->npts = poly->npts;
4680 28 : path->closed = true;
4681 : /* prevent instability in unused pad bytes */
4682 28 : path->dummy = 0;
4683 :
4684 112 : for (i = 0; i < poly->npts; i++)
4685 : {
4686 84 : path->p[i].x = poly->p[i].x;
4687 84 : path->p[i].y = poly->p[i].y;
4688 : }
4689 :
4690 28 : PG_RETURN_PATH_P(path);
4691 : }
4692 :
4693 :
4694 : /***********************************************************************
4695 : **
4696 : ** Routines for circles.
4697 : **
4698 : ***********************************************************************/
4699 :
4700 : /*----------------------------------------------------------
4701 : * Formatting and conversion routines.
4702 : *---------------------------------------------------------*/
4703 :
4704 : /*
4705 : * circle_in - convert a string to internal form.
4706 : *
4707 : * External format: (center and radius of circle)
4708 : * "<(f8,f8),f8>"
4709 : * also supports quick entry style "f8,f8,f8"
4710 : */
4711 : Datum
4712 270 : circle_in(PG_FUNCTION_ARGS)
4713 : {
4714 270 : char *str = PG_GETARG_CSTRING(0);
4715 270 : Node *escontext = fcinfo->context;
4716 270 : CIRCLE *circle = palloc_object(CIRCLE);
4717 : char *s,
4718 : *cp;
4719 270 : int depth = 0;
4720 :
4721 270 : s = str;
4722 286 : while (isspace((unsigned char) *s))
4723 16 : s++;
4724 270 : if (*s == LDELIM_C)
4725 220 : depth++, s++;
4726 50 : else if (*s == LDELIM)
4727 : {
4728 : /* If there are two left parens, consume the first one */
4729 34 : cp = (s + 1);
4730 42 : while (isspace((unsigned char) *cp))
4731 8 : cp++;
4732 34 : if (*cp == LDELIM)
4733 14 : depth++, s = cp;
4734 : }
4735 :
4736 : /* pair_decode will consume parens around the pair, if any */
4737 270 : if (!pair_decode(s, &circle->center.x, &circle->center.y, &s, "circle", str,
4738 : escontext))
4739 8 : PG_RETURN_NULL();
4740 :
4741 254 : if (*s == DELIM)
4742 254 : s++;
4743 :
4744 254 : if (!single_decode(s, &circle->radius, &s, "circle", str, escontext))
4745 0 : PG_RETURN_NULL();
4746 :
4747 : /* We have to accept NaN. */
4748 254 : if (circle->radius < 0.0)
4749 12 : ereturn(escontext, (Datum) 0,
4750 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
4751 : errmsg("invalid input syntax for type %s: \"%s\"",
4752 : "circle", str)));
4753 :
4754 468 : while (depth > 0)
4755 : {
4756 230 : if ((*s == RDELIM) || ((*s == RDELIM_C) && (depth == 1)))
4757 : {
4758 226 : depth--;
4759 226 : s++;
4760 238 : while (isspace((unsigned char) *s))
4761 12 : s++;
4762 : }
4763 : else
4764 4 : ereturn(escontext, (Datum) 0,
4765 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
4766 : errmsg("invalid input syntax for type %s: \"%s\"",
4767 : "circle", str)));
4768 : }
4769 :
4770 238 : if (*s != '\0')
4771 4 : ereturn(escontext, (Datum) 0,
4772 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
4773 : errmsg("invalid input syntax for type %s: \"%s\"",
4774 : "circle", str)));
4775 :
4776 234 : PG_RETURN_CIRCLE_P(circle);
4777 : }
4778 :
4779 : /*
4780 : * circle_out - convert a circle to external form.
4781 : */
4782 : Datum
4783 6104 : circle_out(PG_FUNCTION_ARGS)
4784 : {
4785 6104 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
4786 : StringInfoData str;
4787 :
4788 6104 : initStringInfo(&str);
4789 :
4790 6104 : appendStringInfoChar(&str, LDELIM_C);
4791 6104 : appendStringInfoChar(&str, LDELIM);
4792 6104 : pair_encode(circle->center.x, circle->center.y, &str);
4793 6104 : appendStringInfoChar(&str, RDELIM);
4794 6104 : appendStringInfoChar(&str, DELIM);
4795 6104 : single_encode(circle->radius, &str);
4796 6104 : appendStringInfoChar(&str, RDELIM_C);
4797 :
4798 6104 : PG_RETURN_CSTRING(str.data);
4799 : }
4800 :
4801 : /*
4802 : * circle_recv - converts external binary format to circle
4803 : */
4804 : Datum
4805 0 : circle_recv(PG_FUNCTION_ARGS)
4806 : {
4807 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
4808 : CIRCLE *circle;
4809 :
4810 0 : circle = palloc_object(CIRCLE);
4811 :
4812 0 : circle->center.x = pq_getmsgfloat8(buf);
4813 0 : circle->center.y = pq_getmsgfloat8(buf);
4814 0 : circle->radius = pq_getmsgfloat8(buf);
4815 :
4816 : /* We have to accept NaN. */
4817 0 : if (circle->radius < 0.0)
4818 0 : ereport(ERROR,
4819 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
4820 : errmsg("invalid radius in external \"circle\" value")));
4821 :
4822 0 : PG_RETURN_CIRCLE_P(circle);
4823 : }
4824 :
4825 : /*
4826 : * circle_send - converts circle to binary format
4827 : */
4828 : Datum
4829 0 : circle_send(PG_FUNCTION_ARGS)
4830 : {
4831 0 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
4832 : StringInfoData buf;
4833 :
4834 0 : pq_begintypsend(&buf);
4835 0 : pq_sendfloat8(&buf, circle->center.x);
4836 0 : pq_sendfloat8(&buf, circle->center.y);
4837 0 : pq_sendfloat8(&buf, circle->radius);
4838 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
4839 : }
4840 :
4841 :
4842 : /*----------------------------------------------------------
4843 : * Relational operators for CIRCLEs.
4844 : * <, >, <=, >=, and == are based on circle area.
4845 : *---------------------------------------------------------*/
4846 :
4847 : /*
4848 : * circles identical?
4849 : *
4850 : * We consider NaNs values to be equal to each other to let those circles
4851 : * to be found.
4852 : */
4853 : Datum
4854 257 : circle_same(PG_FUNCTION_ARGS)
4855 : {
4856 257 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4857 257 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4858 :
4859 257 : PG_RETURN_BOOL(((isnan(circle1->radius) && isnan(circle2->radius)) ||
4860 : FPeq(circle1->radius, circle2->radius)) &&
4861 : point_eq_point(&circle1->center, &circle2->center));
4862 : }
4863 :
4864 : /*
4865 : * circle_overlap - does circle1 overlap circle2?
4866 : */
4867 : Datum
4868 12812 : circle_overlap(PG_FUNCTION_ARGS)
4869 : {
4870 12812 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4871 12812 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4872 :
4873 12812 : PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center, NULL),
4874 : float8_pl(circle1->radius, circle2->radius)));
4875 : }
4876 :
4877 : /*
4878 : * circle_overleft - is the right edge of circle1 at or left of
4879 : * the right edge of circle2?
4880 : */
4881 : Datum
4882 256 : circle_overleft(PG_FUNCTION_ARGS)
4883 : {
4884 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4885 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4886 :
4887 256 : PG_RETURN_BOOL(FPle(float8_pl(circle1->center.x, circle1->radius),
4888 : float8_pl(circle2->center.x, circle2->radius)));
4889 : }
4890 :
4891 : /*
4892 : * circle_left - is circle1 strictly left of circle2?
4893 : */
4894 : Datum
4895 256 : circle_left(PG_FUNCTION_ARGS)
4896 : {
4897 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4898 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4899 :
4900 256 : PG_RETURN_BOOL(FPlt(float8_pl(circle1->center.x, circle1->radius),
4901 : float8_mi(circle2->center.x, circle2->radius)));
4902 : }
4903 :
4904 : /*
4905 : * circle_right - is circle1 strictly right of circle2?
4906 : */
4907 : Datum
4908 256 : circle_right(PG_FUNCTION_ARGS)
4909 : {
4910 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4911 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4912 :
4913 256 : PG_RETURN_BOOL(FPgt(float8_mi(circle1->center.x, circle1->radius),
4914 : float8_pl(circle2->center.x, circle2->radius)));
4915 : }
4916 :
4917 : /*
4918 : * circle_overright - is the left edge of circle1 at or right of
4919 : * the left edge of circle2?
4920 : */
4921 : Datum
4922 256 : circle_overright(PG_FUNCTION_ARGS)
4923 : {
4924 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4925 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4926 :
4927 256 : PG_RETURN_BOOL(FPge(float8_mi(circle1->center.x, circle1->radius),
4928 : float8_mi(circle2->center.x, circle2->radius)));
4929 : }
4930 :
4931 : /*
4932 : * circle_contained - is circle1 contained by circle2?
4933 : */
4934 : Datum
4935 256 : circle_contained(PG_FUNCTION_ARGS)
4936 : {
4937 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4938 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4939 :
4940 256 : PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center, NULL),
4941 : float8_mi(circle2->radius, circle1->radius)));
4942 : }
4943 :
4944 : /*
4945 : * circle_contain - does circle1 contain circle2?
4946 : */
4947 : Datum
4948 264 : circle_contain(PG_FUNCTION_ARGS)
4949 : {
4950 264 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4951 264 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4952 :
4953 264 : PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center, NULL),
4954 : float8_mi(circle1->radius, circle2->radius)));
4955 : }
4956 :
4957 :
4958 : /*
4959 : * circle_below - is circle1 strictly below circle2?
4960 : */
4961 : Datum
4962 256 : circle_below(PG_FUNCTION_ARGS)
4963 : {
4964 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4965 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4966 :
4967 256 : PG_RETURN_BOOL(FPlt(float8_pl(circle1->center.y, circle1->radius),
4968 : float8_mi(circle2->center.y, circle2->radius)));
4969 : }
4970 :
4971 : /*
4972 : * circle_above - is circle1 strictly above circle2?
4973 : */
4974 : Datum
4975 256 : circle_above(PG_FUNCTION_ARGS)
4976 : {
4977 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4978 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4979 :
4980 256 : PG_RETURN_BOOL(FPgt(float8_mi(circle1->center.y, circle1->radius),
4981 : float8_pl(circle2->center.y, circle2->radius)));
4982 : }
4983 :
4984 : /*
4985 : * circle_overbelow - is the upper edge of circle1 at or below
4986 : * the upper edge of circle2?
4987 : */
4988 : Datum
4989 256 : circle_overbelow(PG_FUNCTION_ARGS)
4990 : {
4991 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
4992 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
4993 :
4994 256 : PG_RETURN_BOOL(FPle(float8_pl(circle1->center.y, circle1->radius),
4995 : float8_pl(circle2->center.y, circle2->radius)));
4996 : }
4997 :
4998 : /*
4999 : * circle_overabove - is the lower edge of circle1 at or above
5000 : * the lower edge of circle2?
5001 : */
5002 : Datum
5003 256 : circle_overabove(PG_FUNCTION_ARGS)
5004 : {
5005 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
5006 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
5007 :
5008 256 : PG_RETURN_BOOL(FPge(float8_mi(circle1->center.y, circle1->radius),
5009 : float8_mi(circle2->center.y, circle2->radius)));
5010 : }
5011 :
5012 :
5013 : /*
5014 : * circle_relop - is area(circle1) relop area(circle2), within
5015 : * our accuracy constraint?
5016 : */
5017 : Datum
5018 256 : circle_eq(PG_FUNCTION_ARGS)
5019 : {
5020 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
5021 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
5022 :
5023 256 : PG_RETURN_BOOL(FPeq(circle_ar(circle1), circle_ar(circle2)));
5024 : }
5025 :
5026 : Datum
5027 256 : circle_ne(PG_FUNCTION_ARGS)
5028 : {
5029 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
5030 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
5031 :
5032 256 : PG_RETURN_BOOL(FPne(circle_ar(circle1), circle_ar(circle2)));
5033 : }
5034 :
5035 : Datum
5036 1052 : circle_lt(PG_FUNCTION_ARGS)
5037 : {
5038 1052 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
5039 1052 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
5040 :
5041 1052 : PG_RETURN_BOOL(FPlt(circle_ar(circle1), circle_ar(circle2)));
5042 : }
5043 :
5044 : Datum
5045 256 : circle_gt(PG_FUNCTION_ARGS)
5046 : {
5047 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
5048 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
5049 :
5050 256 : PG_RETURN_BOOL(FPgt(circle_ar(circle1), circle_ar(circle2)));
5051 : }
5052 :
5053 : Datum
5054 256 : circle_le(PG_FUNCTION_ARGS)
5055 : {
5056 256 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
5057 256 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
5058 :
5059 256 : PG_RETURN_BOOL(FPle(circle_ar(circle1), circle_ar(circle2)));
5060 : }
5061 :
5062 : Datum
5063 324 : circle_ge(PG_FUNCTION_ARGS)
5064 : {
5065 324 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
5066 324 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
5067 :
5068 324 : PG_RETURN_BOOL(FPge(circle_ar(circle1), circle_ar(circle2)));
5069 : }
5070 :
5071 :
5072 : /*----------------------------------------------------------
5073 : * "Arithmetic" operators on circles.
5074 : *---------------------------------------------------------*/
5075 :
5076 : /*
5077 : * circle_add_pt()
5078 : * Translation operator.
5079 : */
5080 : Datum
5081 320 : circle_add_pt(PG_FUNCTION_ARGS)
5082 : {
5083 320 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5084 320 : Point *point = PG_GETARG_POINT_P(1);
5085 : CIRCLE *result;
5086 :
5087 320 : result = palloc_object(CIRCLE);
5088 :
5089 320 : point_add_point(&result->center, &circle->center, point, NULL);
5090 320 : result->radius = circle->radius;
5091 :
5092 320 : PG_RETURN_CIRCLE_P(result);
5093 : }
5094 :
5095 : Datum
5096 320 : circle_sub_pt(PG_FUNCTION_ARGS)
5097 : {
5098 320 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5099 320 : Point *point = PG_GETARG_POINT_P(1);
5100 : CIRCLE *result;
5101 :
5102 320 : result = palloc_object(CIRCLE);
5103 :
5104 320 : point_sub_point(&result->center, &circle->center, point);
5105 320 : result->radius = circle->radius;
5106 :
5107 320 : PG_RETURN_CIRCLE_P(result);
5108 : }
5109 :
5110 :
5111 : /*
5112 : * circle_mul_pt()
5113 : * Rotation and scaling operators.
5114 : */
5115 : Datum
5116 320 : circle_mul_pt(PG_FUNCTION_ARGS)
5117 : {
5118 320 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5119 320 : Point *point = PG_GETARG_POINT_P(1);
5120 : CIRCLE *result;
5121 :
5122 320 : result = palloc_object(CIRCLE);
5123 :
5124 320 : point_mul_point(&result->center, &circle->center, point);
5125 320 : result->radius = float8_mul(circle->radius, hypot(point->x, point->y));
5126 :
5127 320 : PG_RETURN_CIRCLE_P(result);
5128 : }
5129 :
5130 : Datum
5131 72 : circle_div_pt(PG_FUNCTION_ARGS)
5132 : {
5133 72 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5134 72 : Point *point = PG_GETARG_POINT_P(1);
5135 : CIRCLE *result;
5136 :
5137 72 : result = palloc_object(CIRCLE);
5138 :
5139 72 : point_div_point(&result->center, &circle->center, point);
5140 64 : result->radius = float8_div(circle->radius, hypot(point->x, point->y));
5141 :
5142 64 : PG_RETURN_CIRCLE_P(result);
5143 : }
5144 :
5145 :
5146 : /*
5147 : * circle_area - returns the area of the circle.
5148 : */
5149 : Datum
5150 340 : circle_area(PG_FUNCTION_ARGS)
5151 : {
5152 340 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5153 :
5154 340 : PG_RETURN_FLOAT8(circle_ar(circle));
5155 : }
5156 :
5157 :
5158 : /*
5159 : * circle_diameter - returns the diameter of the circle.
5160 : */
5161 : Datum
5162 64 : circle_diameter(PG_FUNCTION_ARGS)
5163 : {
5164 64 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5165 :
5166 64 : PG_RETURN_FLOAT8(float8_mul(circle->radius, 2.0));
5167 : }
5168 :
5169 :
5170 : /*
5171 : * circle_radius - returns the radius of the circle.
5172 : */
5173 : Datum
5174 12504 : circle_radius(PG_FUNCTION_ARGS)
5175 : {
5176 12504 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5177 :
5178 12504 : PG_RETURN_FLOAT8(circle->radius);
5179 : }
5180 :
5181 :
5182 : /*
5183 : * circle_distance - returns the distance between
5184 : * two circles.
5185 : */
5186 : Datum
5187 112 : circle_distance(PG_FUNCTION_ARGS)
5188 : {
5189 112 : CIRCLE *circle1 = PG_GETARG_CIRCLE_P(0);
5190 112 : CIRCLE *circle2 = PG_GETARG_CIRCLE_P(1);
5191 : float8 result;
5192 :
5193 112 : result = float8_mi(point_dt(&circle1->center, &circle2->center, NULL),
5194 : float8_pl(circle1->radius, circle2->radius));
5195 112 : if (result < 0.0)
5196 48 : result = 0.0;
5197 :
5198 112 : PG_RETURN_FLOAT8(result);
5199 : }
5200 :
5201 :
5202 : Datum
5203 16 : circle_contain_pt(PG_FUNCTION_ARGS)
5204 : {
5205 16 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5206 16 : Point *point = PG_GETARG_POINT_P(1);
5207 : float8 d;
5208 :
5209 16 : d = point_dt(&circle->center, point, NULL);
5210 16 : PG_RETURN_BOOL(d <= circle->radius);
5211 : }
5212 :
5213 :
5214 : Datum
5215 40 : pt_contained_circle(PG_FUNCTION_ARGS)
5216 : {
5217 40 : Point *point = PG_GETARG_POINT_P(0);
5218 40 : CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
5219 : float8 d;
5220 :
5221 40 : d = point_dt(&circle->center, point, NULL);
5222 40 : PG_RETURN_BOOL(d <= circle->radius);
5223 : }
5224 :
5225 :
5226 : /*
5227 : * dist_pc - returns the distance between
5228 : * a point and a circle.
5229 : */
5230 : Datum
5231 564 : dist_pc(PG_FUNCTION_ARGS)
5232 : {
5233 564 : Point *point = PG_GETARG_POINT_P(0);
5234 564 : CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
5235 : float8 result;
5236 :
5237 564 : result = float8_mi(point_dt(point, &circle->center, NULL),
5238 : circle->radius);
5239 564 : if (result < 0.0)
5240 76 : result = 0.0;
5241 :
5242 564 : PG_RETURN_FLOAT8(result);
5243 : }
5244 :
5245 : /*
5246 : * Distance from a circle to a point
5247 : */
5248 : Datum
5249 12484 : dist_cpoint(PG_FUNCTION_ARGS)
5250 : {
5251 12484 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5252 12484 : Point *point = PG_GETARG_POINT_P(1);
5253 : float8 result;
5254 :
5255 12484 : result = float8_mi(point_dt(point, &circle->center, NULL), circle->radius);
5256 12484 : if (result < 0.0)
5257 0 : result = 0.0;
5258 :
5259 12484 : PG_RETURN_FLOAT8(result);
5260 : }
5261 :
5262 : /*
5263 : * circle_center - returns the center point of the circle.
5264 : */
5265 : Datum
5266 12604 : circle_center(PG_FUNCTION_ARGS)
5267 : {
5268 12604 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5269 : Point *result;
5270 :
5271 12604 : result = palloc_object(Point);
5272 12604 : result->x = circle->center.x;
5273 12604 : result->y = circle->center.y;
5274 :
5275 12604 : PG_RETURN_POINT_P(result);
5276 : }
5277 :
5278 :
5279 : /*
5280 : * circle_ar - returns the area of the circle.
5281 : */
5282 : static float8
5283 5140 : circle_ar(CIRCLE *circle)
5284 : {
5285 5140 : return float8_mul(float8_mul(circle->radius, circle->radius), M_PI);
5286 : }
5287 :
5288 :
5289 : /*----------------------------------------------------------
5290 : * Conversion operators.
5291 : *---------------------------------------------------------*/
5292 :
5293 : Datum
5294 120110 : cr_circle(PG_FUNCTION_ARGS)
5295 : {
5296 120110 : Point *center = PG_GETARG_POINT_P(0);
5297 120110 : float8 radius = PG_GETARG_FLOAT8(1);
5298 : CIRCLE *result;
5299 :
5300 120110 : result = palloc_object(CIRCLE);
5301 :
5302 120110 : result->center.x = center->x;
5303 120110 : result->center.y = center->y;
5304 120110 : result->radius = radius;
5305 :
5306 120110 : PG_RETURN_CIRCLE_P(result);
5307 : }
5308 :
5309 : Datum
5310 32 : circle_box(PG_FUNCTION_ARGS)
5311 : {
5312 32 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5313 : BOX *box;
5314 : float8 delta;
5315 :
5316 32 : box = palloc_object(BOX);
5317 :
5318 32 : delta = float8_div_safe(circle->radius, sqrt(2.0), fcinfo->context);
5319 32 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5320 0 : goto fail;
5321 :
5322 32 : box->high.x = float8_pl_safe(circle->center.x, delta, fcinfo->context);
5323 32 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5324 0 : goto fail;
5325 :
5326 32 : box->low.x = float8_mi_safe(circle->center.x, delta, fcinfo->context);
5327 32 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5328 0 : goto fail;
5329 :
5330 32 : box->high.y = float8_pl_safe(circle->center.y, delta, fcinfo->context);
5331 32 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5332 0 : goto fail;
5333 :
5334 32 : box->low.y = float8_mi_safe(circle->center.y, delta, fcinfo->context);
5335 32 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5336 0 : goto fail;
5337 :
5338 32 : PG_RETURN_BOX_P(box);
5339 :
5340 0 : fail:
5341 0 : PG_RETURN_NULL();
5342 : }
5343 :
5344 : /*
5345 : * box_circle()
5346 : * Convert a box to a circle.
5347 : */
5348 : Datum
5349 12420 : box_circle(PG_FUNCTION_ARGS)
5350 : {
5351 12420 : BOX *box = PG_GETARG_BOX_P(0);
5352 : CIRCLE *circle;
5353 : float8 x;
5354 : float8 y;
5355 :
5356 12420 : circle = palloc_object(CIRCLE);
5357 :
5358 12420 : x = float8_pl_safe(box->high.x, box->low.x, fcinfo->context);
5359 12420 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5360 0 : goto fail;
5361 :
5362 12420 : circle->center.x = float8_div_safe(x, 2.0, fcinfo->context);
5363 12420 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5364 0 : goto fail;
5365 :
5366 12420 : y = float8_pl_safe(box->high.y, box->low.y, fcinfo->context);
5367 12420 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5368 0 : goto fail;
5369 :
5370 12420 : circle->center.y = float8_div_safe(y, 2.0, fcinfo->context);
5371 12420 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5372 0 : goto fail;
5373 :
5374 12420 : circle->radius = point_dt(&circle->center, &box->high,
5375 : fcinfo->context);
5376 :
5377 12420 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5378 0 : goto fail;
5379 :
5380 12420 : PG_RETURN_CIRCLE_P(circle);
5381 :
5382 0 : fail:
5383 0 : PG_RETURN_NULL();
5384 : }
5385 :
5386 :
5387 : static POLYGON *
5388 40057 : circle_poly_internal(int32 npts, const CIRCLE *circle, FunctionCallInfo fcinfo)
5389 : {
5390 : POLYGON *poly;
5391 : int base_size,
5392 : size;
5393 : int i;
5394 : float8 angle;
5395 : float8 anglestep;
5396 :
5397 40057 : if (FPzero(circle->radius))
5398 4 : ereturn(fcinfo->context, NULL,
5399 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5400 : errmsg("cannot convert circle with radius zero to polygon")));
5401 :
5402 40053 : if (npts < 2)
5403 4 : ereturn(fcinfo->context, NULL,
5404 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5405 : errmsg("must request at least 2 points")));
5406 :
5407 40049 : base_size = sizeof(poly->p[0]) * npts;
5408 40049 : size = offsetof(POLYGON, p) + base_size;
5409 :
5410 : /* Check for integer overflow */
5411 40049 : if (base_size / npts != sizeof(poly->p[0]) || size <= base_size)
5412 0 : ereturn(fcinfo->context, NULL,
5413 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5414 : errmsg("too many points requested")));
5415 :
5416 40049 : poly = (POLYGON *) palloc0(size); /* zero any holes */
5417 40049 : SET_VARSIZE(poly, size);
5418 40049 : poly->npts = npts;
5419 :
5420 40049 : anglestep = float8_div(2.0 * M_PI, npts);
5421 :
5422 520541 : for (i = 0; i < npts; i++)
5423 : {
5424 : float8 temp;
5425 :
5426 480492 : angle = float8_mul_safe(anglestep, i, fcinfo->context);
5427 480492 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5428 0 : return NULL;
5429 :
5430 480492 : temp = float8_mul_safe(circle->radius, cos(angle), fcinfo->context);
5431 480492 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5432 0 : return NULL;
5433 :
5434 480492 : poly->p[i].x = float8_mi_safe(circle->center.x, temp, fcinfo->context);
5435 480492 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5436 0 : return NULL;
5437 :
5438 480492 : temp = float8_mul_safe(circle->radius, sin(angle), fcinfo->context);
5439 480492 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5440 0 : return NULL;
5441 :
5442 480492 : poly->p[i].y = float8_pl_safe(circle->center.y, temp, fcinfo->context);
5443 480492 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5444 0 : return NULL;
5445 : }
5446 :
5447 40049 : make_bound_box(poly);
5448 :
5449 40049 : return poly;
5450 : }
5451 :
5452 : Datum
5453 32 : circle_poly(PG_FUNCTION_ARGS)
5454 : {
5455 32 : int32 npts = PG_GETARG_INT32(0);
5456 32 : CIRCLE *circle = PG_GETARG_CIRCLE_P(1);
5457 :
5458 32 : PG_RETURN_POLYGON_P(circle_poly_internal(npts, circle, fcinfo));
5459 : }
5460 :
5461 : /* convert circle to 12-vertex polygon */
5462 : Datum
5463 40025 : circle_to_poly(PG_FUNCTION_ARGS)
5464 : {
5465 40025 : int32 npts = 12;
5466 40025 : CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
5467 :
5468 40025 : PG_RETURN_POLYGON_P(circle_poly_internal(npts, circle, fcinfo));
5469 : }
5470 :
5471 : /*
5472 : * Convert polygon to circle
5473 : *
5474 : * The result must be preallocated.
5475 : *
5476 : * XXX This algorithm should use weighted means of line segments
5477 : * rather than straight average values of points - tgl 97/01/21.
5478 : */
5479 : static void
5480 48 : poly_to_circle(CIRCLE *result, POLYGON *poly, Node *escontext)
5481 : {
5482 : int i;
5483 : float8 x;
5484 :
5485 : Assert(poly->npts > 0);
5486 :
5487 48 : result->center.x = 0;
5488 48 : result->center.y = 0;
5489 48 : result->radius = 0;
5490 :
5491 216 : for (i = 0; i < poly->npts; i++)
5492 : {
5493 168 : point_add_point(&result->center, &result->center, &poly->p[i], escontext);
5494 168 : if (SOFT_ERROR_OCCURRED(escontext))
5495 0 : return;
5496 : }
5497 :
5498 48 : result->center.x = float8_div_safe(result->center.x, poly->npts, escontext);
5499 48 : if (SOFT_ERROR_OCCURRED(escontext))
5500 0 : return;
5501 :
5502 48 : result->center.y = float8_div_safe(result->center.y, poly->npts, escontext);
5503 48 : if (SOFT_ERROR_OCCURRED(escontext))
5504 0 : return;
5505 :
5506 216 : for (i = 0; i < poly->npts; i++)
5507 : {
5508 168 : x = point_dt(&poly->p[i], &result->center, escontext);
5509 168 : if (SOFT_ERROR_OCCURRED(escontext))
5510 0 : return;
5511 :
5512 168 : result->radius = float8_pl_safe(result->radius, x, escontext);
5513 168 : if (SOFT_ERROR_OCCURRED(escontext))
5514 0 : return;
5515 : }
5516 :
5517 48 : result->radius = float8_div_safe(result->radius, poly->npts, escontext);
5518 48 : if (SOFT_ERROR_OCCURRED(escontext))
5519 0 : return;
5520 : }
5521 :
5522 : Datum
5523 20 : poly_circle(PG_FUNCTION_ARGS)
5524 : {
5525 20 : POLYGON *poly = PG_GETARG_POLYGON_P(0);
5526 : CIRCLE *result;
5527 :
5528 20 : result = palloc_object(CIRCLE);
5529 :
5530 20 : poly_to_circle(result, poly, fcinfo->context);
5531 20 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
5532 0 : PG_RETURN_NULL();
5533 :
5534 20 : PG_RETURN_CIRCLE_P(result);
5535 : }
5536 :
5537 :
5538 : /***********************************************************************
5539 : **
5540 : ** Private routines for multiple types.
5541 : **
5542 : ***********************************************************************/
5543 :
5544 : /*
5545 : * Test to see if the point is inside the polygon, returns 1/0, or 2 if
5546 : * the point is on the polygon.
5547 : * Code adapted but not copied from integer-based routines in WN: A
5548 : * Server for the HTTP
5549 : * version 1.15.1, file wn/image.c
5550 : * http://hopf.math.northwestern.edu/index.html
5551 : * Description of algorithm: http://www.linuxjournal.com/article/2197
5552 : * http://www.linuxjournal.com/article/2029
5553 : */
5554 :
5555 : #define POINT_ON_POLYGON INT_MAX
5556 :
5557 : static int
5558 164433 : point_inside(Point *p, int npts, Point *plist)
5559 : {
5560 : float8 x0,
5561 : y0;
5562 : float8 prev_x,
5563 : prev_y;
5564 164433 : int i = 0;
5565 : float8 x,
5566 : y;
5567 : int cross,
5568 164433 : total_cross = 0;
5569 :
5570 : Assert(npts > 0);
5571 :
5572 : /* compute first polygon point relative to single point */
5573 164433 : x0 = float8_mi(plist[0].x, p->x);
5574 164433 : y0 = float8_mi(plist[0].y, p->y);
5575 :
5576 164433 : prev_x = x0;
5577 164433 : prev_y = y0;
5578 : /* loop over polygon points and aggregate total_cross */
5579 758628 : for (i = 1; i < npts; i++)
5580 : {
5581 : /* compute next polygon point relative to single point */
5582 594283 : x = float8_mi(plist[i].x, p->x);
5583 594283 : y = float8_mi(plist[i].y, p->y);
5584 :
5585 : /* compute previous to current point crossing */
5586 594283 : if ((cross = lseg_crossing(x, y, prev_x, prev_y)) == POINT_ON_POLYGON)
5587 88 : return 2;
5588 594195 : total_cross += cross;
5589 :
5590 594195 : prev_x = x;
5591 594195 : prev_y = y;
5592 : }
5593 :
5594 : /* now do the first point */
5595 164345 : if ((cross = lseg_crossing(x0, y0, prev_x, prev_y)) == POINT_ON_POLYGON)
5596 72 : return 2;
5597 164273 : total_cross += cross;
5598 :
5599 164273 : if (total_cross != 0)
5600 126129 : return 1;
5601 38144 : return 0;
5602 : }
5603 :
5604 :
5605 : /*
5606 : * lseg_crossing()
5607 : * Returns +/-2 if line segment crosses the positive X-axis in a +/- direction.
5608 : * Returns +/-1 if one point is on the positive X-axis.
5609 : * Returns 0 if both points are on the positive X-axis, or there is no crossing.
5610 : * Returns POINT_ON_POLYGON if the segment contains (0,0).
5611 : * Wow, that is one confusing API, but it is used above, and when summed,
5612 : * can tell is if a point is in a polygon.
5613 : */
5614 :
5615 : static int
5616 758628 : lseg_crossing(float8 x, float8 y, float8 prev_x, float8 prev_y)
5617 : {
5618 : float8 z;
5619 : int y_sign;
5620 :
5621 758628 : if (FPzero(y))
5622 : { /* y == 0, on X axis */
5623 1162 : if (FPzero(x)) /* (x,y) is (0,0)? */
5624 152 : return POINT_ON_POLYGON;
5625 1010 : else if (FPgt(x, 0))
5626 : { /* x > 0 */
5627 673 : if (FPzero(prev_y)) /* y and prev_y are zero */
5628 : /* prev_x > 0? */
5629 40 : return FPgt(prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
5630 633 : return FPlt(prev_y, 0.0) ? 1 : -1;
5631 : }
5632 : else
5633 : { /* x < 0, x not on positive X axis */
5634 337 : if (FPzero(prev_y))
5635 : /* prev_x < 0? */
5636 16 : return FPlt(prev_x, 0.0) ? 0 : POINT_ON_POLYGON;
5637 321 : return 0;
5638 : }
5639 : }
5640 : else
5641 : { /* y != 0 */
5642 : /* compute y crossing direction from previous point */
5643 757466 : y_sign = FPgt(y, 0.0) ? 1 : -1;
5644 :
5645 757466 : if (FPzero(prev_y))
5646 : /* previous point was on X axis, so new point is either off or on */
5647 1018 : return FPlt(prev_x, 0.0) ? 0 : y_sign;
5648 756448 : else if ((y_sign < 0 && FPlt(prev_y, 0.0)) ||
5649 386704 : (y_sign > 0 && FPgt(prev_y, 0.0)))
5650 : /* both above or below X axis */
5651 481664 : return 0; /* same sign */
5652 : else
5653 : { /* y and prev_y cross X-axis */
5654 274784 : if (FPge(x, 0.0) && FPgt(prev_x, 0.0))
5655 : /* both non-negative so cross positive X-axis */
5656 99924 : return 2 * y_sign;
5657 174860 : if (FPlt(x, 0.0) && FPle(prev_x, 0.0))
5658 : /* both non-positive so do not cross positive X-axis */
5659 88296 : return 0;
5660 :
5661 : /* x and y cross axes, see URL above point_inside() */
5662 86564 : z = float8_mi(float8_mul(float8_mi(x, prev_x), y),
5663 : float8_mul(float8_mi(y, prev_y), x));
5664 86564 : if (FPzero(z))
5665 8 : return POINT_ON_POLYGON;
5666 86556 : if ((y_sign < 0 && FPlt(z, 0.0)) ||
5667 49664 : (y_sign > 0 && FPgt(z, 0.0)))
5668 48456 : return 0;
5669 38100 : return 2 * y_sign;
5670 : }
5671 : }
5672 : }
5673 :
5674 :
5675 : static bool
5676 4061 : plist_same(int npts, Point *p1, Point *p2)
5677 : {
5678 : int i,
5679 : ii,
5680 : j;
5681 :
5682 : /* find match for first point */
5683 4173 : for (i = 0; i < npts; i++)
5684 : {
5685 4149 : if (point_eq_point(&p2[i], &p1[0]))
5686 : {
5687 :
5688 : /* match found? then look forward through remaining points */
5689 12122 : for (ii = 1, j = i + 1; ii < npts; ii++, j++)
5690 : {
5691 8093 : if (j >= npts)
5692 12 : j = 0;
5693 8093 : if (!point_eq_point(&p2[j], &p1[ii]))
5694 24 : break;
5695 : }
5696 4053 : if (ii == npts)
5697 4029 : return true;
5698 :
5699 : /* match not found forwards? then look backwards */
5700 56 : for (ii = 1, j = i - 1; ii < npts; ii++, j--)
5701 : {
5702 48 : if (j < 0)
5703 8 : j = (npts - 1);
5704 48 : if (!point_eq_point(&p2[j], &p1[ii]))
5705 16 : break;
5706 : }
5707 24 : if (ii == npts)
5708 8 : return true;
5709 : }
5710 : }
5711 :
5712 24 : return false;
5713 : }
|