1 /**********************************************************************
2  *
3  * rttopo - topology library
4  * http://git.osgeo.org/gitea/rttopo/librttopo
5  *
6  * rttopo is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * rttopo is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with rttopo.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright 2011 Nicklas Avén
22  * Copyright 2011 Nicklas Avén
23  *
24  **********************************************************************/
25 
26 
27 
28 /**********************************************************************
29  *
30  * rttopo - topology library
31  * http://git.osgeo.org/gitea/rttopo/librttopo
32  * Copyright 2011 Nicklas Avén
33  *
34  * This is free software; you can redistribute and/or modify it under
35  * the terms of the GNU General Public Licence. See the COPYING file.
36  *
37  **********************************************************************/
38 
39 #include "rttopo_config.h"
40 #include <string.h>
41 #include <stdlib.h>
42 
43 #include "measures3d.h"
44 #include "rtgeom_log.h"
45 
46 
47 static inline int
get_3dvector_from_points(const RTCTX * ctx,RTPOINT3DZ * p1,RTPOINT3DZ * p2,VECTOR3D * v)48 get_3dvector_from_points(const RTCTX *ctx, RTPOINT3DZ *p1,RTPOINT3DZ *p2, VECTOR3D *v)
49 {
50   v->x=p2->x-p1->x;
51   v->y=p2->y-p1->y;
52   v->z=p2->z-p1->z;
53 
54   return RT_TRUE;
55 }
56 
57 static inline int
get_3dcross_product(const RTCTX * ctx,VECTOR3D * v1,VECTOR3D * v2,VECTOR3D * v)58 get_3dcross_product(const RTCTX *ctx, VECTOR3D *v1,VECTOR3D *v2, VECTOR3D *v)
59 {
60   v->x=(v1->y*v2->z)-(v1->z*v2->y);
61   v->y=(v1->z*v2->x)-(v1->x*v2->z);
62   v->z=(v1->x*v2->y)-(v1->y*v2->x);
63 
64   return RT_TRUE;
65 }
66 
67 
68 /**
69 This function is used to create a vertical line used for cases where one if the
70 geometries lacks z-values. The vertical line crosses the 2d point that is closest
71 and the z-range is from maxz to minz in the geoemtrie that has z values.
72 */
73 static
create_v_line(const RTCTX * ctx,const RTGEOM * rtgeom,double x,double y,int srid)74 RTGEOM* create_v_line(const RTCTX *ctx, const RTGEOM *rtgeom,double x, double y, int srid)
75 {
76 
77   RTPOINT *rtpoints[2];
78   RTGBOX gbox;
79   int rv = rtgeom_calculate_gbox(ctx, rtgeom, &gbox);
80 
81   if ( rv == RT_FAILURE )
82     return NULL;
83 
84   rtpoints[0] = rtpoint_make3dz(ctx, srid, x, y, gbox.zmin);
85   rtpoints[1] = rtpoint_make3dz(ctx, srid, x, y, gbox.zmax);
86 
87    return (RTGEOM *)rtline_from_ptarray(ctx, srid, 2, rtpoints);
88 }
89 
90 RTGEOM *
rtgeom_closest_line_3d(const RTCTX * ctx,const RTGEOM * rt1,const RTGEOM * rt2)91 rtgeom_closest_line_3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2)
92 {
93   return rt_dist3d_distanceline(ctx, rt1, rt2, rt1->srid, DIST_MIN);
94 }
95 
96 RTGEOM *
rtgeom_furthest_line_3d(const RTCTX * ctx,RTGEOM * rt1,RTGEOM * rt2)97 rtgeom_furthest_line_3d(const RTCTX *ctx, RTGEOM *rt1, RTGEOM *rt2)
98 {
99   return rt_dist3d_distanceline(ctx, rt1, rt2, rt1->srid, DIST_MAX);
100 }
101 
102 RTGEOM *
rtgeom_closest_point_3d(const RTCTX * ctx,const RTGEOM * rt1,const RTGEOM * rt2)103 rtgeom_closest_point_3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2)
104 {
105   return rt_dist3d_distancepoint(ctx, rt1, rt2, rt1->srid, DIST_MIN);
106 }
107 
108 
109 /**
110 Function initializing 3dshortestline and 3dlongestline calculations.
111 */
112 RTGEOM *
rt_dist3d_distanceline(const RTCTX * ctx,const RTGEOM * rt1,const RTGEOM * rt2,int srid,int mode)113 rt_dist3d_distanceline(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, int srid, int mode)
114 {
115   RTDEBUG(ctx, 2, "rt_dist3d_distanceline is called");
116   double x1,x2,y1,y2, z1, z2, x, y;
117   double initdistance = ( mode == DIST_MIN ? FLT_MAX : -1.0);
118   DISTPTS3D thedl;
119   RTPOINT *rtpoints[2];
120   RTGEOM *result;
121 
122   thedl.mode = mode;
123   thedl.distance = initdistance;
124   thedl.tolerance = 0.0;
125 
126   /*Check if we really have 3D geoemtries*/
127   /*If not, send it to 2D-calculations which will give the same result*/
128   /*as an infinite z-value at one or two of the geometries*/
129   if(!rtgeom_has_z(ctx, rt1) || !rtgeom_has_z(ctx, rt2))
130   {
131 
132     rtnotice(ctx, "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\"");
133 
134     if(!rtgeom_has_z(ctx, rt1) && !rtgeom_has_z(ctx, rt2))
135       return rt_dist2d_distanceline(ctx, rt1, rt2, srid, mode);
136 
137     DISTPTS thedl2d;
138     thedl2d.mode = mode;
139     thedl2d.distance = initdistance;
140     thedl2d.tolerance = 0.0;
141     if (!rt_dist2d_comp(ctx,  rt1,rt2,&thedl2d))
142     {
143       /*should never get here. all cases ought to be error handled earlier*/
144       rterror(ctx, "Some unspecified error.");
145       result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0);
146     }
147     RTGEOM *vertical_line;
148     if(!rtgeom_has_z(ctx, rt1))
149     {
150       x=thedl2d.p1.x;
151       y=thedl2d.p1.y;
152 
153       vertical_line = create_v_line(ctx, rt2,x,y,srid);
154       if (!rt_dist3d_recursive(ctx, vertical_line, rt2, &thedl))
155       {
156         /*should never get here. all cases ought to be error handled earlier*/
157         rtfree(ctx, vertical_line);
158         rterror(ctx, "Some unspecified error.");
159         result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0);
160       }
161       rtfree(ctx, vertical_line);
162     }
163     if(!rtgeom_has_z(ctx, rt2))
164     {
165       x=thedl2d.p2.x;
166       y=thedl2d.p2.y;
167 
168       vertical_line = create_v_line(ctx, rt1,x,y,srid);
169       if (!rt_dist3d_recursive(ctx, rt1, vertical_line, &thedl))
170       {
171         /*should never get here. all cases ought to be error handled earlier*/
172         rtfree(ctx, vertical_line);
173         rterror(ctx, "Some unspecified error.");
174         return (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0);
175       }
176       rtfree(ctx, vertical_line);
177     }
178 
179   }
180   else
181   {
182     if (!rt_dist3d_recursive(ctx, rt1, rt2, &thedl))
183     {
184       /*should never get here. all cases ought to be error handled earlier*/
185       rterror(ctx, "Some unspecified error.");
186       result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0);
187     }
188   }
189   /*if thedl.distance is unchanged there where only empty geometries input*/
190   if (thedl.distance == initdistance)
191   {
192     RTDEBUG(ctx, 3, "didn't find geometries to measure between, returning null");
193     result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0);
194   }
195   else
196   {
197     x1=thedl.p1.x;
198     y1=thedl.p1.y;
199     z1=thedl.p1.z;
200     x2=thedl.p2.x;
201     y2=thedl.p2.y;
202     z2=thedl.p2.z;
203 
204     rtpoints[0] = rtpoint_make3dz(ctx, srid, x1, y1, z1);
205     rtpoints[1] = rtpoint_make3dz(ctx, srid, x2, y2, z2);
206 
207     result = (RTGEOM *)rtline_from_ptarray(ctx, srid, 2, rtpoints);
208   }
209 
210   return result;
211 }
212 
213 /**
214 Function initializing 3dclosestpoint calculations.
215 */
216 RTGEOM *
rt_dist3d_distancepoint(const RTCTX * ctx,const RTGEOM * rt1,const RTGEOM * rt2,int srid,int mode)217 rt_dist3d_distancepoint(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, int srid, int mode)
218 {
219 
220   double x,y,z;
221   DISTPTS3D thedl;
222   double initdistance = FLT_MAX;
223   RTGEOM *result;
224 
225   thedl.mode = mode;
226   thedl.distance= initdistance;
227   thedl.tolerance = 0;
228 
229   RTDEBUG(ctx, 2, "rt_dist3d_distancepoint is called");
230 
231   /*Check if we really have 3D geoemtries*/
232   /*If not, send it to 2D-calculations which will give the same result*/
233   /*as an infinite z-value at one or two of the geometries*/
234   if(!rtgeom_has_z(ctx, rt1) || !rtgeom_has_z(ctx, rt2))
235   {
236     rtnotice(ctx, "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\"");
237 
238     if(!rtgeom_has_z(ctx, rt1) && !rtgeom_has_z(ctx, rt2))
239       return rt_dist2d_distancepoint(ctx, rt1, rt2, srid, mode);
240 
241 
242     DISTPTS thedl2d;
243     thedl2d.mode = mode;
244     thedl2d.distance = initdistance;
245     thedl2d.tolerance = 0.0;
246     if (!rt_dist2d_comp(ctx,  rt1,rt2,&thedl2d))
247     {
248       /*should never get here. all cases ought to be error handled earlier*/
249       rterror(ctx, "Some unspecified error.");
250       return (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0);
251     }
252 
253     RTGEOM *vertical_line;
254     if(!rtgeom_has_z(ctx, rt1))
255     {
256       x=thedl2d.p1.x;
257       y=thedl2d.p1.y;
258 
259       vertical_line = create_v_line(ctx, rt2,x,y,srid);
260       if (!rt_dist3d_recursive(ctx, vertical_line, rt2, &thedl))
261       {
262         /*should never get here. all cases ought to be error handled earlier*/
263         rtfree(ctx, vertical_line);
264         rterror(ctx, "Some unspecified error.");
265         return (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0);
266       }
267       rtfree(ctx, vertical_line);
268     }
269 
270     if(!rtgeom_has_z(ctx, rt2))
271     {
272       x=thedl2d.p2.x;
273       y=thedl2d.p2.y;
274 
275       vertical_line = create_v_line(ctx, rt1,x,y,srid);
276       if (!rt_dist3d_recursive(ctx, rt1, vertical_line, &thedl))
277       {
278         /*should never get here. all cases ought to be error handled earlier*/
279         rtfree(ctx, vertical_line);
280         rterror(ctx, "Some unspecified error.");
281         result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0);
282       }
283       rtfree(ctx, vertical_line);
284     }
285 
286   }
287   else
288   {
289     if (!rt_dist3d_recursive(ctx, rt1, rt2, &thedl))
290     {
291       /*should never get here. all cases ought to be error handled earlier*/
292       rterror(ctx, "Some unspecified error.");
293       result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0);
294     }
295   }
296   if (thedl.distance == initdistance)
297   {
298     RTDEBUG(ctx, 3, "didn't find geometries to measure between, returning null");
299     result = (RTGEOM *)rtcollection_construct_empty(ctx, RTCOLLECTIONTYPE, srid, 0, 0);
300   }
301   else
302   {
303     x=thedl.p1.x;
304     y=thedl.p1.y;
305     z=thedl.p1.z;
306     result = (RTGEOM *)rtpoint_make3dz(ctx, srid, x, y, z);
307   }
308 
309   return result;
310 }
311 
312 
313 /**
314 Function initializing 3d max distance calculation
315 */
316 double
rtgeom_maxdistance3d(const RTCTX * ctx,const RTGEOM * rt1,const RTGEOM * rt2)317 rtgeom_maxdistance3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2)
318 {
319   RTDEBUG(ctx, 2, "rtgeom_maxdistance3d is called");
320 
321   return rtgeom_maxdistance3d_tolerance(ctx,  rt1, rt2, 0.0 );
322 }
323 
324 /**
325 Function handling 3d max distance calculations and dfullywithin calculations.
326 The difference is just the tolerance.
327 */
328 double
rtgeom_maxdistance3d_tolerance(const RTCTX * ctx,const RTGEOM * rt1,const RTGEOM * rt2,double tolerance)329 rtgeom_maxdistance3d_tolerance(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, double tolerance)
330 {
331   if(!rtgeom_has_z(ctx, rt1) || !rtgeom_has_z(ctx, rt2))
332   {
333     rtnotice(ctx, "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\"");
334     return rtgeom_maxdistance2d_tolerance(ctx, rt1, rt2, tolerance);
335   }
336   /*double thedist;*/
337   DISTPTS3D thedl;
338   RTDEBUG(ctx, 2, "rtgeom_maxdistance3d_tolerance is called");
339   thedl.mode = DIST_MAX;
340   thedl.distance= -1;
341   thedl.tolerance = tolerance;
342   if (rt_dist3d_recursive(ctx, rt1, rt2, &thedl))
343   {
344     return thedl.distance;
345   }
346   /*should never get here. all cases ought to be error handled earlier*/
347   rterror(ctx, "Some unspecified error.");
348   return -1;
349 }
350 
351 /**
352   Function initializing 3d min distance calculation
353 */
354 double
rtgeom_mindistance3d(const RTCTX * ctx,const RTGEOM * rt1,const RTGEOM * rt2)355 rtgeom_mindistance3d(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2)
356 {
357   RTDEBUG(ctx, 2, "rtgeom_mindistance3d is called");
358   return rtgeom_mindistance3d_tolerance(ctx,  rt1, rt2, 0.0 );
359 }
360 
361 /**
362   Function handling 3d min distance calculations and dwithin calculations.
363   The difference is just the tolerance.
364 */
365 double
rtgeom_mindistance3d_tolerance(const RTCTX * ctx,const RTGEOM * rt1,const RTGEOM * rt2,double tolerance)366 rtgeom_mindistance3d_tolerance(const RTCTX *ctx, const RTGEOM *rt1, const RTGEOM *rt2, double tolerance)
367 {
368   if(!rtgeom_has_z(ctx, rt1) || !rtgeom_has_z(ctx, rt2))
369   {
370     rtnotice(ctx, "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\"");
371 
372     return rtgeom_mindistance2d_tolerance(ctx, rt1, rt2, tolerance);
373   }
374   DISTPTS3D thedl;
375   RTDEBUG(ctx, 2, "rtgeom_mindistance3d_tolerance is called");
376   thedl.mode = DIST_MIN;
377   thedl.distance= FLT_MAX;
378   thedl.tolerance = tolerance;
379   if (rt_dist3d_recursive(ctx, rt1, rt2, &thedl))
380   {
381     return thedl.distance;
382   }
383   /*should never get here. all cases ought to be error handled earlier*/
384   rterror(ctx, "Some unspecified error.");
385   return FLT_MAX;
386 }
387 
388 
389 /*------------------------------------------------------------------------------------------------------------
390 End of Initializing functions
391 --------------------------------------------------------------------------------------------------------------*/
392 
393 /*------------------------------------------------------------------------------------------------------------
394 Preprocessing functions
395 Functions preparing geometries for distance-calculations
396 --------------------------------------------------------------------------------------------------------------*/
397 
398 
399 /**
400 This is a recursive function delivering every possible combination of subgeometries
401 */
rt_dist3d_recursive(const RTCTX * ctx,const RTGEOM * rtg1,const RTGEOM * rtg2,DISTPTS3D * dl)402 int rt_dist3d_recursive(const RTCTX *ctx, const RTGEOM *rtg1,const RTGEOM *rtg2, DISTPTS3D *dl)
403 {
404   int i, j;
405   int n1=1;
406   int n2=1;
407   RTGEOM *g1 = NULL;
408   RTGEOM *g2 = NULL;
409   RTCOLLECTION *c1 = NULL;
410   RTCOLLECTION *c2 = NULL;
411 
412   RTDEBUGF(ctx, 2, "rt_dist3d_recursive is called with type1=%d, type2=%d", rtg1->type, rtg2->type);
413 
414   if (rtgeom_is_collection(ctx, rtg1))
415   {
416     RTDEBUG(ctx, 3, "First geometry is collection");
417     c1 = rtgeom_as_rtcollection(ctx, rtg1);
418     n1 = c1->ngeoms;
419   }
420   if (rtgeom_is_collection(ctx, rtg2))
421   {
422     RTDEBUG(ctx, 3, "Second geometry is collection");
423     c2 = rtgeom_as_rtcollection(ctx, rtg2);
424     n2 = c2->ngeoms;
425   }
426 
427   for ( i = 0; i < n1; i++ )
428   {
429 
430     if (rtgeom_is_collection(ctx, rtg1))
431     {
432       g1 = c1->geoms[i];
433     }
434     else
435     {
436       g1 = (RTGEOM*)rtg1;
437     }
438 
439     if (rtgeom_is_empty(ctx, g1)) return RT_TRUE;
440 
441     if (rtgeom_is_collection(ctx, g1))
442     {
443       RTDEBUG(ctx, 3, "Found collection inside first geometry collection, recursing");
444       if (!rt_dist3d_recursive(ctx, g1, rtg2, dl)) return RT_FALSE;
445       continue;
446     }
447     for ( j = 0; j < n2; j++ )
448     {
449       if (rtgeom_is_collection(ctx, rtg2))
450       {
451         g2 = c2->geoms[j];
452       }
453       else
454       {
455         g2 = (RTGEOM*)rtg2;
456       }
457       if (rtgeom_is_collection(ctx, g2))
458       {
459         RTDEBUG(ctx, 3, "Found collection inside second geometry collection, recursing");
460         if (!rt_dist3d_recursive(ctx, g1, g2, dl)) return RT_FALSE;
461         continue;
462       }
463 
464 
465       /*If one of geometries is empty, return. True here only means continue searching. False would have stoped the process*/
466       if (rtgeom_is_empty(ctx, g1)||rtgeom_is_empty(ctx, g2)) return RT_TRUE;
467 
468 
469       if (!rt_dist3d_distribute_bruteforce(ctx, g1, g2, dl)) return RT_FALSE;
470       if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return RT_TRUE; /*just a check if  the answer is already given*/
471     }
472   }
473   return RT_TRUE;
474 }
475 
476 
477 
478 /**
479 
480 This function distributes the brute-force for 3D so far the only type, tasks depending on type
481 */
482 int
rt_dist3d_distribute_bruteforce(const RTCTX * ctx,const RTGEOM * rtg1,const RTGEOM * rtg2,DISTPTS3D * dl)483 rt_dist3d_distribute_bruteforce(const RTCTX *ctx, const RTGEOM *rtg1, const RTGEOM *rtg2, DISTPTS3D *dl)
484 {
485 
486   int  t1 = rtg1->type;
487   int  t2 = rtg2->type;
488 
489   RTDEBUGF(ctx, 2, "rt_dist3d_distribute_bruteforce is called with typ1=%d, type2=%d", rtg1->type, rtg2->type);
490 
491   if  ( t1 == RTPOINTTYPE )
492   {
493     if  ( t2 == RTPOINTTYPE )
494     {
495       dl->twisted=1;
496       return rt_dist3d_point_point(ctx, (RTPOINT *)rtg1, (RTPOINT *)rtg2, dl);
497     }
498     else if  ( t2 == RTLINETYPE )
499     {
500       dl->twisted=1;
501       return rt_dist3d_point_line(ctx, (RTPOINT *)rtg1, (RTLINE *)rtg2, dl);
502     }
503     else if  ( t2 == RTPOLYGONTYPE )
504     {
505       dl->twisted=1;
506       return rt_dist3d_point_poly(ctx, (RTPOINT *)rtg1, (RTPOLY *)rtg2,dl);
507     }
508     else
509     {
510       rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2));
511       return RT_FALSE;
512     }
513   }
514   else if ( t1 == RTLINETYPE )
515   {
516     if ( t2 == RTPOINTTYPE )
517     {
518       dl->twisted=(-1);
519       return rt_dist3d_point_line(ctx, (RTPOINT *)rtg2,(RTLINE *)rtg1,dl);
520     }
521     else if ( t2 == RTLINETYPE )
522     {
523       dl->twisted=1;
524       return rt_dist3d_line_line(ctx, (RTLINE *)rtg1,(RTLINE *)rtg2,dl);
525     }
526     else if ( t2 == RTPOLYGONTYPE )
527     {
528       dl->twisted=1;
529       return rt_dist3d_line_poly(ctx, (RTLINE *)rtg1,(RTPOLY *)rtg2,dl);
530     }
531     else
532     {
533       rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2));
534       return RT_FALSE;
535     }
536   }
537   else if ( t1 == RTPOLYGONTYPE )
538   {
539     if ( t2 == RTPOLYGONTYPE )
540     {
541       dl->twisted=1;
542       return rt_dist3d_poly_poly(ctx, (RTPOLY *)rtg1, (RTPOLY *)rtg2,dl);
543     }
544     else if ( t2 == RTPOINTTYPE )
545     {
546       dl->twisted=-1;
547       return rt_dist3d_point_poly(ctx, (RTPOINT *)rtg2, (RTPOLY *)rtg1,dl);
548     }
549     else if ( t2 == RTLINETYPE )
550     {
551       dl->twisted=-1;
552       return rt_dist3d_line_poly(ctx, (RTLINE *)rtg2,(RTPOLY *)rtg1,dl);
553     }
554     else
555     {
556       rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t2));
557       return RT_FALSE;
558     }
559   }
560   else
561   {
562     rterror(ctx, "Unsupported geometry type: %s", rttype_name(ctx, t1));
563     return RT_FALSE;
564   }
565   /*You shouldn't being able to get here*/
566   rterror(ctx, "unspecified error in function rt_dist3d_distribute_bruteforce");
567   return RT_FALSE;
568 }
569 
570 
571 
572 /*------------------------------------------------------------------------------------------------------------
573 End of Preprocessing functions
574 --------------------------------------------------------------------------------------------------------------*/
575 
576 
577 /*------------------------------------------------------------------------------------------------------------
578 Brute force functions
579 So far the only way to do 3D-calculations
580 --------------------------------------------------------------------------------------------------------------*/
581 
582 /**
583 
584 point to point calculation
585 */
586 int
rt_dist3d_point_point(const RTCTX * ctx,RTPOINT * point1,RTPOINT * point2,DISTPTS3D * dl)587 rt_dist3d_point_point(const RTCTX *ctx, RTPOINT *point1, RTPOINT *point2, DISTPTS3D *dl)
588 {
589   RTPOINT3DZ p1;
590   RTPOINT3DZ p2;
591   RTDEBUG(ctx, 2, "rt_dist3d_point_point is called");
592 
593   rt_getPoint3dz_p(ctx, point1->point, 0, &p1);
594   rt_getPoint3dz_p(ctx, point2->point, 0, &p2);
595 
596   return rt_dist3d_pt_pt(ctx, &p1, &p2,dl);
597 }
598 /**
599 
600 point to line calculation
601 */
602 int
rt_dist3d_point_line(const RTCTX * ctx,RTPOINT * point,RTLINE * line,DISTPTS3D * dl)603 rt_dist3d_point_line(const RTCTX *ctx, RTPOINT *point, RTLINE *line, DISTPTS3D *dl)
604 {
605   RTPOINT3DZ p;
606   RTPOINTARRAY *pa = line->points;
607   RTDEBUG(ctx, 2, "rt_dist3d_point_line is called");
608 
609   rt_getPoint3dz_p(ctx, point->point, 0, &p);
610   return rt_dist3d_pt_ptarray(ctx, &p, pa, dl);
611 }
612 
613 /**
614 
615 Computes point to polygon distance
616 For mindistance that means:
617 1)find the plane of the polygon
618 2)projecting the point to the plane of the polygon
619 3)finding if that projected point is inside the polygon, if so the distance is measured to that projected point
620 4) if not in polygon above, check the distance against the boundary of the polygon
621 for max distance it is artays point against boundary
622 
623 */
624 int
rt_dist3d_point_poly(const RTCTX * ctx,RTPOINT * point,RTPOLY * poly,DISTPTS3D * dl)625 rt_dist3d_point_poly(const RTCTX *ctx, RTPOINT *point, RTPOLY *poly, DISTPTS3D *dl)
626 {
627   RTPOINT3DZ p, projp;/*projp is "point projected on plane"*/
628   PLANE3D plane;
629   RTDEBUG(ctx, 2, "rt_dist3d_point_poly is called");
630   rt_getPoint3dz_p(ctx, point->point, 0, &p);
631 
632   /*If we are lookig for max distance, longestline or dfullywithin*/
633   if (dl->mode == DIST_MAX)
634   {
635     RTDEBUG(ctx, 3, "looking for maxdistance");
636     return rt_dist3d_pt_ptarray(ctx, &p, poly->rings[0], dl);
637   }
638 
639   /*Find the plane of the polygon, the "holes" have to be on the same plane. so we only care about the boudary*/
640   if(!define_plane(ctx, poly->rings[0], &plane))
641     return RT_FALSE;
642 
643   /*get our point projected on the plane of the polygon*/
644   project_point_on_plane(ctx, &p, &plane, &projp);
645 
646   return rt_dist3d_pt_poly(ctx, &p, poly,&plane, &projp, dl);
647 }
648 
649 
650 /**
651 
652 line to line calculation
653 */
654 int
rt_dist3d_line_line(const RTCTX * ctx,RTLINE * line1,RTLINE * line2,DISTPTS3D * dl)655 rt_dist3d_line_line(const RTCTX *ctx, RTLINE *line1, RTLINE *line2, DISTPTS3D *dl)
656 {
657   RTPOINTARRAY *pa1 = line1->points;
658   RTPOINTARRAY *pa2 = line2->points;
659   RTDEBUG(ctx, 2, "rt_dist3d_line_line is called");
660 
661   return rt_dist3d_ptarray_ptarray(ctx, pa1, pa2, dl);
662 }
663 
664 /**
665 
666 line to polygon calculation
667 */
rt_dist3d_line_poly(const RTCTX * ctx,RTLINE * line,RTPOLY * poly,DISTPTS3D * dl)668 int rt_dist3d_line_poly(const RTCTX *ctx, RTLINE *line, RTPOLY *poly, DISTPTS3D *dl)
669 {
670   PLANE3D plane;
671   RTDEBUG(ctx, 2, "rt_dist3d_line_poly is called");
672 
673   if (dl->mode == DIST_MAX)
674   {
675     return rt_dist3d_ptarray_ptarray(ctx, line->points, poly->rings[0], dl);
676   }
677 
678   if(!define_plane(ctx, poly->rings[0], &plane))
679     return RT_FALSE;
680 
681   return rt_dist3d_ptarray_poly(ctx, line->points, poly,&plane, dl);
682 }
683 
684 /**
685 
686 polygon to polygon calculation
687 */
rt_dist3d_poly_poly(const RTCTX * ctx,RTPOLY * poly1,RTPOLY * poly2,DISTPTS3D * dl)688 int rt_dist3d_poly_poly(const RTCTX *ctx, RTPOLY *poly1, RTPOLY *poly2, DISTPTS3D *dl)
689 {
690   PLANE3D plane;
691   RTDEBUG(ctx, 2, "rt_dist3d_poly_poly is called");
692   if (dl->mode == DIST_MAX)
693   {
694     return rt_dist3d_ptarray_ptarray(ctx, poly1->rings[0], poly2->rings[0], dl);
695   }
696 
697   if(!define_plane(ctx, poly2->rings[0], &plane))
698     return RT_FALSE;
699 
700   /*What we do here is to compare the bondary of one polygon with the other polygon
701   and then take the second boudary comparing with the first polygon*/
702   dl->twisted=1;
703   if(!rt_dist3d_ptarray_poly(ctx, poly1->rings[0], poly2,&plane, dl))
704     return RT_FALSE;
705   if(dl->distance==0.0) /*Just check if the answer already is given*/
706     return RT_TRUE;
707 
708   if(!define_plane(ctx, poly1->rings[0], &plane))
709     return RT_FALSE;
710   dl->twisted=-1; /*because we swithc the order of geometries we swithch "twisted" to -1 which will give the right order of points in shortest line.*/
711   return rt_dist3d_ptarray_poly(ctx, poly2->rings[0], poly1,&plane, dl);
712 }
713 
714 /**
715 
716  * search all the segments of pointarray to see which one is closest to p
717  * Returns distance between point and pointarray
718  */
719 int
rt_dist3d_pt_ptarray(const RTCTX * ctx,RTPOINT3DZ * p,RTPOINTARRAY * pa,DISTPTS3D * dl)720 rt_dist3d_pt_ptarray(const RTCTX *ctx, RTPOINT3DZ *p, RTPOINTARRAY *pa,DISTPTS3D *dl)
721 {
722   int t;
723   RTPOINT3DZ  start, end;
724   int twist = dl->twisted;
725 
726   RTDEBUG(ctx, 2, "rt_dist3d_pt_ptarray is called");
727 
728   rt_getPoint3dz_p(ctx, pa, 0, &start);
729 
730   for (t=1; t<pa->npoints; t++)
731   {
732     dl->twisted=twist;
733     rt_getPoint3dz_p(ctx, pa, t, &end);
734     if (!rt_dist3d_pt_seg(ctx, p, &start, &end,dl)) return RT_FALSE;
735 
736     if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return RT_TRUE; /*just a check if  the answer is already given*/
737     start = end;
738   }
739 
740   return RT_TRUE;
741 }
742 
743 
744 /**
745 
746 If searching for min distance, this one finds the closest point on segment A-B from p.
747 if searching for max distance it just sends p-A and p-B to pt-pt calculation
748 */
749 int
rt_dist3d_pt_seg(const RTCTX * ctx,RTPOINT3DZ * p,RTPOINT3DZ * A,RTPOINT3DZ * B,DISTPTS3D * dl)750 rt_dist3d_pt_seg(const RTCTX *ctx, RTPOINT3DZ *p, RTPOINT3DZ *A, RTPOINT3DZ *B, DISTPTS3D *dl)
751 {
752   RTPOINT3DZ c;
753   double  r;
754   /*if start==end, then use pt distance */
755   if (  ( A->x == B->x) && (A->y == B->y) && (A->z == B->z)  )
756   {
757     return rt_dist3d_pt_pt(ctx, p,A,dl);
758   }
759 
760 
761   r = ( (p->x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y)  + ( p->z-A->z) * (B->z-A->z) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y)+(B->z-A->z)*(B->z-A->z) );
762 
763   /*This is for finding the 3Dmaxdistance.
764   the maxdistance have to be between two vertexes,
765   compared to mindistance which can be between
766   tvo vertexes vertex.*/
767   if (dl->mode == DIST_MAX)
768   {
769     if (r>=0.5)
770     {
771       return rt_dist3d_pt_pt(ctx, p,A,dl);
772     }
773     if (r<0.5)
774     {
775       return rt_dist3d_pt_pt(ctx, p,B,dl);
776     }
777   }
778 
779   if (r<0)  /*If the first vertex A is closest to the point p*/
780   {
781     return rt_dist3d_pt_pt(ctx, p,A,dl);
782   }
783   if (r>1)  /*If the second vertex B is closest to the point p*/
784   {
785     return rt_dist3d_pt_pt(ctx, p,B,dl);
786   }
787 
788   /*else if the point p is closer to some point between a and b
789   then we find that point and send it to rt_dist3d_pt_pt*/
790   c.x=A->x + r * (B->x-A->x);
791   c.y=A->y + r * (B->y-A->y);
792   c.z=A->z + r * (B->z-A->z);
793 
794   return rt_dist3d_pt_pt(ctx, p,&c,dl);
795 }
796 
797 double
distance3d_pt_pt(const RTCTX * ctx,const POINT3D * p1,const POINT3D * p2)798 distance3d_pt_pt(const RTCTX *ctx, const POINT3D *p1, const POINT3D *p2)
799 {
800   double dx = p2->x - p1->x;
801   double dy = p2->y - p1->y;
802   double dz = p2->z - p1->z;
803   return sqrt ( dx*dx + dy*dy + dz*dz);
804 }
805 
806 
807 /**
808 
809 Compares incomming points and
810 stores the points closest to each other
811 or most far away from each other
812 depending on dl->mode (max or min)
813 */
814 int
rt_dist3d_pt_pt(const RTCTX * ctx,RTPOINT3DZ * thep1,RTPOINT3DZ * thep2,DISTPTS3D * dl)815 rt_dist3d_pt_pt(const RTCTX *ctx, RTPOINT3DZ *thep1, RTPOINT3DZ *thep2,DISTPTS3D *dl)
816 {
817   double dx = thep2->x - thep1->x;
818   double dy = thep2->y - thep1->y;
819   double dz = thep2->z - thep1->z;
820   double dist = sqrt ( dx*dx + dy*dy + dz*dz);
821   RTDEBUGF(ctx, 2, "rt_dist3d_pt_pt called (with points: p1.x=%f, p1.y=%f,p1.z=%f,p2.x=%f, p2.y=%f,p2.z=%f)",thep1->x,thep1->y,thep1->z,thep2->x,thep2->y,thep2->z );
822 
823   if (((dl->distance - dist)*(dl->mode))>0) /*multiplication with mode to handle mindistance (mode=1)  and maxdistance (mode = (-1)*/
824   {
825     dl->distance = dist;
826 
827     if (dl->twisted>0)  /*To get the points in right order. twisted is updated between 1 and (-1) every time the order is changed earlier in the chain*/
828     {
829       dl->p1 = *thep1;
830       dl->p2 = *thep2;
831     }
832     else
833     {
834       dl->p1 = *thep2;
835       dl->p2 = *thep1;
836     }
837   }
838   return RT_TRUE;
839 }
840 
841 
842 /**
843 
844 Finds all combinationes of segments between two pointarrays
845 */
846 int
rt_dist3d_ptarray_ptarray(const RTCTX * ctx,RTPOINTARRAY * l1,RTPOINTARRAY * l2,DISTPTS3D * dl)847 rt_dist3d_ptarray_ptarray(const RTCTX *ctx, RTPOINTARRAY *l1, RTPOINTARRAY *l2,DISTPTS3D *dl)
848 {
849   int t,u;
850   RTPOINT3DZ  start, end;
851   RTPOINT3DZ  start2, end2;
852   int twist = dl->twisted;
853   RTDEBUGF(ctx, 2, "rt_dist3d_ptarray_ptarray called (points: %d-%d)",l1->npoints, l2->npoints);
854 
855 
856 
857   if (dl->mode == DIST_MAX)/*If we are searching for maxdistance we go straight to point-point calculation since the maxdistance have to be between two vertexes*/
858   {
859     for (t=0; t<l1->npoints; t++) /*for each segment in L1 */
860     {
861       rt_getPoint3dz_p(ctx, l1, t, &start);
862       for (u=0; u<l2->npoints; u++) /*for each segment in L2 */
863       {
864         rt_getPoint3dz_p(ctx, l2, u, &start2);
865         rt_dist3d_pt_pt(ctx, &start,&start2,dl);
866         RTDEBUGF(ctx, 4, "maxdist_ptarray_ptarray; seg %i * seg %i, dist = %g\n",t,u,dl->distance);
867         RTDEBUGF(ctx, 3, " seg%d-seg%d dist: %f, mindist: %f",
868                  t, u, dl->distance, dl->tolerance);
869       }
870     }
871   }
872   else
873   {
874     rt_getPoint3dz_p(ctx, l1, 0, &start);
875     for (t=1; t<l1->npoints; t++) /*for each segment in L1 */
876     {
877       rt_getPoint3dz_p(ctx, l1, t, &end);
878       rt_getPoint3dz_p(ctx, l2, 0, &start2);
879       for (u=1; u<l2->npoints; u++) /*for each segment in L2 */
880       {
881         rt_getPoint3dz_p(ctx, l2, u, &end2);
882         dl->twisted=twist;
883         rt_dist3d_seg_seg(ctx, &start, &end, &start2, &end2,dl);
884         RTDEBUGF(ctx, 4, "mindist_ptarray_ptarray; seg %i * seg %i, dist = %g\n",t,u,dl->distance);
885         RTDEBUGF(ctx, 3, " seg%d-seg%d dist: %f, mindist: %f",
886                  t, u, dl->distance, dl->tolerance);
887         if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return RT_TRUE; /*just a check if  the answer is already given*/
888         start2 = end2;
889       }
890       start = end;
891     }
892   }
893   return RT_TRUE;
894 }
895 
896 /**
897 
898 Finds the two closest points on two linesegments
899 */
900 int
rt_dist3d_seg_seg(const RTCTX * ctx,RTPOINT3DZ * s1p1,RTPOINT3DZ * s1p2,RTPOINT3DZ * s2p1,RTPOINT3DZ * s2p2,DISTPTS3D * dl)901 rt_dist3d_seg_seg(const RTCTX *ctx, RTPOINT3DZ *s1p1, RTPOINT3DZ *s1p2, RTPOINT3DZ *s2p1, RTPOINT3DZ *s2p2, DISTPTS3D *dl)
902 {
903   VECTOR3D v1, v2, vl;
904   double s1k, s2k; /*two variables representing where on Line 1 (s1k) and where on Line 2 (s2k) a connecting line between the two lines is perpendicular to both lines*/
905   RTPOINT3DZ p1, p2;
906   double a, b, c, d, e, D;
907 
908   /*s1p1 and s1p2 are the same point */
909   if (  ( s1p1->x == s1p2->x) && (s1p1->y == s1p2->y) && (s1p1->z == s1p2->z) )
910   {
911     return rt_dist3d_pt_seg(ctx, s1p1,s2p1,s2p2,dl);
912   }
913   /*s2p1 and s2p2 are the same point */
914   if (  ( s2p1->x == s2p2->x) && (s2p1->y == s2p2->y) && (s2p1->z == s2p2->z) )
915   {
916     dl->twisted= ((dl->twisted) * (-1));
917     return rt_dist3d_pt_seg(ctx, s2p1,s1p1,s1p2,dl);
918   }
919 
920 /*
921   Here we use algorithm from softsurfer.com
922   that can be found here
923   http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
924 */
925 
926   if (!get_3dvector_from_points(ctx, s1p1, s1p2, &v1))
927     return RT_FALSE;
928 
929   if (!get_3dvector_from_points(ctx, s2p1, s2p2, &v2))
930     return RT_FALSE;
931 
932   if (!get_3dvector_from_points(ctx, s2p1, s1p1, &vl))
933     return RT_FALSE;
934 
935   a = DOT(v1,v1);
936   b = DOT(v1,v2);
937   c = DOT(v2,v2);
938   d = DOT(v1,vl);
939   e = DOT(v2,vl);
940   D = a*c - b*b;
941 
942 
943   if (D <0.000000001)
944   {        /* the lines are almost parallel*/
945     s1k = 0.0; /*If the lines are paralell we try by using the startpoint of first segment. If that gives a projected point on the second line outside segment 2 it wil be found that s2k is >1 or <0.*/
946     if(b>c)   /* use the largest denominator*/
947     {
948       s2k=d/b;
949     }
950     else
951     {
952       s2k =e/c;
953     }
954   }
955   else
956   {
957     s1k = (b*e - c*d) / D;
958     s2k = (a*e - b*d) / D;
959   }
960 
961   /* Now we check if the projected closest point on the infinite lines is outside our segments. If so the combinations with start and end points will be tested*/
962   if(s1k<0.0||s1k>1.0||s2k<0.0||s2k>1.0)
963   {
964     if(s1k<0.0)
965     {
966 
967       if (!rt_dist3d_pt_seg(ctx, s1p1, s2p1, s2p2, dl))
968       {
969         return RT_FALSE;
970       }
971     }
972     if(s1k>1.0)
973     {
974 
975       if (!rt_dist3d_pt_seg(ctx, s1p2, s2p1, s2p2, dl))
976       {
977         return RT_FALSE;
978       }
979     }
980     if(s2k<0.0)
981     {
982       dl->twisted= ((dl->twisted) * (-1));
983       if (!rt_dist3d_pt_seg(ctx, s2p1, s1p1, s1p2, dl))
984       {
985         return RT_FALSE;
986       }
987     }
988     if(s2k>1.0)
989     {
990       dl->twisted= ((dl->twisted) * (-1));
991       if (!rt_dist3d_pt_seg(ctx, s2p2, s1p1, s1p2, dl))
992       {
993         return RT_FALSE;
994       }
995     }
996   }
997   else
998   {/*Find the closest point on the edges of both segments*/
999     p1.x=s1p1->x+s1k*(s1p2->x-s1p1->x);
1000     p1.y=s1p1->y+s1k*(s1p2->y-s1p1->y);
1001     p1.z=s1p1->z+s1k*(s1p2->z-s1p1->z);
1002 
1003     p2.x=s2p1->x+s2k*(s2p2->x-s2p1->x);
1004     p2.y=s2p1->y+s2k*(s2p2->y-s2p1->y);
1005     p2.z=s2p1->z+s2k*(s2p2->z-s2p1->z);
1006 
1007     if (!rt_dist3d_pt_pt(ctx, &p1,&p2,dl))/* Send the closest points to point-point calculation*/
1008     {
1009       return RT_FALSE;
1010     }
1011   }
1012   return RT_TRUE;
1013 }
1014 
1015 /**
1016 
1017 Checking if the point projected on the plane of the polygon actually is inside that polygon.
1018 If so the mindistance is between that projected point and our original point.
1019 If not we check from original point to the bounadary.
1020 If the projected point is inside a hole of the polygon we check the distance to the boudary of that hole.
1021 */
1022 int
rt_dist3d_pt_poly(const RTCTX * ctx,RTPOINT3DZ * p,RTPOLY * poly,PLANE3D * plane,RTPOINT3DZ * projp,DISTPTS3D * dl)1023 rt_dist3d_pt_poly(const RTCTX *ctx, RTPOINT3DZ *p, RTPOLY *poly, PLANE3D *plane,RTPOINT3DZ *projp, DISTPTS3D *dl)
1024 {
1025   int i;
1026 
1027   RTDEBUG(ctx, 2, "rt_dist3d_point_poly called");
1028 
1029 
1030   if(pt_in_ring_3d(ctx, projp, poly->rings[0], plane))
1031   {
1032     for (i=1; i<poly->nrings; i++)
1033     {
1034       /* Inside a hole. Distance = pt -> ring */
1035       if ( pt_in_ring_3d(ctx, projp, poly->rings[i], plane ))
1036       {
1037         RTDEBUG(ctx, 3, " inside an hole");
1038         return rt_dist3d_pt_ptarray(ctx, p, poly->rings[i], dl);
1039       }
1040     }
1041 
1042     return rt_dist3d_pt_pt(ctx, p,projp,dl);/* If the projected point is inside the polygon the shortest distance is between that point and the inputed point*/
1043   }
1044   else
1045   {
1046     return rt_dist3d_pt_ptarray(ctx, p, poly->rings[0], dl); /*If the projected point is outside the polygon we search for the closest distance against the boundarry instead*/
1047   }
1048 
1049   return RT_TRUE;
1050 
1051 }
1052 
1053 /**
1054 
1055 Computes pointarray to polygon distance
1056 */
rt_dist3d_ptarray_poly(const RTCTX * ctx,RTPOINTARRAY * pa,RTPOLY * poly,PLANE3D * plane,DISTPTS3D * dl)1057 int rt_dist3d_ptarray_poly(const RTCTX *ctx, RTPOINTARRAY *pa, RTPOLY *poly,PLANE3D *plane, DISTPTS3D *dl)
1058 {
1059 
1060 
1061   int i,j,k;
1062   double f, s1, s2;
1063   VECTOR3D projp1_projp2;
1064   RTPOINT3DZ p1, p2,projp1, projp2, intersectionp;
1065 
1066   rt_getPoint3dz_p(ctx, pa, 0, &p1);
1067 
1068   s1=project_point_on_plane(ctx, &p1, plane, &projp1); /*the sign of s1 tells us on which side of the plane the point is. */
1069   rt_dist3d_pt_poly(ctx, &p1, poly, plane,&projp1, dl);
1070 
1071   for (i=1;i<pa->npoints;i++)
1072   {
1073     int intersects;
1074     rt_getPoint3dz_p(ctx, pa, i, &p2);
1075     s2=project_point_on_plane(ctx, &p2, plane, &projp2);
1076     rt_dist3d_pt_poly(ctx, &p2, poly, plane,&projp2, dl);
1077 
1078     /*If s1and s2 has different signs that means they are on different sides of the plane of the polygon.
1079     That means that the edge between the points crosses the plane and might intersect with the polygon*/
1080     if((s1*s2)<=0)
1081     {
1082       f=fabs(s1)/(fabs(s1)+fabs(s2)); /*The size of s1 and s2 is the distance from the point to the plane.*/
1083       get_3dvector_from_points(ctx, &projp1, &projp2,&projp1_projp2);
1084 
1085       /*get the point where the line segment crosses the plane*/
1086       intersectionp.x=projp1.x+f*projp1_projp2.x;
1087       intersectionp.y=projp1.y+f*projp1_projp2.y;
1088       intersectionp.z=projp1.z+f*projp1_projp2.z;
1089 
1090       intersects = RT_TRUE; /*We set intersects to true until the opposite is proved*/
1091 
1092       if(pt_in_ring_3d(ctx, &intersectionp, poly->rings[0], plane)) /*Inside outer ring*/
1093       {
1094         for (k=1;k<poly->nrings; k++)
1095         {
1096           /* Inside a hole, so no intersection with the polygon*/
1097           if ( pt_in_ring_3d(ctx, &intersectionp, poly->rings[k], plane ))
1098           {
1099             intersects=RT_FALSE;
1100             break;
1101           }
1102         }
1103         if(intersects)
1104         {
1105           dl->distance=0.0;
1106           dl->p1.x=intersectionp.x;
1107           dl->p1.y=intersectionp.y;
1108           dl->p1.z=intersectionp.z;
1109 
1110           dl->p2.x=intersectionp.x;
1111           dl->p2.y=intersectionp.y;
1112           dl->p2.z=intersectionp.z;
1113           return RT_TRUE;
1114 
1115         }
1116       }
1117     }
1118 
1119     projp1=projp2;
1120     s1=s2;
1121     p1=p2;
1122   }
1123 
1124   /*check or pointarray against boundary and inner boundaries of the polygon*/
1125   for (j=0;j<poly->nrings;j++)
1126   {
1127     rt_dist3d_ptarray_ptarray(ctx, pa, poly->rings[j], dl);
1128   }
1129 
1130 return RT_TRUE;
1131 }
1132 
1133 
1134 /**
1135 
1136 Here we define the plane of a polygon (boundary pointarray of a polygon)
1137 the plane is stored as a pont in plane (plane.pop) and a normal vector (plane.pv)
1138 */
1139 int
define_plane(const RTCTX * ctx,RTPOINTARRAY * pa,PLANE3D * pl)1140 define_plane(const RTCTX *ctx, RTPOINTARRAY *pa, PLANE3D *pl)
1141 {
1142   int i,j, numberofvectors, pointsinslice;
1143   RTPOINT3DZ p, p1, p2;
1144 
1145   double sumx=0;
1146   double sumy=0;
1147   double sumz=0;
1148   double vl; /*vector length*/
1149 
1150   VECTOR3D v1, v2, v;
1151 
1152   if((pa->npoints-1)==3) /*Triangle is special case*/
1153   {
1154     pointsinslice=1;
1155   }
1156   else
1157   {
1158     pointsinslice=(int) floor((pa->npoints-1)/4); /*divide the pointarray into 4 slices*/
1159   }
1160 
1161   /*find the avg point*/
1162   for (i=0;i<(pa->npoints-1);i++)
1163   {
1164     rt_getPoint3dz_p(ctx, pa, i, &p);
1165     sumx+=p.x;
1166     sumy+=p.y;
1167     sumz+=p.z;
1168   }
1169   pl->pop.x=(sumx/(pa->npoints-1));
1170   pl->pop.y=(sumy/(pa->npoints-1));
1171   pl->pop.z=(sumz/(pa->npoints-1));
1172 
1173   sumx=0;
1174   sumy=0;
1175   sumz=0;
1176   numberofvectors= floor((pa->npoints-1)/pointsinslice); /*the number of vectors we try can be 3, 4 or 5*/
1177 
1178   rt_getPoint3dz_p(ctx, pa, 0, &p1);
1179   for (j=pointsinslice;j<pa->npoints;j+=pointsinslice)
1180   {
1181     rt_getPoint3dz_p(ctx, pa, j, &p2);
1182 
1183     if (!get_3dvector_from_points(ctx, &(pl->pop), &p1, &v1) || !get_3dvector_from_points(ctx, &(pl->pop), &p2, &v2))
1184       return RT_FALSE;
1185     /*perpendicular vector is cross product of v1 and v2*/
1186     if (!get_3dcross_product(ctx, &v1,&v2, &v))
1187       return RT_FALSE;
1188     vl=VECTORLENGTH(v);
1189     sumx+=(v.x/vl);
1190     sumy+=(v.y/vl);
1191     sumz+=(v.z/vl);
1192     p1=p2;
1193   }
1194   pl->pv.x=(sumx/numberofvectors);
1195   pl->pv.y=(sumy/numberofvectors);
1196   pl->pv.z=(sumz/numberofvectors);
1197 
1198   return 1;
1199 }
1200 
1201 /**
1202 
1203 Finds a point on a plane from where the original point is perpendicular to the plane
1204 */
1205 double
project_point_on_plane(const RTCTX * ctx,RTPOINT3DZ * p,PLANE3D * pl,RTPOINT3DZ * p0)1206 project_point_on_plane(const RTCTX *ctx, RTPOINT3DZ *p,  PLANE3D *pl, RTPOINT3DZ *p0)
1207 {
1208 /*In our plane definition we have a point on the plane and a normal vektor (pl.pv), perpendicular to the plane
1209 this vector will be paralell to the line between our inputted point above the plane and the point we are searching for on the plane.
1210 So, we already have a direction from p to find p0, but we don't know the distance.
1211 */
1212 
1213   VECTOR3D v1;
1214   double f;
1215 
1216   if (!get_3dvector_from_points(ctx, &(pl->pop), p, &v1))
1217   return RT_FALSE;
1218 
1219   f=-(DOT(pl->pv,v1)/DOT(pl->pv,pl->pv));
1220 
1221   p0->x=p->x+pl->pv.x*f;
1222   p0->y=p->y+pl->pv.y*f;
1223   p0->z=p->z+pl->pv.z*f;
1224 
1225   return f;
1226 }
1227 
1228 
1229 
1230 
1231 /**
1232  * pt_in_ring_3d(ctx): crossing number test for a point in a polygon
1233  *      input:   p = a point,
1234  *               pa = vertex points of a ring V[n+1] with V[n]=V[0]
1235 *    plane=the plane that the vertex points are lying on
1236  *      returns: 0 = outside, 1 = inside
1237  *
1238  *  Our polygons have first and last point the same,
1239  *
1240 *  The difference in 3D variant is that we exclude the dimension that faces the plane least.
1241 *  That is the dimension with the highest number in pv
1242  */
1243 int
pt_in_ring_3d(const RTCTX * ctx,const RTPOINT3DZ * p,const RTPOINTARRAY * ring,PLANE3D * plane)1244 pt_in_ring_3d(const RTCTX *ctx, const RTPOINT3DZ *p, const RTPOINTARRAY *ring,PLANE3D *plane)
1245 {
1246 
1247   int cn = 0;    /* the crossing number counter */
1248   int i;
1249   RTPOINT3DZ v1, v2;
1250 
1251   RTPOINT3DZ  first, last;
1252 
1253   rt_getPoint3dz_p(ctx, ring, 0, &first);
1254   rt_getPoint3dz_p(ctx, ring, ring->npoints-1, &last);
1255   if ( memcmp(&first, &last, sizeof(RTPOINT3DZ)) )
1256   {
1257     rterror(ctx, "pt_in_ring_3d: V[n] != V[0] (%g %g %g!= %g %g %g)",
1258             first.x, first.y, first.z, last.x, last.y, last.z);
1259     return RT_FALSE;
1260   }
1261 
1262   RTDEBUGF(ctx, 2, "pt_in_ring_3d called with point: %g %g %g", p->x, p->y, p->z);
1263   /* printPA(ctx, ring); */
1264 
1265   /* loop through all edges of the polygon */
1266   rt_getPoint3dz_p(ctx, ring, 0, &v1);
1267 
1268 
1269   if(fabs(plane->pv.z)>=fabs(plane->pv.x)&&fabs(plane->pv.z)>=fabs(plane->pv.y))  /*If the z vector of the normal vector to the plane is larger than x and y vector we project the ring to the xy-plane*/
1270   {
1271     for (i=0; i<ring->npoints-1; i++)
1272     {
1273       double vt;
1274       rt_getPoint3dz_p(ctx, ring, i+1, &v2);
1275 
1276       /* edge from vertex i to vertex i+1 */
1277       if
1278       (
1279           /* an upward crossing */
1280           ((v1.y <= p->y) && (v2.y > p->y))
1281           /* a downward crossing */
1282           || ((v1.y > p->y) && (v2.y <= p->y))
1283       )
1284       {
1285 
1286         vt = (double)(p->y - v1.y) / (v2.y - v1.y);
1287 
1288         /* P.x <intersect */
1289         if (p->x < v1.x + vt * (v2.x - v1.x))
1290         {
1291           /* a valid crossing of y=p.y right of p.x */
1292           ++cn;
1293         }
1294       }
1295       v1 = v2;
1296     }
1297   }
1298   else if(fabs(plane->pv.y)>=fabs(plane->pv.x)&&fabs(plane->pv.y)>=fabs(plane->pv.z))  /*If the y vector of the normal vector to the plane is larger than x and z vector we project the ring to the xz-plane*/
1299   {
1300     for (i=0; i<ring->npoints-1; i++)
1301       {
1302         double vt;
1303         rt_getPoint3dz_p(ctx, ring, i+1, &v2);
1304 
1305         /* edge from vertex i to vertex i+1 */
1306         if
1307         (
1308             /* an upward crossing */
1309             ((v1.z <= p->z) && (v2.z > p->z))
1310             /* a downward crossing */
1311             || ((v1.z > p->z) && (v2.z <= p->z))
1312         )
1313         {
1314 
1315           vt = (double)(p->z - v1.z) / (v2.z - v1.z);
1316 
1317           /* P.x <intersect */
1318           if (p->x < v1.x + vt * (v2.x - v1.x))
1319           {
1320             /* a valid crossing of y=p.y right of p.x */
1321             ++cn;
1322           }
1323         }
1324         v1 = v2;
1325       }
1326   }
1327   else  /*Hopefully we only have the cases where x part of the normal vector is largest left*/
1328   {
1329     for (i=0; i<ring->npoints-1; i++)
1330       {
1331         double vt;
1332         rt_getPoint3dz_p(ctx, ring, i+1, &v2);
1333 
1334         /* edge from vertex i to vertex i+1 */
1335         if
1336         (
1337             /* an upward crossing */
1338             ((v1.z <= p->z) && (v2.z > p->z))
1339             /* a downward crossing */
1340             || ((v1.z > p->z) && (v2.z <= p->z))
1341         )
1342         {
1343 
1344           vt = (double)(p->z - v1.z) / (v2.z - v1.z);
1345 
1346           /* P.x <intersect */
1347           if (p->y < v1.y + vt * (v2.y - v1.y))
1348           {
1349             /* a valid crossing of y=p.y right of p.x */
1350             ++cn;
1351           }
1352         }
1353         v1 = v2;
1354       }
1355   }
1356   RTDEBUGF(ctx, 3, "pt_in_ring_3d returning %d", cn&1);
1357 
1358   return (cn&1);    /* 0 if even (out), and 1 if odd (in) */
1359 }
1360 
1361 
1362 
1363 /*------------------------------------------------------------------------------------------------------------
1364 End of Brute force functions
1365 --------------------------------------------------------------------------------------------------------------*/
1366 
1367 
1368 
1369