Skip to content

Commit

Permalink
Merge pull request #47 from phohenberger/main
Browse files Browse the repository at this point in the history
Add static mesh, new shapes, dynamic color and variable render resolution
  • Loading branch information
SamTov authored Aug 7, 2024
2 parents 8bcf3b8 + e0a0c10 commit 3f3ddfa
Show file tree
Hide file tree
Showing 22 changed files with 944 additions and 151 deletions.
2 changes: 1 addition & 1 deletion CI/unit_tests/mesh/test_cylinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def test_build_cylinder(self):
-------
Test if a sphere mesh is constructed correctly.
"""
cylinder = self.cylinder.create_mesh(
cylinder = self.cylinder.instantiate_mesh(
starting_position=np.array([1, 1, 1]),
starting_orientation=np.array([1, 1, 1]),
)
Expand Down
2 changes: 1 addition & 1 deletion CI/unit_tests/mesh/test_sphere.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def test_build_sphere(self):
-------
Test if a sphere mesh is constructed correctly.
"""
sphere = self.sphere.create_mesh(starting_position=np.array([1, 1, 1]))
sphere = self.sphere.instantiate_mesh(starting_position=np.array([1, 1, 1]))
self.assertEqual(sphere.has_vertex_normals(), True)
self.assertEqual(type(sphere), o3d.geometry.TriangleMesh)
np.testing.assert_almost_equal(sphere.get_center(), [1.0, 1.0, 1.0])
123 changes: 123 additions & 0 deletions examples/all_shapes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""
ZnVis: A Zincwarecode package.
License
-------
This program and the accompanying materials are made available under the terms
of the Eclipse Public License v2.0 which accompanies this distribution, and is
available at https://www.eclipse.org/legal/epl-v20.html
SPDX-License-Identifier: EPL-2.0
Copyright Contributors to the Zincwarecode Project.
Contact Information
-------------------
email: [email protected]
github: https://github.com/zincware
web: https://zincwarecode.com/
Citation
--------
If you use this module please cite us with:
Summary
-------
Tutorial script to visualize simple spheres over a random trajectory.
"""

import numpy as np

import znvis as vis

if __name__ == "__main__":
"""
Run the all shapes example.
"""

material_1 = vis.Material(colour=np.array([30, 144, 255]) / 255, alpha=0.9)
# Define the sphere.
trajectory = np.random.uniform(-10, 10, (10, 1, 3))
mesh = vis.Sphere(radius=2.0, material=material_1, resolution=30)
particle = vis.Particle(name="Sphere", mesh=mesh, position=trajectory)

material_2 = vis.Material(colour=np.array([255, 140, 0]) / 255, alpha=1.0)
# Define the cylinder.
trajectory_2 = np.random.uniform(-10, 10, (10, 1, 3))
mesh_2 = vis.Cylinder(radius=1.0,
height=2.0,
split=1,
material=material_2,
resolution=30)
particle_2 = vis.Particle(name="Cylinder", mesh=mesh_2, position=trajectory_2)

material_3 = vis.Material(colour=np.array([100, 255, 130]) / 255, alpha=1.0)
# Define the icosahedron.
trajectory_3 = np.random.uniform(-10, 10, (10, 1, 3))
mesh_3 = vis.Icosahedron(radius=2.0, material=material_3)
particle_3 = vis.Particle(name="Icosahedron", mesh=mesh_3, position=trajectory_3)

material_4 = vis.Material(colour=np.array([255, 200, 50]) / 255, alpha=1.0)
# Define the torus.
trajectory_4 = np.random.uniform(-10, 10, (10, 1, 3))
mesh_4 = vis.Torus(torus_radius=1.0,
tube_radius=0.5,
tubular_resolution=30,
radial_resolution=30,
material=material_4)
particle_4 = vis.Particle(name="Torus", mesh=mesh_4, position=trajectory_4)

material_5 = vis.Material(colour=np.array([250, 50, 20]) / 255, alpha=1.0)
# Define the mobius loop.
trajectory_5 = np.random.uniform(-10, 10, (10, 1, 3))
mesh_5 = vis.MobiusLoop(twists=3,
radius=2,
flatness=1,
width=2, scale=1,
length_split=200,
width_split=200,
material=material_5)
particle_5 = vis.Particle(name="MobiusLoop", mesh=mesh_5, position=trajectory_5)

material_6 = vis.Material(colour=np.array([255, 90, 255]) / 255, alpha=1.0)
# Define the octahedron.
trajectory_6 = np.random.uniform(-10, 10, (10, 1, 3))
mesh_6 = vis.Octahedron(radius=2.0, material=material_6)
particle_6 = vis.Particle(name="Octahedron", mesh=mesh_6, position=trajectory_6)

material_7 = vis.Material(colour=np.array([255, 220, 100]) / 255, alpha=1.0)
# Define the tetrahedron.
trajectory_7 = np.random.uniform(-10, 10, (10, 1, 3))
mesh_7 = vis.Tetrahedron(radius=2.0, material=material_7)
particle_7 = vis.Particle(name="Tetrahedron", mesh=mesh_7, position=trajectory_7)

material_8 = vis.Material(colour=np.array([255, 200, 240]) / 255, alpha=1.0)
# Define the arrow.
trajectory_8 = np.random.uniform(-10, 10, (10, 1, 3))
direction_8 = np.random.uniform(-1, 1, (10, 1, 3))
mesh_8 = vis.Arrow(scale=2, material=material_8, resolution=30)
particle_8 = vis.Particle(name="Arrow",
mesh=mesh_8,
position=trajectory_8,
director=direction_8)

material_9 = vis.Material(colour=np.array([150, 255, 230]) / 255, alpha=1.0)
# Define the box.
trajectory_9 = np.random.uniform(-10, 10, (10, 1, 3))
mesh_9 = vis.Box(width=1, height=3, depth=0.5, material=material_9)
particle_9 = vis.Particle(name="BoxMesh", mesh=mesh_9, position=trajectory_9)

material_10 = vis.Material(colour=np.array([255, 10, 100]) / 255, alpha=1.0)
# Define the cone.
trajectory_10 = np.random.uniform(-10, 10, (10, 1, 3))
mesh_10 = vis.Cone(radius=1.0, height=2.0, material=material_10, resolution=30)
particle_10 = vis.Particle(name="Cone", mesh=mesh_10, position=trajectory_10)

particle_list = [particle, particle_2, particle_3, particle_4, particle_5,
particle_6, particle_7, particle_8, particle_9, particle_10]

# Create a bounding box
bounding_box = vis.BoundingBox(
center=np.array([0, 0, 0]), box_size=np.array([20, 20, 20])
)

# Construct the visualizer and run
visualizer = vis.Visualizer(
particles=particle_list, frame_rate=20, bounding_box=bounding_box
)
visualizer.run_visualization()
64 changes: 64 additions & 0 deletions examples/dynamic_coloring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
ZnVis: A Zincwarecode package.
License
-------
This program and the accompanying materials are made available under the terms
of the Eclipse Public License v2.0 which accompanies this distribution, and is
available at https://www.eclipse.org/legal/epl-v20.html
SPDX-License-Identifier: EPL-2.0
Copyright Contributors to the Zincwarecode Project.
Contact Information
-------------------
email: [email protected]
github: https://github.com/zincware
web: https://zincwarecode.com/
Citation
--------
If you use this module please cite us with:
Summary
-------
Tutorial script to visualize simple spheres over a random trajectory.
"""

