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