Hey everyone,
For this week’s Snippet Wednesday I’d like to share something I’ve used multiple times in the past when I had to deal with complex geometric operations in a VIKTOR GeometryView
.
The goal
So, let’s say you want to do something out of the ordinary, for this example, let’s say I want to truncate (or slice) a sphere:
VIKTOR provides a Geometry toolbox out-of-the-box, which covers most use-cases. For more advanced features (such as slicing) we should use a dedicated package that is specifically developed to do geometric operations such as trimesh
. This can then be exported to a gltf
file to be rendered in a GeometryView
.
The solution
One possible library to build geometries is Trimesh.
Let’s start with building a spere using trimesh in a VIKTOR GeometryView
:
Creating the sphere
from trimesh.creation import icosphere
...
@GeometryView('Trimesh truncated sphere', duration_guess=1)
def create_truncated_sphere(self, params, **kwargs):
radius = params.radius
height = params.height
# create sphere mesh
sphere_mesh = icosphere(radius=radius)
Next we can define a plane to slice the circle at, in this case obviously based on the height parameter set by the user. That plane can then be used to slice the original sphere mesh using the Trimesh function slice_mesh_plane()
Slicing the sphere
from trimesh.creation import icosphere
from trimesh.intersections import slice_mesh_plane
...
@GeometryView('Trimesh truncated sphere', duration_guess=1)
def create_truncated_sphere(self, params, **kwargs):
radius = params.radius
height = params.height
# create sphere mesh
sphere_mesh = icosphere(radius=radius)
# create intersection plane
plane_normal = (0, 0, -1)
plane_origin = (0, 0, height)
truncated_sphere = slice_mesh_plane(
mesh=sphere_mesh,
plane_normal=plane_normal,
plane_origin=plane_origin,
cap=True,
)
Exporting the scene
Finally, all we need to do is export this whole scene (fun-fact, multiple Trimesh objects can be included in a single scene) to a gltf
object.
scene = Scene(geometry={"truncated_sphere": truncated_sphere})
# export scene as gltf
geom = File() # create a writable file
with geom.open_binary() as w:
w.write(gltf.export_glb(scene))
return GeometryResult(geom, geometry_type="gltf")
So if you ever feel held back by the VIKTOR Geometry toolbox, know that there is always a way to hack yourself around it and create the beautiful visuals you need!!
Full code
For the lazy among us (and let’s face it, a good engineer is a lazy engineer) the full functional app-code for this example can be found here:
Complete code
from trimesh import Scene
from trimesh.creation import icosphere
from trimesh.exchange import gltf
from trimesh.intersections import slice_mesh_plane
from viktor.core import ViktorController, File
from viktor.views import GeometryResult, GeometryView
from viktor.parametrization import NumberField, Parametrization, Lookup
class CalculationParametrization(Parametrization):
radius = NumberField('Radius (r)', suffix='mm', default=10)
height = NumberField('Height (h)', suffix='mm', default=0)
class CalculationController(ViktorController):
label = 'Trimesh scene creator'
parametrization = CalculationParametrization
@GeometryView('Trimesh truncated sphere', duration_guess=1)
def create_truncated_sphere(self, params, **kwargs):
radius = params.radius
height = params.height
# create sphere mesh
sphere_mesh = icosphere(radius=radius)
# create intersection plane
plane_normal = (0, 0, -1)
plane_origin = (0, 0, height)
truncated_sphere = slice_mesh_plane(
mesh=sphere_mesh,
plane_normal=plane_normal,
plane_origin=plane_origin,
cap=True,
)
scene = Scene(geometry={"truncated_sphere": truncated_sphere})
# export scene as gltf
geom = File() # create a writable file
with geom.open_binary() as w:
w.write(gltf.export_glb(scene))
return GeometryResult(geom, geometry_type="gltf")