import numpy as np

import znvis as vis

if __name__ == "__main__":
"""
Run the dynamic color example.
"""

# Create a color list (N_frames, N_particles, 3 (RGB))
# Basically give each particle a specified color for each frame
colours = np.tile([30, 144, 255], (100, 5, 1))
# Change the color of the first particle to red
colours[:, 0, 0] = np.linspace(30, 255, 100)
# Change the color of the second particle to green
colours[:, 1, 1] = np.linspace(144, 255, 100)
colours[:, 1, 2] = np.linspace(255, 30, 100)
# Change the color of the third particle to blue
colours[:, 2, 0] = np.linspace(30, 10, 100)
colours[:, 2, 1] = np.linspace(140, 90, 100)
# Change the color of the fourth particle to white
colours[:, 3, 0] = np.linspace(30, 255, 100)
colours[:, 3, 1] = np.linspace(144, 255, 100)
# Change the color of the fifth particle to black
colours[:, 4, 0] = np.linspace(30, 0, 100)
colours[:, 4, 1] = np.linspace(144, 0, 100)
colours[:, 4, 2] = np.linspace(255, 0, 100)

material_1 = vis.Material(colour=colours / 255, alpha=1.0)
# Define the first particle.
trajectory = np.random.uniform(-5, 5, (1, 5, 3))
trajectory = np.tile(trajectory, (100, 1, 1))
# Turn on dynamic coloring for the mesh
mesh = vis.Sphere(radius=2.0, resolution=20, material=material_1)
particle = vis.Particle(
name="Spheres", mesh=mesh, position=trajectory, smoothing=False
)

