Coverage Report

Created: 2026-06-09 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
--------------------------------------------------------------------------------------------------------------*/