pgsql: Refactor geometric functions and operators
Refactor geometric functions and operators
The primary goal of this patch is to eliminate duplicate code and share
code between different geometric data types more often, to prepare the
ground for additional patches. Until now the code reuse was limited,
probably because the simpler types (line and point) were implemented
after the more complex ones.
The changes are quite extensive and can be summarised as:
* Eliminate SQL-level function calls.
* Re-use more functions to implement others.
* Unify internal function names and signatures.
* Remove private functions from geo_decls.h.
* Replace should-not-happen checks with assertions.
* Add comments describe for various functions.
* Remove some unreachable code.
* Define delimiter symbols of line datatype like the other ones.
* Remove the GEODEBUG macro and printf() calls.
* Unify code style of a few oddly formatted lines.
While the goal was to cause minimal user-visible changes, it was not
possible to keep the original behavior in all cases - for example when
handling NaN values, or when reusing code makes the functions return
consistent results.
Author: Emre Hasegeli
Reviewed-by: Kyotaro Horiguchi, me
Discussion: /messages/by-id/CAE2gYzxF7-5djV6-cEvqQu-fNsnt=EqbOURx7ZDg+Vv6ZMTWbg@mail.gmail.com
Branch
------
master
Details
-------
https://git.postgresql.org/pg/commitdiff/a7dc63d904a6044d299aebdf59ad3199b6a9e99d
Modified Files
--------------
src/backend/utils/adt/geo_ops.c | 1882 +++++++++++++++++-------------------
src/backend/utils/adt/geo_spgist.c | 3 +-
src/include/utils/geo_decls.h | 4 -
src/test/regress/regress.c | 7 +-
4 files changed, 870 insertions(+), 1026 deletions(-)
On Sun, Jul 29, 2018 at 12:43:29AM +0000, Tomas Vondra wrote:
Refactor geometric functions and operators
The primary goal of this patch is to eliminate duplicate code and share
code between different geometric data types more often, to prepare the
ground for additional patches. Until now the code reuse was limited,
probably because the simpler types (line and point) were implemented
after the more complex ones.
It looks that this commit has upset a couple of OSX animals, locus and
prairiedog:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=locust&dt=2018-07-29%2013%3A13%3A35
Some AIX animals are also upset for the same reason with the handling
of a negative sign with 0.
--
Michael
On 07/29/2018 04:59 PM, Michael Paquier wrote:
On Sun, Jul 29, 2018 at 12:43:29AM +0000, Tomas Vondra wrote:
Refactor geometric functions and operators
The primary goal of this patch is to eliminate duplicate code and share
code between different geometric data types more often, to prepare the
ground for additional patches. Until now the code reuse was limited,
probably because the simpler types (line and point) were implemented
after the more complex ones.It looks that this commit has upset a couple of OSX animals, locus and
prairiedog:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=locust&dt=2018-07-29%2013%3A13%3A35Some AIX animals are also upset for the same reason with the handling
of a negative sign with 0.
Yeah, the commit broke this by inadvertedly undoing 43fe90f66a0 :-(
I've posted a fix to hackers [1]/messages/by-id/2fce5d35-2f07-8d72-42ec-81ed97fbe640@2ndquadrant.com. If someone with access to an affected
machine could test if it actually does the trick, that would be nice.
[1]: /messages/by-id/2fce5d35-2f07-8d72-42ec-81ed97fbe640@2ndquadrant.com
/messages/by-id/2fce5d35-2f07-8d72-42ec-81ed97fbe640@2ndquadrant.com
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Hello guys. Coverity complained about this patch as below. What, if
anything, should be done about it? One solution is to mark it as a
false-positive in Coverity, of course.
On 2018-Jul-29, scan-admin@coverity.com wrote:
** CID 1438146: API usage errors (SWAPPED_ARGUMENTS)
________________________________________________________________________________________________________
*** CID 1438146: API usage errors (SWAPPED_ARGUMENTS)
/srv/coverity/git/pgsql-git/postgresql/src/backend/utils/adt/geo_ops.c: 2647 in close_lseg()
2641 LSEG *l1 = PG_GETARG_LSEG_P(0);
2642 LSEG *l2 = PG_GETARG_LSEG_P(1);
2643 Point *result;
2644
2645 result = (Point *) palloc(sizeof(Point));
2646CID 1438146: API usage errors (SWAPPED_ARGUMENTS)
The positions of arguments in the call to "lseg_closept_lseg" do not match the ordering of the parameters:* "l2" is passed to "l1"
* "l1" is passed to "l2"
2647 lseg_closept_lseg(result, l2, l1);
2648
2649 PG_RETURN_POINT_P(result);
2650 }
2651
2652
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
To make coverity happy we might be able to suppress this false alarm by
adding a line like below:
```
/* coverity[SWAPPED_ARGUMENTS] */
lseg_closept_lseg(result, l2, l1);
```
From my point of view it's better to also put some comments for humans to
understand why we are passing l1 and l2 in reverse order.
On Wed, Aug 1, 2018 at 1:21 AM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
Show quoted text
Hello guys. Coverity complained about this patch as below. What, if
anything, should be done about it? One solution is to mark it as a
false-positive in Coverity, of course.On 2018-Jul-29, scan-admin@coverity.com wrote:
** CID 1438146: API usage errors (SWAPPED_ARGUMENTS)
____________________________________________________________
____________________________________________
*** CID 1438146: API usage errors (SWAPPED_ARGUMENTS)
/srv/coverity/git/pgsql-git/postgresql/src/backend/utils/adt/geo_ops.c:2647 in close_lseg()
2641 LSEG *l1 = PG_GETARG_LSEG_P(0);
2642 LSEG *l2 = PG_GETARG_LSEG_P(1);
2643 Point *result;
2644
2645 result = (Point *) palloc(sizeof(Point));
2646CID 1438146: API usage errors (SWAPPED_ARGUMENTS)
The positions of arguments in the call to "lseg_closept_lseg" donot match the ordering of the parameters:
* "l2" is passed to "l1"
* "l1" is passed to "l2"
2647 lseg_closept_lseg(result, l2, l1);
2648
2649 PG_RETURN_POINT_P(result);
2650 }
2651
2652--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Ning Yu <nyu@pivotal.io> writes:
From my point of view it's better to also put some comments for humans to
understand why we are passing l1 and l2 in reverse order.
The header comment for lseg_closept_lseg() is pretty far from adequate
as well. After studying it for awhile I think I've puzzled out the
indeterminacies, but I shouldn't have had to. I think it probably
should have read
* Closest point on line segment l2 to line segment l1
*
* This returns the minimum distance from l1 to the closest point on l2.
* If result is not NULL, the closest point on l2 is stored at *result.
Or perhaps I have it backwards and "l1" and "l2" need to be swapped in
that description. But the mere fact that there is any question about
that means that the function is poorly documented and perhaps poorly
named as well. For that matter, is there a good reason why l1/l2
have those roles and not the reverse?
While Coverity is surely operating from a shaky heuristic here, it's
got a point. The fact that it makes a difference which argument is
given first means that you've got to be really careful about which
is which, and this API spec is giving no help in that regard at all.
regards, tom lane
On 08/01/2018 04:22 AM, Tom Lane wrote:
Ning Yu <nyu@pivotal.io> writes:
From my point of view it's better to also put some comments for humans to
understand why we are passing l1 and l2 in reverse order.The header comment for lseg_closept_lseg() is pretty far from adequate
as well. After studying it for awhile I think I've puzzled out the
indeterminacies, but I shouldn't have had to. I think it probably
should have read* Closest point on line segment l2 to line segment l1
*
* This returns the minimum distance from l1 to the closest point on l2.
* If result is not NULL, the closest point on l2 is stored at *result.Or perhaps I have it backwards and "l1" and "l2" need to be swapped in
that description. But the mere fact that there is any question about
that means that the function is poorly documented and perhaps poorly
named as well. For that matter, is there a good reason why l1/l2
have those roles and not the reverse?While Coverity is surely operating from a shaky heuristic here, it's
got a point. The fact that it makes a difference which argument is
given first means that you've got to be really careful about which
is which, and this API spec is giving no help in that regard at all.
Yeah, I agree. The comments don't make it very clear what is the API
semantics. Will fix.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Or perhaps I have it backwards and "l1" and "l2" need to be swapped in
that description. But the mere fact that there is any question about
that means that the function is poorly documented and perhaps poorly
named as well. For that matter, is there a good reason why l1/l2
have those roles and not the reverse?
Consistency. I organized all xxx_closept_yyy(Point *result, xxx *l1,
yyy *l2) functions in a way that they find the find the point on "l1".
On 08/01/2018 11:55 AM, Emre Hasegeli wrote:
Or perhaps I have it backwards and "l1" and "l2" need to be swapped in
that description. But the mere fact that there is any question about
that means that the function is poorly documented and perhaps poorly
named as well. For that matter, is there a good reason why l1/l2
have those roles and not the reverse?Consistency. I organized all xxx_closept_yyy(Point *result, xxx *l1,
yyy *l2) functions in a way that they find the find the point on "l1".
IMHO the main issue here is that the rule is not obvious / documented
anywhere. I think the best way to do that is by making it clear in a
comment for each such such function.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Tomas Vondra <tomas.vondra@2ndquadrant.com> writes:
On 08/01/2018 11:55 AM, Emre Hasegeli wrote:
Consistency. I organized all xxx_closept_yyy(Point *result, xxx *l1,
yyy *l2) functions in a way that they find the find the point on "l1".
IMHO the main issue here is that the rule is not obvious / documented
anywhere. I think the best way to do that is by making it clear in a
comment for each such such function.
I think there are three different things that need to be addressed:
* Underspecified comments.
* The function names and argument names are badly chosen IMO, because even
granted a convention such as the above, it's not very obvious what roles
"l1" and "l2" play. I'm not exactly sure what would be better, but if you
used names like "ofseg" and "otherseg" you'd at least be trying. I'd go
with an asymmetrical function name too, to make miswriting of calls less
likely.
* And lastly, are we sure there aren't actual *bugs* here? I'd initially
supposed that lseg_closept_lseg acted as Emre says above, but reading the
code makes me think it's the other way around. Its first two potential
assignments to *result are definitely assigning points on l2 not l1.
regards, tom lane
I think there are three different things that need to be addressed:
* Underspecified comments.
I agree. My patch added comments, the next one with actual fixes adds
more. I just didn't want to invest even more time on them while the
code is its current shape.
* The function names and argument names are badly chosen IMO, because even
granted a convention such as the above, it's not very obvious what roles
"l1" and "l2" play. I'm not exactly sure what would be better, but if you
used names like "ofseg" and "otherseg" you'd at least be trying. I'd go
with an asymmetrical function name too, to make miswriting of calls less
likely.
Good idea. I haven't though about that, but now such names makes more
sense to me. I will prepare another patch to improve the naming and
comments to be applied on top of my current patches.
* And lastly, are we sure there aren't actual *bugs* here? I'd initially
supposed that lseg_closept_lseg acted as Emre says above, but reading the
code makes me think it's the other way around. Its first two potential
assignments to *result are definitely assigning points on l2 not l1.
Yes, it is wrong beyond understanding. The committed patch intended
to keep answers as they were. The next one actually fixes those.
On 08/01/2018 04:07 PM, Emre Hasegeli wrote:
I think there are three different things that need to be addressed:
* Underspecified comments.
I agree. My patch added comments, the next one with actual fixes adds
more. I just didn't want to invest even more time on them while the
code is its current shape.* The function names and argument names are badly chosen IMO, because even
granted a convention such as the above, it's not very obvious what roles
"l1" and "l2" play. I'm not exactly sure what would be better, but if you
used names like "ofseg" and "otherseg" you'd at least be trying. I'd go
with an asymmetrical function name too, to make miswriting of calls less
likely.Good idea. I haven't though about that, but now such names makes more
sense to me. I will prepare another patch to improve the naming and
comments to be applied on top of my current patches.* And lastly, are we sure there aren't actual *bugs* here? I'd initially
supposed that lseg_closept_lseg acted as Emre says above, but reading the
code makes me think it's the other way around. Its first two potential
assignments to *result are definitely assigning points on l2 not l1.Yes, it is wrong beyond understanding. The committed patch intended
to keep answers as they were. The next one actually fixes those.
OK, so I think we should not do anything about the issues reported by
coverity until we commit all the patches. Which may resolve some of
those (pre-existing) issues, for example.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Good idea. I haven't though about that, but now such names makes more
sense to me. I will prepare another patch to improve the naming and
comments to be applied on top of my current patches.
The patch is attached to improve the comments and variable names. I
explained the functions with the same signature on the file header. I
can duplicate those on the function headers if you find that better.
Attachments:
geo-ops-comments-v00.patchapplication/octet-stream; name=geo-ops-comments-v00.patchDownload
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index e7c1160131..28304b547d 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -1,15 +1,57 @@
/*-------------------------------------------------------------------------
*
* geo_ops.c
* 2D geometric operations
*
+ * This module implements the geometric functions and operators. The
+ * geometric types are (from simple to more complicated):
+ *
+ * - point
+ * - line
+ * - line segment
+ * - box
+ * - circle
+ * - polygon
+ *
+ * The functions to construct them has the signature:
+ *
+ * void type_construct(Type *result, ...);
+ *
+ * The functions to implement operators usually has the signature:
+ *
+ * void type1_operator_type2(Type *result, Type1 *type1, Type2 *type2);
+ *
+ * There are certain operators between different types. The functions
+ * that return the intersection point between 2 types has signature:
+ *
+ * bool type1_interpt_type2(Point *result, Type1 *type1, Type2 *type2);
+ *
+ * These return whether the two objects intersect, and set the intersection
+ * point to pre-allocated *result when it is not NULL. Those functions may be
+ * used to implement multiple SQL level operators. For example, 2 lines are
+ * parallel when they do not intersect.
+ *
+ * The functions that return whether an object contains another has
+ * the signature:
+ *
+ * bool type1_contain_type2(Type1, *type1, Type2 *type2);
+ *
+ * The functions to get the closest point on an object to the second object
+ * has the signature:
+ *
+ * float8 type1_closept_type2(Point *result, Type1 *type1, Type2 *type2);
+ *
+ * They return the shortest distance between the two objects, and set
+ * the closest point on the first object to the second object to pre-allocated
+ * *result when it is not NULL.
+ *
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/utils/adt/geo_ops.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
@@ -57,44 +99,44 @@ static float8 line_closept_point(Point *result, LINE *line, Point *pt);
/* Routines for line segments */
static inline void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
static inline float8 lseg_sl(LSEG *lseg);
static inline float8 lseg_invsl(LSEG *lseg);
static bool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line);
static bool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2);
static int lseg_crossing(float8 x, float8 y, float8 px, float8 py);
static bool lseg_contain_point(LSEG *lseg, Point *point);
static float8 lseg_closept_point(Point *result, LSEG *lseg, Point *pt);
static float8 lseg_closept_line(Point *result, LSEG *lseg, LINE *line);
-static float8 lseg_closept_lseg(Point *result, LSEG *l1, LSEG *l2);
+static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
/* Routines for boxes */
static inline void box_construct(BOX *result, Point *pt1, Point *pt2);
static void box_cn(Point *center, BOX *box);
static bool box_ov(BOX *box1, BOX *box2);
static float8 box_ar(BOX *box);
static float8 box_ht(BOX *box);
static float8 box_wd(BOX *box);
static bool box_contain_point(BOX *box, Point *point);
-static bool box_contain_box(BOX *box1, BOX *box2);
+static bool box_contain_box(BOX *contains_box1, BOX *contained_box2);
static bool box_contain_lseg(BOX *box, LSEG *lseg);
static bool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg);
static float8 box_closept_point(Point *result, BOX *box, Point *point);
static float8 box_closept_lseg(Point *result, BOX *box, LSEG *lseg);
/* Routines for circles */
static float8 circle_ar(CIRCLE *circle);
/* Routines for polygons */
static void make_bound_box(POLYGON *poly);
static void poly_to_circle(CIRCLE *result, POLYGON *poly);
static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
-static bool poly_contain_poly(POLYGON *polya, POLYGON *polyb);
+static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
static bool plist_same(int npts, Point *p1, Point *p2);
static float8 dist_ppoly_internal(Point *pt, POLYGON *poly);
/* Routines for encoding and decoding */
static float8 single_decode(char *num, char **endptr_p,
const char *type_name, const char *orig_string);
static void single_encode(float8 x, StringInfo str);
static void pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
const char *type_name, const char *orig_string);
static void pair_encode(float8 x, float8 y, StringInfo str);
@@ -644,26 +686,26 @@ box_contain(PG_FUNCTION_ARGS)
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
PG_RETURN_BOOL(box_contain_box(box1, box2));
}
/*
* Check whether the box is in the box or on its border
*/
static bool
-box_contain_box(BOX *box1, BOX *box2)
+box_contain_box(BOX *contains_box, BOX *contained_box)
{
- return FPge(box1->high.x, box2->high.x) &&
- FPle(box1->low.x, box2->low.x) &&
- FPge(box1->high.y, box2->high.y) &&
- FPle(box1->low.y, box2->low.y);
+ return FPge(contains_box->high.x, contained_box->high.x) &&
+ FPle(contains_box->low.x, contained_box->low.x) &&
+ FPge(contains_box->high.y, contained_box->high.y) &&
+ FPle(contains_box->low.y, contained_box->low.y);
}
/* box_positionop -
* is box1 entirely {above,below} box2?
*
* box_below_eq and box_above_eq are obsolete versions that (probably
* erroneously) accept the equal-boundaries case. Since these are not
* in sync with the box_left and box_right code, they are deprecated and
* not supported in the PG 8.1 rtree operator class extension.
@@ -1216,24 +1258,20 @@ line_interpt(PG_FUNCTION_ARGS)
result = (Point *) palloc(sizeof(Point));
if (!line_interpt_line(result, l1, l2))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Internal version of line_interpt
*
- * This returns true if two lines intersect (they do, if they are not
- * parallel), false if they do not. This also sets the intersection point
- * to *result, if it is not NULL.
- *
* NOTE: If the lines are identical then we will find they are parallel
* and report "no intersection". This is a little weird, but since
* there's no *unique* intersection, maybe it's appropriate behavior.
*
* If the lines have NaN constants, we will return true, and the intersection
* point would have NaN coordinates. We shouldn't return false in this case
* because that would mean the lines are parallel.
*/
static bool
line_interpt_line(Point *result, LINE *l1, LINE *l2)
@@ -2239,22 +2277,20 @@ lseg_center(PG_FUNCTION_ARGS)
result->x = float8_div(float8_pl(lseg->p[0].x, lseg->p[1].x), 2.0);
result->y = float8_div(float8_pl(lseg->p[0].y, lseg->p[1].y), 2.0);
PG_RETURN_POINT_P(result);
}
/*
* Find the intersection point of two segments (if any).
*
- * This returns true if two line segments intersect, false if they do not.
- * This also sets the intersection point to *result, if it is not NULL.
* This function is almost perfectly symmetric, even though it doesn't look
* like it. See lseg_interpt_line() for the other half of it.
*/
static bool
lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2)
{
Point interpt;
LINE tmp;
line_construct(&tmp, &l2->p[0], lseg_sl(l2));
@@ -2501,24 +2537,20 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
/*---------------------------------------------------------------------
* interpt_
* Intersection point of objects.
* We choose to ignore the "point" of intersection between
* lines and boxes, since there are typically two.
*-------------------------------------------------------------------*/
/*
* Check if the line segment intersects with the line
- *
- * This returns true if line segment intersects with line, false if they
- * do not. This also sets the intersection point to *result, if it is not
- * NULL.
*/
static bool
lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
{
Point interpt;
LINE tmp;
/*
* First, we promote the line segment to a line, because we know how
* to find the intersection point of two lines. If they don't have
@@ -2554,23 +2586,20 @@ lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
}
/*---------------------------------------------------------------------
* close_
* Point of closest proximity between objects.
*-------------------------------------------------------------------*/
/*
* The intersection point of a perpendicular of the line
* through the point.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
line_closept_point(Point *result, LINE *line, Point *point)
{
Point closept;
LINE tmp;
/*
* We drop a perpendicular to find the intersection point. Ordinarily
* we should always find it, but that can fail in the presence of NaN
@@ -2602,23 +2631,20 @@ close_pl(PG_FUNCTION_ARGS)
if (isnan(line_closept_point(result, line, pt)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on line segment to specified point.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
lseg_closept_point(Point *result, LSEG *lseg, Point *pt)
{
Point closept;
LINE tmp;
/*
* To find the closest point, we draw a perpendicular line from the point
* to the line segment.
@@ -2643,62 +2669,59 @@ close_ps(PG_FUNCTION_ARGS)
if (isnan(lseg_closept_point(result, lseg, pt)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on line segment to line segment
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
-lseg_closept_lseg(Point *result, LSEG *l1, LSEG *l2)
+lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg))
{
Point point;
float8 dist,
d;
/* First, we handle the case when the line segments are intersecting. */
- if (lseg_interpt_lseg(result, l1, l2))
+ if (lseg_interpt_lseg(result, on_lseg, to_lseg))
return 0.0;
/*
* Then, we find the closest points from the endpoints of the second
* line segment, and keep the closest one.
*/
- dist = lseg_closept_point(result, l1, &l2->p[0]);
- d = lseg_closept_point(&point, l1, &l2->p[1]);
+ dist = lseg_closept_point(result, on_lseg, &to_lseg->p[0]);
+ d = lseg_closept_point(&point, on_lseg, &to_lseg->p[1]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = point;
}
/* The closest point can still be one of the endpoints, so we test them. */
- d = lseg_closept_point(NULL, l2, &l1->p[0]);
+ d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[0]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
- *result = l1->p[0];
+ *result = on_lseg->p[0];
}
- d = lseg_closept_point(NULL, l2, &l1->p[1]);
+ d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[1]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
- *result = l1->p[1];
+ *result = on_lseg->p[1];
}
return dist;
}
Datum
close_lseg(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
@@ -2711,23 +2734,20 @@ close_lseg(PG_FUNCTION_ARGS)
if (isnan(lseg_closept_lseg(result, l2, l1)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on or in box to specified point.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
box_closept_point(Point *result, BOX *box, Point *pt)
{
float8 dist,
d;
Point point,
closept;
LSEG lseg;
@@ -2830,23 +2850,20 @@ close_sl(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function \"close_sl\" not implemented")));
PG_RETURN_NULL();
}
/*
* Closest point on line segment to line.
*
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
- *
* NOTE: When the lines are parallel, endpoints of one of the line segment
* are FPeq(), in presence of NaN or Infinitive coordinates, or perhaps =
* even because of simple roundoff issues, there may not be a single closest
* point. We are likely to set the result to the second endpoint in these
* cases.
*/
static float8
lseg_closept_line(Point *result, LSEG *lseg, LINE *line)
{
float8 dist1,
@@ -2888,23 +2905,20 @@ close_ls(PG_FUNCTION_ARGS)
if (isnan(lseg_closept_line(result, lseg, line)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on or in box to line segment.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
box_closept_lseg(Point *result, BOX *box, LSEG *lseg)
{
float8 dist,
d;
Point point,
closept;
LSEG bseg;
@@ -3818,39 +3832,39 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
res = point_inside(&p, poly->npts, poly->p);
}
return res;
}
/*-----------------------------------------------------------------
* Determine if polygon A contains polygon B.
*-----------------------------------------------------------------*/
static bool
-poly_contain_poly(POLYGON *polya, POLYGON *polyb)
+poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly)
{
int i;
LSEG s;
- Assert(polya->npts > 0 && polyb->npts > 0);
+ Assert(contains_poly->npts > 0 && contained_poly->npts > 0);
/*
- * Quick check to see if bounding box is contained.
+ * Quick check to see if bounding box is contained_poly.
*/
- if (!box_contain_box(&polya->boundbox, &polyb->boundbox))
+ if (!box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox))
return false;
- s.p[0] = polyb->p[polyb->npts - 1];
+ s.p[0] = contained_poly->p[contained_poly->npts - 1];
- for (i = 0; i < polyb->npts; i++)
+ for (i = 0; i < contained_poly->npts; i++)
{
- s.p[1] = polyb->p[i];
- if (!lseg_inside_poly(s.p, s.p + 1, polya, 0))
+ s.p[1] = contained_poly->p[i];
+ if (!lseg_inside_poly(s.p, s.p + 1, contains_poly, 0))
return false;
s.p[0] = s.p[1];
}
return true;
}
Datum
poly_contain(PG_FUNCTION_ARGS)
{
On 2018-Nov-06, Emre Hasegeli wrote:
The patch is attached to improve the comments and variable names. I
explained the functions with the same signature on the file header. I
can duplicate those on the function headers if you find that better.
Surely the comment in line 3839 deserves an update :-)
This seems good material. I would put the detailed conventions comment
separately from the head of the file, like this (where I also changed
"Type1 *type1" into "Type1 *obj1", and a few "has" to "have")
Thanks
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
geo-ops-comments-v01.patchtext/x-diff; charset=us-asciiDownload
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index e7c1160131..620d8a8c80 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -3,6 +3,16 @@
* geo_ops.c
* 2D geometric operations
*
+ * This module implements the geometric functions and operators. The
+ * geometric types are (from simple to more complicated):
+ *
+ * - point
+ * - line
+ * - line segment
+ * - box
+ * - circle
+ * - polygon
+ *
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -25,6 +35,39 @@
#include "utils/fmgrprotos.h"
#include "utils/geo_decls.h"
+/*
+ * The functions to implement the types have the signature:
+ *
+ * void type_construct(Type *result, ...);
+ *
+ * The functions to implement operators usually have signatures like:
+ *
+ * void type1_operator_type2(Type *result, Type1 *obj1, Type2 *obj2);
+ *
+ * There are certain operators between different types. The functions
+ * that return the intersection point between 2 types has signature:
+ *
+ * bool type1_interpt_type2(Point *result, Type1 *obj1, Type2 *obj2);
+ *
+ * These return whether the two objects intersect, and set the intersection
+ * point to pre-allocated *result when it is not NULL. Those functions may be
+ * used to implement multiple SQL level operators. For example, determining
+ * whether two lines are parallel is done by checking whether they intersect.
+ *
+ * The functions that return whether an object contains another have
+ * the signature:
+ *
+ * bool type1_contain_type2(Type1, *obj1, Type2 *obj2);
+ *
+ * The functions to get the closest point on an object to the second object
+ * have the signature:
+ *
+ * float8 type1_closept_type2(Point *result, Type1 *obj1, Type2 *obj2);
+ *
+ * They return the shortest distance between the two objects, and set
+ * the closest point on the first object to the second object to pre-allocated
+ * *result when it is not NULL.
+ */
/*
* Internal routines
@@ -64,7 +107,7 @@ static int lseg_crossing(float8 x, float8 y, float8 px, float8 py);
static bool lseg_contain_point(LSEG *lseg, Point *point);
static float8 lseg_closept_point(Point *result, LSEG *lseg, Point *pt);
static float8 lseg_closept_line(Point *result, LSEG *lseg, LINE *line);
-static float8 lseg_closept_lseg(Point *result, LSEG *l1, LSEG *l2);
+static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
/* Routines for boxes */
static inline void box_construct(BOX *result, Point *pt1, Point *pt2);
@@ -74,7 +117,7 @@ static float8 box_ar(BOX *box);
static float8 box_ht(BOX *box);
static float8 box_wd(BOX *box);
static bool box_contain_point(BOX *box, Point *point);
-static bool box_contain_box(BOX *box1, BOX *box2);
+static bool box_contain_box(BOX *contains_box1, BOX *contained_box2);
static bool box_contain_lseg(BOX *box, LSEG *lseg);
static bool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg);
static float8 box_closept_point(Point *result, BOX *box, Point *point);
@@ -87,7 +130,7 @@ static float8 circle_ar(CIRCLE *circle);
static void make_bound_box(POLYGON *poly);
static void poly_to_circle(CIRCLE *result, POLYGON *poly);
static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
-static bool poly_contain_poly(POLYGON *polya, POLYGON *polyb);
+static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
static bool plist_same(int npts, Point *p1, Point *p2);
static float8 dist_ppoly_internal(Point *pt, POLYGON *poly);
@@ -651,12 +694,12 @@ box_contain(PG_FUNCTION_ARGS)
* Check whether the box is in the box or on its border
*/
static bool
-box_contain_box(BOX *box1, BOX *box2)
+box_contain_box(BOX *contains_box, BOX *contained_box)
{
- return FPge(box1->high.x, box2->high.x) &&
- FPle(box1->low.x, box2->low.x) &&
- FPge(box1->high.y, box2->high.y) &&
- FPle(box1->low.y, box2->low.y);
+ return FPge(contains_box->high.x, contained_box->high.x) &&
+ FPle(contains_box->low.x, contained_box->low.x) &&
+ FPge(contains_box->high.y, contained_box->high.y) &&
+ FPle(contains_box->low.y, contained_box->low.y);
}
@@ -1223,10 +1266,6 @@ line_interpt(PG_FUNCTION_ARGS)
/*
* Internal version of line_interpt
*
- * This returns true if two lines intersect (they do, if they are not
- * parallel), false if they do not. This also sets the intersection point
- * to *result, if it is not NULL.
- *
* NOTE: If the lines are identical then we will find they are parallel
* and report "no intersection". This is a little weird, but since
* there's no *unique* intersection, maybe it's appropriate behavior.
@@ -2246,8 +2285,6 @@ lseg_center(PG_FUNCTION_ARGS)
/*
* Find the intersection point of two segments (if any).
*
- * This returns true if two line segments intersect, false if they do not.
- * This also sets the intersection point to *result, if it is not NULL.
* This function is almost perfectly symmetric, even though it doesn't look
* like it. See lseg_interpt_line() for the other half of it.
*/
@@ -2508,10 +2545,6 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
/*
* Check if the line segment intersects with the line
- *
- * This returns true if line segment intersects with line, false if they
- * do not. This also sets the intersection point to *result, if it is not
- * NULL.
*/
static bool
lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
@@ -2561,9 +2594,6 @@ lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
/*
* The intersection point of a perpendicular of the line
* through the point.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
line_closept_point(Point *result, LINE *line, Point *point)
@@ -2609,9 +2639,6 @@ close_pl(PG_FUNCTION_ARGS)
/*
* Closest point on line segment to specified point.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
lseg_closept_point(Point *result, LSEG *lseg, Point *pt)
@@ -2650,27 +2677,24 @@ close_ps(PG_FUNCTION_ARGS)
/*
* Closest point on line segment to line segment
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
-lseg_closept_lseg(Point *result, LSEG *l1, LSEG *l2)
+lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg))
{
Point point;
float8 dist,
d;
/* First, we handle the case when the line segments are intersecting. */
- if (lseg_interpt_lseg(result, l1, l2))
+ if (lseg_interpt_lseg(result, on_lseg, to_lseg))
return 0.0;
/*
* Then, we find the closest points from the endpoints of the second
* line segment, and keep the closest one.
*/
- dist = lseg_closept_point(result, l1, &l2->p[0]);
- d = lseg_closept_point(&point, l1, &l2->p[1]);
+ dist = lseg_closept_point(result, on_lseg, &to_lseg->p[0]);
+ d = lseg_closept_point(&point, on_lseg, &to_lseg->p[1]);
if (float8_lt(d, dist))
{
dist = d;
@@ -2679,19 +2703,19 @@ lseg_closept_lseg(Point *result, LSEG *l1, LSEG *l2)
}
/* The closest point can still be one of the endpoints, so we test them. */
- d = lseg_closept_point(NULL, l2, &l1->p[0]);
+ d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[0]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
- *result = l1->p[0];
+ *result = on_lseg->p[0];
}
- d = lseg_closept_point(NULL, l2, &l1->p[1]);
+ d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[1]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
- *result = l1->p[1];
+ *result = on_lseg->p[1];
}
return dist;
@@ -2718,9 +2742,6 @@ close_lseg(PG_FUNCTION_ARGS)
/*
* Closest point on or in box to specified point.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
box_closept_point(Point *result, BOX *box, Point *pt)
@@ -2837,9 +2858,6 @@ close_sl(PG_FUNCTION_ARGS)
/*
* Closest point on line segment to line.
*
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
- *
* NOTE: When the lines are parallel, endpoints of one of the line segment
* are FPeq(), in presence of NaN or Infinitive coordinates, or perhaps =
* even because of simple roundoff issues, there may not be a single closest
@@ -2895,9 +2913,6 @@ close_ls(PG_FUNCTION_ARGS)
/*
* Closest point on or in box to line segment.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
box_closept_lseg(Point *result, BOX *box, LSEG *lseg)
@@ -3825,25 +3840,25 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
* Determine if polygon A contains polygon B.
*-----------------------------------------------------------------*/
static bool
-poly_contain_poly(POLYGON *polya, POLYGON *polyb)
+poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly)
{
int i;
LSEG s;
- Assert(polya->npts > 0 && polyb->npts > 0);
+ Assert(contains_poly->npts > 0 && contained_poly->npts > 0);
/*
- * Quick check to see if bounding box is contained.
+ * Quick check to see if bounding box is contained_poly.
*/
- if (!box_contain_box(&polya->boundbox, &polyb->boundbox))
+ if (!box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox))
return false;
- s.p[0] = polyb->p[polyb->npts - 1];
+ s.p[0] = contained_poly->p[contained_poly->npts - 1];
- for (i = 0; i < polyb->npts; i++)
+ for (i = 0; i < contained_poly->npts; i++)
{
- s.p[1] = polyb->p[i];
- if (!lseg_inside_poly(s.p, s.p + 1, polya, 0))
+ s.p[1] = contained_poly->p[i];
+ if (!lseg_inside_poly(s.p, s.p + 1, contains_poly, 0))
return false;
s.p[0] = s.p[1];
}
Surely the comment in line 3839 deserves an update :-)
Done.
This seems good material. I would put the detailed conventions comment
separately from the head of the file, like this (where I also changed
"Type1 *type1" into "Type1 *obj1", and a few "has" to "have")
Looks better to me. I found one more "has" and changed it.
Attachments:
geo-ops-comments-v02.patchapplication/octet-stream; name=geo-ops-comments-v02.patchDownload
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index e7c1160131..047afdbe1a 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -1,15 +1,25 @@
/*-------------------------------------------------------------------------
*
* geo_ops.c
* 2D geometric operations
*
+ * This module implements the geometric functions and operators. The
+ * geometric types are (from simple to more complicated):
+ *
+ * - point
+ * - line
+ * - line segment
+ * - box
+ * - circle
+ * - polygon
+ *
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/utils/adt/geo_ops.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
@@ -18,20 +28,53 @@
#include <limits.h>
#include <float.h>
#include <ctype.h>
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "utils/float.h"
#include "utils/fmgrprotos.h"
#include "utils/geo_decls.h"
+/*
+ * The functions to implement the types have the signature:
+ *
+ * void type_construct(Type *result, ...);
+ *
+ * The functions to implement operators usually have signatures like:
+ *
+ * void type1_operator_type2(Type *result, Type1 *obj1, Type2 *obj2);
+ *
+ * There are certain operators between different types. The functions
+ * that return the intersection point between 2 types have signature:
+ *
+ * bool type1_interpt_type2(Point *result, Type1 *obj1, Type2 *obj2);
+ *
+ * These return whether the two objects intersect, and set the intersection
+ * point to pre-allocated *result when it is not NULL. Those functions may be
+ * used to implement multiple SQL level operators. For example, determining
+ * whether two lines are parallel is done by checking whether they intersect.
+ *
+ * The functions that return whether an object contains another have
+ * the signature:
+ *
+ * bool type1_contain_type2(Type1, *obj1, Type2 *obj2);
+ *
+ * The functions to get the closest point on an object to the second object
+ * have the signature:
+ *
+ * float8 type1_closept_type2(Point *result, Type1 *obj1, Type2 *obj2);
+ *
+ * They return the shortest distance between the two objects, and set
+ * the closest point on the first object to the second object to pre-allocated
+ * *result when it is not NULL.
+ */
/*
* Internal routines
*/
enum path_delim
{
PATH_NONE, PATH_OPEN, PATH_CLOSED
};
@@ -57,44 +100,44 @@ static float8 line_closept_point(Point *result, LINE *line, Point *pt);
/* Routines for line segments */
static inline void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
static inline float8 lseg_sl(LSEG *lseg);
static inline float8 lseg_invsl(LSEG *lseg);
static bool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line);
static bool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2);
static int lseg_crossing(float8 x, float8 y, float8 px, float8 py);
static bool lseg_contain_point(LSEG *lseg, Point *point);
static float8 lseg_closept_point(Point *result, LSEG *lseg, Point *pt);
static float8 lseg_closept_line(Point *result, LSEG *lseg, LINE *line);
-static float8 lseg_closept_lseg(Point *result, LSEG *l1, LSEG *l2);
+static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
/* Routines for boxes */
static inline void box_construct(BOX *result, Point *pt1, Point *pt2);
static void box_cn(Point *center, BOX *box);
static bool box_ov(BOX *box1, BOX *box2);
static float8 box_ar(BOX *box);
static float8 box_ht(BOX *box);
static float8 box_wd(BOX *box);
static bool box_contain_point(BOX *box, Point *point);
-static bool box_contain_box(BOX *box1, BOX *box2);
+static bool box_contain_box(BOX *contains_box1, BOX *contained_box2);
static bool box_contain_lseg(BOX *box, LSEG *lseg);
static bool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg);
static float8 box_closept_point(Point *result, BOX *box, Point *point);
static float8 box_closept_lseg(Point *result, BOX *box, LSEG *lseg);
/* Routines for circles */
static float8 circle_ar(CIRCLE *circle);
/* Routines for polygons */
static void make_bound_box(POLYGON *poly);
static void poly_to_circle(CIRCLE *result, POLYGON *poly);
static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
-static bool poly_contain_poly(POLYGON *polya, POLYGON *polyb);
+static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
static bool plist_same(int npts, Point *p1, Point *p2);
static float8 dist_ppoly_internal(Point *pt, POLYGON *poly);
/* Routines for encoding and decoding */
static float8 single_decode(char *num, char **endptr_p,
const char *type_name, const char *orig_string);
static void single_encode(float8 x, StringInfo str);
static void pair_decode(char *str, float8 *x, float8 *y, char **endptr_p,
const char *type_name, const char *orig_string);
static void pair_encode(float8 x, float8 y, StringInfo str);
@@ -644,26 +687,26 @@ box_contain(PG_FUNCTION_ARGS)
BOX *box1 = PG_GETARG_BOX_P(0);
BOX *box2 = PG_GETARG_BOX_P(1);
PG_RETURN_BOOL(box_contain_box(box1, box2));
}
/*
* Check whether the box is in the box or on its border
*/
static bool
-box_contain_box(BOX *box1, BOX *box2)
+box_contain_box(BOX *contains_box, BOX *contained_box)
{
- return FPge(box1->high.x, box2->high.x) &&
- FPle(box1->low.x, box2->low.x) &&
- FPge(box1->high.y, box2->high.y) &&
- FPle(box1->low.y, box2->low.y);
+ return FPge(contains_box->high.x, contained_box->high.x) &&
+ FPle(contains_box->low.x, contained_box->low.x) &&
+ FPge(contains_box->high.y, contained_box->high.y) &&
+ FPle(contains_box->low.y, contained_box->low.y);
}
/* box_positionop -
* is box1 entirely {above,below} box2?
*
* box_below_eq and box_above_eq are obsolete versions that (probably
* erroneously) accept the equal-boundaries case. Since these are not
* in sync with the box_left and box_right code, they are deprecated and
* not supported in the PG 8.1 rtree operator class extension.
@@ -1216,24 +1259,20 @@ line_interpt(PG_FUNCTION_ARGS)
result = (Point *) palloc(sizeof(Point));
if (!line_interpt_line(result, l1, l2))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Internal version of line_interpt
*
- * This returns true if two lines intersect (they do, if they are not
- * parallel), false if they do not. This also sets the intersection point
- * to *result, if it is not NULL.
- *
* NOTE: If the lines are identical then we will find they are parallel
* and report "no intersection". This is a little weird, but since
* there's no *unique* intersection, maybe it's appropriate behavior.
*
* If the lines have NaN constants, we will return true, and the intersection
* point would have NaN coordinates. We shouldn't return false in this case
* because that would mean the lines are parallel.
*/
static bool
line_interpt_line(Point *result, LINE *l1, LINE *l2)
@@ -2239,22 +2278,20 @@ lseg_center(PG_FUNCTION_ARGS)
result->x = float8_div(float8_pl(lseg->p[0].x, lseg->p[1].x), 2.0);
result->y = float8_div(float8_pl(lseg->p[0].y, lseg->p[1].y), 2.0);
PG_RETURN_POINT_P(result);
}
/*
* Find the intersection point of two segments (if any).
*
- * This returns true if two line segments intersect, false if they do not.
- * This also sets the intersection point to *result, if it is not NULL.
* This function is almost perfectly symmetric, even though it doesn't look
* like it. See lseg_interpt_line() for the other half of it.
*/
static bool
lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2)
{
Point interpt;
LINE tmp;
line_construct(&tmp, &l2->p[0], lseg_sl(l2));
@@ -2501,24 +2538,20 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
/*---------------------------------------------------------------------
* interpt_
* Intersection point of objects.
* We choose to ignore the "point" of intersection between
* lines and boxes, since there are typically two.
*-------------------------------------------------------------------*/
/*
* Check if the line segment intersects with the line
- *
- * This returns true if line segment intersects with line, false if they
- * do not. This also sets the intersection point to *result, if it is not
- * NULL.
*/
static bool
lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
{
Point interpt;
LINE tmp;
/*
* First, we promote the line segment to a line, because we know how
* to find the intersection point of two lines. If they don't have
@@ -2554,23 +2587,20 @@ lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
}
/*---------------------------------------------------------------------
* close_
* Point of closest proximity between objects.
*-------------------------------------------------------------------*/
/*
* The intersection point of a perpendicular of the line
* through the point.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
line_closept_point(Point *result, LINE *line, Point *point)
{
Point closept;
LINE tmp;
/*
* We drop a perpendicular to find the intersection point. Ordinarily
* we should always find it, but that can fail in the presence of NaN
@@ -2602,23 +2632,20 @@ close_pl(PG_FUNCTION_ARGS)
if (isnan(line_closept_point(result, line, pt)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on line segment to specified point.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
lseg_closept_point(Point *result, LSEG *lseg, Point *pt)
{
Point closept;
LINE tmp;
/*
* To find the closest point, we draw a perpendicular line from the point
* to the line segment.
@@ -2643,62 +2670,59 @@ close_ps(PG_FUNCTION_ARGS)
if (isnan(lseg_closept_point(result, lseg, pt)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on line segment to line segment
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
-lseg_closept_lseg(Point *result, LSEG *l1, LSEG *l2)
+lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg))
{
Point point;
float8 dist,
d;
/* First, we handle the case when the line segments are intersecting. */
- if (lseg_interpt_lseg(result, l1, l2))
+ if (lseg_interpt_lseg(result, on_lseg, to_lseg))
return 0.0;
/*
* Then, we find the closest points from the endpoints of the second
* line segment, and keep the closest one.
*/
- dist = lseg_closept_point(result, l1, &l2->p[0]);
- d = lseg_closept_point(&point, l1, &l2->p[1]);
+ dist = lseg_closept_point(result, on_lseg, &to_lseg->p[0]);
+ d = lseg_closept_point(&point, on_lseg, &to_lseg->p[1]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
*result = point;
}
/* The closest point can still be one of the endpoints, so we test them. */
- d = lseg_closept_point(NULL, l2, &l1->p[0]);
+ d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[0]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
- *result = l1->p[0];
+ *result = on_lseg->p[0];
}
- d = lseg_closept_point(NULL, l2, &l1->p[1]);
+ d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[1]);
if (float8_lt(d, dist))
{
dist = d;
if (result != NULL)
- *result = l1->p[1];
+ *result = on_lseg->p[1];
}
return dist;
}
Datum
close_lseg(PG_FUNCTION_ARGS)
{
LSEG *l1 = PG_GETARG_LSEG_P(0);
LSEG *l2 = PG_GETARG_LSEG_P(1);
@@ -2711,23 +2735,20 @@ close_lseg(PG_FUNCTION_ARGS)
if (isnan(lseg_closept_lseg(result, l2, l1)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on or in box to specified point.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
box_closept_point(Point *result, BOX *box, Point *pt)
{
float8 dist,
d;
Point point,
closept;
LSEG lseg;
@@ -2830,23 +2851,20 @@ close_sl(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function \"close_sl\" not implemented")));
PG_RETURN_NULL();
}
/*
* Closest point on line segment to line.
*
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
- *
* NOTE: When the lines are parallel, endpoints of one of the line segment
* are FPeq(), in presence of NaN or Infinitive coordinates, or perhaps =
* even because of simple roundoff issues, there may not be a single closest
* point. We are likely to set the result to the second endpoint in these
* cases.
*/
static float8
lseg_closept_line(Point *result, LSEG *lseg, LINE *line)
{
float8 dist1,
@@ -2888,23 +2906,20 @@ close_ls(PG_FUNCTION_ARGS)
if (isnan(lseg_closept_line(result, lseg, line)))
PG_RETURN_NULL();
PG_RETURN_POINT_P(result);
}
/*
* Closest point on or in box to line segment.
- *
- * This sets the closest point to the *result if it is not NULL and returns
- * the distance to the closest point.
*/
static float8
box_closept_lseg(Point *result, BOX *box, LSEG *lseg)
{
float8 dist,
d;
Point point,
closept;
LSEG bseg;
@@ -3814,43 +3829,43 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
*/
p.x = float8_div(float8_pl(t.p[0].x, t.p[1].x), 2.0);
p.y = float8_div(float8_pl(t.p[0].y, t.p[1].y), 2.0);
res = point_inside(&p, poly->npts, poly->p);
}
return res;
}
-/*-----------------------------------------------------------------
- * Determine if polygon A contains polygon B.
- *-----------------------------------------------------------------*/
+/*
+ * Check whether the polygon contains another
+ */
static bool
-poly_contain_poly(POLYGON *polya, POLYGON *polyb)
+poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly)
{
int i;
LSEG s;
- Assert(polya->npts > 0 && polyb->npts > 0);
+ Assert(contains_poly->npts > 0 && contained_poly->npts > 0);
/*
- * Quick check to see if bounding box is contained.
+ * Quick check to see if bounding box is contained_poly.
*/
- if (!box_contain_box(&polya->boundbox, &polyb->boundbox))
+ if (!box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox))
return false;
- s.p[0] = polyb->p[polyb->npts - 1];
+ s.p[0] = contained_poly->p[contained_poly->npts - 1];
- for (i = 0; i < polyb->npts; i++)
+ for (i = 0; i < contained_poly->npts; i++)
{
- s.p[1] = polyb->p[i];
- if (!lseg_inside_poly(s.p, s.p + 1, polya, 0))
+ s.p[1] = contained_poly->p[i];
+ if (!lseg_inside_poly(s.p, s.p + 1, contains_poly, 0))
return false;
s.p[0] = s.p[1];
}
return true;
}
Datum
poly_contain(PG_FUNCTION_ARGS)
{
On 2018-Nov-06, Emre Hasegeli wrote:
Surely the comment in line 3839 deserves an update :-)
Done.
This seems good material. I would put the detailed conventions comment
separately from the head of the file, like this (where I also changed
"Type1 *type1" into "Type1 *obj1", and a few "has" to "have")Looks better to me. I found one more "has" and changed it.
Pushed, with some further minor changes. I decided not to remove the
redundant comments that your patch was removing, as I felt that it's
better to keep the API contract together with the function definition.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services