@@ -48,6 +48,7 @@ static int point_inside(Point *p, int npts, Point *plist);
4848
4949/* Routines for lines */
5050static inline void line_construct (LINE * result , Point * pt , float8 m );
51+ static inline float8 line_sl (LINE * line );
5152static inline float8 line_invsl (LINE * line );
5253static bool line_interpt_line (Point * result , LINE * l1 , LINE * l2 );
5354static bool line_contain_point (LINE * line , Point * point );
@@ -980,6 +981,11 @@ line_recv(PG_FUNCTION_ARGS)
980981 line -> B = pq_getmsgfloat8 (buf );
981982 line -> C = pq_getmsgfloat8 (buf );
982983
984+ if (FPzero (line -> A ) && FPzero (line -> B ))
985+ ereport (ERROR ,
986+ (errcode (ERRCODE_INVALID_BINARY_REPRESENTATION ),
987+ errmsg ("invalid line specification: A and B cannot both be zero" )));
988+
983989 PG_RETURN_LINE_P (line );
984990}
985991
@@ -1040,6 +1046,11 @@ line_construct_pp(PG_FUNCTION_ARGS)
10401046 Point * pt2 = PG_GETARG_POINT_P (1 );
10411047 LINE * result = (LINE * ) palloc (sizeof (LINE ));
10421048
1049+ if (point_eq_point (pt1 , pt2 ))
1050+ ereport (ERROR ,
1051+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
1052+ errmsg ("invalid line specification: must be two distinct points" )));
1053+
10431054 line_construct (result , pt1 , point_sl (pt1 , pt2 ));
10441055
10451056 PG_RETURN_LINE_P (result );
@@ -1076,11 +1087,15 @@ line_perp(PG_FUNCTION_ARGS)
10761087
10771088 if (FPzero (l1 -> A ))
10781089 PG_RETURN_BOOL (FPzero (l2 -> B ));
1079- else if (FPzero (l1 -> B ))
1090+ if (FPzero (l2 -> A ))
1091+ PG_RETURN_BOOL (FPzero (l1 -> B ));
1092+ if (FPzero (l1 -> B ))
10801093 PG_RETURN_BOOL (FPzero (l2 -> A ));
1094+ if (FPzero (l2 -> B ))
1095+ PG_RETURN_BOOL (FPzero (l1 -> A ));
10811096
1082- PG_RETURN_BOOL (FPeq (float8_div (float8_mul (l1 -> A , l2 -> B ),
1083- float8_mul (l1 -> B , l2 -> A )), -1.0 ));
1097+ PG_RETURN_BOOL (FPeq (float8_div (float8_mul (l1 -> A , l2 -> A ),
1098+ float8_mul (l1 -> B , l2 -> B )), -1.0 ));
10841099}
10851100
10861101Datum
@@ -1135,6 +1150,20 @@ line_eq(PG_FUNCTION_ARGS)
11351150 * Line arithmetic routines.
11361151 *---------------------------------------------------------*/
11371152
1153+ /*
1154+ * Return slope of the line
1155+ */
1156+ static inline float8
1157+ line_sl (LINE * line )
1158+ {
1159+ if (FPzero (line -> A ))
1160+ return 0.0 ;
1161+ if (FPzero (line -> B ))
1162+ return DBL_MAX ;
1163+ return float8_div (line -> A , - line -> B );
1164+ }
1165+
1166+
11381167/*
11391168 * Return inverse slope of the line
11401169 */
@@ -1157,16 +1186,21 @@ line_distance(PG_FUNCTION_ARGS)
11571186{
11581187 LINE * l1 = PG_GETARG_LINE_P (0 );
11591188 LINE * l2 = PG_GETARG_LINE_P (1 );
1160- float8 result ;
1161- Point tmp ;
1189+ float8 ratio ;
11621190
11631191 if (line_interpt_line (NULL , l1 , l2 )) /* intersecting? */
11641192 PG_RETURN_FLOAT8 (0.0 );
1165- if (FPzero (l1 -> B )) /* vertical? */
1166- PG_RETURN_FLOAT8 (fabs (float8_mi (l1 -> C , l2 -> C )));
1167- point_construct (& tmp , 0.0 , l1 -> C );
1168- result = line_closept_point (NULL , l2 , & tmp );
1169- PG_RETURN_FLOAT8 (result );
1193+
1194+ if (!FPzero (l1 -> A ) && !isnan (l1 -> A ) && !FPzero (l2 -> A ) && !isnan (l2 -> A ))
1195+ ratio = float8_div (l1 -> A , l2 -> A );
1196+ else if (!FPzero (l1 -> B ) && !isnan (l1 -> B ) && !FPzero (l2 -> B ) && !isnan (l2 -> B ))
1197+ ratio = float8_div (l1 -> B , l2 -> B );
1198+ else
1199+ ratio = 1.0 ;
1200+
1201+ PG_RETURN_FLOAT8 (float8_div (fabs (float8_mi (l1 -> C ,
1202+ float8_mul (ratio , l2 -> C ))),
1203+ HYPOT (l1 -> A , l1 -> B )));
11701204}
11711205
11721206/* line_interpt()
@@ -1207,27 +1241,36 @@ line_interpt_line(Point *result, LINE *l1, LINE *l2)
12071241 float8 x ,
12081242 y ;
12091243
1210- if (FPzero (l1 -> B )) /* l1 vertical? */
1244+ if (! FPzero (l1 -> B ))
12111245 {
1212- if (FPzero (l2 -> B )) /* l2 vertical? */
1246+ if (FPeq (l2 -> A , float8_mul ( l1 -> A , float8_div ( l2 -> B , l1 -> B ))))
12131247 return false;
12141248
1215- x = l1 -> C ;
1216- y = float8_pl (float8_mul (l2 -> A , x ), l2 -> C );
1217- }
1218- else if (FPzero (l2 -> B )) /* l2 vertical? */
1219- {
1220- x = l2 -> C ;
1221- y = float8_pl (float8_mul (l1 -> A , x ), l1 -> C );
1249+ x = float8_div (float8_mi (float8_mul (l1 -> B , l2 -> C ),
1250+ float8_mul (l2 -> B , l1 -> C )),
1251+ float8_mi (float8_mul (l1 -> A , l2 -> B ),
1252+ float8_mul (l2 -> A , l1 -> B )));
1253+ y = float8_div (- float8_pl (float8_mul (l1 -> A , x ), l1 -> C ), l1 -> B );
12221254 }
1223- else
1255+ else if (! FPzero ( l2 -> B ))
12241256 {
1225- if (FPeq (l2 -> A , float8_mul (l1 -> A , float8_div (l2 -> B , l1 -> B ))))
1257+ if (FPeq (l1 -> A , float8_mul (l2 -> A , float8_div (l1 -> B , l2 -> B ))))
12261258 return false;
12271259
1228- x = float8_div (float8_mi (l1 -> C , l2 -> C ), float8_mi (l2 -> A , l1 -> A ));
1229- y = float8_pl (float8_mul (l1 -> A , x ), l1 -> C );
1260+ x = float8_div (float8_mi (float8_mul (l2 -> B , l1 -> C ),
1261+ float8_mul (l1 -> B , l2 -> C )),
1262+ float8_mi (float8_mul (l2 -> A , l1 -> B ),
1263+ float8_mul (l1 -> A , l2 -> B )));
1264+ y = float8_div (- float8_pl (float8_mul (l2 -> A , x ), l2 -> C ), l2 -> B );
12301265 }
1266+ else
1267+ return false;
1268+
1269+ /* On some platforms, the preceding expressions tend to produce -0. */
1270+ if (x == 0.0 )
1271+ x = 0.0 ;
1272+ if (y == 0.0 )
1273+ y = 0.0 ;
12311274
12321275 if (result != NULL )
12331276 point_construct (result , x , y );
@@ -2347,16 +2390,8 @@ dist_sl(PG_FUNCTION_ARGS)
23472390{
23482391 LSEG * lseg = PG_GETARG_LSEG_P (0 );
23492392 LINE * line = PG_GETARG_LINE_P (1 );
2350- float8 result ;
2351-
2352- if (lseg_interpt_line (NULL , lseg , line ))
2353- result = 0.0 ;
2354- else
2355- /* XXX shouldn't we take the min not max? */
2356- result = float8_max (line_closept_point (NULL , line , & lseg -> p [0 ]),
2357- line_closept_point (NULL , line , & lseg -> p [1 ]));
23582393
2359- PG_RETURN_FLOAT8 (result );
2394+ PG_RETURN_FLOAT8 (lseg_closept_line ( NULL , lseg , line ) );
23602395}
23612396
23622397/*
@@ -2533,20 +2568,26 @@ lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
25332568static float8
25342569line_closept_point (Point * result , LINE * line , Point * point )
25352570{
2536- bool retval PG_USED_FOR_ASSERTS_ONLY ;
2537- Point closept ;
2571+ Point closept ;
25382572 LINE tmp ;
25392573
2540- /* We drop a perpendicular to find the intersection point. */
2574+ /*
2575+ * We drop a perpendicular to find the intersection point. Ordinarily
2576+ * we should always find it, but that can fail in the presence of NaN
2577+ * coordinates, and perhaps even from simple roundoff issues.
2578+ */
25412579 line_construct (& tmp , point , line_invsl (line ));
2542- retval = line_interpt_line (& closept , line , & tmp );
2580+ if (!line_interpt_line (& closept , & tmp , line ))
2581+ {
2582+ if (result != NULL )
2583+ * result = * point ;
25432584
2544- Assert (retval ); /* perpendicular lines always intersect */
2585+ return get_float8_nan ();
2586+ }
25452587
25462588 if (result != NULL )
25472589 * result = closept ;
25482590
2549- /* Then we calculate the distance between the points. */
25502591 return point_dt (& closept , point );
25512592}
25522593
@@ -2620,27 +2661,38 @@ lseg_closept_lseg(Point *result, LSEG *l1, LSEG *l2)
26202661 float8 dist ,
26212662 d ;
26222663
2623- d = lseg_closept_point (NULL , l1 , & l2 -> p [0 ]);
2624- dist = d ;
2625- if (result != NULL )
2626- * result = l2 -> p [0 ];
2664+ /* First, we handle the case when the line segments are intersecting. */
2665+ if (lseg_interpt_lseg (result , l1 , l2 ))
2666+ return 0.0 ;
26272667
2628- d = lseg_closept_point (NULL , l1 , & l2 -> p [1 ]);
2668+ /*
2669+ * Then, we find the closest points from the endpoints of the second
2670+ * line segment, and keep the closest one.
2671+ */
2672+ dist = lseg_closept_point (result , l1 , & l2 -> p [0 ]);
2673+ d = lseg_closept_point (& point , l1 , & l2 -> p [1 ]);
26292674 if (float8_lt (d , dist ))
26302675 {
26312676 dist = d ;
26322677 if (result != NULL )
2633- * result = l2 -> p [ 1 ] ;
2678+ * result = point ;
26342679 }
26352680
2636- if (float8_lt (lseg_closept_point (& point , l2 , & l1 -> p [0 ]), dist ))
2637- d = lseg_closept_point (result , l1 , & point );
2638-
2639- if (float8_lt (lseg_closept_point (& point , l2 , & l1 -> p [1 ]), dist ))
2640- d = lseg_closept_point (result , l1 , & point );
2641-
2681+ /* The closest point can still be one of the endpoints, so we test them. */
2682+ d = lseg_closept_point (NULL , l2 , & l1 -> p [0 ]);
2683+ if (float8_lt (d , dist ))
2684+ {
2685+ dist = d ;
2686+ if (result != NULL )
2687+ * result = l1 -> p [0 ];
2688+ }
2689+ d = lseg_closept_point (NULL , l2 , & l1 -> p [1 ]);
26422690 if (float8_lt (d , dist ))
2691+ {
26432692 dist = d ;
2693+ if (result != NULL )
2694+ * result = l1 -> p [1 ];
2695+ }
26442696
26452697 return dist ;
26462698}
@@ -2652,6 +2704,9 @@ close_lseg(PG_FUNCTION_ARGS)
26522704 LSEG * l2 = PG_GETARG_LSEG_P (1 );
26532705 Point * result ;
26542706
2707+ if (lseg_sl (l1 ) == lseg_sl (l2 ))
2708+ PG_RETURN_NULL ();
2709+
26552710 result = (Point * ) palloc (sizeof (Point ));
26562711
26572712 if (isnan (lseg_closept_lseg (result , l2 , l1 )))
@@ -2826,6 +2881,9 @@ close_ls(PG_FUNCTION_ARGS)
28262881 LSEG * lseg = PG_GETARG_LSEG_P (1 );
28272882 Point * result ;
28282883
2884+ if (lseg_sl (lseg ) == line_sl (line ))
2885+ PG_RETURN_NULL ();
2886+
28292887 result = (Point * ) palloc (sizeof (Point ));
28302888
28312889 if (isnan (lseg_closept_line (result , lseg , line )))
0 commit comments