# Construct the visualizer and run
visualizer = vis.Visualizer(particles=[particle], frame_rate=20)
visualizer.run_visualization()
72 changes: 72 additions & 0 deletions examples/simple_vector_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
ZnVis: A Zincwarecode package.
License
-------
This program and the accompanying materials are made available under the terms
of the Eclipse Public License v2.0 which accompanies this distribution, and is
available at https://www.eclipse.org/legal/epl-v20.html
SPDX-License-Identifier: EPL-2.0
Copyright Contributors to the Zincwarecode Project.
Contact Information
-------------------
email: [email protected]
github: https://github.com/zincware
web: https://zincwarecode.com/
Citation
--------
If you use this module please cite us with:
Summary
-------
Tutorial script to visualize simple spheres over a random trajectory.
"""

import numpy as np

import znvis as vis

if __name__ == "__main__":
"""
Run the vector field example.
"""

# Build a grid
x_values = np.linspace(-10, 10, 21)
y_values = np.linspace(-10, 10, 21)
z_values = np.linspace(0, 0, 1)

grid = np.meshgrid(x_values, y_values, z_values)
grid = np.array(grid).T.reshape(-1, 3)
grid = np.tile(grid, (100, 1, 1))

# Define arrow mesh and insert in vector field
material = vis.Material(colour=np.array([30, 144, 255]) / 255, alpha=0.6)
mesh = vis.Arrow(scale=0.5, resolution=20, material=material)

directions = np.random.uniform(-1, 1, (100, 441, 3))
# confine the directions to be in the z = 0 plane
directions[:,:,2] = 0

vector_field = vis.VectorField(name="VectorField",
mesh=mesh,
position=grid,
direction=directions)

# Define particles
material_2 = vis.Material(colour=np.array([255, 140, 0]) / 255, alpha=1.0)
mesh_2 = vis.Sphere(radius=1.0, resolution=20, material=material_2)

trajectory_2 = np.random.uniform(-10, 10, (100, 1, 3))
# confine the particles to be in the z = 0 plane
trajectory_2[:,:,2] = 0

particle = vis.Particle(name="Spheres",
mesh=mesh_2,
position=trajectory_2,
smoothing=False)

# Construct the visualizer and run
visualizer = vis.Visualizer(particles=[particle],
vector_field=[vector_field],
frame_rate=20)
visualizer.run_visualization()
15 changes: 15 additions & 0 deletions znvis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@
from znvis.mesh.custom import CustomMesh
from znvis.mesh.cylinder import Cylinder
from znvis.mesh.sphere import Sphere
from znvis.mesh.arrow import Arrow
from znvis.mesh.box import Box
from znvis.mesh.cone import Cone
from znvis.mesh.tetrahedron import Tetrahedron
from znvis.mesh.octahedron import Octahedron
from znvis.mesh.icosahedron import Icosahedron
from znvis.mesh.torus import Torus
from znvis.mesh.mobius_loop import MobiusLoop
from znvis.particle.particle import Particle
from znvis.particle.vector_field import VectorField
from znvis.visualizer.visualizer import Visualizer
Expand All @@ -37,6 +45,13 @@
Particle.__name__,
Sphere.__name__,
Arrow.__name__,
Box.__name__,
Cone.__name__,
Tetrahedron.__name__,
Octahedron.__name__,
Icosahedron.__name__,
Torus.__name__,
MobiusLoop.__name__,
VectorField.__name__,
Visualizer.__name__,
Cylinder.__name__,
Expand Down
49 changes: 22 additions & 27 deletions znvis/mesh/arrow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
@dataclass
class Arrow(Mesh):
"""
A class to produce arrow meshes.
A class to produce arrow meshes. Arrow meshes are a special case and need to
overwrite the instantiate_mesh of the parent mesh class.
Attributes
----------
Expand All @@ -46,44 +47,38 @@ class Arrow(Mesh):
scale: float = 1.0
resolution: int = 10

def create_mesh(
self, starting_position: np.ndarray, direction: np.ndarray = None
def instantiate_mesh(
self, starting_position: np.ndarray, starting_orientation: np.ndarray = None
) -> o3d.geometry.TriangleMesh:
"""
Create a mesh object defined by the dataclass.
Create and correctly orient an arrow mesh. Overwrites the parent class
"""
mesh = self.create_mesh(starting_orientation)
mesh.compute_vertex_normals()
if starting_orientation is not None:
matrix = rotation_matrix(np.array([0, 0, 1]), starting_orientation)
mesh.rotate(matrix, center=(0, 0, 0))

Parameters
----------
starting_position : np.ndarray shape=(3,)
Starting position of the mesh.
direction : np.ndarray shape=(3,) (default = None)
Direction of the mesh.
# Translate the arrow to the starting position and center the origin
mesh.translate(starting_position.astype(float))

Returns
-------
mesh : o3d.geometry.TriangleMesh
"""
return mesh

def create_mesh(self, direction: np.ndarray) -> o3d.geometry.TriangleMesh:
"""
Creates an arrow mesh object.
"""
direction_length = np.linalg.norm(direction)

cylinder_radius = 0.06 * direction_length * self.scale
cylinder_height = 0.85 * direction_length * self.scale
cone_radius = 0.15 * direction_length * self.scale
cone_height = 0.15 * direction_length * self.scale

arrow = o3d.geometry.TriangleMesh.create_arrow(
cylinder_radius=cylinder_radius,
cylinder_height=cylinder_height,
cone_radius=cone_radius,
return o3d.geometry.TriangleMesh.create_arrow(
cylinder_radius=cylinder_radius,
cylinder_height=cylinder_height,
cone_radius=cone_radius,
cone_height=cone_height,
resolution=self.resolution,
)

arrow.compute_vertex_normals()
matrix = rotation_matrix(np.array([0, 0, 1]), direction)
arrow.rotate(matrix, center=(0, 0, 0))

# Translate the arrow to the starting position and center the origin
arrow.translate(starting_position.astype(float))

return arrow
Loading

0 comments on commit 3f3ddfa

Please sign in to comment.