Skip to main content

Inject geometry

A powerful feature of the Treble SDK is its ability to generate and inject geometries to existing models. This can be useful for parametric studies of geometries, programmatic furnishing of rooms, or to simply enable a modular workflow, where single components can be easily reused and adapted for different projects.

This is enabled via few key components,

  • GeometryDefinition - A mutable definition of a geometry object, this consists of a base room geometry, either uploaded or generated (see Create Room) and a collection of GeometryComponent objects.

  • GeometryComponent - A geometry component is a ‘small’ component (such as chairs, desks, etc.) that can be added to a GeometryDefinition to create a more detailed model.

  • GeometryComponentLibrary - A library of geometry components provided by the SDK.

  • GeometryComponentGenerator - Can be used to create simple geometry components such as boxes, planes, triangles and loudspeakers.

Geometry components can be injected into GeometryDefinition objects to form furnished room models.

Utility classes

Note that this functionality relies heavily on various treble_tsdk.utility_classes for representing geometric quantities and transformations, these are available through either the treble_namespace or they can be explicitly imported via utility_classes:

from treble_tsdk import tsdk_namespace as treble

# Define some utility class instances
vector = treble.Vector3d(1, 0, 0)
rotation = treble.Rotation(90, 0, 0)
transform = treble.Transform3d(vector, rotation)

# Explicit import
from treble_tsdk.utility_classes import (
Point3d,
Vector3d,
Transform3d,
Rotation,
BoundingBox,
)
note

Point3d and Vector3d are internally identical, differing only in their semantic usage to distinguish between positional coordinates and directional vectors.

Component Library

The Treble SDK provides a library of geometry components that can be easily retrieved and visualized. This section demonstrates how to access and display these components. We can retrieve a table geometry component from the library and visualize it via:

# Initiate instance of SDK
from treble_tsdk import tsdk_namespace as treble
from treble_tsdk import display_data as dd # for clean display
tsdk = treble.TSDK()

dd.display(tsdk.geometry_component_library.get_groups_with_count())
== SDK package is up to date ==
              Categories              
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃ Name                        Count ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│ chest                      │ 2     │
│ table                      │ 1     │
│ conference table           │ 1     │
│ basin                      │ 3     │
│ dining table               │ 5     │
│ kitchen                    │ 5     │
│ table w chairs             │ 4     │
│ shower                     │ 1     │
│ lamp                       │ 2     │
│ dining table w chairs      │ 2     │
│ desk with chair and books  │ 1     │
│ coffee table               │ 2     │
│ bed table                  │ 2     │
│ chair                      │ 3     │
│ desk with screen           │ 1     │
│ sofa                       │ 6     │
│ wardrobe                   │ 5     │
│ desk with chair and screen │ 1     │
│ wc                         │ 4     │
│ desk with chair            │ 3     │
│ bed                        │ 9     │
│ desk                       │ 1     │
│ bookcase                   │ 2     │
└────────────────────────────┴───────┘

This displays the resulting table, which lists the different categories and the count of available components for each.

Subsequently, we can query the library by:

table_gc_list = tsdk.geometry_component_library.query(group='table')
table_gc_list[0] # GeometryComponent instance
GeometryComponent(id=cfbb7008-62bc-49c3-9e9f-bbb43ed11fa8, name=table_05, group_name=table)

This way we have a GeometryComponent object which we can inject into a GeometryDefinition, see Managing Geometry Components.

Component generators

The Component Generator class provides several methods to generate geometry components directly in the treble SDK. These can simply be accessed either via the TSDK namespace, or an explicit import:

# Access via SDK namespace
from treble_tsdk import tsdk_namespace as treble
treble.GeometryComponentGenerator

# Explicit import
from treble_tsdk.geometry.generator import GeometryComponentGenerator

create_triangle

Triangles can be defined via three Point3d objects, each representing a unique vertex. Here point0 acts as the reference point for translations, and rotations.

point0point1point2

Example:

