Skip to main content

Create a geometry view

This guide explains how to add a GeometryView to an editor. This can be used to visualize 3D models.

There are 2 ways to create a GeometryResult:

  1. From geometry elements from the viktor.geometry module
  2. From a glTF/GLB file (>= v12.12.0)

A geometry view can be combined with a data view using a GeometryAndDataView (and corresponding GeometryAndDataResult)

Geometry view using geometry elements (3D)

Geometry view using geometry elements (2D)

Geometry elements

A GeometryResult can be constructed using geometry elements (TransformableObject) from the viktor.geometry module. Only 3D elements can be visualized (e.g. an extrusion); sketch elements (e.g. Point, Line) are only used to construct other visualization elements. The created 3D visualization uses a right-handed axis system with Z pointing upwards by default (see view settings to overwrite this).

Groups

A visualization group (Group) can be used to combine multiple visualization elements. A Group can contain two types of objects: Group, TransformableObject. This enables a hierarchical construction of visualization elements.

Example:

from viktor.geometry import Group, Point, Spherefrom viktor.views import GeometryResult, GeometryViewclass Controller(ViktorController):    ...    @GeometryView("Solar system", duration_guess=1)    def visualize_solar_system(self, params, **kwargs):        origin = Point(0, 0)        # distance to sun scaled by 1/100        distance_to_sun = {            'earth': 1496000,            'moon': 1496000 + 38440,  # distance moon-earth scaled by 1/10            'mars': 2279200,        }        earth = Sphere(Point(distance_to_sun['earth'], 0), 6371)        moon = Sphere(Point(distance_to_sun['moon'], 0), 1737.5)        earth_system = Group([earth, moon])        mars = Sphere(Point(distance_to_sun['mars'], 0), 3389.5)        sun = Sphere(origin, 695508)        solar_system = Group([sun, mars, earth_system])        return GeometryResult(solar_system)

Specifying color

Every TransformableObject has an assigned default material. This material can be overwritten with your own, specifying, for example, the color:

from viktor import Color...earth = Sphere(..., material=Material('water', color=Color(0, 0, 255))# orearth = Sphere(...)earth.material = Material('water', color=Color(0, 0, 255))

Transformations

Transformation methods can be called on a TransformableObject, to position and orientate the object. The Vector object can come in handy if your visualization requires vector transformations. The following methods are available on all geometric objects (through inheritance of TransformableObject):

For each type of transformation an example is provided below. Chaining of transformations is possible, however be careful with the specified order.

translate

from viktor.geometry import Point, Spheresphere = Sphere(Point(0, 0, 0), radius=10)# sphere is now centered around (0, 0, 0)sphere.translate([10, 0, 0])# sphere is now centered around (10, 0, 0)

rotate

import mathfrom viktor.geometry import Line, Point, RectangularExtrusionbox = RectangularExtrusion(1, 1, line=Line(Point(0, 0, -0.5), Point(0, 0, 0.5)))# box is now centered around (0, 0, 0)box.rotate(math.pi / 4, direction=[0, 0, 1])# box is now rotated 45 degrees around the z-axis according to the right-hand-rule

mirror

from viktor.geometry import Point, Spheresphere = Sphere(Point(0, 0, 1), radius=10)# sphere is now centered around (0, 0, 1)sphere.mirror(Point(0, 0, 0), [0, 0, 1])# sphere is a now mirrored in the x-y plane (normal vector in z-direction), centered around (0, 0, -1)

scale

from viktor.geometry import Point, Spheresphere = Sphere(Point(0, 0, 1), radius=10)# sphere is now centered around (0, 0, 1)sphere.scale([2, 2, 1])# sphere is stretched in x- and y-direction, centered around (0, 0, 1)

glTF/GLB file

New in v12.12.0

A GeometryResult can also be constructed from a glTF/GLB (v2.0) file.

Geometry view showing glTF model (up-axis = 'Y')

This can be an existing local model:

geometry = File.from_path(Path(__file__).parent / "my_model.gltf")  # or .glb
return GeometryResult(geometry)

Or one available on the internet:

geometry = File.from_url("https://github.com/KhronosGroup/glTF-Sample-Models/raw/master/2.0/CesiumMilkTruck/glTF-Binary/CesiumMilkTruck.glb")
return GeometryResult(geometry)

Or can be generated by means of trimesh (or any other Python geometry package that allows for exporting to glTF/GLB):

import trimesh
...

sphere = trimesh.creation.uv_sphere(10)
scene = trimesh.Scene(geometry={'sphere': sphere})

geometry = File() # create a writable file
with geometry.open_binary() as w:
w.write(trimesh.exchange.gltf.export_glb(scene))

return GeometryResult(geometry)
note

glTF/GLB models are defined with the Y-axis up. In VIKTOR the default up-axis is the Z-axis, resulting in the model to be shown on its side. In order to overwrite this, you can set the up_axis manually in the view settings:

@GeometryView("My glTF View", up_axis='Y')
...

Labels

Regardless of how the geometry is defined (geometry elements or glTF/GLB), additional labels can be added to the visualization:

from viktor.views import Labelfrom viktor.geometry import Point...labels = [    Label(Point(0, 0, 0), "Origin"),    Label(Point(1, 0, 0), "x=1")]return GeometryResult(solar_system, labels=labels)

View settings

The following settings can be applied to a GeometryView:

  • view_mode: str value to switch between '3D' (default) and '2D'. The created 2D visualization shows the xy-plane.
  • default_shadow: bool value (False by default) to define whether shadow is turned on or off when entering the editor. User can still overrule.
  • up_axis (>= v12.12.0): str value ('Y' or 'Z') to define the upwards pointing axis (view_mode = '3D' only, default: 'Z').

Example:

@GeometryView("Top view", duration_guess=1, view_mode='2D', default_shadow=False)
...

Testing

New in v13.3.0

Methods decorated with @GeometryView or @GeometryAndDataView need to be mocked within the context of (automated) testing.

import unittestfrom viktor.testing import mock_Viewfrom app.my_entity_type.controller import MyEntityTypeControllerclass TestMyEntityTypeController(unittest.TestCase):    @mock_View(MyEntityTypeController)    def test_geometry_view(self):        params = ...        result = MyEntityTypeController().geometry_view(params=params)        self.assertEqual(result.geometry, ...)        self.assertEqual(result.labels, ...)