237 lines
7.5 KiB
Python
237 lines
7.5 KiB
Python
# MIT License
|
|
#
|
|
# Copyright (C) 2025 Ryan L. Guy & Dennis Fassbaender
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in all
|
|
# copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
# SOFTWARE.
|
|
|
|
from .vector3 import Vector3, Vector3_t
|
|
from .gridindex import GridIndex
|
|
from . import method_decorators as decorators
|
|
import ctypes
|
|
|
|
class AABB_t(ctypes.Structure):
|
|
_fields_ = [("position", Vector3_t),
|
|
("width", ctypes.c_float),
|
|
("height", ctypes.c_float),
|
|
("depth", ctypes.c_float)]
|
|
|
|
class AABB(object):
|
|
|
|
def __init__(self, *args):
|
|
if len(args) == 4 and isinstance(args[0], Vector3):
|
|
self.position = args[0]
|
|
self.width = args[1]
|
|
self.height = args[2]
|
|
self.depth = args[3]
|
|
elif len(args) == 6:
|
|
self._position = Vector3(args[0], args[1], args[2])
|
|
self.width = args[3]
|
|
self.height = args[4]
|
|
self.depth = args[5]
|
|
elif len(args) == 0:
|
|
self.position = Vector3()
|
|
self.width = 0.0
|
|
self.height = 0.0
|
|
self.depth = 0.0
|
|
else:
|
|
errmsg = "AABB must be initialized with types:\n"
|
|
errmsg += "x: " + (str(float) + "\n" +
|
|
"y: " + str(float) + "\n" +
|
|
"z: " + str(float) + "\n" +
|
|
"width: " + str(float) + "\n" +
|
|
"height: " + str(float) + "\n" +
|
|
"depth: " + str(float) + "\n\n" +
|
|
"or\n\n" +
|
|
"position: " + (str(Vector3)) + "\n" +
|
|
"width: " + str(float) + "\n" +
|
|
"height: " + str(float) + "\n" +
|
|
"depth: " + str(float))
|
|
raise TypeError(errmsg)
|
|
|
|
def __str__(self):
|
|
return (str(self.position) + " " + str(self.width) + " " +
|
|
str(self.height) + " " +
|
|
str(self.depth))
|
|
|
|
@classmethod
|
|
@decorators.check_type(Vector3)
|
|
def from_corners(cls, pmin = Vector3(), pmax = Vector3()):
|
|
minx = min(pmin.x, pmax.x)
|
|
miny = min(pmin.y, pmax.y)
|
|
minz = min(pmin.z, pmax.z)
|
|
maxx = max(pmin.x, pmax.x)
|
|
maxy = max(pmin.y, pmax.y)
|
|
maxz = max(pmin.z, pmax.z)
|
|
width = maxx - minx
|
|
height = maxy - miny
|
|
depth = maxz - minz
|
|
|
|
return cls(minx, miny, minz, width, height, depth)
|
|
|
|
@classmethod
|
|
def from_points(cls, point_list):
|
|
if len(point_list) == 0:
|
|
return cls()
|
|
|
|
minx, miny, minz = point_list[0]
|
|
maxx, maxy, maxz = point_list[0]
|
|
for p in point_list:
|
|
minx = min(p.x, minx);
|
|
miny = min(p.y, miny);
|
|
minz = min(p.z, minz);
|
|
maxx = max(p.x, maxx);
|
|
maxy = max(p.y, maxy);
|
|
maxz = max(p.z, maxz);
|
|
|
|
eps = 1e-9;
|
|
width = maxx - minx + eps;
|
|
height = maxy - miny + eps;
|
|
depth = maxz - minz + eps;
|
|
|
|
return cls(minx, miny, minz, width, height, depth)
|
|
|
|
@classmethod
|
|
def from_struct(cls, cstruct):
|
|
return cls(Vector3.from_struct(cstruct.position),
|
|
float(cstruct.width),
|
|
float(cstruct.height),
|
|
float(cstruct.depth))
|
|
|
|
def to_struct(self):
|
|
return AABB_t(Vector3_t(self.x, self.y, self.z),
|
|
self.width, self.height, self.depth)
|
|
|
|
@classmethod
|
|
def from_grid_index(cls, grid_index = GridIndex(), dx = 0.0):
|
|
return cls(grid_index.i*dx, grid_index.j*dx, grid_index.k*dx, dx, dx, dx)
|
|
|
|
@property
|
|
def x(self):
|
|
return self._position.x
|
|
|
|
@property
|
|
def y(self):
|
|
return self._position.y
|
|
|
|
@property
|
|
def z(self):
|
|
return self._position.z
|
|
|
|
@property
|
|
def width(self):
|
|
return self._width
|
|
|
|
@property
|
|
def height(self):
|
|
return self._height
|
|
|
|
@property
|
|
def depth(self):
|
|
return self._depth
|
|
|
|
@property
|
|
def position(self):
|
|
return self._position
|
|
|
|
@x.setter
|
|
def x(self, value):
|
|
self._position.x = value
|
|
|
|
@y.setter
|
|
def y(self, value):
|
|
self._position.y = value
|
|
|
|
@z.setter
|
|
def z(self, value):
|
|
self._position.z = value
|
|
|
|
@width.setter
|
|
def width(self, value):
|
|
self._width = float(value)
|
|
|
|
@height.setter
|
|
def height(self, value):
|
|
self._height = float(value)
|
|
|
|
@depth.setter
|
|
def depth(self, value):
|
|
self._depth = float(value)
|
|
|
|
@position.setter
|
|
@decorators.check_type(Vector3)
|
|
def position(self, vector):
|
|
self._position = vector
|
|
|
|
def expand(self, v):
|
|
h = 0.5 * v;
|
|
self.position -= Vector3(h, h, h);
|
|
self.width += v;
|
|
self.height += v;
|
|
self.depth += v;
|
|
|
|
@decorators.xyz_or_vector
|
|
def contains_point(self, x, y, z):
|
|
return (x >= self.x and y >= self.y and z >= self.z and
|
|
x < self.x + self.width and
|
|
y < self.y + self.height and
|
|
z < self.z + self.depth)
|
|
|
|
def get_min_point(self):
|
|
return self.position
|
|
|
|
def get_max_point(self):
|
|
return self.position + Vector3(self.width, self.height, self.depth)
|
|
|
|
def get_intersection(self, bbox):
|
|
minp1 = self.get_min_point()
|
|
minp2 = bbox.get_min_point()
|
|
maxp1 = self.get_max_point()
|
|
maxp2 = bbox.get_max_point()
|
|
|
|
if minp1.x > maxp2.x or minp1.y > maxp2.y or minp1.z > maxp2.z:
|
|
return AABB()
|
|
|
|
interminx = max(minp1.x, minp2.x)
|
|
interminy = max(minp1.y, minp2.y)
|
|
interminz = max(minp1.z, minp2.z)
|
|
intermaxx = min(maxp1.x, maxp2.x)
|
|
intermaxy = min(maxp1.y, maxp2.y)
|
|
intermaxz = min(maxp1.z, maxp2.z)
|
|
|
|
return AABB.from_corners(Vector3(interminx, interminy, interminz),
|
|
Vector3(intermaxx, intermaxy, intermaxz))
|
|
|
|
def get_union(self, bbox):
|
|
minp1 = self.get_min_point()
|
|
minp2 = bbox.get_min_point()
|
|
maxp1 = self.get_max_point()
|
|
maxp2 = bbox.get_max_point()
|
|
|
|
if minp1.x > maxp2.x or minp1.y > maxp2.y or minp1.z > maxp2.z:
|
|
return AABB()
|
|
|
|
unionminx = min(minp1.x, minp2.x)
|
|
unionminy = min(minp1.y, minp2.y)
|
|
unionminz = min(minp1.z, minp2.z)
|
|
unionmaxx = max(maxp1.x, maxp2.x)
|
|
unionmaxy = max(maxp1.y, maxp2.y)
|
|
unionmaxz = max(maxp1.z, maxp2.z)
|
|
|
|
return AABB.from_corners(Vector3(unionminx, unionminy, unionminz),
|
|
Vector3(unionmaxx, unionmaxy, unionmaxz)) |