triangle = GeometryComponentGenerator.create_triangle(
point0=Point3d(0, 0, 0),
point1=Point3d(1, 0, 0),
point2=Point3d(0, 1, 0),
layer_name="my_triangle",
)

create_plane

Creates a 2D rectangle centered in the yzyz-plane via length and width.

lengthwidthOriginyz

Example:

plane = GeometryComponentGenerator.create_plane(
length=2,
width=1,
layer_name="my_plane",
)

create_box

Boxes can be defined through use of treble_tsdk.utility_classes.BoundingBox, which takes two Point3d objects, min and max, or the minimum and maximum points of the box respectively.

minmax

Example:

box = GeometryComponentGenerator.create_box(
BoundingBox(
min=Point3d(0, 0, 0),
max=Point3d(1, 1, 1),
),
layer_name="my_box",
)

create_loudspeaker

Loudspeakers can be defined via their width, height and depth to control the shape of the box enclosure. To place the membrane the parameters membrane_diameter and membrane_center_height are used. The center of the membrane is placed at the origin.

membrane_diameter membrane_center_heightheightwidthdepthzxy

Example:

speaker = GeometryComponentGenerator.create_loudspeaker(
width=0.2,
height=0.4,
depth=0.3,
membrane_diameter=0.1,
membrane_center_height=0.1,
membrane_layer_name="my_membrane",
enclosure_layer_name="my_enclosure"
)

load_3dm

You can load custom geometry components using Rhino .3dm files with this method. The resulting GeometryComponent object retains the original translation and rotation, which will be used as the object’s origin within the scene.

Example:

speaker = GeometryComponentGenerator.load_3dm(
component_name="my_custom_object",
path="path/to/my/3dm/object.3dm"
)

Managing Geometry Components

To manage components within a GeometryDefinition several methods are provided. Below we assume an existing geometry definition named geo_def, see see Create Room for further details, or refer to the full example below.

A simple GeometryDefinition can be created by:

geo_def = treble.GeometryDefinitionGenerator.create_shoebox_room(3, 3, 3)

add_geometry_component

Adds a geometry component to the model with a specified transformation.

Example:

geo_def.add_geometry_component(
name="name",
geometry_component=box,
transform=Transform3d(Vector3d(1, 1, 0.5), Rotation(45, 0, 0)),
)

get_geometry_component_transform

Retrieves a component’s position and rotation for analysis or modification.

Example:

geo_def.get_geometry_component_transform("name") # Transform3d class instance
Transform3d(translation=Vector3d(x=1.0, y=1.0, z=0.5), rotation=Rotation(azimuth=45.0, elevation=0.0, roll=0.0))

set_geometry_component_transform

Updates a component’s position and rotation to refine spatial placement.

Example:

geo_def.set_geometry_component_transform(
name="name",
transform=Transform3d(
Vector3d(2, 2, 0),
Rotation(90, 0, 0),
),
)

set_geometry_component_rotation

Adjusts the rotation of a component without changing its position.

Example:

geo_def.set_geometry_component_rotation("name", Rotation(135, 0, 0))

move_geometry_component

Moves a component by a given vector without redefining its transformation.

Example:

geo_def.move_geometry_component("name", Vector3d(1, 1, 0))

remove_geometry_component

Removes a specified component from the model, keeping the scene clean and adaptable.

Example:

geo_def.remove_geometry_component("name")

Automatic Placement of Components

The GeometryComponentPlacement class defines how a set of geometry components should be placed in a GeometryDefinition. It takes a list of GeometryComponent objects, from which the placement algorithm will randomly select one each time it attempts a placement. This allows for variation while maintaining control over the types of components used.

You can specify a preferred_count, which indicates how many instances you’d like to place. Note that this number is a guideline, and actual placement may vary depending on spatial constraints.

To control placement behavior, you can also define minimum distance thresholds:

  • min_dist_from_walls sets the minimum allowed distance from any wall (in meters).

  • min_dist_from_objects ensures spacing from other placed objects (also in meters).

  • Optionally, you can pass a ComponentAnglePool to control the rotation behavior during placement, allowing for customized orientation logic.

