Skip to content

Commit

Permalink
Voxel to Mesh Workaround
Browse files Browse the repository at this point in the history
  • Loading branch information
Eric-Vin committed Mar 15, 2024
1 parent 9ebfe93 commit 60e3dac
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 42 deletions.
45 changes: 35 additions & 10 deletions src/scenic/core/pruning.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,14 +240,26 @@ def pruneContainment(scenario, verbosity):
# We can do an exact erosion
container = container.buffer(-maxErosion)
elif isinstance(container, MeshVolumeRegion):
# We can attempt to erode a voxel approximation of the MeshVolumeRegion.
eroded_container = container._erodeOverapproximate(
maxErosion, PRUNING_PITCH
)
current_pitch = PRUNING_PITCH
eroded_container = None

while eroded_container is None:
# We can attempt to erode a voxel approximation of the MeshVolumeRegion.
eroded_container = container._erodeOverapproximate(
maxErosion, PRUNING_PITCH
)

# Now check if this erosion is useful, i.e. do we have less volume to sample from.
# If so, replace the original container.
if eroded_container.size < container.size:
if isinstance(eroded_container, VoxelRegion):
eroded_container = eroded_container.mesh

current_pitch = min(2 * current_pitch, 1)

# Now check if this erosion is valid and useful, i.e. do we have less volume
# to sample from. If so, replace the original container.
if (
eroded_container is not None
and eroded_container.size < container.size
):
container = eroded_container

# Restrict the base region to the possibly eroded container, unless
Expand Down Expand Up @@ -416,9 +428,22 @@ def bufferHelper(viewRegion):
if needsSampling(viewRegion):
return viewRegion._bufferOverapproximate(buffer_quantity, 1)
else:
return viewRegion._bufferOverapproximate(
buffer_quantity, PRUNING_PITCH
)
current_pitch = PRUNING_PITCH
buffered_container = None

while buffered_container is None:
buffered_container = viewRegion._bufferOverapproximate(
buffer_quantity, current_pitch
)

if isinstance(buffered_container, VoxelRegion):
buffered_container = buffered_container.mesh

current_pitch = min(2 * current_pitch, 1)

assert buffered_container is not None

return buffered_container
else:
return viewRegion

Expand Down
14 changes: 7 additions & 7 deletions src/scenic/core/regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2227,6 +2227,7 @@ def dilation(self, iterations, structure=None):
)
return VoxelRegion(voxelGrid=new_voxel_grid)

@cached_property
def mesh(self):
# Extract values for original voxel grid and the surface of the voxel grid.
dense_encoding = self.voxelGrid.encoding.dense
Expand Down Expand Up @@ -2262,10 +2263,6 @@ def actual_face(index):
[base_index[0] + 1, base_index[1], base_index[2]]
)
if actual_face(target_index):
# # Check for an existing interior/exterior face. If an interior is found,
# # wipe it. If an exterior is found, don't set this one.
# conflicting_points = [(p, fm) for p, fm in point_face_mask_list
# if p[1] == base_index[1] and p[2] == base_index[2]]
face_mask[0] = True

# Left
Expand Down Expand Up @@ -2411,9 +2408,12 @@ def actual_face(index):
)

out_mesh = trimesh.Trimesh(**trimesh.triangles.to_kwargs(triangles))
out_mesh.show()
assert out_mesh.is_volume
return out_mesh

# TODO: Ensure the mesh is a proper volume
if not out_mesh.is_volume:
return None
else:
return MeshVolumeRegion(out_mesh, centerMesh=False)

@property
def AABB(self):
Expand Down
27 changes: 13 additions & 14 deletions src/scenic/core/scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,6 @@ def __init__(
self._instances + paramDeps + tuple(requirementDeps) + tuple(behaviorDeps)
)

self.validate()

# Setup the default checker
self.defaultRequirements = self.generateDefaultRequirements()
self.setSampleChecker(WeightedAcceptanceChecker(bufferSize=100))
Expand Down Expand Up @@ -347,6 +345,19 @@ def validate(self):
# Trivial case where container is empty
if isinstance(container, EmptyRegion):
raise InvalidScenarioError(f"Container region of {oi} is empty")
# Ensure we are not sampling position from AllRegion
if isinstance(
oi.position._conditioned, PointInRegionDistribution
) and isinstance(oi.position._conditioned.region, AllRegion):
if oi.position.tag == "visible":
raise InvalidScenarioError(
f"Object {oi} uses the visible specifier to specify position, but it lacks enough information to do so."
f" The simplest solution to this is to define a workspace or specify position in some other fashion."
)
else:
raise InvalidScenarioError(
f"Object {oi} has position sampled from everywhere."
)
# skip objects with unknown positions or bounding boxes
if not staticBounds[i]:
continue
Expand All @@ -371,18 +382,6 @@ def validate(self):
raise InvalidScenarioError(
f"{oi} at {oi.position} intersects" f" {oj} at {oj.position}"
)
if isinstance(oi.position, PointInRegionDistribution) and isinstance(
oi.position.region, AllRegion
):
if oi.position.tag == "visible":
raise InvalidScenarioError(
f"Object {oi} uses the visible specifier to specify position, but it lacks enough information to do so."
f" The simplest solution to this is to define a workspace or specify position in some other fashion."
)
else:
raise InvalidScenarioError(
f"Object {oi} has position sampled from everywhere."
)

def generate(self, maxIterations=2000, verbosity=0, feedback=None):
"""Sample a `Scene` from this scenario.
Expand Down
3 changes: 3 additions & 0 deletions src/scenic/syntax/translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,4 +687,7 @@ def isModularScenario(thing):
if usePruning:
pruning.prune(scenario, verbosity=errors.verbosityLevel)

# Validate scenario
scenario.validate()

return scenario
11 changes: 0 additions & 11 deletions tests/core/test_regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,17 +606,6 @@ def test_mesh_voxelization(getAssetPath):
assert vr.containsPoint(sampled_pt)


def test_voxel_to_mesh(getAssetPath):
plane_region = MeshVolumeRegion.fromFile(getAssetPath("meshes/classic_plane.obj.bz2"))
vr = plane_region.voxelized(max(plane_region.mesh.extents) / 100)
mr = vr.mesh

points = [vr.uniformPointInner() for _ in range(100)]

for pt in points:
assert vr.containsPoint(pt) == mr.containsPoint(pt)


def test_empty_erosion():
box_region = BoxRegion(position=(0, 0, 0), dimensions=(1, 1, 1))
vr = box_region.voxelized(pitch=0.1)
Expand Down

0 comments on commit 60e3dac

Please sign in to comment.