Skip to content

Commit

Permalink
Merge pull request #175 from BerkeleyLearnVerify/3DPruningImprovements
Browse files Browse the repository at this point in the history
Pruning Improvements and Voxel Regions + Bugfix & Cleanup
  • Loading branch information
dfremont authored Jan 12, 2024
2 parents e18eb95 + 0a75bfc commit c4895b4
Show file tree
Hide file tree
Showing 6 changed files with 867 additions and 93 deletions.
155 changes: 107 additions & 48 deletions src/scenic/core/object_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import trimesh

from scenic.core.distributions import (
FunctionDistribution,
MultiplexerDistribution,
RandomControlFlowError,
Samplable,
Expand Down Expand Up @@ -1328,67 +1329,125 @@ def boundingBox(self):
@cached_property
def inradius(self):
"""A lower bound on the inradius of this object"""
# First check if all needed variables are defined. If so, we can
# compute the inradius exactly.
width, length, height = self.width, self.length, self.height
shape = self.shape
if not any(needsSampling(val) for val in (width, length, height, shape)):
shapeRegion = MeshVolumeRegion(
mesh=shape.mesh, dimensions=(width, length, height)
)
return shapeRegion.inradius

# If we havea uniform distribution over shapes and a supportInterval for each dimension,
# we can compute a supportInterval for this object's inradius
# Define a helper function that computes the support of the inradius,
# given the sub supports.
def inradiusSupport(width_s, length_s, height_s, shape_s):
# Unpack the dimension supports (and ignore the shape support)
min_width, max_width = width_s
min_length, max_length = length_s
min_height, max_height = height_s

if None in [
min_width,
max_width,
min_length,
max_length,
min_height,
max_height,
]:
# Can't get a bound on one or more dimensions, abort
return None, None

min_bounds = np.array([min_width, min_length, min_height])
max_bounds = np.array([max_width, max_length, max_height])

# Extract a list of possible shapes
if isinstance(self.shape, Shape):
shapes = [self.shape]
elif isinstance(self.shape, MultiplexerDistribution) and all(
isinstance(opt, Shape) for opt in self.shape.options
):
shapes = self.shape.options
else:
# Something we don't recognize, abort
return None, None

# Define helper class
class InradiusHelper:
def __init__(self, support):
self.support = support
# Get the inradius for each shape with the min and max bounds
min_distances = [
MeshVolumeRegion(mesh=shape.mesh, dimensions=min_bounds).inradius
for shape in shapes
]
max_distances = [
MeshVolumeRegion(mesh=shape.mesh, dimensions=max_bounds).inradius
for shape in shapes
]

def supportInterval(self):
return self.support
distance_range = (min(min_distances), max(max_distances))

# Extract bounds on all dimensions
min_width, max_width = supportInterval(width)
min_length, max_length = supportInterval(length)
min_height, max_height = supportInterval(height)
return distance_range

if None in [min_width, max_width, min_length, max_length, min_height, max_height]:
# Can't get a bound on one or more dimensions, abort
return 0
# Define a helper function that computes the actual inradius
@distributionFunction(support=inradiusSupport)
def inradiusActual(width, length, height, shape):
return MeshVolumeRegion(
mesh=shape.mesh, dimensions=(width, length, height)
).inradius

min_bounds = np.array([min_width, min_length, min_height])
max_bounds = np.array([max_width, max_length, max_height])
# Return the inradius (possibly a distribution) with proper support information
return inradiusActual(self.width, self.length, self.height, self.shape)

# Extract a list of possible shapes
if isinstance(shape, Shape):
shapes = [shape]
elif isinstance(shape, MultiplexerDistribution):
if all(isinstance(opt, Shape) for opt in shape.options):
shapes = shape.options
@cached_property
def planarInradius(self):
"""A lower bound on the planar inradius of this object.
This is defined as the inradius of the polygon of the occupiedSpace
of this object projected into the XY plane, assuming that pitch and
roll are both 0.
"""

# Define a helper function that computes the support of the inradius,
# given the sub supports.
def planarInradiusSupport(width_s, length_s, shape_s):
# Unpack the dimension supports (and ignore the shape support)
min_width, max_width = width_s
min_length, max_length = length_s

if None in [min_width, max_width, min_length, max_length]:
# Can't get a bound on one or more dimensions, abort
return None, None

min_bounds = np.array([min_width, min_length, 1])
max_bounds = np.array([max_width, max_length, 1])

# Extract a list of possible shapes
if isinstance(self.shape, Shape):
shapes = [self.shape]
elif isinstance(self.shape, MultiplexerDistribution) and all(
isinstance(opt, Shape) for opt in self.shape.options
):
shapes = self.shape.options
else:
# Something we don't recognize, abort
return 0
return None, None

# Get the inradius of the projected for each shape with the min and max bounds
min_distances = [
MeshVolumeRegion(
mesh=shape.mesh, dimensions=min_bounds
).boundingPolygon.inradius
for shape in shapes
]
max_distances = [
MeshVolumeRegion(
mesh=shape.mesh, dimensions=max_bounds
).boundingPolygon.inradius
for shape in shapes
]

# Check that all possible shapes contain the origin
if not all(shape.containsCenter for shape in shapes):
# One or more shapes has inradius 0
return 0
distance_range = (min(min_distances), max(max_distances))

# Get the inradius for each shape with the min and max bounds
min_distances = [
MeshVolumeRegion(mesh=shape.mesh, dimensions=min_bounds).inradius
for shape in shapes
]
max_distances = [
MeshVolumeRegion(mesh=shape.mesh, dimensions=max_bounds).inradius
for shape in shapes
]
return distance_range

distance_range = (min(min_distances), max(max_distances))
# Define a helper function that computes the actual planarInradius
@distributionFunction(support=planarInradiusSupport)
def planarInradiusActual(width, length, shape):
return MeshVolumeRegion(
mesh=shape.mesh, dimensions=(width, length, 1)
).boundingPolygon.inradius

return InradiusHelper(support=distance_range)
# Return the planar inradius (possibly a distribution) with proper support information
return planarInradiusActual(self.width, self.length, self.shape)

@cached_property
def surface(self):
Expand Down
Loading

0 comments on commit c4895b4

Please sign in to comment.