Skip to content

Commit

Permalink
Merge pull request #35 from A-Chaudhary/rain-angle
Browse files Browse the repository at this point in the history
Rain angle for directional vertex selection during Erosion simulation
  • Loading branch information
A-Chaudhary committed May 6, 2023
2 parents 55ba8db + f9f4721 commit 6b87d79
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 157 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.3.0
current_version = 0.4.0
commit = True
tag = True

Expand Down
1 change: 1 addition & 0 deletions age3d/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
get_mask,
mesh_subdivision,
calculate_bounds_height,
find_accessible,
erode,
)
2 changes: 1 addition & 1 deletion age3d/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.3.0"
__version__ = "0.4.0"
81 changes: 77 additions & 4 deletions age3d/age3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def export_mesh(file_path: str, mesh):
Args:
file_path (str): The file path where the mesh will be saved.
mesh: The mesh to be exported.
Returns:
Expand Down Expand Up @@ -57,7 +58,7 @@ def mesh_details(mesh) -> tuple:
Returns:
tuple: A tuple of two numpy arrays. The first array contains the vertices of the mesh
and the second array contains the triangles of the mesh.
and the second array contains the triangles of the mesh.
"""
return (np.asarray(mesh.vertices), np.asarray(mesh.triangles))

Expand All @@ -68,6 +69,7 @@ def visualize(entries, show_wireframe=False) -> None:
Args:
entries: The mesh/meshes to be visualized. It can be a single mesh or a list of meshes.
show_wireframe (bool): A flag to show/hide the wireframe of the mesh/meshes.
Returns:
Expand All @@ -86,6 +88,7 @@ def get_mask(mesh, idx):
Args:
mesh: The mesh for which the mask is to be created.
idx: The index of the vertex for which the mask is to be created.
Returns:
Expand All @@ -103,7 +106,9 @@ def find_minimum(mesh, k: int = 1, idx_mask=[]):
Args:
mesh (open3d.geometry.TriangleMesh): The input mesh.
k (int, optional):: The number of minimum vertices to be found.
idx_mask (list, optional): A list of indices of vertices to be considered for finding minimum vertices.
Returns:
Expand Down Expand Up @@ -133,7 +138,9 @@ def find_maximum(mesh, k: int = 1, idx_mask=[]):
Args:
mesh (open3d.geometry.TriangleMesh): The input mesh.
k (int, optional): The number of maximum values to return. Defaults to 1.
idx_mask (list, optional): List of vertex indices to consider for the maximum search. Defaults to empty list.
Returns:
Expand Down Expand Up @@ -163,7 +170,9 @@ def find_all_below(mesh, value: float, inclusive=False):
Args:
mesh (open3d.geometry.TriangleMesh): The input mesh.
value (float): The value below which to find vertices.
inclusive (bool, optional): Whether to include vertices with value equal to the given value. Defaults to False.
Returns:
Expand Down Expand Up @@ -194,7 +203,9 @@ def find_all_above(mesh, value: float, inclusive=False):
Args:
mesh (open3d.geometry.TriangleMesh): The input mesh.
value (float): The value above which to find vertices.
inclusive (bool, optional): Whether to include vertices with value equal to the given value. Defaults to False.
Returns:
Expand Down Expand Up @@ -225,7 +236,9 @@ def find_all_between(mesh, lower_value: float, higher_value: float) -> np.ndarra
Args:
mesh (open3d.geometry.TriangleMesh): A triangle mesh object.
lower_value (float): The lower bound of the z-coordinate.
higher_value (float): The higher bound of the z-coordinate.
Returns:
Expand All @@ -247,6 +260,7 @@ def make_point_cloud(vertices, color):
Args:
vertices (np.ndarray): A NumPy array of vertices.
color (tuple): A tuple of RGB values (0-255).
Returns:
Expand All @@ -264,6 +278,7 @@ def find_neighbors(mesh, index: int):
Args:
mesh (open3d.geometry.TriangleMesh): A triangle mesh object.
index (int): The index of the vertex.
Returns:
Expand All @@ -287,6 +302,7 @@ def mesh_subdivision(mesh, iterations=1):
Args:
mesh (open3d.geometry.TriangleMesh): A triangle mesh object.
iterations (int): The number of times to subdivide the mesh.
Returns:
Expand Down Expand Up @@ -314,14 +330,68 @@ def calculate_bounds_height(mesh):
return min(min_x_vertex[2], max_x_vertex[2], min_y_vertex[2], max_y_vertex[2])


def erode(mesh: o3d.geometry.TriangleMesh, iterations: int = 2, erosion_lifetime: int = 10, verbose: list = []):
def find_accessible(mesh, rain_direction):
"""
Returns the indices and coordinates of the vertices accessible by a given angle of particles' direction.
Args:
mesh (open3d.geometry.TriangleMesh): The mesh to check.
rain_direction (numpy.ndarray): The direction angle of the rain as a numpy array of shape (3,).
If direction is [0,0,0], every vertex is chosen.
Returns:
Tuple[np.ndarray, np.ndarray]: A tuple of two NumPy arrays
representing the neighboring vertex indices and coordinates.
"""
rain_direction = np.asarray(rain_direction)
# if rain_direction[2] == 0:
# return np.array([]), np.array([])
raycast_angle = rain_direction * -1
# print(raycast_angle, type(rain_direction))
mesh_vertices_np = np.asarray(mesh.vertices)

angle_np = np.full(mesh_vertices_np.shape, raycast_angle)
ray_np = np.append(mesh_vertices_np, angle_np, axis=1)

rays = o3d.core.Tensor(ray_np, dtype=o3d.core.Dtype.Float32)
# print('ray', rays)
rays[:, 0] += 1e-6 * np.sign(raycast_angle[0])
rays[:, 1] += 1e-6 * np.sign(raycast_angle[1])
rays[:, 2] += 1e-6 * np.sign(raycast_angle[2])

# print('ray added', rays)
raycasting_scene = o3d.t.geometry.RaycastingScene()
mesh_legacy = o3d.t.geometry.TriangleMesh.from_legacy(mesh)
_ = raycasting_scene.add_triangles(mesh_legacy)
collision = raycasting_scene.cast_rays(rays)

idx = []
res = []
collisions_hit = collision['t_hit'].numpy()
# print('hit', collisions_hit)

for v, vertex in enumerate(mesh_vertices_np):
if collisions_hit[v] == np.inf:
idx.append(v)
res.append(vertex)
return np.array(idx), np.array(res).reshape((-1, 3))


def erode(mesh: o3d.geometry.TriangleMesh, iterations: int = 2, erosion_lifetime: int = 10, direction=None, verbose=[]):
"""
Erodes the mesh using the particle deposition and erosion method.
Args:
mesh (open3d.geometry.TriangleMesh): The mesh to be eroded.
iterations (int, optional): The number of iterations for the erosion process. Defaults to 2.
erosion_lifetime (int, optional): The maximum number of times a vertex can be eroded. Defaults to 10.
direction (numpy.ndarray, optional): The direction of the rain as a numpy array of shape (3,).
If not provided, the vertex mask is calculated from the mesh's bounding box. Defaults to None.
verbose (list[str], optional): A list of strings containing information to be printed.
Possible strings include 'all', 'vertex_progression', 'vector_direction',
'vector_angle', 'ray', 'ray_scene', 'mesh', and 'collision'.
Expand All @@ -346,7 +416,10 @@ def erode(mesh: o3d.geometry.TriangleMesh, iterations: int = 2, erosion_lifetime
if len(verbose) > 0:
print('Printing with Settings:', verbose)

vertices_idx, vertices = find_all_above(mesh, calculate_bounds_height(mesh), True)
if direction is None:
vertices_idx, vertices = find_all_above(mesh, calculate_bounds_height(mesh), True)
else:
vertices_idx, vertices = find_accessible(mesh, rain_direction=direction)

set_vertices_idx = set()
for idx in vertices_idx:
Expand Down Expand Up @@ -377,7 +450,7 @@ def erode(mesh: o3d.geometry.TriangleMesh, iterations: int = 2, erosion_lifetime
neighbors_idx, _ = find_neighbors(new_mesh, v_idx_curr)
v_idx_next = int(find_minimum(new_mesh, 1, neighbors_idx)[0])

if v_idx_next not in vertices_idx:
if v_idx_next not in vertices_idx and direction is None:
break

if v_idx_prev:
Expand Down
162 changes: 160 additions & 2 deletions age3d/examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -425,13 +425,41 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Erode"
"## Find Accessible"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Open3D WARNING] [ViewControl] ConvertToPinholeCameraParameters() failed because orthogonal view cannot be translated to a pinhole camera.\n"
]
}
],
"source": [
"direction = [0, 0, -1]\n",
"accessible_idx, accessible_vertices = a3d.find_accessible(mesh, direction)\n",
"accessible_pc = a3d.make_point_cloud(accessible_vertices, (255, 0, 0))\n",
"a3d.visualize([mesh, accessible_pc], show_wireframe=True)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Erode"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
Expand Down Expand Up @@ -551,7 +579,137 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"a3d.visualize([eroded_mesh, updated_pc])"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Iter: 0 , V_idx: 7693\n",
"Iter: 1 , V_idx: 1537\n",
"Iter: 2 , V_idx: 6131\n",
"Iter: 3 , V_idx: 1454\n",
"Iter: 4 , V_idx: 5660\n",
"Iter: 5 , V_idx: 1288\n",
"Iter: 6 , V_idx: 5911\n",
"Iter: 7 , V_idx: 6360\n",
"Iter: 8 , V_idx: 4954\n",
"Iter: 9 , V_idx: 7694\n",
"Iter: 10 , V_idx: 6119\n",
"Iter: 11 , V_idx: 3023\n",
"Iter: 12 , V_idx: 5750\n",
"Iter: 13 , V_idx: 6003\n",
"Iter: 14 , V_idx: 6121\n",
"Iter: 15 , V_idx: 7661\n",
"Iter: 16 , V_idx: 1448\n",
"Iter: 17 , V_idx: 5992\n",
"Iter: 18 , V_idx: 1296\n",
"Iter: 19 , V_idx: 6747\n",
"Iter: 20 , V_idx: 1615\n",
"Iter: 21 , V_idx: 6376\n",
"Iter: 22 , V_idx: 2978\n",
"Iter: 23 , V_idx: 7713\n",
"Iter: 24 , V_idx: 5667\n",
"Iter: 25 , V_idx: 3001\n",
"Iter: 26 , V_idx: 2939\n",
"Iter: 27 , V_idx: 5793\n",
"Iter: 28 , V_idx: 3017\n",
"Iter: 29 , V_idx: 5897\n",
"Iter: 30 , V_idx: 1462\n",
"Iter: 31 , V_idx: 1935\n",
"Iter: 32 , V_idx: 6400\n",
"Iter: 33 , V_idx: 5785\n",
"Iter: 34 , V_idx: 1954\n",
"Iter: 35 , V_idx: 1454\n",
"Iter: 36 , V_idx: 1289\n",
"Iter: 37 , V_idx: 1769\n",
"Iter: 38 , V_idx: 3087\n",
"Iter: 39 , V_idx: 5854\n",
"Iter: 40 , V_idx: 6354\n",
"Iter: 41 , V_idx: 6035\n",
"Iter: 42 , V_idx: 3025\n",
"Iter: 43 , V_idx: 1453\n",
"Iter: 44 , V_idx: 4999\n",
"Iter: 45 , V_idx: 4971\n",
"Iter: 46 , V_idx: 5748\n",
"Iter: 47 , V_idx: 3073\n",
"Iter: 48 , V_idx: 5833\n",
"Iter: 49 , V_idx: 1258\n",
"Iter: 50 , V_idx: 1625\n",
"Iter: 51 , V_idx: 6023\n",
"Iter: 52 , V_idx: 7773\n",
"Iter: 53 , V_idx: 6087\n",
"Iter: 54 , V_idx: 6362\n",
"Iter: 55 , V_idx: 6069\n",
"Iter: 56 , V_idx: 4998\n",
"Iter: 57 , V_idx: 5982\n",
"Iter: 58 , V_idx: 5754\n",
"Iter: 59 , V_idx: 4990\n",
"Iter: 60 , V_idx: 2598\n",
"Iter: 61 , V_idx: 6393\n",
"Iter: 62 , V_idx: 399\n",
"Iter: 63 , V_idx: 6099\n",
"Iter: 64 , V_idx: 6417\n",
"Iter: 65 , V_idx: 4951\n",
"Iter: 66 , V_idx: 6348\n",
"Iter: 67 , V_idx: 3027\n",
"Iter: 68 , V_idx: 5670\n",
"Iter: 69 , V_idx: 5712\n",
"Iter: 70 , V_idx: 1516\n",
"Iter: 71 , V_idx: 3016\n",
"Iter: 72 , V_idx: 2927\n",
"Iter: 73 , V_idx: 5000\n",
"Iter: 74 , V_idx: 5891\n",
"Iter: 75 , V_idx: 5904\n",
"Iter: 76 , V_idx: 5676\n",
"Iter: 77 , V_idx: 5009\n",
"Iter: 78 , V_idx: 5654\n",
"Iter: 79 , V_idx: 3004\n",
"Iter: 80 , V_idx: 1487\n",
"Iter: 81 , V_idx: 2968\n",
"Iter: 82 , V_idx: 5854\n",
"Iter: 83 , V_idx: 394\n",
"Iter: 84 , V_idx: 5833\n",
"Iter: 85 , V_idx: 2929\n",
"Iter: 86 , V_idx: 1506\n",
"Iter: 87 , V_idx: 5044\n",
"Iter: 88 , V_idx: 3046\n",
"Iter: 89 , V_idx: 780\n",
"Iter: 90 , V_idx: 5941\n",
"Iter: 91 , V_idx: 6064\n",
"Iter: 92 , V_idx: 355\n",
"Iter: 93 , V_idx: 5821\n",
"Iter: 94 , V_idx: 1936\n",
"Iter: 95 , V_idx: 4956\n",
"Iter: 96 , V_idx: 6062\n",
"Iter: 97 , V_idx: 5687\n",
"Iter: 98 , V_idx: 120\n",
"Iter: 99 , V_idx: 192\n"
]
}
],
"source": [
"updated_idxs, eroded_mesh = a3d.erode(mesh, iterations=100, erosion_lifetime=10, direction=direction)\n",
"eroded_mesh.compute_vertex_normals()\n",
"\n",
"updated_pc = a3d.make_point_cloud(vertices[updated_idxs], (255, 0, 0))\n",
"\n",
"a3d.visualize([eroded_mesh, updated_pc], True)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
Expand Down
Loading

0 comments on commit 6b87d79

Please sign in to comment.