Example:

placements = treble.GeometryComponentPlacement(
components=tsdk.geometry_component_library.query(group="table"),
preferred_count=3,
rotation_settings=treble.ComponentAnglePool([0, 90, 180, 270]),
min_dist_from_walls=0.1,
min_dist_from_objects=0.1,
)

Populating the Room

Once the placements are defined, the room can be populated and visualized. Rerunning this will generate different layouts.

# Clear existing components
geo_def.clear_geometry_components()

# Populate the room
geo_def.populate_with_geometry_components(
components=[placements],
selection_algorithm=treble.ComponentSelectionAlgorithm.random,
)

Selection Algorithms

Different selection algorithms are available to control how components are placed:

  • random: Randomly selects components for placement.
  • ordered_single: Places one instance of each component sequentially.
  • ordered_all: Places all instances of a component before moving to the next.

Example: Generating a Furnished Room

To begin, we must import the required libraries, initialize an instance of TSDK, and load create a new project.

from treble_tsdk import tsdk_namespace as treble
from treble_tsdk.utility_classes import (
Transform3d,
Vector3d,
Rotation,
Point3d,
BoundingBox,
)
from treble_tsdk import display_data as dd

tsdk = treble.TSDK()
p = tsdk.get_or_create_project("my_geometry_test_project")

Create a Model and Add Geometry

Next, we generate a T-shaped room and visualize its structure.

geo_def = treble.GeometryDefinitionGenerator.create_T_shaped_room(
a_side=6,
b_side=2,
c_side=2,
d_side=2,
height_z=2.5,
)
geo_def.plot()
T-shaped room.

Add Geometry Components

We query the component library for tables, and choose the first component from the resulting list,

gc = tsdk.geometry_component_library.query(group='table')[0]
gc.plot()
Example table.

Now, we place multiple tables in the room at specific positions and orientations.

geo_def.add_geometry_component(
"my_table", gc, Transform3d(Vector3d(1, 1, 0), Rotation(0, 0, 0))
)
geo_def.add_geometry_component(
"my_table2", gc, Transform3d(Vector3d(5, 1, 0), Rotation(0, 0, 0))
)
geo_def.add_geometry_component(
"my_table3", gc, Transform3d(Vector3d(3, 1, 0), Rotation(0, 0, 0))
)
geo_def.plot()
Room with table.

Add a Box and Loudspeaker

We create a box, place it inside the room.

box = treble.GeometryComponentGenerator.create_box(
BoundingBox(Point3d(-0.5, 0.5, -0.5), Point3d(0.5, -0.5, 0.5))
)
geo_def.add_geometry_component(
"name", box, Transform3d(Vector3d(3, 3, 0.5), Rotation(45, 0, 0))
)
geo_def.plot()
Room with box.

Next, we position a loudspeaker on top.

speaker = treble.GeometryComponentGenerator.create_loudspeaker(
width=0.2, height=0.3, depth=0.5, membrane_diameter=0.1, membrane_center_height=0.1
)
geo_def.add_geometry_component(
"my_speaker", speaker, Transform3d(Vector3d(3, 3, 1.1), Rotation(-90, 0, 0))
)
geo_def.plot()
Room with box and loudspeaker.

Finalizing for Simulation

Once the geometry is finalized, we can add the model a project.

p.delete_model(p.get_model_by_name("my_model"))
model = p.add_model("my_model", geo_def)
model.as_live_model_status()
my_model
├── Id: 5b027c50-9bff-4ee7-81c8-4f5ebe705dbe
├── Status: Valid
├── Status message: OK
├── Is watertight: True
├── Filename: joined_model.zip
├── Filesize: 16.58KB
└── Layers: ['box', 'tshape_walls', 'tshape_ceiling', 'tshape_floor', 'enclosure', 'membrane', 'Furniture/Dining ta
Done