/src/postgis/liblwgeom/measures3d.c
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * PostGIS - Spatial Types for PostgreSQL |
4 | | * http://postgis.net |
5 | | * |
6 | | * PostGIS 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 | | * PostGIS 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 PostGIS. If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | ********************************************************************** |
20 | | * |
21 | | * Copyright 2011 Nicklas Avén |
22 | | * Copyright 2019 Darafei Praliaskouski |
23 | | * |
24 | | **********************************************************************/ |
25 | | |
26 | | #include <string.h> |
27 | | #include <stdlib.h> |
28 | | |
29 | | #include "measures3d.h" |
30 | | #include "lwrandom.h" |
31 | | #include "lwgeom_log.h" |
32 | | |
33 | | static inline int |
34 | | get_3dvector_from_points(const POINT3DZ *p1, const POINT3DZ *p2, VECTOR3D *v) |
35 | 0 | { |
36 | 0 | v->x = p2->x - p1->x; |
37 | 0 | v->y = p2->y - p1->y; |
38 | 0 | v->z = p2->z - p1->z; |
39 | |
|
40 | 0 | return (!FP_IS_ZERO(v->x) || !FP_IS_ZERO(v->y) || !FP_IS_ZERO(v->z)); |
41 | 0 | } |
42 | | |
43 | | static inline int |
44 | | get_3dcross_product(const VECTOR3D *v1, const VECTOR3D *v2, VECTOR3D *v) |
45 | 0 | { |
46 | 0 | v->x = (v1->y * v2->z) - (v1->z * v2->y); |
47 | 0 | v->y = (v1->z * v2->x) - (v1->x * v2->z); |
48 | 0 | v->z = (v1->x * v2->y) - (v1->y * v2->x); |
49 | |
|
50 | 0 | return (!FP_IS_ZERO(v->x) || !FP_IS_ZERO(v->y) || !FP_IS_ZERO(v->z)); |
51 | 0 | } |
52 | | |
53 | | /** |
54 | | Finds a point on a plane from where the original point is perpendicular to the plane |
55 | | */ |
56 | | static double |
57 | | project_point_on_plane(const POINT3DZ *p, PLANE3D *pl, POINT3DZ *p0) |
58 | 0 | { |
59 | | /*In our plane definition we have a point on the plane and a normal vector (pl.pv), perpendicular to the plane |
60 | | this vector will be parallel to the line between our inputted point above the plane and the point we are |
61 | | searching for on the plane. So, we already have a direction from p to find p0, but we don't know the distance. |
62 | | */ |
63 | |
|
64 | 0 | VECTOR3D v1; |
65 | 0 | double f; |
66 | |
|
67 | 0 | if (!get_3dvector_from_points(&(pl->pop), p, &v1)) |
68 | 0 | return 0.0; |
69 | | |
70 | 0 | f = DOT(pl->pv, v1); |
71 | 0 | if (FP_IS_ZERO(f)) |
72 | 0 | { |
73 | | /* Point is in the plane */ |
74 | 0 | *p0 = *p; |
75 | 0 | return 0; |
76 | 0 | } |
77 | | |
78 | 0 | f = -f / DOT(pl->pv, pl->pv); |
79 | |
|
80 | 0 | p0->x = p->x + pl->pv.x * f; |
81 | 0 | p0->y = p->y + pl->pv.y * f; |
82 | 0 | p0->z = p->z + pl->pv.z * f; |
83 | |
|
84 | 0 | return f; |
85 | 0 | } |
86 | | |
87 | | /** |
88 | | This function is used to create a vertical line used for cases where one if the |
89 | | geometries lacks z-values. The vertical line crosses the 2d point that is closest |
90 | | and the z-range is from maxz to minz in the geometry that has z values. |
91 | | */ |
92 | | static LWGEOM * |
93 | | create_v_line(const LWGEOM *lwgeom, double x, double y, int32_t srid) |
94 | 0 | { |
95 | |
|
96 | 0 | LWPOINT *lwpoints[2]; |
97 | 0 | GBOX gbox; |
98 | 0 | int rv = lwgeom_calculate_gbox(lwgeom, &gbox); |
99 | |
|
100 | 0 | if (rv == LW_FAILURE) |
101 | 0 | return NULL; |
102 | | |
103 | 0 | lwpoints[0] = lwpoint_make3dz(srid, x, y, gbox.zmin); |
104 | 0 | lwpoints[1] = lwpoint_make3dz(srid, x, y, gbox.zmax); |
105 | |
|
106 | 0 | return (LWGEOM *)lwline_from_ptarray(srid, 2, lwpoints); |
107 | 0 | } |
108 | | |
109 | | LWGEOM * |
110 | | lwgeom_closest_line_3d(const LWGEOM *lw1, const LWGEOM *lw2) |
111 | 0 | { |
112 | 0 | return lw_dist3d_distanceline(lw1, lw2, lw1->srid, DIST_MIN); |
113 | 0 | } |
114 | | |
115 | | LWGEOM * |
116 | | lwgeom_furthest_line_3d(LWGEOM *lw1, LWGEOM *lw2) |
117 | 0 | { |
118 | 0 | return lw_dist3d_distanceline(lw1, lw2, lw1->srid, DIST_MAX); |
119 | 0 | } |
120 | | |
121 | | LWGEOM * |
122 | | lwgeom_closest_point_3d(const LWGEOM *lw1, const LWGEOM *lw2) |
123 | 0 | { |
124 | 0 | return lw_dist3d_distancepoint(lw1, lw2, lw1->srid, DIST_MIN); |
125 | 0 | } |
126 | | |
127 | | /** |
128 | | Function initializing 3dshortestline and 3dlongestline calculations. |
129 | | */ |
130 | | LWGEOM * |
131 | | lw_dist3d_distanceline(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode) |
132 | 0 | { |
133 | 0 | LWDEBUG(2, "lw_dist3d_distanceline is called"); |
134 | 0 | double x1, x2, y1, y2, z1, z2, x, y; |
135 | 0 | double initdistance = (mode == DIST_MIN ? DBL_MAX : -1.0); |
136 | 0 | DISTPTS3D thedl; |
137 | 0 | LWPOINT *lwpoints[2]; |
138 | 0 | LWGEOM *result; |
139 | |
|
140 | 0 | thedl.mode = mode; |
141 | 0 | thedl.distance = initdistance; |
142 | 0 | thedl.tolerance = 0.0; |
143 | | |
144 | | /* Check if we really have 3D geometries |
145 | | * If not, send it to 2D-calculations which will give the same result |
146 | | * as an infinite z-value at one or two of the geometries */ |
147 | 0 | if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2)) |
148 | 0 | { |
149 | |
|
150 | 0 | lwnotice( |
151 | 0 | "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); |
152 | |
|
153 | 0 | if (!lwgeom_has_z(lw1) && !lwgeom_has_z(lw2)) |
154 | 0 | return lw_dist2d_distanceline(lw1, lw2, srid, mode); |
155 | | |
156 | 0 | DISTPTS thedl2d; |
157 | 0 | thedl2d.mode = mode; |
158 | 0 | thedl2d.distance = initdistance; |
159 | 0 | thedl2d.tolerance = 0.0; |
160 | 0 | if (!lw_dist2d_comp(lw1, lw2, &thedl2d)) |
161 | 0 | { |
162 | | /* should never get here. all cases ought to be error handled earlier */ |
163 | 0 | lwerror("Some unspecified error."); |
164 | 0 | result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); |
165 | 0 | } |
166 | 0 | LWGEOM *vertical_line; |
167 | 0 | if (!lwgeom_has_z(lw1)) |
168 | 0 | { |
169 | 0 | x = thedl2d.p1.x; |
170 | 0 | y = thedl2d.p1.y; |
171 | |
|
172 | 0 | vertical_line = create_v_line(lw2, x, y, srid); |
173 | 0 | if (!lw_dist3d_recursive(vertical_line, lw2, &thedl)) |
174 | 0 | { |
175 | | /* should never get here. all cases ought to be error handled earlier */ |
176 | 0 | lwfree(vertical_line); |
177 | 0 | lwerror("Some unspecified error."); |
178 | 0 | result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); |
179 | 0 | } |
180 | 0 | lwfree(vertical_line); |
181 | 0 | } |
182 | 0 | if (!lwgeom_has_z(lw2)) |
183 | 0 | { |
184 | 0 | x = thedl2d.p2.x; |
185 | 0 | y = thedl2d.p2.y; |
186 | |
|
187 | 0 | vertical_line = create_v_line(lw1, x, y, srid); |
188 | 0 | if (!lw_dist3d_recursive(lw1, vertical_line, &thedl)) |
189 | 0 | { |
190 | | /* should never get here. all cases ought to be error handled earlier */ |
191 | 0 | lwfree(vertical_line); |
192 | 0 | lwerror("Some unspecified error."); |
193 | 0 | return (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); |
194 | 0 | } |
195 | 0 | lwfree(vertical_line); |
196 | 0 | } |
197 | 0 | } |
198 | 0 | else |
199 | 0 | { |
200 | 0 | if (!lw_dist3d_recursive(lw1, lw2, &thedl)) |
201 | 0 | { |
202 | | /* should never get here. all cases ought to be error handled earlier */ |
203 | 0 | lwerror("Some unspecified error."); |
204 | 0 | result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); |
205 | 0 | } |
206 | 0 | } |
207 | | /*if thedl.distance is unchanged there where only empty geometries input*/ |
208 | 0 | if (thedl.distance == initdistance) |
209 | 0 | { |
210 | 0 | LWDEBUG(3, "didn't find geometries to measure between, returning null"); |
211 | 0 | result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); |
212 | 0 | } |
213 | 0 | else |
214 | 0 | { |
215 | 0 | x1 = thedl.p1.x; |
216 | 0 | y1 = thedl.p1.y; |
217 | 0 | z1 = thedl.p1.z; |
218 | 0 | x2 = thedl.p2.x; |
219 | 0 | y2 = thedl.p2.y; |
220 | 0 | z2 = thedl.p2.z; |
221 | |
|
222 | 0 | lwpoints[0] = lwpoint_make3dz(srid, x1, y1, z1); |
223 | 0 | lwpoints[1] = lwpoint_make3dz(srid, x2, y2, z2); |
224 | |
|
225 | 0 | result = (LWGEOM *)lwline_from_ptarray(srid, 2, lwpoints); |
226 | 0 | } |
227 | |
|
228 | 0 | return result; |
229 | 0 | } |
230 | | |
231 | | /** |
232 | | Function initializing 3dclosestpoint calculations. |
233 | | */ |
234 | | LWGEOM * |
235 | | lw_dist3d_distancepoint(const LWGEOM *lw1, const LWGEOM *lw2, int32_t srid, int mode) |
236 | 0 | { |
237 | |
|
238 | 0 | double x, y, z; |
239 | 0 | DISTPTS3D thedl; |
240 | 0 | double initdistance = DBL_MAX; |
241 | 0 | LWGEOM *result; |
242 | |
|
243 | 0 | thedl.mode = mode; |
244 | 0 | thedl.distance = initdistance; |
245 | 0 | thedl.tolerance = 0; |
246 | |
|
247 | 0 | LWDEBUG(2, "lw_dist3d_distancepoint is called"); |
248 | | |
249 | | /* Check if we really have 3D geometries |
250 | | * If not, send it to 2D-calculations which will give the same result |
251 | | * as an infinite z-value at one or two of the geometries |
252 | | */ |
253 | 0 | if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2)) |
254 | 0 | { |
255 | 0 | lwnotice( |
256 | 0 | "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); |
257 | |
|
258 | 0 | if (!lwgeom_has_z(lw1) && !lwgeom_has_z(lw2)) |
259 | 0 | return lw_dist2d_distancepoint(lw1, lw2, srid, mode); |
260 | | |
261 | 0 | DISTPTS thedl2d; |
262 | 0 | thedl2d.mode = mode; |
263 | 0 | thedl2d.distance = initdistance; |
264 | 0 | thedl2d.tolerance = 0.0; |
265 | 0 | if (!lw_dist2d_comp(lw1, lw2, &thedl2d)) |
266 | 0 | { |
267 | | /* should never get here. all cases ought to be error handled earlier */ |
268 | 0 | lwerror("Some unspecified error."); |
269 | 0 | return (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); |
270 | 0 | } |
271 | | |
272 | 0 | LWGEOM *vertical_line; |
273 | 0 | if (!lwgeom_has_z(lw1)) |
274 | 0 | { |
275 | 0 | x = thedl2d.p1.x; |
276 | 0 | y = thedl2d.p1.y; |
277 | |
|
278 | 0 | vertical_line = create_v_line(lw2, x, y, srid); |
279 | 0 | if (!lw_dist3d_recursive(vertical_line, lw2, &thedl)) |
280 | 0 | { |
281 | | /* should never get here. all cases ought to be error handled earlier */ |
282 | 0 | lwfree(vertical_line); |
283 | 0 | lwerror("Some unspecified error."); |
284 | 0 | return (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); |
285 | 0 | } |
286 | 0 | lwfree(vertical_line); |
287 | 0 | } |
288 | | |
289 | 0 | if (!lwgeom_has_z(lw2)) |
290 | 0 | { |
291 | 0 | x = thedl2d.p2.x; |
292 | 0 | y = thedl2d.p2.y; |
293 | |
|
294 | 0 | vertical_line = create_v_line(lw1, x, y, srid); |
295 | 0 | if (!lw_dist3d_recursive(lw1, vertical_line, &thedl)) |
296 | 0 | { |
297 | | /* should never get here. all cases ought to be error handled earlier */ |
298 | 0 | lwfree(vertical_line); |
299 | 0 | lwerror("Some unspecified error."); |
300 | 0 | result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); |
301 | 0 | } |
302 | 0 | lwfree(vertical_line); |
303 | 0 | } |
304 | 0 | } |
305 | 0 | else |
306 | 0 | { |
307 | 0 | if (!lw_dist3d_recursive(lw1, lw2, &thedl)) |
308 | 0 | { |
309 | | /* should never get here. all cases ought to be error handled earlier */ |
310 | 0 | lwerror("Some unspecified error."); |
311 | 0 | result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); |
312 | 0 | } |
313 | 0 | } |
314 | 0 | if (thedl.distance == initdistance) |
315 | 0 | { |
316 | 0 | LWDEBUG(3, "didn't find geometries to measure between, returning null"); |
317 | 0 | result = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, srid, 0, 0); |
318 | 0 | } |
319 | 0 | else |
320 | 0 | { |
321 | 0 | x = thedl.p1.x; |
322 | 0 | y = thedl.p1.y; |
323 | 0 | z = thedl.p1.z; |
324 | 0 | result = (LWGEOM *)lwpoint_make3dz(srid, x, y, z); |
325 | 0 | } |
326 | |
|
327 | 0 | return result; |
328 | 0 | } |
329 | | |
330 | | /** |
331 | | Function initializing 3d max distance calculation |
332 | | */ |
333 | | double |
334 | | lwgeom_maxdistance3d(const LWGEOM *lw1, const LWGEOM *lw2) |
335 | 0 | { |
336 | 0 | return lwgeom_maxdistance3d_tolerance(lw1, lw2, 0.0); |
337 | 0 | } |
338 | | |
339 | | /** |
340 | | Function handling 3d max distance calculations and dfullywithin calculations. |
341 | | The difference is just the tolerance. |
342 | | */ |
343 | | double |
344 | | lwgeom_maxdistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance) |
345 | 0 | { |
346 | 0 | if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2)) |
347 | 0 | { |
348 | 0 | lwnotice( |
349 | 0 | "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); |
350 | 0 | return lwgeom_maxdistance2d_tolerance(lw1, lw2, tolerance); |
351 | 0 | } |
352 | 0 | DISTPTS3D thedl; |
353 | 0 | LWDEBUG(2, "lwgeom_maxdistance3d_tolerance is called"); |
354 | 0 | thedl.mode = DIST_MAX; |
355 | 0 | thedl.distance = -1; |
356 | 0 | thedl.tolerance = tolerance; |
357 | 0 | if (lw_dist3d_recursive(lw1, lw2, &thedl)) |
358 | 0 | return thedl.distance; |
359 | | |
360 | | /* should never get here. all cases ought to be error handled earlier */ |
361 | 0 | lwerror("Some unspecified error."); |
362 | 0 | return -1; |
363 | 0 | } |
364 | | |
365 | | /** |
366 | | Function initializing 3d min distance calculation |
367 | | */ |
368 | | double |
369 | | lwgeom_mindistance3d(const LWGEOM *lw1, const LWGEOM *lw2) |
370 | 0 | { |
371 | 0 | return lwgeom_mindistance3d_tolerance(lw1, lw2, 0.0); |
372 | 0 | } |
373 | | |
374 | | static inline int |
375 | | gbox_contains_3d(const GBOX *g1, const GBOX *g2) |
376 | 0 | { |
377 | 0 | return (g2->xmin >= g1->xmin) && (g2->xmax <= g1->xmax) && (g2->ymin >= g1->ymin) && (g2->ymax <= g1->ymax) && |
378 | 0 | (g2->zmin >= g1->zmin) && (g2->zmax <= g1->zmax); |
379 | 0 | } |
380 | | |
381 | | static inline int |
382 | | lwgeom_solid_contains_lwgeom(const LWGEOM *solid, const LWGEOM *g) |
383 | 0 | { |
384 | 0 | const GBOX *b1; |
385 | 0 | const GBOX *b2; |
386 | | |
387 | | /* If solid isn't solid it can't contain anything */ |
388 | 0 | if (!FLAGS_GET_SOLID(solid->flags)) |
389 | 0 | return LW_FALSE; |
390 | | |
391 | 0 | b1 = lwgeom_get_bbox(solid); |
392 | 0 | b2 = lwgeom_get_bbox(g); |
393 | | |
394 | | /* If box won't contain box, shape won't contain shape */ |
395 | 0 | if (!gbox_contains_3d(b1, b2)) |
396 | 0 | return LW_FALSE; |
397 | 0 | else /* Raycast upwards in Z */ |
398 | 0 | { |
399 | 0 | POINT4D pt; |
400 | | /* We'll skew copies if we're not lucky */ |
401 | 0 | LWGEOM *solid_copy = lwgeom_clone_deep(solid); |
402 | 0 | LWGEOM *g_copy = lwgeom_clone_deep(g); |
403 | |
|
404 | 0 | while (LW_TRUE) |
405 | 0 | { |
406 | 0 | uint8_t is_boundary = LW_FALSE; |
407 | 0 | uint8_t is_inside = LW_FALSE; |
408 | | |
409 | | /* take first point */ |
410 | 0 | if (!lwgeom_startpoint(g_copy, &pt)) |
411 | 0 | return LW_FALSE; |
412 | | |
413 | | /* get part of solid that is upwards */ |
414 | 0 | LWCOLLECTION *c = lwgeom_clip_to_ordinate_range(solid_copy, 'Z', pt.z, DBL_MAX, 0); |
415 | |
|
416 | 0 | for (uint32_t i = 0; i < c->ngeoms; i++) |
417 | 0 | { |
418 | 0 | if (c->geoms[i]->type == POLYGONTYPE) |
419 | 0 | { |
420 | | /* 3D raycast along Z is 2D point in polygon */ |
421 | 0 | int t = lwpoly_contains_point((LWPOLY *)c->geoms[i], (POINT2D *)&pt); |
422 | |
|
423 | 0 | if (t == LW_INSIDE) |
424 | 0 | is_inside = !is_inside; |
425 | 0 | else if (t == LW_BOUNDARY) |
426 | 0 | { |
427 | 0 | is_boundary = LW_TRUE; |
428 | 0 | break; |
429 | 0 | } |
430 | 0 | } |
431 | 0 | else if (c->geoms[i]->type == TRIANGLETYPE) |
432 | 0 | { |
433 | | /* 3D raycast along Z is 2D point in polygon */ |
434 | 0 | LWTRIANGLE *tri = (LWTRIANGLE *)c->geoms[i]; |
435 | 0 | int t = ptarray_contains_point(tri->points, (POINT2D *)&pt); |
436 | |
|
437 | 0 | if (t == LW_INSIDE) |
438 | 0 | is_inside = !is_inside; |
439 | 0 | else if (t == LW_BOUNDARY) |
440 | 0 | { |
441 | 0 | is_boundary = LW_TRUE; |
442 | 0 | break; |
443 | 0 | } |
444 | 0 | } |
445 | 0 | } |
446 | |
|
447 | 0 | lwcollection_free(c); |
448 | 0 | lwgeom_free(solid_copy); |
449 | 0 | lwgeom_free(g_copy); |
450 | |
|
451 | 0 | if (is_boundary) |
452 | 0 | { |
453 | | /* randomly skew a bit and restart*/ |
454 | 0 | double cx = lwrandom_uniform() - 0.5; |
455 | 0 | double cy = lwrandom_uniform() - 0.5; |
456 | 0 | AFFINE aff = {1, 0, cx, 0, 1, cy, 0, 0, 1, 0, 0, 0}; |
457 | |
|
458 | 0 | solid_copy = lwgeom_clone_deep(solid); |
459 | 0 | lwgeom_affine(solid_copy, &aff); |
460 | |
|
461 | 0 | g_copy = lwgeom_clone_deep(g); |
462 | 0 | lwgeom_affine(g_copy, &aff); |
463 | |
|
464 | 0 | continue; |
465 | 0 | } |
466 | 0 | return is_inside; |
467 | 0 | } |
468 | 0 | } |
469 | 0 | } |
470 | | |
471 | | /** |
472 | | Function handling 3d min distance calculations and dwithin calculations. |
473 | | The difference is just the tolerance. |
474 | | */ |
475 | | double |
476 | | lwgeom_mindistance3d_tolerance(const LWGEOM *lw1, const LWGEOM *lw2, double tolerance) |
477 | 0 | { |
478 | 0 | assert(tolerance >= 0); |
479 | 0 | if (!lwgeom_has_z(lw1) || !lwgeom_has_z(lw2)) |
480 | 0 | { |
481 | 0 | lwnotice( |
482 | 0 | "One or both of the geometries is missing z-value. The unknown z-value will be regarded as \"any value\""); |
483 | |
|
484 | 0 | return lwgeom_mindistance2d_tolerance(lw1, lw2, tolerance); |
485 | 0 | } |
486 | 0 | DISTPTS3D thedl; |
487 | 0 | thedl.mode = DIST_MIN; |
488 | 0 | thedl.distance = DBL_MAX; |
489 | 0 | thedl.tolerance = tolerance; |
490 | |
|
491 | 0 | if (lw_dist3d_recursive(lw1, lw2, &thedl)) |
492 | 0 | { |
493 | 0 | if (thedl.distance <= tolerance) |
494 | 0 | return thedl.distance; |
495 | 0 | if (lwgeom_solid_contains_lwgeom(lw1, lw2) || lwgeom_solid_contains_lwgeom(lw2, lw1)) |
496 | 0 | return 0; |
497 | | |
498 | 0 | return thedl.distance; |
499 | 0 | } |
500 | | |
501 | | /* should never get here. all cases ought to be error handled earlier */ |
502 | 0 | lwerror("Some unspecified error."); |
503 | 0 | return DBL_MAX; |
504 | 0 | } |
505 | | |
506 | | /*------------------------------------------------------------------------------------------------------------ |
507 | | End of Initializing functions |
508 | | --------------------------------------------------------------------------------------------------------------*/ |
509 | | |
510 | | /*------------------------------------------------------------------------------------------------------------ |
511 | | Preprocessing functions |
512 | | Functions preparing geometries for distance-calculations |
513 | | --------------------------------------------------------------------------------------------------------------*/ |
514 | | |
515 | | /** |
516 | | This is a recursive function delivering every possible combination of subgeometries |
517 | | */ |
518 | | int |
519 | | lw_dist3d_recursive(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl) |
520 | 0 | { |
521 | 0 | int i, j; |
522 | 0 | int n1 = 1; |
523 | 0 | int n2 = 1; |
524 | 0 | LWGEOM *g1 = NULL; |
525 | 0 | LWGEOM *g2 = NULL; |
526 | 0 | LWCOLLECTION *c1 = NULL; |
527 | 0 | LWCOLLECTION *c2 = NULL; |
528 | |
|
529 | 0 | LWDEBUGF(2, "lw_dist3d_recursive is called with type1=%d, type2=%d", lwg1->type, lwg2->type); |
530 | |
|
531 | 0 | if (lwgeom_is_collection(lwg1)) |
532 | 0 | { |
533 | 0 | LWDEBUG(3, "First geometry is collection"); |
534 | 0 | c1 = lwgeom_as_lwcollection(lwg1); |
535 | 0 | n1 = c1->ngeoms; |
536 | 0 | } |
537 | 0 | if (lwgeom_is_collection(lwg2)) |
538 | 0 | { |
539 | 0 | LWDEBUG(3, "Second geometry is collection"); |
540 | 0 | c2 = lwgeom_as_lwcollection(lwg2); |
541 | 0 | n2 = c2->ngeoms; |
542 | 0 | } |
543 | |
|
544 | 0 | for (i = 0; i < n1; i++) |
545 | 0 | { |
546 | 0 | if (lwgeom_is_collection(lwg1)) |
547 | 0 | g1 = c1->geoms[i]; |
548 | 0 | else |
549 | 0 | g1 = (LWGEOM *)lwg1; |
550 | |
|
551 | 0 | if (lwgeom_is_empty(g1)) |
552 | 0 | continue; |
553 | | |
554 | 0 | if (lwgeom_is_collection(g1)) |
555 | 0 | { |
556 | 0 | LWDEBUG(3, "Found collection inside first geometry collection, recursing"); |
557 | 0 | if (!lw_dist3d_recursive(g1, lwg2, dl)) |
558 | 0 | return LW_FALSE; |
559 | 0 | continue; |
560 | 0 | } |
561 | 0 | for (j = 0; j < n2; j++) |
562 | 0 | { |
563 | 0 | if (lwgeom_is_collection(lwg2)) |
564 | 0 | g2 = c2->geoms[j]; |
565 | 0 | else |
566 | 0 | g2 = (LWGEOM *)lwg2; |
567 | |
|
568 | 0 | if (lwgeom_is_empty(g2)) |
569 | 0 | continue; |
570 | | |
571 | 0 | if (lwgeom_is_collection(g2)) |
572 | 0 | { |
573 | 0 | LWDEBUG(3, "Found collection inside second geometry collection, recursing"); |
574 | 0 | if (!lw_dist3d_recursive(g1, g2, dl)) |
575 | 0 | return LW_FALSE; |
576 | 0 | continue; |
577 | 0 | } |
578 | | |
579 | | /*If one of geometries is empty, return. True here only means continue searching. False would |
580 | | * have stopped the process*/ |
581 | 0 | if (lwgeom_is_empty(g1) || lwgeom_is_empty(g2)) |
582 | 0 | return LW_TRUE; |
583 | | |
584 | 0 | if (!lw_dist3d_distribute_bruteforce(g1, g2, dl)) |
585 | 0 | return LW_FALSE; |
586 | 0 | if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) |
587 | 0 | return LW_TRUE; /*just a check if the answer is already given*/ |
588 | 0 | } |
589 | 0 | } |
590 | 0 | return LW_TRUE; |
591 | 0 | } |
592 | | |
593 | | /** |
594 | | |
595 | | This function distributes the brute-force for 3D so far the only type, tasks depending on type |
596 | | */ |
597 | | int |
598 | | lw_dist3d_distribute_bruteforce(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl) |
599 | 0 | { |
600 | 0 | int t1 = lwg1->type; |
601 | 0 | int t2 = lwg2->type; |
602 | |
|
603 | 0 | LWDEBUGF(2, "lw_dist3d_distribute_bruteforce is called with typ1=%d, type2=%d", lwg1->type, lwg2->type); |
604 | |
|
605 | 0 | if (t1 == POINTTYPE) |
606 | 0 | { |
607 | 0 | if (t2 == POINTTYPE) |
608 | 0 | { |
609 | 0 | dl->twisted = 1; |
610 | 0 | return lw_dist3d_point_point((LWPOINT *)lwg1, (LWPOINT *)lwg2, dl); |
611 | 0 | } |
612 | 0 | else if (t2 == LINETYPE) |
613 | 0 | { |
614 | 0 | dl->twisted = 1; |
615 | 0 | return lw_dist3d_point_line((LWPOINT *)lwg1, (LWLINE *)lwg2, dl); |
616 | 0 | } |
617 | 0 | else if (t2 == POLYGONTYPE) |
618 | 0 | { |
619 | 0 | dl->twisted = 1; |
620 | 0 | return lw_dist3d_point_poly((LWPOINT *)lwg1, (LWPOLY *)lwg2, dl); |
621 | 0 | } |
622 | 0 | else if (t2 == TRIANGLETYPE) |
623 | 0 | { |
624 | 0 | dl->twisted = 1; |
625 | 0 | return lw_dist3d_point_tri((LWPOINT *)lwg1, (LWTRIANGLE *)lwg2, dl); |
626 | 0 | } |
627 | 0 | else |
628 | 0 | { |
629 | 0 | lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); |
630 | 0 | return LW_FALSE; |
631 | 0 | } |
632 | 0 | } |
633 | 0 | else if (t1 == LINETYPE) |
634 | 0 | { |
635 | 0 | if (t2 == POINTTYPE) |
636 | 0 | { |
637 | 0 | dl->twisted = -1; |
638 | 0 | return lw_dist3d_point_line((LWPOINT *)lwg2, (LWLINE *)lwg1, dl); |
639 | 0 | } |
640 | 0 | else if (t2 == LINETYPE) |
641 | 0 | { |
642 | 0 | dl->twisted = 1; |
643 | 0 | return lw_dist3d_line_line((LWLINE *)lwg1, (LWLINE *)lwg2, dl); |
644 | 0 | } |
645 | 0 | else if (t2 == POLYGONTYPE) |
646 | 0 | { |
647 | 0 | dl->twisted = 1; |
648 | 0 | return lw_dist3d_line_poly((LWLINE *)lwg1, (LWPOLY *)lwg2, dl); |
649 | 0 | } |
650 | 0 | else if (t2 == TRIANGLETYPE) |
651 | 0 | { |
652 | 0 | dl->twisted = 1; |
653 | 0 | return lw_dist3d_line_tri((LWLINE *)lwg1, (LWTRIANGLE *)lwg2, dl); |
654 | 0 | } |
655 | 0 | else |
656 | 0 | { |
657 | 0 | lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); |
658 | 0 | return LW_FALSE; |
659 | 0 | } |
660 | 0 | } |
661 | 0 | else if (t1 == POLYGONTYPE) |
662 | 0 | { |
663 | 0 | if (t2 == POLYGONTYPE) |
664 | 0 | { |
665 | 0 | dl->twisted = 1; |
666 | 0 | return lw_dist3d_poly_poly((LWPOLY *)lwg1, (LWPOLY *)lwg2, dl); |
667 | 0 | } |
668 | 0 | else if (t2 == POINTTYPE) |
669 | 0 | { |
670 | 0 | dl->twisted = -1; |
671 | 0 | return lw_dist3d_point_poly((LWPOINT *)lwg2, (LWPOLY *)lwg1, dl); |
672 | 0 | } |
673 | 0 | else if (t2 == LINETYPE) |
674 | 0 | { |
675 | 0 | dl->twisted = -1; |
676 | 0 | return lw_dist3d_line_poly((LWLINE *)lwg2, (LWPOLY *)lwg1, dl); |
677 | 0 | } |
678 | 0 | else if (t2 == TRIANGLETYPE) |
679 | 0 | { |
680 | 0 | dl->twisted = 1; |
681 | 0 | return lw_dist3d_poly_tri((LWPOLY *)lwg1, (LWTRIANGLE *)lwg2, dl); |
682 | 0 | } |
683 | 0 | else |
684 | 0 | { |
685 | 0 | lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); |
686 | 0 | return LW_FALSE; |
687 | 0 | } |
688 | 0 | } |
689 | 0 | else if (t1 == TRIANGLETYPE) |
690 | 0 | { |
691 | 0 | if (t2 == POLYGONTYPE) |
692 | 0 | { |
693 | 0 | dl->twisted = -1; |
694 | 0 | return lw_dist3d_poly_tri((LWPOLY *)lwg2, (LWTRIANGLE *)lwg1, dl); |
695 | 0 | } |
696 | 0 | else if (t2 == POINTTYPE) |
697 | 0 | { |
698 | 0 | dl->twisted = -1; |
699 | 0 | return lw_dist3d_point_tri((LWPOINT *)lwg2, (LWTRIANGLE *)lwg1, dl); |
700 | 0 | } |
701 | 0 | else if (t2 == LINETYPE) |
702 | 0 | { |
703 | 0 | dl->twisted = -1; |
704 | 0 | return lw_dist3d_line_tri((LWLINE *)lwg2, (LWTRIANGLE *)lwg1, dl); |
705 | 0 | } |
706 | 0 | else if (t2 == TRIANGLETYPE) |
707 | 0 | { |
708 | 0 | dl->twisted = 1; |
709 | 0 | return lw_dist3d_tri_tri((LWTRIANGLE *)lwg1, (LWTRIANGLE *)lwg2, dl); |
710 | 0 | } |
711 | 0 | else |
712 | 0 | { |
713 | 0 | lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t2)); |
714 | 0 | return LW_FALSE; |
715 | 0 | } |
716 | 0 | } |
717 | | |
718 | 0 | else |
719 | 0 | { |
720 | 0 | lwerror("%s: Unsupported geometry type: %s", __func__, lwtype_name(t1)); |
721 | 0 | return LW_FALSE; |
722 | 0 | } |
723 | 0 | } |
724 | | |
725 | | /*------------------------------------------------------------------------------------------------------------ |
726 | | End of Preprocessing functions |
727 | | --------------------------------------------------------------------------------------------------------------*/ |
728 | | |
729 | | /*------------------------------------------------------------------------------------------------------------ |
730 | | Brute force functions |
731 | | So far the only way to do 3D-calculations |
732 | | --------------------------------------------------------------------------------------------------------------*/ |
733 | | |
734 | | /** |
735 | | |
736 | | point to point calculation |
737 | | */ |
738 | | int |
739 | | lw_dist3d_point_point(const LWPOINT *point1, const LWPOINT *point2, DISTPTS3D *dl) |
740 | 0 | { |
741 | 0 | POINT3DZ p1; |
742 | 0 | POINT3DZ p2; |
743 | 0 | LWDEBUG(2, "lw_dist3d_point_point is called"); |
744 | |
|
745 | 0 | getPoint3dz_p(point1->point, 0, &p1); |
746 | 0 | getPoint3dz_p(point2->point, 0, &p2); |
747 | |
|
748 | 0 | return lw_dist3d_pt_pt(&p1, &p2, dl); |
749 | 0 | } |
750 | | /** |
751 | | |
752 | | point to line calculation |
753 | | */ |
754 | | int |
755 | | lw_dist3d_point_line(const LWPOINT *point, const LWLINE *line, DISTPTS3D *dl) |
756 | 0 | { |
757 | 0 | POINT3DZ p; |
758 | 0 | POINTARRAY *pa = line->points; |
759 | 0 | LWDEBUG(2, "lw_dist3d_point_line is called"); |
760 | |
|
761 | 0 | getPoint3dz_p(point->point, 0, &p); |
762 | 0 | return lw_dist3d_pt_ptarray(&p, pa, dl); |
763 | 0 | } |
764 | | |
765 | | /** |
766 | | |
767 | | Computes point to polygon distance |
768 | | For mindistance that means: |
769 | | 1) find the plane of the polygon |
770 | | 2) projecting the point to the plane of the polygon |
771 | | 3) finding if that projected point is inside the polygon, if so the distance is measured to that projected point |
772 | | 4) if not in polygon above, check the distance against the boundary of the polygon |
773 | | for max distance it is always point against boundary |
774 | | |
775 | | */ |
776 | | |
777 | | int |
778 | | lw_dist3d_point_poly(const LWPOINT *point, const LWPOLY *poly, DISTPTS3D *dl) |
779 | 0 | { |
780 | 0 | POINT3DZ p, projp; /*projp is "point projected on plane"*/ |
781 | 0 | PLANE3D plane; |
782 | 0 | LWDEBUG(2, "lw_dist3d_point_poly is called"); |
783 | 0 | getPoint3dz_p(point->point, 0, &p); |
784 | | |
785 | | /* If we are looking for max distance, longestline or dfullywithin */ |
786 | 0 | if (dl->mode == DIST_MAX) |
787 | 0 | return lw_dist3d_pt_ptarray(&p, poly->rings[0], dl); |
788 | | |
789 | | /* Find the plane of the polygon, the "holes" have to be on the same plane. so we only care about the boundary */ |
790 | 0 | if (!define_plane(poly->rings[0], &plane)) |
791 | 0 | { |
792 | | /* Polygon does not define a plane: Return distance point -> line */ |
793 | 0 | return lw_dist3d_pt_ptarray(&p, poly->rings[0], dl); |
794 | 0 | } |
795 | | |
796 | | /* Get our point projected on the plane of the polygon */ |
797 | 0 | project_point_on_plane(&p, &plane, &projp); |
798 | |
|
799 | 0 | return lw_dist3d_pt_poly(&p, poly, &plane, &projp, dl); |
800 | 0 | } |
801 | | |
802 | | /* point to triangle calculation */ |
803 | | int |
804 | | lw_dist3d_point_tri(const LWPOINT *point, const LWTRIANGLE *tri, DISTPTS3D *dl) |
805 | 0 | { |
806 | 0 | POINT3DZ p, projp; /*projp is "point projected on plane"*/ |
807 | 0 | PLANE3D plane; |
808 | 0 | getPoint3dz_p(point->point, 0, &p); |
809 | | |
810 | | /* If we are looking for max distance, longestline or dfullywithin */ |
811 | 0 | if (dl->mode == DIST_MAX) |
812 | 0 | return lw_dist3d_pt_ptarray(&p, tri->points, dl); |
813 | | |
814 | | /* If triangle does not define a plane, treat it as a line */ |
815 | 0 | if (!define_plane(tri->points, &plane)) |
816 | 0 | return lw_dist3d_pt_ptarray(&p, tri->points, dl); |
817 | | |
818 | | /* Get our point projected on the plane of triangle */ |
819 | 0 | project_point_on_plane(&p, &plane, &projp); |
820 | |
|
821 | 0 | return lw_dist3d_pt_tri(&p, tri, &plane, &projp, dl); |
822 | 0 | } |
823 | | |
824 | | /** line to line calculation */ |
825 | | int |
826 | | lw_dist3d_line_line(const LWLINE *line1, const LWLINE *line2, DISTPTS3D *dl) |
827 | 0 | { |
828 | 0 | POINTARRAY *pa1 = line1->points; |
829 | 0 | POINTARRAY *pa2 = line2->points; |
830 | 0 | LWDEBUG(2, "lw_dist3d_line_line is called"); |
831 | |
|
832 | 0 | return lw_dist3d_ptarray_ptarray(pa1, pa2, dl); |
833 | 0 | } |
834 | | |
835 | | /** line to polygon calculation */ |
836 | | int |
837 | | lw_dist3d_line_poly(const LWLINE *line, const LWPOLY *poly, DISTPTS3D *dl) |
838 | 0 | { |
839 | 0 | PLANE3D plane; |
840 | 0 | LWDEBUG(2, "lw_dist3d_line_poly is called"); |
841 | |
|
842 | 0 | if (dl->mode == DIST_MAX) |
843 | 0 | return lw_dist3d_ptarray_ptarray(line->points, poly->rings[0], dl); |
844 | | |
845 | | /* if polygon does not define a plane: Return distance line to line */ |
846 | 0 | if (!define_plane(poly->rings[0], &plane)) |
847 | 0 | return lw_dist3d_ptarray_ptarray(line->points, poly->rings[0], dl); |
848 | | |
849 | 0 | return lw_dist3d_ptarray_poly(line->points, poly, &plane, dl); |
850 | 0 | } |
851 | | |
852 | | /** line to triangle calculation */ |
853 | | int |
854 | | lw_dist3d_line_tri(const LWLINE *line, const LWTRIANGLE *tri, DISTPTS3D *dl) |
855 | 0 | { |
856 | 0 | PLANE3D plane; |
857 | |
|
858 | 0 | if (dl->mode == DIST_MAX) |
859 | 0 | return lw_dist3d_ptarray_ptarray(line->points, tri->points, dl); |
860 | | |
861 | | /* if triangle does not define a plane: Return distance line to line */ |
862 | 0 | if (!define_plane(tri->points, &plane)) |
863 | 0 | return lw_dist3d_ptarray_ptarray(line->points, tri->points, dl); |
864 | | |
865 | 0 | return lw_dist3d_ptarray_tri(line->points, tri, &plane, dl); |
866 | 0 | } |
867 | | |
868 | | /** polygon to polygon calculation */ |
869 | | int |
870 | | lw_dist3d_poly_poly(const LWPOLY *poly1, const LWPOLY *poly2, DISTPTS3D *dl) |
871 | 0 | { |
872 | 0 | PLANE3D plane1, plane2; |
873 | 0 | int planedef1, planedef2; |
874 | 0 | LWDEBUG(2, "lw_dist3d_poly_poly is called"); |
875 | 0 | if (dl->mode == DIST_MAX) |
876 | 0 | return lw_dist3d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl); |
877 | | |
878 | 0 | planedef1 = define_plane(poly1->rings[0], &plane1); |
879 | 0 | planedef2 = define_plane(poly2->rings[0], &plane2); |
880 | |
|
881 | 0 | if (!planedef1 || !planedef2) |
882 | 0 | { |
883 | | /* Neither polygon define a plane: Return distance line to line */ |
884 | 0 | if (!planedef1 && !planedef2) |
885 | 0 | return lw_dist3d_ptarray_ptarray(poly1->rings[0], poly2->rings[0], dl); |
886 | | |
887 | | /* Only poly2 defines a plane: Return distance from line (poly1) to poly2 */ |
888 | 0 | else if (!planedef1) |
889 | 0 | return lw_dist3d_ptarray_poly(poly1->rings[0], poly2, &plane2, dl); |
890 | | |
891 | | /* Only poly1 defines a plane: Return distance from line (poly2) to poly1 */ |
892 | 0 | else |
893 | 0 | return lw_dist3d_ptarray_poly(poly2->rings[0], poly1, &plane1, dl); |
894 | 0 | } |
895 | | |
896 | | /* What we do here is to compare the boundary of one polygon with the other polygon |
897 | | and then take the second boundary comparing with the first polygon */ |
898 | 0 | dl->twisted = 1; |
899 | 0 | if (!lw_dist3d_ptarray_poly(poly1->rings[0], poly2, &plane2, dl)) |
900 | 0 | return LW_FALSE; |
901 | 0 | if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/ |
902 | 0 | return LW_TRUE; |
903 | | |
904 | 0 | dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the |
905 | | right order of points in shortest line. */ |
906 | 0 | return lw_dist3d_ptarray_poly(poly2->rings[0], poly1, &plane1, dl); |
907 | 0 | } |
908 | | |
909 | | /** polygon to triangle calculation */ |
910 | | int |
911 | | lw_dist3d_poly_tri(const LWPOLY *poly, const LWTRIANGLE *tri, DISTPTS3D *dl) |
912 | 0 | { |
913 | 0 | PLANE3D plane1, plane2; |
914 | 0 | int planedef1, planedef2; |
915 | |
|
916 | 0 | if (dl->mode == DIST_MAX) |
917 | 0 | return lw_dist3d_ptarray_ptarray(poly->rings[0], tri->points, dl); |
918 | | |
919 | 0 | planedef1 = define_plane(poly->rings[0], &plane1); |
920 | 0 | planedef2 = define_plane(tri->points, &plane2); |
921 | |
|
922 | 0 | if (!planedef1 || !planedef2) |
923 | 0 | { |
924 | | /* Neither define a plane: Return distance line to line */ |
925 | 0 | if (!planedef1 && !planedef2) |
926 | 0 | return lw_dist3d_ptarray_ptarray(poly->rings[0], tri->points, dl); |
927 | | |
928 | | /* Only tri defines a plane: Return distance from line (poly) to tri */ |
929 | 0 | else if (!planedef1) |
930 | 0 | return lw_dist3d_ptarray_tri(poly->rings[0], tri, &plane2, dl); |
931 | | |
932 | | /* Only poly defines a plane: Return distance from line (tri) to poly */ |
933 | 0 | else |
934 | 0 | return lw_dist3d_ptarray_poly(tri->points, poly, &plane1, dl); |
935 | 0 | } |
936 | | |
937 | | /* What we do here is to compare the boundary of one polygon with the other polygon |
938 | | and then take the second boundary comparing with the first polygon */ |
939 | 0 | dl->twisted = 1; |
940 | 0 | if (!lw_dist3d_ptarray_tri(poly->rings[0], tri, &plane2, dl)) |
941 | 0 | return LW_FALSE; |
942 | 0 | if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/ |
943 | 0 | return LW_TRUE; |
944 | | |
945 | 0 | dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the |
946 | | right order of points in shortest line. */ |
947 | 0 | return lw_dist3d_ptarray_poly(tri->points, poly, &plane1, dl); |
948 | 0 | } |
949 | | |
950 | | /** triangle to triangle calculation */ |
951 | | int |
952 | | lw_dist3d_tri_tri(const LWTRIANGLE *tri1, const LWTRIANGLE *tri2, DISTPTS3D *dl) |
953 | 0 | { |
954 | 0 | PLANE3D plane1, plane2; |
955 | 0 | int planedef1, planedef2; |
956 | |
|
957 | 0 | if (dl->mode == DIST_MAX) |
958 | 0 | return lw_dist3d_ptarray_ptarray(tri1->points, tri2->points, dl); |
959 | | |
960 | 0 | planedef1 = define_plane(tri1->points, &plane1); |
961 | 0 | planedef2 = define_plane(tri2->points, &plane2); |
962 | |
|
963 | 0 | if (!planedef1 || !planedef2) |
964 | 0 | { |
965 | | /* Neither define a plane: Return distance line to line */ |
966 | 0 | if (!planedef1 && !planedef2) |
967 | 0 | return lw_dist3d_ptarray_ptarray(tri1->points, tri2->points, dl); |
968 | | |
969 | | /* Only tri defines a plane: Return distance from line (tri1) to tri2 */ |
970 | 0 | else if (!planedef1) |
971 | 0 | return lw_dist3d_ptarray_tri(tri1->points, tri2, &plane2, dl); |
972 | | |
973 | | /* Only poly defines a plane: Return distance from line (tri2) to tri1 */ |
974 | 0 | else |
975 | 0 | return lw_dist3d_ptarray_tri(tri2->points, tri1, &plane1, dl); |
976 | 0 | } |
977 | | |
978 | | /* What we do here is to compare the boundary of one polygon with the other polygon |
979 | | and then take the second boundary comparing with the first polygon */ |
980 | 0 | dl->twisted = 1; |
981 | 0 | if (!lw_dist3d_ptarray_tri(tri1->points, tri2, &plane2, dl)) |
982 | 0 | return LW_FALSE; |
983 | 0 | if (dl->distance < dl->tolerance) /* Just check if the answer already is given*/ |
984 | 0 | return LW_TRUE; |
985 | | |
986 | 0 | dl->twisted = -1; /* because we switch the order of geometries we switch "twisted" to -1 which will give the |
987 | | right order of points in shortest line. */ |
988 | 0 | return lw_dist3d_ptarray_tri(tri2->points, tri1, &plane1, dl); |
989 | 0 | } |
990 | | |
991 | | /** |
992 | | * search all the segments of pointarray to see which one is closest to p |
993 | | * Returns distance between point and pointarray |
994 | | */ |
995 | | int |
996 | | lw_dist3d_pt_ptarray(const POINT3DZ *p, const POINTARRAY *pa, DISTPTS3D *dl) |
997 | 0 | { |
998 | 0 | uint32_t t; |
999 | 0 | POINT3DZ start, end; |
1000 | 0 | int twist = dl->twisted; |
1001 | 0 | if (!pa) |
1002 | 0 | return LW_FALSE; |
1003 | | |
1004 | 0 | getPoint3dz_p(pa, 0, &start); |
1005 | |
|
1006 | 0 | for (t = 1; t < pa->npoints; t++) |
1007 | 0 | { |
1008 | 0 | dl->twisted = twist; |
1009 | 0 | getPoint3dz_p(pa, t, &end); |
1010 | 0 | if (!lw_dist3d_pt_seg(p, &start, &end, dl)) |
1011 | 0 | return LW_FALSE; |
1012 | | |
1013 | 0 | if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) |
1014 | 0 | return LW_TRUE; /*just a check if the answer is already given*/ |
1015 | 0 | start = end; |
1016 | 0 | } |
1017 | | |
1018 | 0 | return LW_TRUE; |
1019 | 0 | } |
1020 | | |
1021 | | /** |
1022 | | If searching for min distance, this one finds the closest point on segment A-B from p. |
1023 | | if searching for max distance it just sends p-A and p-B to pt-pt calculation |
1024 | | */ |
1025 | | int |
1026 | | lw_dist3d_pt_seg(const POINT3DZ *p, const POINT3DZ *A, const POINT3DZ *B, DISTPTS3D *dl) |
1027 | 0 | { |
1028 | 0 | POINT3DZ c; |
1029 | 0 | double r; |
1030 | | /*if start==end, then use pt distance */ |
1031 | 0 | if ((A->x == B->x) && (A->y == B->y) && (A->z == B->z)) |
1032 | 0 | { |
1033 | 0 | return lw_dist3d_pt_pt(p, A, dl); |
1034 | 0 | } |
1035 | | |
1036 | 0 | 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)) / |
1037 | 0 | ((B->x - A->x) * (B->x - A->x) + (B->y - A->y) * (B->y - A->y) + (B->z - A->z) * (B->z - A->z)); |
1038 | | |
1039 | | /*This is for finding the 3Dmaxdistance. |
1040 | | the maxdistance have to be between two vertices, |
1041 | | compared to mindistance which can be between |
1042 | | two vertices vertex.*/ |
1043 | 0 | if (dl->mode == DIST_MAX) |
1044 | 0 | { |
1045 | 0 | if (r >= 0.5) |
1046 | 0 | return lw_dist3d_pt_pt(p, A, dl); |
1047 | 0 | if (r < 0.5) |
1048 | 0 | return lw_dist3d_pt_pt(p, B, dl); |
1049 | 0 | } |
1050 | | |
1051 | 0 | if (r <= 0) /*If the first vertex A is closest to the point p*/ |
1052 | 0 | return lw_dist3d_pt_pt(p, A, dl); |
1053 | 0 | if (r >= 1) /*If the second vertex B is closest to the point p*/ |
1054 | 0 | return lw_dist3d_pt_pt(p, B, dl); |
1055 | | |
1056 | | /*else if the point p is closer to some point between a and b |
1057 | | then we find that point and send it to lw_dist3d_pt_pt*/ |
1058 | 0 | c.x = A->x + r * (B->x - A->x); |
1059 | 0 | c.y = A->y + r * (B->y - A->y); |
1060 | 0 | c.z = A->z + r * (B->z - A->z); |
1061 | |
|
1062 | 0 | return lw_dist3d_pt_pt(p, &c, dl); |
1063 | 0 | } |
1064 | | |
1065 | | double |
1066 | | distance3d_pt_pt(const POINT3D *p1, const POINT3D *p2) |
1067 | 0 | { |
1068 | 0 | double dx = p2->x - p1->x; |
1069 | 0 | double dy = p2->y - p1->y; |
1070 | 0 | double dz = p2->z - p1->z; |
1071 | 0 | return sqrt(dx * dx + dy * dy + dz * dz); |
1072 | 0 | } |
1073 | | |
1074 | | /** |
1075 | | |
1076 | | Compares incoming points and |
1077 | | stores the points closest to each other |
1078 | | or most far away from each other |
1079 | | depending on dl->mode (max or min) |
1080 | | */ |
1081 | | int |
1082 | | lw_dist3d_pt_pt(const POINT3DZ *thep1, const POINT3DZ *thep2, DISTPTS3D *dl) |
1083 | 0 | { |
1084 | 0 | double dx = thep2->x - thep1->x; |
1085 | 0 | double dy = thep2->y - thep1->y; |
1086 | 0 | double dz = thep2->z - thep1->z; |
1087 | 0 | double dist = sqrt(dx * dx + dy * dy + dz * dz); |
1088 | 0 | LWDEBUGF(2, |
1089 | 0 | "lw_dist3d_pt_pt called (with points: p1.x=%f, p1.y=%f,p1.z=%f,p2.x=%f, p2.y=%f,p2.z=%f)", |
1090 | 0 | thep1->x, |
1091 | 0 | thep1->y, |
1092 | 0 | thep1->z, |
1093 | 0 | thep2->x, |
1094 | 0 | thep2->y, |
1095 | 0 | thep2->z); |
1096 | |
|
1097 | 0 | if (((dl->distance - dist) * (dl->mode)) > |
1098 | 0 | 0) /*multiplication with mode to handle mindistance (mode=1) and maxdistance (mode = (-1)*/ |
1099 | 0 | { |
1100 | 0 | dl->distance = dist; |
1101 | |
|
1102 | 0 | if (dl->twisted > 0) /*To get the points in right order. twisted is updated between 1 and (-1) every |
1103 | | time the order is changed earlier in the chain*/ |
1104 | 0 | { |
1105 | 0 | dl->p1 = *thep1; |
1106 | 0 | dl->p2 = *thep2; |
1107 | 0 | } |
1108 | 0 | else |
1109 | 0 | { |
1110 | 0 | dl->p1 = *thep2; |
1111 | 0 | dl->p2 = *thep1; |
1112 | 0 | } |
1113 | 0 | } |
1114 | 0 | return LW_TRUE; |
1115 | 0 | } |
1116 | | |
1117 | | /** |
1118 | | |
1119 | | Finds all combinations of segments between two pointarrays |
1120 | | */ |
1121 | | int |
1122 | | lw_dist3d_ptarray_ptarray(const POINTARRAY *l1, const POINTARRAY *l2, DISTPTS3D *dl) |
1123 | 0 | { |
1124 | 0 | uint32_t t, u; |
1125 | 0 | POINT3DZ start, end; |
1126 | 0 | POINT3DZ start2, end2; |
1127 | 0 | int twist = dl->twisted; |
1128 | 0 | LWDEBUGF(2, "lw_dist3d_ptarray_ptarray called (points: %d-%d)", l1->npoints, l2->npoints); |
1129 | |
|
1130 | 0 | if (dl->mode == DIST_MAX) /*If we are searching for maxdistance we go straight to point-point calculation since |
1131 | | the maxdistance have to be between two vertices*/ |
1132 | 0 | { |
1133 | 0 | for (t = 0; t < l1->npoints; t++) /*for each segment in L1 */ |
1134 | 0 | { |
1135 | 0 | getPoint3dz_p(l1, t, &start); |
1136 | 0 | for (u = 0; u < l2->npoints; u++) /*for each segment in L2 */ |
1137 | 0 | { |
1138 | 0 | getPoint3dz_p(l2, u, &start2); |
1139 | 0 | lw_dist3d_pt_pt(&start, &start2, dl); |
1140 | 0 | } |
1141 | 0 | } |
1142 | 0 | } |
1143 | 0 | else |
1144 | 0 | { |
1145 | 0 | getPoint3dz_p(l1, 0, &start); |
1146 | 0 | for (t = 1; t < l1->npoints; t++) /*for each segment in L1 */ |
1147 | 0 | { |
1148 | 0 | getPoint3dz_p(l1, t, &end); |
1149 | 0 | getPoint3dz_p(l2, 0, &start2); |
1150 | 0 | for (u = 1; u < l2->npoints; u++) /*for each segment in L2 */ |
1151 | 0 | { |
1152 | 0 | getPoint3dz_p(l2, u, &end2); |
1153 | 0 | dl->twisted = twist; |
1154 | 0 | lw_dist3d_seg_seg(&start, &end, &start2, &end2, dl); |
1155 | 0 | LWDEBUGF( |
1156 | 0 | 4, "mindist_ptarray_ptarray; seg %i * seg %i, dist = %g\n", t, u, dl->distance); |
1157 | 0 | LWDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f", t, u, dl->distance, dl->tolerance); |
1158 | 0 | if (dl->distance <= dl->tolerance && dl->mode == DIST_MIN) |
1159 | 0 | return LW_TRUE; /*just a check if the answer is already given*/ |
1160 | 0 | start2 = end2; |
1161 | 0 | } |
1162 | 0 | start = end; |
1163 | 0 | } |
1164 | 0 | } |
1165 | 0 | return LW_TRUE; |
1166 | 0 | } |
1167 | | |
1168 | | /** |
1169 | | |
1170 | | Finds the two closest points on two linesegments |
1171 | | */ |
1172 | | int |
1173 | | lw_dist3d_seg_seg(const POINT3DZ *s1p1, const POINT3DZ *s1p2, const POINT3DZ *s2p1, const POINT3DZ *s2p2, DISTPTS3D *dl) |
1174 | 0 | { |
1175 | 0 | VECTOR3D v1, v2, vl; |
1176 | 0 | double s1k, s2k; /*two variables representing where on Line 1 (s1k) and where on Line 2 (s2k) a connecting line |
1177 | | between the two lines is perpendicular to both lines*/ |
1178 | 0 | POINT3DZ p1, p2; |
1179 | 0 | double a, b, c, d, e, D; |
1180 | | |
1181 | | /*s1p1 and s1p2 are the same point */ |
1182 | 0 | if (p3dz_same(s1p1, s1p2)) |
1183 | 0 | { |
1184 | 0 | return lw_dist3d_pt_seg(s1p1, s2p1, s2p2, dl); |
1185 | 0 | } |
1186 | | /*s2p1 and s2p2 are the same point */ |
1187 | 0 | if (p3dz_same(s2p1, s2p2)) |
1188 | 0 | { |
1189 | 0 | dl->twisted = ((dl->twisted) * (-1)); |
1190 | 0 | return lw_dist3d_pt_seg(s2p1, s1p1, s1p2, dl); |
1191 | 0 | } |
1192 | | /*s2p1 and s1p1 are the same point */ |
1193 | 0 | if (p3dz_same(s2p1, s1p1)) |
1194 | 0 | { |
1195 | 0 | dl->distance = 0.0; |
1196 | 0 | dl->p1 = dl->p2 = *s2p1; |
1197 | 0 | return LW_TRUE; |
1198 | 0 | } |
1199 | | /* |
1200 | | Here we use algorithm from softsurfer.com |
1201 | | that can be found here |
1202 | | http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm |
1203 | | */ |
1204 | | |
1205 | 0 | if (!get_3dvector_from_points(s1p1, s1p2, &v1)) |
1206 | 0 | return LW_FALSE; |
1207 | | |
1208 | 0 | if (!get_3dvector_from_points(s2p1, s2p2, &v2)) |
1209 | 0 | return LW_FALSE; |
1210 | | |
1211 | 0 | if (!get_3dvector_from_points(s2p1, s1p1, &vl)) |
1212 | 0 | return LW_FALSE; |
1213 | | |
1214 | 0 | a = DOT(v1, v1); |
1215 | 0 | b = DOT(v1, v2); |
1216 | 0 | c = DOT(v2, v2); |
1217 | 0 | d = DOT(v1, vl); |
1218 | 0 | e = DOT(v2, vl); |
1219 | 0 | D = a * c - b * b; |
1220 | |
|
1221 | 0 | if (D < 0.000000001) |
1222 | 0 | { /* the lines are almost parallel*/ |
1223 | 0 | s1k = |
1224 | 0 | 0.0; /*If the lines are parallel we try by using the startpoint of first segment. If that gives a |
1225 | | projected point on the second line outside segment 2 it will be found that s2k is >1 or <0.*/ |
1226 | 0 | if (b > c) /* use the largest denominator*/ |
1227 | 0 | s2k = d / b; |
1228 | 0 | else |
1229 | 0 | s2k = e / c; |
1230 | 0 | } |
1231 | 0 | else |
1232 | 0 | { |
1233 | 0 | s1k = (b * e - c * d) / D; |
1234 | 0 | s2k = (a * e - b * d) / D; |
1235 | 0 | } |
1236 | | |
1237 | | /* Now we check if the projected closest point on the infinite lines is outside our segments. If so the |
1238 | | * combinations with start and end points will be tested*/ |
1239 | |
|
1240 | 0 | if (s1k <= 0.0 || s1k >= 1.0 || s2k <= 0.0 || s2k >= 1.0) |
1241 | 0 | { |
1242 | 0 | if (s1k <= 0.0) |
1243 | 0 | { |
1244 | 0 | if (!lw_dist3d_pt_seg(s1p1, s2p1, s2p2, dl)) |
1245 | 0 | return LW_FALSE; |
1246 | 0 | } |
1247 | 0 | if (s1k >= 1.0) |
1248 | 0 | { |
1249 | 0 | if (!lw_dist3d_pt_seg(s1p2, s2p1, s2p2, dl)) |
1250 | 0 | return LW_FALSE; |
1251 | 0 | } |
1252 | 0 | if (s2k <= 0.0) |
1253 | 0 | { |
1254 | 0 | dl->twisted = ((dl->twisted) * (-1)); |
1255 | 0 | if (!lw_dist3d_pt_seg(s2p1, s1p1, s1p2, dl)) |
1256 | 0 | return LW_FALSE; |
1257 | 0 | } |
1258 | 0 | if (s2k >= 1.0) |
1259 | 0 | { |
1260 | 0 | dl->twisted = ((dl->twisted) * (-1)); |
1261 | 0 | if (!lw_dist3d_pt_seg(s2p2, s1p1, s1p2, dl)) |
1262 | 0 | return LW_FALSE; |
1263 | 0 | } |
1264 | 0 | } |
1265 | 0 | else |
1266 | 0 | { /*Find the closest point on the edges of both segments*/ |
1267 | 0 | p1.x = s1p1->x + s1k * (s1p2->x - s1p1->x); |
1268 | 0 | p1.y = s1p1->y + s1k * (s1p2->y - s1p1->y); |
1269 | 0 | p1.z = s1p1->z + s1k * (s1p2->z - s1p1->z); |
1270 | |
|
1271 | 0 | p2.x = s2p1->x + s2k * (s2p2->x - s2p1->x); |
1272 | 0 | p2.y = s2p1->y + s2k * (s2p2->y - s2p1->y); |
1273 | 0 | p2.z = s2p1->z + s2k * (s2p2->z - s2p1->z); |
1274 | |
|
1275 | 0 | if (!lw_dist3d_pt_pt(&p1, &p2, dl)) /* Send the closest points to point-point calculation*/ |
1276 | 0 | { |
1277 | 0 | return LW_FALSE; |
1278 | 0 | } |
1279 | 0 | } |
1280 | 0 | return LW_TRUE; |
1281 | 0 | } |
1282 | | |
1283 | | /** |
1284 | | |
1285 | | Checking if the point projected on the plane of the polygon actually is inside that polygon. |
1286 | | If so the mindistance is between that projected point and our original point. |
1287 | | If not we check from original point to the boundary. |
1288 | | If the projected point is inside a hole of the polygon we check the distance to the boundary of that hole. |
1289 | | */ |
1290 | | int |
1291 | | lw_dist3d_pt_poly(const POINT3DZ *p, const LWPOLY *poly, PLANE3D *plane, POINT3DZ *projp, DISTPTS3D *dl) |
1292 | 0 | { |
1293 | 0 | uint32_t i; |
1294 | |
|
1295 | 0 | if (pt_in_ring_3d(projp, poly->rings[0], plane)) |
1296 | 0 | { |
1297 | 0 | for (i = 1; i < poly->nrings; i++) |
1298 | 0 | { |
1299 | | /* Inside a hole. Distance = pt -> ring */ |
1300 | 0 | if (pt_in_ring_3d(projp, poly->rings[i], plane)) |
1301 | 0 | return lw_dist3d_pt_ptarray(p, poly->rings[i], dl); |
1302 | 0 | } |
1303 | | |
1304 | | /* if the projected point is inside the polygon the shortest distance is between that point and the |
1305 | | * input point */ |
1306 | 0 | return lw_dist3d_pt_pt(p, projp, dl); |
1307 | 0 | } |
1308 | 0 | else |
1309 | | /* if the projected point is outside the polygon we search for the closest distance against the boundary |
1310 | | * instead */ |
1311 | 0 | return lw_dist3d_pt_ptarray(p, poly->rings[0], dl); |
1312 | | |
1313 | 0 | return LW_TRUE; |
1314 | 0 | } |
1315 | | |
1316 | | int |
1317 | | lw_dist3d_pt_tri(const POINT3DZ *p, const LWTRIANGLE *tri, PLANE3D *plane, POINT3DZ *projp, DISTPTS3D *dl) |
1318 | 0 | { |
1319 | 0 | if (pt_in_ring_3d(projp, tri->points, plane)) |
1320 | | /* if the projected point is inside the polygon the shortest distance is between that point and the |
1321 | | * input point */ |
1322 | 0 | return lw_dist3d_pt_pt(p, projp, dl); |
1323 | 0 | else |
1324 | | /* if the projected point is outside the polygon we search for the closest distance against the boundary |
1325 | | * instead */ |
1326 | 0 | return lw_dist3d_pt_ptarray(p, tri->points, dl); |
1327 | | |
1328 | 0 | return LW_TRUE; |
1329 | 0 | } |
1330 | | |
1331 | | /** Computes pointarray to polygon distance */ |
1332 | | int |
1333 | | lw_dist3d_ptarray_poly(const POINTARRAY *pa, const LWPOLY *poly, PLANE3D *plane, DISTPTS3D *dl) |
1334 | 0 | { |
1335 | 0 | uint32_t i, j, k; |
1336 | 0 | double f, s1, s2; |
1337 | 0 | VECTOR3D projp1_projp2; |
1338 | 0 | POINT3DZ p1, p2, projp1, projp2, intersectionp; |
1339 | |
|
1340 | 0 | getPoint3dz_p(pa, 0, &p1); |
1341 | | |
1342 | | /* the sign of s1 tells us on which side of the plane the point is. */ |
1343 | 0 | s1 = project_point_on_plane(&p1, plane, &projp1); |
1344 | 0 | lw_dist3d_pt_poly(&p1, poly, plane, &projp1, dl); |
1345 | 0 | if ((s1 == 0.0) && (dl->distance < dl->tolerance)) |
1346 | 0 | return LW_TRUE; |
1347 | | |
1348 | 0 | for (i = 1; i < pa->npoints; i++) |
1349 | 0 | { |
1350 | 0 | int intersects; |
1351 | 0 | getPoint3dz_p(pa, i, &p2); |
1352 | 0 | s2 = project_point_on_plane(&p2, plane, &projp2); |
1353 | 0 | lw_dist3d_pt_poly(&p2, poly, plane, &projp2, dl); |
1354 | 0 | if ((s2 == 0.0) && (dl->distance < dl->tolerance)) |
1355 | 0 | return LW_TRUE; |
1356 | | |
1357 | | /* If s1 and s2 has different signs that means they are on different sides of the plane of the polygon. |
1358 | | * That means that the edge between the points crosses the plane and might intersect with the polygon */ |
1359 | 0 | if ((s1 * s2) < 0) |
1360 | 0 | { |
1361 | | /* The size of s1 and s2 is the distance from the point to the plane. */ |
1362 | 0 | f = fabs(s1) / (fabs(s1) + fabs(s2)); |
1363 | 0 | get_3dvector_from_points(&projp1, &projp2, &projp1_projp2); |
1364 | | |
1365 | | /* Get the point where the line segment crosses the plane */ |
1366 | 0 | intersectionp.x = projp1.x + f * projp1_projp2.x; |
1367 | 0 | intersectionp.y = projp1.y + f * projp1_projp2.y; |
1368 | 0 | intersectionp.z = projp1.z + f * projp1_projp2.z; |
1369 | | |
1370 | | /* We set intersects to true until the opposite is proved */ |
1371 | 0 | intersects = LW_TRUE; |
1372 | |
|
1373 | 0 | if (pt_in_ring_3d(&intersectionp, poly->rings[0], plane)) /*Inside outer ring*/ |
1374 | 0 | { |
1375 | 0 | for (k = 1; k < poly->nrings; k++) |
1376 | 0 | { |
1377 | | /* Inside a hole, so no intersection with the polygon*/ |
1378 | 0 | if (pt_in_ring_3d(&intersectionp, poly->rings[k], plane)) |
1379 | 0 | { |
1380 | 0 | intersects = LW_FALSE; |
1381 | 0 | break; |
1382 | 0 | } |
1383 | 0 | } |
1384 | 0 | if (intersects) |
1385 | 0 | { |
1386 | 0 | dl->distance = 0.0; |
1387 | 0 | dl->p1.x = intersectionp.x; |
1388 | 0 | dl->p1.y = intersectionp.y; |
1389 | 0 | dl->p1.z = intersectionp.z; |
1390 | |
|
1391 | 0 | dl->p2.x = intersectionp.x; |
1392 | 0 | dl->p2.y = intersectionp.y; |
1393 | 0 | dl->p2.z = intersectionp.z; |
1394 | 0 | return LW_TRUE; |
1395 | 0 | } |
1396 | 0 | } |
1397 | 0 | } |
1398 | | |
1399 | 0 | projp1 = projp2; |
1400 | 0 | s1 = s2; |
1401 | 0 | p1 = p2; |
1402 | 0 | } |
1403 | | |
1404 | | /* check our pointarray against boundary and inner boundaries of the polygon */ |
1405 | 0 | for (j = 0; j < poly->nrings; j++) |
1406 | 0 | lw_dist3d_ptarray_ptarray(pa, poly->rings[j], dl); |
1407 | |
|
1408 | 0 | return LW_TRUE; |
1409 | 0 | } |
1410 | | |
1411 | | /** Computes pointarray to triangle distance */ |
1412 | | int |
1413 | | lw_dist3d_ptarray_tri(const POINTARRAY *pa, const LWTRIANGLE *tri, PLANE3D *plane, DISTPTS3D *dl) |
1414 | 0 | { |
1415 | 0 | uint32_t i; |
1416 | 0 | double f, s1, s2; |
1417 | 0 | VECTOR3D projp1_projp2; |
1418 | 0 | POINT3DZ p1, p2, projp1, projp2, intersectionp; |
1419 | |
|
1420 | 0 | getPoint3dz_p(pa, 0, &p1); |
1421 | | |
1422 | | /* the sign of s1 tells us on which side of the plane the point is. */ |
1423 | 0 | s1 = project_point_on_plane(&p1, plane, &projp1); |
1424 | 0 | lw_dist3d_pt_tri(&p1, tri, plane, &projp1, dl); |
1425 | 0 | if ((s1 == 0.0) && (dl->distance < dl->tolerance)) |
1426 | 0 | return LW_TRUE; |
1427 | | |
1428 | 0 | for (i = 1; i < pa->npoints; i++) |
1429 | 0 | { |
1430 | 0 | int intersects; |
1431 | 0 | getPoint3dz_p(pa, i, &p2); |
1432 | 0 | s2 = project_point_on_plane(&p2, plane, &projp2); |
1433 | 0 | lw_dist3d_pt_tri(&p2, tri, plane, &projp2, dl); |
1434 | 0 | if ((s2 == 0.0) && (dl->distance < dl->tolerance)) |
1435 | 0 | return LW_TRUE; |
1436 | | |
1437 | | /* If s1 and s2 has different signs that means they are on different sides of the plane of the triangle. |
1438 | | * That means that the edge between the points crosses the plane and might intersect with the triangle |
1439 | | */ |
1440 | 0 | if ((s1 * s2) < 0) |
1441 | 0 | { |
1442 | | /* The size of s1 and s2 is the distance from the point to the plane. */ |
1443 | 0 | f = fabs(s1) / (fabs(s1) + fabs(s2)); |
1444 | 0 | get_3dvector_from_points(&projp1, &projp2, &projp1_projp2); |
1445 | | |
1446 | | /* Get the point where the line segment crosses the plane */ |
1447 | 0 | intersectionp.x = projp1.x + f * projp1_projp2.x; |
1448 | 0 | intersectionp.y = projp1.y + f * projp1_projp2.y; |
1449 | 0 | intersectionp.z = projp1.z + f * projp1_projp2.z; |
1450 | | |
1451 | | /* We set intersects to true until the opposite is proved */ |
1452 | 0 | intersects = LW_TRUE; |
1453 | |
|
1454 | 0 | if (pt_in_ring_3d(&intersectionp, tri->points, plane)) /*Inside outer ring*/ |
1455 | 0 | { |
1456 | 0 | if (intersects) |
1457 | 0 | { |
1458 | 0 | dl->distance = 0.0; |
1459 | 0 | dl->p1.x = intersectionp.x; |
1460 | 0 | dl->p1.y = intersectionp.y; |
1461 | 0 | dl->p1.z = intersectionp.z; |
1462 | |
|
1463 | 0 | dl->p2.x = intersectionp.x; |
1464 | 0 | dl->p2.y = intersectionp.y; |
1465 | 0 | dl->p2.z = intersectionp.z; |
1466 | 0 | return LW_TRUE; |
1467 | 0 | } |
1468 | 0 | } |
1469 | 0 | } |
1470 | | |
1471 | 0 | projp1 = projp2; |
1472 | 0 | s1 = s2; |
1473 | 0 | p1 = p2; |
1474 | 0 | } |
1475 | | |
1476 | | /* check our pointarray against triangle contour */ |
1477 | 0 | lw_dist3d_ptarray_ptarray(pa, tri->points, dl); |
1478 | 0 | return LW_TRUE; |
1479 | 0 | } |
1480 | | |
1481 | | /* Here we define the plane of a polygon (boundary pointarray of a polygon) |
1482 | | * the plane is stored as a point in plane (plane.pop) and a normal vector (plane.pv) |
1483 | | * |
1484 | | * We are calculating the average point and using it to break the polygon into |
1485 | | * POL_BREAKS (3) parts. Then we calculate the normal of those 3 vectors and |
1486 | | * use its average to define the normal of the plane.This is done to minimize |
1487 | | * the error introduced by FP precision |
1488 | | * We use [3] breaks to contemplate the special case of 3d triangles |
1489 | | */ |
1490 | | int |
1491 | | define_plane(const POINTARRAY *pa, PLANE3D *pl) |
1492 | 0 | { |
1493 | 0 | const uint32_t POL_BREAKS = 3; |
1494 | |
|
1495 | 0 | assert(pa); |
1496 | 0 | assert(pa->npoints > 3); |
1497 | 0 | if (!pa) |
1498 | 0 | return LW_FALSE; |
1499 | | |
1500 | 0 | uint32_t unique_points = pa->npoints - 1; |
1501 | 0 | uint32_t i; |
1502 | |
|
1503 | 0 | if (pa->npoints < 3) |
1504 | 0 | return LW_FALSE; |
1505 | | |
1506 | | /* Calculate the average point */ |
1507 | 0 | pl->pop.x = 0.0; |
1508 | 0 | pl->pop.y = 0.0; |
1509 | 0 | pl->pop.z = 0.0; |
1510 | 0 | for (i = 0; i < unique_points; i++) |
1511 | 0 | { |
1512 | 0 | POINT3DZ p; |
1513 | 0 | getPoint3dz_p(pa, i, &p); |
1514 | 0 | pl->pop.x += p.x; |
1515 | 0 | pl->pop.y += p.y; |
1516 | 0 | pl->pop.z += p.z; |
1517 | 0 | } |
1518 | |
|
1519 | 0 | pl->pop.x /= unique_points; |
1520 | 0 | pl->pop.y /= unique_points; |
1521 | 0 | pl->pop.z /= unique_points; |
1522 | |
|
1523 | 0 | pl->pv.x = 0.0; |
1524 | 0 | pl->pv.y = 0.0; |
1525 | 0 | pl->pv.z = 0.0; |
1526 | 0 | for (i = 0; i < POL_BREAKS; i++) |
1527 | 0 | { |
1528 | 0 | POINT3DZ point1, point2; |
1529 | 0 | VECTOR3D v1, v2, vp; |
1530 | 0 | uint32_t n1, n2; |
1531 | 0 | n1 = i * unique_points / POL_BREAKS; |
1532 | 0 | n2 = n1 + unique_points / POL_BREAKS; /* May overflow back to the first / last point */ |
1533 | |
|
1534 | 0 | if (n1 == n2) |
1535 | 0 | continue; |
1536 | | |
1537 | 0 | getPoint3dz_p(pa, n1, &point1); |
1538 | 0 | if (!get_3dvector_from_points(&pl->pop, &point1, &v1)) |
1539 | 0 | continue; |
1540 | | |
1541 | 0 | getPoint3dz_p(pa, n2, &point2); |
1542 | 0 | if (!get_3dvector_from_points(&pl->pop, &point2, &v2)) |
1543 | 0 | continue; |
1544 | | |
1545 | 0 | if (get_3dcross_product(&v1, &v2, &vp)) |
1546 | 0 | { |
1547 | | /* If the cross product isn't zero these 3 points aren't collinear |
1548 | | * We divide by its lengthsq to normalize the additions */ |
1549 | 0 | double vl = vp.x * vp.x + vp.y * vp.y + vp.z * vp.z; |
1550 | 0 | pl->pv.x += vp.x / vl; |
1551 | 0 | pl->pv.y += vp.y / vl; |
1552 | 0 | pl->pv.z += vp.z / vl; |
1553 | 0 | } |
1554 | 0 | } |
1555 | |
|
1556 | 0 | return (!FP_IS_ZERO(pl->pv.x) || !FP_IS_ZERO(pl->pv.y) || !FP_IS_ZERO(pl->pv.z)); |
1557 | 0 | } |
1558 | | |
1559 | | |
1560 | | /** |
1561 | | * pt_in_ring_3d(): crossing number test for a point in a polygon |
1562 | | * input: p = a point, |
1563 | | * pa = vertex points of a ring V[n+1] with V[n]=V[0] |
1564 | | * plane=the plane that the vertex points are lying on |
1565 | | * returns: 0 = outside, 1 = inside |
1566 | | * |
1567 | | * Our polygons have first and last point the same, |
1568 | | * |
1569 | | * The difference in 3D variant is that we exclude the dimension that faces the plane least. |
1570 | | * That is the dimension with the highest number in pv |
1571 | | */ |
1572 | | int |
1573 | | pt_in_ring_3d(const POINT3DZ *p, const POINTARRAY *ring, PLANE3D *plane) |
1574 | 0 | { |
1575 | |
|
1576 | 0 | uint32_t cn = 0; /* the crossing number counter */ |
1577 | 0 | uint32_t i; |
1578 | 0 | POINT3DZ v1, v2; |
1579 | |
|
1580 | 0 | POINT3DZ first, last; |
1581 | |
|
1582 | 0 | if ( !ring || ring->npoints == 0 ) |
1583 | 0 | lwerror("%s called on empty pointarray", __func__); |
1584 | |
|
1585 | 0 | getPoint3dz_p(ring, 0, &first); |
1586 | 0 | getPoint3dz_p(ring, ring->npoints - 1, &last); |
1587 | 0 | if (memcmp(&first, &last, sizeof(POINT3DZ))) |
1588 | 0 | { |
1589 | 0 | lwerror("pt_in_ring_3d: V[n] != V[0] (%g %g %g!= %g %g %g)", |
1590 | 0 | first.x, |
1591 | 0 | first.y, |
1592 | 0 | first.z, |
1593 | 0 | last.x, |
1594 | 0 | last.y, |
1595 | 0 | last.z); |
1596 | 0 | return LW_FALSE; |
1597 | 0 | } |
1598 | | |
1599 | 0 | LWDEBUGF(2, "pt_in_ring_3d called with point: %g %g %g", p->x, p->y, p->z); |
1600 | | /* printPA(ring); */ |
1601 | | |
1602 | | /* loop through all edges of the polygon */ |
1603 | 0 | getPoint3dz_p(ring, 0, &v1); |
1604 | |
|
1605 | 0 | if (fabs(plane->pv.z) >= fabs(plane->pv.x) && |
1606 | 0 | fabs(plane->pv.z) >= fabs(plane->pv.y)) /*If the z vector of the normal vector to the plane is larger than x |
1607 | | and y vector we project the ring to the xy-plane*/ |
1608 | 0 | { |
1609 | 0 | for (i = 0; i < ring->npoints - 1; i++) |
1610 | 0 | { |
1611 | 0 | double vt; |
1612 | 0 | getPoint3dz_p(ring, i + 1, &v2); |
1613 | | |
1614 | | /* edge from vertex i to vertex i+1 */ |
1615 | 0 | if ( |
1616 | | /* an upward crossing */ |
1617 | 0 | ((v1.y <= p->y) && (v2.y > p->y)) |
1618 | | /* a downward crossing */ |
1619 | 0 | || ((v1.y > p->y) && (v2.y <= p->y))) |
1620 | 0 | { |
1621 | |
|
1622 | 0 | vt = (double)(p->y - v1.y) / (v2.y - v1.y); |
1623 | | |
1624 | | /* P.x <intersect */ |
1625 | 0 | if (p->x < v1.x + vt * (v2.x - v1.x)) |
1626 | 0 | { |
1627 | | /* a valid crossing of y=p.y right of p.x */ |
1628 | 0 | ++cn; |
1629 | 0 | } |
1630 | 0 | } |
1631 | 0 | v1 = v2; |
1632 | 0 | } |
1633 | 0 | } |
1634 | 0 | else if (fabs(plane->pv.y) >= fabs(plane->pv.x) && |
1635 | 0 | fabs(plane->pv.y) >= fabs(plane->pv.z)) /*If the y vector of the normal vector to the plane is larger |
1636 | | than x and z vector we project the ring to the xz-plane*/ |
1637 | 0 | { |
1638 | 0 | for (i = 0; i < ring->npoints - 1; i++) |
1639 | 0 | { |
1640 | 0 | double vt; |
1641 | 0 | getPoint3dz_p(ring, i + 1, &v2); |
1642 | | |
1643 | | /* edge from vertex i to vertex i+1 */ |
1644 | 0 | if ( |
1645 | | /* an upward crossing */ |
1646 | 0 | ((v1.z <= p->z) && (v2.z > p->z)) |
1647 | | /* a downward crossing */ |
1648 | 0 | || ((v1.z > p->z) && (v2.z <= p->z))) |
1649 | 0 | { |
1650 | |
|
1651 | 0 | vt = (double)(p->z - v1.z) / (v2.z - v1.z); |
1652 | | |
1653 | | /* P.x <intersect */ |
1654 | 0 | if (p->x < v1.x + vt * (v2.x - v1.x)) |
1655 | 0 | { |
1656 | | /* a valid crossing of y=p.y right of p.x */ |
1657 | 0 | ++cn; |
1658 | 0 | } |
1659 | 0 | } |
1660 | 0 | v1 = v2; |
1661 | 0 | } |
1662 | 0 | } |
1663 | 0 | else /*Hopefully we only have the cases where x part of the normal vector is largest left*/ |
1664 | 0 | { |
1665 | 0 | for (i = 0; i < ring->npoints - 1; i++) |
1666 | 0 | { |
1667 | 0 | double vt; |
1668 | 0 | getPoint3dz_p(ring, i + 1, &v2); |
1669 | | |
1670 | | /* edge from vertex i to vertex i+1 */ |
1671 | 0 | if ( |
1672 | | /* an upward crossing */ |
1673 | 0 | ((v1.z <= p->z) && (v2.z > p->z)) |
1674 | | /* a downward crossing */ |
1675 | 0 | || ((v1.z > p->z) && (v2.z <= p->z))) |
1676 | 0 | { |
1677 | |
|
1678 | 0 | vt = (double)(p->z - v1.z) / (v2.z - v1.z); |
1679 | | |
1680 | | /* P.x <intersect */ |
1681 | 0 | if (p->y < v1.y + vt * (v2.y - v1.y)) |
1682 | 0 | { |
1683 | | /* a valid crossing of y=p.y right of p.x */ |
1684 | 0 | ++cn; |
1685 | 0 | } |
1686 | 0 | } |
1687 | 0 | v1 = v2; |
1688 | 0 | } |
1689 | 0 | } |
1690 | 0 | LWDEBUGF(3, "pt_in_ring_3d returning %d", cn & 1); |
1691 | |
|
1692 | 0 | return (cn & 1); /* 0 if even (out), and 1 if odd (in) */ |
1693 | 0 | } |
1694 | | |
1695 | | /*------------------------------------------------------------------------------------------------------------ |
1696 | | End of Brute force functions |
1697 | | --------------------------------------------------------------------------------------------------------------*/ |