postgis/postgis/lwgeom_box3d.c
2022-12-13 17:43:08 -08:00

937 lines
23 KiB
C

/**********************************************************************
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.net
*
* PostGIS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* PostGIS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
*
**********************************************************************
*
* Copyright 2009 Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
* Copyright 2009-2017 Paul Ramsey <pramsey@cleverelephant.ca>
* Copyright 2018 Darafei Praliaskouski <me@komzpa.net>
*
**********************************************************************/
#include "postgres.h"
#include "fmgr.h"
#include "utils/elog.h"
#include "utils/geo_decls.h"
#include "gserialized_spgist_3d.h"
#include "../postgis_config.h"
#include "lwgeom_pg.h"
#include "liblwgeom.h"
#include "liblwgeom_internal.h"
#include "lwgeom_box3d.h"
#include <math.h>
#include <float.h>
#include <string.h>
#include <stdio.h>
/**
* BOX3D_in - takes a string rep of BOX3D and returns internal rep
*
* example:
* "BOX3D(x1 y1 z1,x2 y2 z2)"
* or "BOX3D(x1 y1,x2 y2)" z1 and z2 = 0.0
*
*/
PG_FUNCTION_INFO_V1(BOX3D_in);
Datum BOX3D_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
int nitems;
BOX3D *box = (BOX3D *)palloc(sizeof(BOX3D));
box->zmin = 0;
box->zmax = 0;
if (strstr(str, "BOX3D(") != str)
{
pfree(box);
elog(ERROR, "BOX3D parser - doesn't start with BOX3D(");
PG_RETURN_NULL();
}
nitems = sscanf(str,
"BOX3D(%le %le %le ,%le %le %le)",
&box->xmin,
&box->ymin,
&box->zmin,
&box->xmax,
&box->ymax,
&box->zmax);
if (nitems != 6)
{
nitems = sscanf(str, "BOX3D(%le %le ,%le %le)", &box->xmin, &box->ymin, &box->xmax, &box->ymax);
if (nitems != 4)
{
pfree(box);
elog(
ERROR,
"BOX3D parser - couldn't parse. It should look like: BOX3D(xmin ymin zmin,xmax ymax zmax) or BOX3D(xmin ymin,xmax ymax)");
PG_RETURN_NULL();
}
}
if (box->xmin > box->xmax)
{
float tmp = box->xmin;
box->xmin = box->xmax;
box->xmax = tmp;
}
if (box->ymin > box->ymax)
{
float tmp = box->ymin;
box->ymin = box->ymax;
box->ymax = tmp;
}
if (box->zmin > box->zmax)
{
float tmp = box->zmin;
box->zmin = box->zmax;
box->zmax = tmp;
}
box->srid = SRID_UNKNOWN;
PG_RETURN_POINTER(box);
}
/**
* Takes an internal rep of a BOX3D and returns a string rep.
*
* example:
* "BOX3D(xmin ymin zmin, xmin ymin zmin)"
*/
PG_FUNCTION_INFO_V1(BOX3D_out);
Datum BOX3D_out(PG_FUNCTION_ARGS)
{
BOX3D *bbox = (BOX3D *)PG_GETARG_POINTER(0);
static const int precision = 15;
static int size = OUT_MAX_BYTES_DOUBLE * 6 + 5 + 2 + 4 + 5 + 1; /* double * 6 + "BOX3D"+ "()" + commas + null */
int i = 0;
char *result;
if (bbox == NULL)
{
result = palloc(5);
strcat(result, "NULL");
PG_RETURN_CSTRING(result);
}
result = (char *)palloc(size);
result[i++] = 'B';
result[i++] = 'O';
result[i++] = 'X';
result[i++] = '3';
result[i++] = 'D';
result[i++] = '(';
i += lwprint_double(bbox->xmin, precision, &result[i]);
result[i++] = ' ';
i += lwprint_double(bbox->ymin, precision, &result[i]);
result[i++] = ' ';
i += lwprint_double(bbox->zmin, precision, &result[i]);
result[i++] = ',';
i += lwprint_double(bbox->xmax, precision, &result[i]);
result[i++] = ' ';
i += lwprint_double(bbox->ymax, precision, &result[i]);
result[i++] = ' ';
i += lwprint_double(bbox->zmax, precision, &result[i]);
result[i++] = ')';
result[i++] = '\0';
PG_RETURN_CSTRING(result);
}
PG_FUNCTION_INFO_V1(BOX3D_to_BOX2D);
Datum BOX3D_to_BOX2D(PG_FUNCTION_ARGS)
{
BOX3D *in = (BOX3D *)PG_GETARG_POINTER(0);
GBOX *out = box3d_to_gbox(in);
PG_RETURN_POINTER(out);
}
static void
box3d_to_box_p(BOX3D *box, BOX *out)
{
if (!box)
return;
out->low.x = box->xmin;
out->low.y = box->ymin;
out->high.x = box->xmax;
out->high.y = box->ymax;
}
PG_FUNCTION_INFO_V1(BOX3D_to_BOX);
Datum BOX3D_to_BOX(PG_FUNCTION_ARGS)
{
BOX3D *in = (BOX3D *)PG_GETARG_POINTER(0);
BOX *box = palloc(sizeof(BOX));
box3d_to_box_p(in, box);
PG_RETURN_POINTER(box);
}
PG_FUNCTION_INFO_V1(BOX3D_to_LWGEOM);
Datum BOX3D_to_LWGEOM(PG_FUNCTION_ARGS)
{
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
POINTARRAY *pa;
GSERIALIZED *result;
POINT4D pt;
/**
* Alter BOX3D cast so that a valid geometry is always
* returned depending upon the size of the BOX3D. The
* code makes the following assumptions:
* - If the BOX3D is a single point then return a POINT geometry
* - If the BOX3D represents a line in any of X, Y or Z dimension,
* return a LINESTRING geometry
* - If the BOX3D represents a plane in the X, Y, or Z dimension,
* return a POLYGON geometry
* - Otherwise return a POLYHEDRALSURFACE geometry
*/
pa = ptarray_construct_empty(LW_TRUE, LW_FALSE, 5);
/* BOX3D is a point */
if ((box->xmin == box->xmax) && (box->ymin == box->ymax) && (box->zmin == box->zmax))
{
LWPOINT *lwpt = lwpoint_construct(SRID_UNKNOWN, NULL, pa);
pt.x = box->xmin;
pt.y = box->ymin;
pt.z = box->zmin;
ptarray_append_point(pa, &pt, LW_TRUE);
result = geometry_serialize(lwpoint_as_lwgeom(lwpt));
lwpoint_free(lwpt);
}
/* BOX3D is a line */
else if (((box->xmin == box->xmax || box->ymin == box->ymax) && box->zmin == box->zmax) ||
((box->xmin == box->xmax || box->zmin == box->zmax) && box->ymin == box->ymax) ||
((box->ymin == box->ymax || box->zmin == box->zmax) && box->xmin == box->xmax))
{
LWLINE *lwline = lwline_construct(SRID_UNKNOWN, NULL, pa);
pt.x = box->xmin;
pt.y = box->ymin;
pt.z = box->zmin;
ptarray_append_point(pa, &pt, LW_TRUE);
pt.x = box->xmax;
pt.y = box->ymax;
pt.z = box->zmax;
ptarray_append_point(pa, &pt, LW_TRUE);
result = geometry_serialize(lwline_as_lwgeom(lwline));
lwline_free(lwline);
}
/* BOX3D is a polygon in the X plane */
else if (box->xmin == box->xmax)
{
POINT4D points[4];
LWPOLY *lwpoly;
/* Initialize the 4 vertices of the polygon */
points[0] = (POINT4D){box->xmin, box->ymin, box->zmin, 0.0};
points[1] = (POINT4D){box->xmin, box->ymax, box->zmin, 0.0};
points[2] = (POINT4D){box->xmin, box->ymax, box->zmax, 0.0};
points[3] = (POINT4D){box->xmin, box->ymin, box->zmax, 0.0};
lwpoly = lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[1], &points[2], &points[3]);
result = geometry_serialize(lwpoly_as_lwgeom(lwpoly));
lwpoly_free(lwpoly);
}
/* BOX3D is a polygon in the Y plane */
else if (box->ymin == box->ymax)
{
POINT4D points[4];
LWPOLY *lwpoly;
/* Initialize the 4 vertices of the polygon */
points[0] = (POINT4D){box->xmin, box->ymin, box->zmin, 0.0};
points[1] = (POINT4D){box->xmax, box->ymin, box->zmin, 0.0};
points[2] = (POINT4D){box->xmax, box->ymin, box->zmax, 0.0};
points[3] = (POINT4D){box->xmin, box->ymin, box->zmax, 0.0};
lwpoly = lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[1], &points[2], &points[3]);
result = geometry_serialize(lwpoly_as_lwgeom(lwpoly));
lwpoly_free(lwpoly);
}
/* BOX3D is a polygon in the Z plane */
else if (box->zmin == box->zmax)
{
POINT4D points[4];
LWPOLY *lwpoly;
/* Initialize the 4 vertices of the polygon */
points[0] = (POINT4D){box->xmin, box->ymin, box->zmin, 0.0};
points[1] = (POINT4D){box->xmin, box->ymax, box->zmin, 0.0};
points[2] = (POINT4D){box->xmax, box->ymax, box->zmin, 0.0};
points[3] = (POINT4D){box->xmax, box->ymin, box->zmin, 0.0};
lwpoly = lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[1], &points[2], &points[3]);
result = geometry_serialize(lwpoly_as_lwgeom(lwpoly));
lwpoly_free(lwpoly);
}
/* BOX3D is a polyhedron */
else
{
POINT4D points[8];
static const int ngeoms = 6;
LWGEOM **geoms = (LWGEOM **)lwalloc(sizeof(LWGEOM *) * ngeoms);
LWGEOM *geom = NULL;
/* Initialize the 8 vertices of the box */
points[0] = (POINT4D){box->xmin, box->ymin, box->zmin, 0.0};
points[1] = (POINT4D){box->xmin, box->ymax, box->zmin, 0.0};
points[2] = (POINT4D){box->xmax, box->ymax, box->zmin, 0.0};
points[3] = (POINT4D){box->xmax, box->ymin, box->zmin, 0.0};
points[4] = (POINT4D){box->xmin, box->ymin, box->zmax, 0.0};
points[5] = (POINT4D){box->xmin, box->ymax, box->zmax, 0.0};
points[6] = (POINT4D){box->xmax, box->ymax, box->zmax, 0.0};
points[7] = (POINT4D){box->xmax, box->ymin, box->zmax, 0.0};
/* add bottom polygon */
geoms[0] = lwpoly_as_lwgeom(
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[1], &points[2], &points[3]));
/* add top polygon */
geoms[1] = lwpoly_as_lwgeom(
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[4], &points[7], &points[6], &points[5]));
/* add left polygon */
geoms[2] = lwpoly_as_lwgeom(
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[4], &points[5], &points[1]));
/* add right polygon */
geoms[3] = lwpoly_as_lwgeom(
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[3], &points[2], &points[6], &points[7]));
/* add front polygon */
geoms[4] = lwpoly_as_lwgeom(
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[3], &points[7], &points[4]));
/* add back polygon */
geoms[5] = lwpoly_as_lwgeom(
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[1], &points[5], &points[6], &points[2]));
geom = (LWGEOM *)lwcollection_construct(POLYHEDRALSURFACETYPE, SRID_UNKNOWN, NULL, ngeoms, geoms);
FLAGS_SET_SOLID(geom->flags, 1);
result = geometry_serialize(geom);
lwcollection_free((LWCOLLECTION *)geom);
}
gserialized_set_srid(result, box->srid);
PG_RETURN_POINTER(result);
}
/** Expand given box of 'd' units in all directions */
void
expand_box3d(BOX3D *box, double d)
{
box->xmin -= d;
box->ymin -= d;
box->zmin -= d;
box->xmax += d;
box->ymax += d;
box->zmax += d;
}
static void
expand_box3d_xyz(BOX3D *box, double dx, double dy, double dz)
{
box->xmin -= dx;
box->xmax += dx;
box->ymin -= dy;
box->ymax += dy;
box->zmin -= dz;
box->zmax += dz;
}
PG_FUNCTION_INFO_V1(BOX3D_expand);
Datum BOX3D_expand(PG_FUNCTION_ARGS)
{
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
BOX3D *result = (BOX3D *)palloc(sizeof(BOX3D));
memcpy(result, box, sizeof(BOX3D));
if (PG_NARGS() == 2)
{
/* Expand the box the same amount in all directions */
double d = PG_GETARG_FLOAT8(1);
expand_box3d(result, d);
}
else
{
double dx = PG_GETARG_FLOAT8(1);
double dy = PG_GETARG_FLOAT8(2);
double dz = PG_GETARG_FLOAT8(3);
expand_box3d_xyz(result, dx, dy, dz);
}
PG_RETURN_POINTER(result);
}
/**
* convert a GSERIALIZED to BOX3D
*
* NOTE: the bounding box is always recomputed as the cache is a BOX2D, not a BOX3D.
*
*/
PG_FUNCTION_INFO_V1(LWGEOM_to_BOX3D);
Datum LWGEOM_to_BOX3D(PG_FUNCTION_ARGS)
{
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
GBOX gbox;
BOX3D *result;
int rv = lwgeom_calculate_gbox(lwgeom, &gbox);
if (rv == LW_FAILURE)
PG_RETURN_NULL();
result = box3d_from_gbox(&gbox);
result->srid = lwgeom->srid;
lwgeom_free(lwgeom);
PG_RETURN_POINTER(result);
}
PG_FUNCTION_INFO_V1(BOX3D_xmin);
Datum BOX3D_xmin(PG_FUNCTION_ARGS)
{
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
PG_RETURN_FLOAT8(Min(box->xmin, box->xmax));
}
PG_FUNCTION_INFO_V1(BOX3D_ymin);
Datum BOX3D_ymin(PG_FUNCTION_ARGS)
{
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
PG_RETURN_FLOAT8(Min(box->ymin, box->ymax));
}
PG_FUNCTION_INFO_V1(BOX3D_zmin);
Datum BOX3D_zmin(PG_FUNCTION_ARGS)
{
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
PG_RETURN_FLOAT8(Min(box->zmin, box->zmax));
}
PG_FUNCTION_INFO_V1(BOX3D_xmax);
Datum BOX3D_xmax(PG_FUNCTION_ARGS)
{
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
PG_RETURN_FLOAT8(Max(box->xmin, box->xmax));
}
PG_FUNCTION_INFO_V1(BOX3D_ymax);
Datum BOX3D_ymax(PG_FUNCTION_ARGS)
{
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
PG_RETURN_FLOAT8(Max(box->ymin, box->ymax));
}
PG_FUNCTION_INFO_V1(BOX3D_zmax);
Datum BOX3D_zmax(PG_FUNCTION_ARGS)
{
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
PG_RETURN_FLOAT8(Max(box->zmin, box->zmax));
}
/**
* Used in the ST_Extent and ST_Extent3D aggregates, does not read the
* serialized cached bounding box (since that is floating point)
* but calculates the box in full from the underlying geometry.
*/
PG_FUNCTION_INFO_V1(BOX3D_combine);
Datum BOX3D_combine(PG_FUNCTION_ARGS)
{
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
GSERIALIZED *geom = PG_ARGISNULL(1) ? NULL : (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
LWGEOM *lwgeom = NULL;
BOX3D *result = NULL;
GBOX gbox;
int32_t srid;
int rv;
/* Can't do anything with null inputs */
if (!box && !geom)
{
PG_RETURN_NULL();
}
/* Null geometry but non-null box, return the box */
else if (!geom)
{
result = palloc(sizeof(BOX3D));
memcpy(result, box, sizeof(BOX3D));
PG_RETURN_POINTER(result);
}
/*
* Deserialize geometry and *calculate* the box
* We can't use the cached box because it's float, we *must* calculate
*/
lwgeom = lwgeom_from_gserialized(geom);
srid = lwgeom->srid;
rv = lwgeom_calculate_gbox(lwgeom, &gbox);
lwgeom_free(lwgeom);
/* If we couldn't calculate the box, return what we know */
if (rv == LW_FAILURE)
{
PG_FREE_IF_COPY(geom, 1);
/* No geom box, no input box, so null return */
if (!box)
PG_RETURN_NULL();
result = palloc(sizeof(BOX3D));
memcpy(result, box, sizeof(BOX3D));
PG_RETURN_POINTER(result);
}
/* Null box and non-null geometry, just return the geometry box */
if (!box)
{
PG_FREE_IF_COPY(geom, 1);
result = box3d_from_gbox(&gbox);
result->srid = srid;
PG_RETURN_POINTER(result);
}
result = palloc(sizeof(BOX3D));
result->xmax = Max(box->xmax, gbox.xmax);
result->ymax = Max(box->ymax, gbox.ymax);
result->zmax = Max(box->zmax, gbox.zmax);
result->xmin = Min(box->xmin, gbox.xmin);
result->ymin = Min(box->ymin, gbox.ymin);
result->zmin = Min(box->zmin, gbox.zmin);
result->srid = srid;
PG_FREE_IF_COPY(geom, 1);
PG_RETURN_POINTER(result);
}
PG_FUNCTION_INFO_V1(BOX3D_combine_BOX3D);
Datum BOX3D_combine_BOX3D(PG_FUNCTION_ARGS)
{
BOX3D *box0 = (BOX3D *)(PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));
BOX3D *box1 = (BOX3D *)(PG_ARGISNULL(1) ? NULL : PG_GETARG_POINTER(1));
BOX3D *result;
if (box0 && !box1)
PG_RETURN_POINTER(box0);
if (box1 && !box0)
PG_RETURN_POINTER(box1);
if (!box1 && !box0)
PG_RETURN_NULL();
result = palloc(sizeof(BOX3D));
result->xmax = Max(box0->xmax, box1->xmax);
result->ymax = Max(box0->ymax, box1->ymax);
result->zmax = Max(box0->zmax, box1->zmax);
result->xmin = Min(box0->xmin, box1->xmin);
result->ymin = Min(box0->ymin, box1->ymin);
result->zmin = Min(box0->zmin, box1->zmin);
result->srid = box0->srid;
PG_RETURN_POINTER(result);
}
PG_FUNCTION_INFO_V1(BOX3D_construct);
Datum BOX3D_construct(PG_FUNCTION_ARGS)
{
GSERIALIZED *min = PG_GETARG_GSERIALIZED_P(0);
GSERIALIZED *max = PG_GETARG_GSERIALIZED_P(1);
BOX3D *result = palloc(sizeof(BOX3D));
LWGEOM *minpoint, *maxpoint;
POINT3DZ minp, maxp;
minpoint = lwgeom_from_gserialized(min);
maxpoint = lwgeom_from_gserialized(max);
if (minpoint->type != POINTTYPE || maxpoint->type != POINTTYPE)
{
elog(ERROR, "BOX3D_construct: args must be points");
PG_RETURN_NULL();
}
if (lwgeom_is_empty(minpoint) || lwgeom_is_empty(maxpoint) ){
elog(ERROR, "BOX3D_construct: args can not be empty points");
PG_RETURN_NULL();
}
gserialized_error_if_srid_mismatch(min, max, __func__);
getPoint3dz_p(((LWPOINT *)minpoint)->point, 0, &minp);
getPoint3dz_p(((LWPOINT *)maxpoint)->point, 0, &maxp);
result->xmax = maxp.x;
result->ymax = maxp.y;
result->zmax = maxp.z;
result->xmin = minp.x;
result->ymin = minp.y;
result->zmin = minp.z;
result->srid = minpoint->srid;
PG_RETURN_POINTER(result);
}
/** needed for sp-gist support PostgreSQL 11+ **/
/*****************************************************************************
* BOX3D functions
*****************************************************************************/
/* contains? */
bool
BOX3D_contains_internal(BOX3D *box1, BOX3D *box2)
{
return (box1->xmax >= box2->xmax && box1->xmin <= box2->xmin) &&
(box1->ymax >= box2->ymax && box1->ymin <= box2->ymin) &&
(box1->zmax >= box2->zmax && box1->zmin <= box2->zmin);
}
PG_FUNCTION_INFO_V1(BOX3D_contains);
PGDLLEXPORT Datum BOX3D_contains(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_contains_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* contained by? */
bool
BOX3D_contained_internal(BOX3D *box1, BOX3D *box2)
{
return BOX3D_contains_internal(box2, box1);
}
PG_FUNCTION_INFO_V1(BOX3D_contained);
PGDLLEXPORT Datum BOX3D_contained(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_contained_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* overlaps? */
bool
BOX3D_overlaps_internal(BOX3D *box1, BOX3D *box2)
{
return (box1->xmin <= box2->xmax && box2->xmin <= box1->xmax) &&
(box1->ymin <= box2->ymax && box2->ymin <= box1->ymax) &&
(box1->zmin <= box2->zmax && box2->zmin <= box1->zmax);
}
PG_FUNCTION_INFO_V1(BOX3D_overlaps);
PGDLLEXPORT Datum BOX3D_overlaps(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_overlaps_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* same? */
bool
BOX3D_same_internal(BOX3D *box1, BOX3D *box2)
{
return (FPeq(box1->xmax, box2->xmax) && FPeq(box1->xmin, box2->xmin)) &&
(FPeq(box1->ymax, box2->ymax) && FPeq(box1->ymin, box2->ymin)) &&
(FPeq(box1->zmax, box2->zmax) && FPeq(box1->zmin, box2->zmin));
}
PG_FUNCTION_INFO_V1(BOX3D_same);
PGDLLEXPORT Datum BOX3D_same(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_same_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* strictly left of? */
bool
BOX3D_left_internal(BOX3D *box1, BOX3D *box2)
{
return box1->xmax < box2->xmin;
}
PG_FUNCTION_INFO_V1(BOX3D_left);
PGDLLEXPORT Datum BOX3D_left(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_left_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* does not extend to right of? */
bool
BOX3D_overleft_internal(BOX3D *box1, BOX3D *box2)
{
return box1->xmax <= box2->xmax;
}
PG_FUNCTION_INFO_V1(BOX3D_overleft);
PGDLLEXPORT Datum BOX3D_overleft(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_overleft_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* strictly right of? */
bool
BOX3D_right_internal(BOX3D *box1, BOX3D *box2)
{
return box1->xmin > box2->xmax;
}
PG_FUNCTION_INFO_V1(BOX3D_right);
PGDLLEXPORT Datum BOX3D_right(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_right_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* does not extend to left of? */
bool
BOX3D_overright_internal(BOX3D *box1, BOX3D *box2)
{
return box1->xmin >= box2->xmin;
}
PG_FUNCTION_INFO_V1(BOX3D_overright);
PGDLLEXPORT Datum BOX3D_overright(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_overright_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* strictly below of? */
bool
BOX3D_below_internal(BOX3D *box1, BOX3D *box2)
{
return box1->ymax < box2->ymin;
}
PG_FUNCTION_INFO_V1(BOX3D_below);
PGDLLEXPORT Datum BOX3D_below(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_below_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* does not extend above of? */
bool
BOX3D_overbelow_internal(BOX3D *box1, BOX3D *box2)
{
return box1->ymax <= box2->ymax;
}
PG_FUNCTION_INFO_V1(BOX3D_overbelow);
PGDLLEXPORT Datum BOX3D_overbelow(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_overbelow_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* strictly above of? */
bool
BOX3D_above_internal(BOX3D *box1, BOX3D *box2)
{
return box1->ymin > box2->ymax;
}
PG_FUNCTION_INFO_V1(BOX3D_above);
PGDLLEXPORT Datum BOX3D_above(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_above_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* does not extend below of? */
bool
BOX3D_overabove_internal(BOX3D *box1, BOX3D *box2)
{
return box1->ymin >= box2->ymin;
}
PG_FUNCTION_INFO_V1(BOX3D_overabove);
PGDLLEXPORT Datum BOX3D_overabove(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_overabove_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* strictly in before of? */
bool
BOX3D_front_internal(BOX3D *box1, BOX3D *box2)
{
return box1->zmax < box2->zmin;
}
PG_FUNCTION_INFO_V1(BOX3D_front);
PGDLLEXPORT Datum BOX3D_front(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_front_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* does not extend to the after of? */
bool
BOX3D_overfront_internal(BOX3D *box1, BOX3D *box2)
{
return box1->zmax <= box2->zmax;
}
PG_FUNCTION_INFO_V1(BOX3D_overfront);
PGDLLEXPORT Datum BOX3D_overfront(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_overfront_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* strictly after of? */
bool
BOX3D_back_internal(BOX3D *box1, BOX3D *box2)
{
return box1->zmin > box2->zmax;
}
PG_FUNCTION_INFO_V1(BOX3D_back);
PGDLLEXPORT Datum BOX3D_back(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_back_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* does not extend to the before of? */
bool
BOX3D_overback_internal(BOX3D *box1, BOX3D *box2)
{
return box1->zmin >= box2->zmin;
}
PG_FUNCTION_INFO_V1(BOX3D_overback);
PGDLLEXPORT Datum BOX3D_overback(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
bool result = BOX3D_overback_internal(box1, box2);
PG_RETURN_BOOL(result);
}
/* Minimum distance between 2 bounding boxes */
double
BOX3D_distance_internal(BOX3D *box1, BOX3D *box2)
{
double sqrDist = 0;
double d;
if (BOX3D_overlaps_internal(box1, box2))
return 0.0;
/* X axis */
if (box1->xmax < box2->xmin)
{
d = box1->xmax - box2->xmin;
sqrDist += d * d;
}
else if (box1->xmin > box2->xmax)
{
d = box1->xmin - box2->xmax;
sqrDist += d * d;
}
/* Y axis */
if (box1->ymax < box2->ymin)
{
d = box1->ymax - box2->ymin;
sqrDist += d * d;
}
else if (box1->ymin > box2->ymax)
{
d = box1->ymin - box2->ymax;
sqrDist += d * d;
}
/* Z axis */
if (box1->zmax < box2->zmin)
{
d = box1->zmax - box2->zmin;
sqrDist += d * d;
}
else if (box1->zmin > box2->zmax)
{
d = box1->zmin - box2->zmax;
sqrDist += d * d;
}
return sqrt(sqrDist);
}
PG_FUNCTION_INFO_V1(BOX3D_distance);
PGDLLEXPORT Datum BOX3D_distance(PG_FUNCTION_ARGS)
{
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
PG_RETURN_FLOAT8(BOX3D_distance_internal(box1, box2));
}