@@ -2127,14 +2127,30 @@ void get_prism_bounding_box(prism *prsm, geom_box *box)
21272127}
21282128
21292129/***************************************************************/
2130- /* find the value of s at which the line p+s*d intersects the */
2131- /* line segment connecting v1 to v2 (in 2 dimensions) */
2130+ /* given 2D points p,v1,v2 and a 2D direction vector d, */
2131+ /* determine whether or not the line p + s*d intersects the */
2132+ /* line segment v1--v2. */
21322133/* algorithm: solve the 2x2 linear system p+s*d = a+t*b */
21332134/* where s,t are scalars and p,d,a,b are 2-vectors with */
2134- /* a=v1, b=v2-v1 */
2135+ /* a=v1, b=v2-v1. intersection then corresponds to 0 <= t < 1. */
2136+ /* return values: */
2137+ /* ** case 1: d is not parallel to v1--v2 ** */
2138+ /* NON_INTERSECTING test negative */
2139+ /* INTERSECTING test positive */
2140+ /* ** case 2: d is parallel to v1--v2 ** */
2141+ /* IN_SEGMENT p lies on line segment v1--v2 */
2142+ /* ON_RAY p does not lie on v1--v2, but there is a */
2143+ /* *positive* value of s such that p+s*d */
2144+ /* lies on v1--v2 */
2145+ /* NON_INTERSECTING neither of the above */
21352146/***************************************************************/
2136- boolean intersect_line_with_segment (double px , double py , double dx , double dy ,
2137- vector3 v1 , vector3 v2 , double * s )
2147+ #define THRESH 1.0e-5
2148+ #define NON_INTERSECTING 0
2149+ #define INTERSECTING 1
2150+ #define IN_SEGMENT 2
2151+ #define ON_RAY 3
2152+ int intersect_line_with_segment (double px , double py , double dx , double dy ,
2153+ vector3 v1 , vector3 v2 , double * s )
21382154{
21392155 double ax = v1 .x , ay = v1 .y ;
21402156 double bx = v2 .x - v1 .x , by = v2 .y - v1 .y ;
@@ -2143,17 +2159,32 @@ boolean intersect_line_with_segment(double px, double py, double dx, double dy,
21432159 double RHSx = ax - px , RHSy = ay - py ;
21442160 double DetM = M00 * M11 - M01 * M10 ;
21452161 double L2 = bx * bx + by * by ; // squared length of edge
2146- if ( fabs (DetM ) < 1.0e-10 * L2 ) // d zero or nearly parallel to edge-->no intersection
2147- return 0 ;
2162+ if ( fabs (DetM ) < 1.0e-10 * L2 )
2163+ { // d zero or nearly parallel to edge
2164+ double pmv1x = px - v1 .x , pmv1y = py - v1 .y , npmv1 = sqrt (pmv1x * pmv1x + pmv1y * pmv1y );
2165+ double pmv2x = px - v2 .x , pmv2y = py - v2 .y , npmv2 = sqrt (pmv2x * pmv2x + pmv2y * pmv2y );
2166+ double dot = pmv1x * pmv2x + pmv1y * pmv2y ;
2167+ if ( fabs (dot ) < (1.0 - THRESH )* npmv1 * npmv2 )
2168+ return NON_INTERSECTING ;
2169+ else if (dot < 0.0 )
2170+ { * s = 0.0 ;
2171+ return IN_SEGMENT ;
2172+ }
2173+ else if ( (dx * pmv1x + dy * pmv1y ) < 0.0 )
2174+ { * s = fmin (npmv1 , npmv2 ) / sqrt (dx * dx + dy * dy );
2175+ return ON_RAY ;
2176+ }
2177+ return NON_INTERSECTING ;
2178+ }
21482179
2149- double t = (M00 * RHSy - M10 * RHSx )/DetM ;
2180+ float t = (M00 * RHSy - M10 * RHSx )/DetM ;
21502181 if (s ) * s = (M11 * RHSx - M01 * RHSy )/DetM ;
21512182
21522183 // the plumb line intersects the segment if 0<=t<=1, with t==0,1
21532184 // corresponding to the endpoints; for our purposes we count
21542185 // the intersection if the plumb line runs through the t==0 vertex, but
21552186 // NOT the t==1 vertex, to avoid double-counting for complete polygons.
2156- return t < 0 || (( float ) t ) >= 1 ? 0 : 1 ;
2187+ return ( t < - THRESH || t >=( 1 - THRESH ) ) ? NON_INTERSECTING : INTERSECTING ;
21572188}
21582189
21592190// like the previous routine, but only count intersections if s>=0
@@ -2162,10 +2193,10 @@ boolean intersect_ray_with_segment(double px, double py, double dx, double dy,
21622193{
21632194 double ss ;
21642195 int status = intersect_line_with_segment (px ,py ,dx ,dy ,v1 ,v2 ,& ss );
2165- if (status == 0 || ss < 0.0 )
2166- return 0 ;
2196+ if (status == INTERSECTING && ss < 0.0 )
2197+ return NON_INTERSECTING ;
21672198 if (s ) * s = ss ;
2168- return 1 ;
2199+ return status ;
21692200}
21702201
21712202/***************************************************************/
@@ -2175,33 +2206,67 @@ boolean intersect_ray_with_segment(double px, double py, double dx, double dy,
21752206/* p to infinity and count the number of edges intersected; */
21762207/* point lies in polygon iff this is number is odd. */
21772208/***************************************************************/
2178- boolean point_in_polygon (double px , double py , vector3 * vertices , int num_vertices )
2209+ boolean point_in_or_on_polygon (double px , double py ,
2210+ vector3 * vertices , int num_vertices ,
2211+ boolean include_boundaries )
21792212{
21802213 double dx = 0.0 , dy = -1.0 ;
2181- int num_side_intersections = 0 ;
2182- int nv ;
2214+ int nv , edges_crossed = 0 ;
21832215 for (nv = 0 ; nv < num_vertices ; nv ++ )
2184- num_side_intersections
2185- += intersect_ray_with_segment (px , py , dx , dy ,
2186- vertices [nv ], vertices [(nv + 1 )%num_vertices ],
2187- 0 );
2188- return num_side_intersections %2 ;
2216+ { int status = intersect_ray_with_segment (px , py , dx , dy ,
2217+ vertices [nv ], vertices [(nv + 1 )%num_vertices ], 0 );
2218+ if (status == IN_SEGMENT )
2219+ return include_boundaries ;
2220+ else if (status == INTERSECTING )
2221+ edges_crossed ++ ;
2222+ else if (status == ON_RAY )
2223+ { vector3 vm1 = vertices [ (nv == 0 ? num_vertices - 1 : nv - 1 ) ];
2224+ vector3 v = vertices [ nv ];
2225+ vector3 vp1 = vertices [ (nv + 1 ) % num_vertices ];
2226+ vector3 vp2 = vertices [ (nv + 2 ) % num_vertices ];
2227+ int last_status = intersect_ray_with_segment (px , py , dx , dy , vm1 , v , 0 );
2228+ if (last_status == INTERSECTING ) edges_crossed -- ;
2229+ int next_status = intersect_ray_with_segment (px , py , dx , dy , vp1 , vp2 , 0 );
2230+ if (next_status == INTERSECTING ) edges_crossed -- ;
2231+ }
2232+ }
2233+ return (edges_crossed % 2 );
21892234}
21902235
2236+ boolean point_in_polygon (double px , double py ,
2237+ vector3 * vertices , int num_vertices )
2238+ { return point_in_or_on_polygon (px , py , vertices , num_vertices , 1 ); }
2239+
21912240/***************************************************************/
21922241/* return 1 or 0 if xc lies inside or outside the prism */
21932242/***************************************************************/
2194- boolean point_in_prism (prism * prsm , vector3 xc )
2243+ boolean point_in_or_on_prism (prism * prsm , vector3 xc , boolean include_boundaries )
21952244{
21962245 vector3 * vertices = prsm -> vertices .items ;
21972246 int num_vertices = prsm -> vertices .num_items ;
21982247 double height = prsm -> height ;
21992248 vector3 xp = prism_coordinate_c2p (prsm , xc );
22002249 if ( xp .z < 0.0 || xp .z > prsm -> height )
22012250 return 0 ;
2202- return point_in_polygon (xp .x , xp .y , vertices , num_vertices );
2251+ return point_in_or_on_polygon (xp .x , xp .y , vertices , num_vertices , include_boundaries );
22032252}
22042253
2254+ boolean point_in_prism (prism * prsm , vector3 xc )
2255+ {
2256+ // by default, points on polygon edges are considered to lie inside the
2257+ // polygon; this can be reversed by setting the environment variable
2258+ // LIBCTL_EXCLUDE_BOUNDARIES=1
2259+ static boolean include_boundaries = 1 , init = 0 ;
2260+ if (init == 0 )
2261+ { init = 1 ;
2262+ char * s = getenv ("LIBCTL_EXCLUDE_BOUNDARIES" );
2263+ if (s && s [0 ]== '1' ) include_boundaries = 0 ;
2264+ }
2265+
2266+ return point_in_or_on_prism (prsm , xc , include_boundaries );
2267+ }
2268+
2269+
22052270// comparator for qsort
22062271static int dcmp (const void * pd1 , const void * pd2 )
22072272{ double d1 = * ((double * )pd1 ), d2 = * ((double * )pd2 );
@@ -2242,9 +2307,9 @@ int intersect_line_with_prism(prism *prsm, vector3 p, vector3 d, double *slist)
22422307 // intersection of the XY-plane projection of the line with the
22432308 // polygon edge between vertices (nv,nv+1).
22442309 double s ;
2245- if (! intersect_line_with_segment (p_prsm .x , p_prsm .y , d_prsm .x , d_prsm .y ,
2246- vertices [nv ], vertices [nvp1 ], & s )
2247- ) continue ;
2310+ int status = intersect_line_with_segment (p_prsm .x , p_prsm .y , d_prsm .x , d_prsm .y ,
2311+ vertices [nv ], vertices [nvp1 ], & s );
2312+ if ( status == NON_INTERSECTING || status == ON_RAY ) continue ;
22482313
22492314 // OK, we know the XY-plane projection of the line intersects the polygon edge;
22502315 // now go back to 3D, ask for the z-coordinate at which the line intersects
@@ -2397,6 +2462,8 @@ double min_distance_to_prism_roof_or_ceiling(vector3 p,
23972462 return sqrt (s * s + d * d );
23982463}
23992464
2465+ /* utility routines for writing points, lines, quadrilaterals */
2466+ /* to text files for viewing in e.g. gnuplot */
24002467void GPPoint (FILE * f , vector3 v , prism * prsm )
24012468{ if (prsm )
24022469 v = prism_coordinate_p2c (prsm , v );
@@ -2492,8 +2559,7 @@ void display_prism_info(int indentby, prism *prsm)
24922559 for (nv = 0 ; nv < num_vertices ; nv ++ )
24932560 { vector3 v = matrix3x3_vector3_mult (m_p2c , vertices [nv ]);
24942561 printf ("%*s {%e,%e,%e}\n" ,indentby ,"" ,v .x ,v .y ,v .z );
2495- };
2496-
2562+ }
24972563}
24982564
24992565/***************************************************************/
0 commit comments