Skip to main content

Generate valid positions

Use PointsGenerator to generate valid source or receiver positions in a room model. You can generate random positions that satisfy a PointRuleset, or generate positions on a uniform grid. Both approaches can enforce minimum distances from room model surfaces and from existing points such as source positions.

Use the following methods to generate positions:

  • generate_valid_points() returns random positions that satisfy a PointRuleset.
  • generate_valid_grid() returns positions on a uniform grid that satisfy distance and coordinate constraints.

The examples below use a project named "Valid positions example", a polygonal room model, two source positions, and a PointsGenerator. Run the following setup before you generate positions:

from treble_tsdk import treble

# This example requires SDK credentials configured in the default location.
tsdk = treble.TSDK()
project = tsdk.get_or_create_project("Valid positions example")

# Create a polygonal room.
polygon_points = [
[3.5, 0],
[2.5, 0],
[2.5, 2.5],
[0, 2.5],
[0, 3.5],
[2.5, 3.5],
[3, 6],
[3.5, 3.5],
[6, 3.5],
[6, 2.5],
[4.5, 1.5],
]
poly_room = treble.GeometryDefinitionGenerator.create_polygon_room(
points_xy=polygon_points,
height_z=2.2,
join_wall_layers=False,
)

poly_model = project.add_model("Polygon room", poly_room)

# Define two source positions in the room.
source_height = 1.1
source_pos = [
treble.Point3d(3, 3, source_height),
treble.Point3d(3, 1, source_height),
]

pg = treble.PointsGenerator()

Generate random positions

Define point validation rules

Define a PointRuleset before you generate random positions. The following example defines rules for receiver positions:

  • The generated points must be at least 0.250.25 m from all room model surfaces.
  • The generated points must be at least 0.50.5 m from each other.
  • The generated points must be at least 0.50.5 m from existing sources.
  • The generated points must be at least 0.50.5 m from existing receivers.
receivers_ruleset = treble.PointRuleset(
min_dist_from_surface=0.25,
min_dist_from_other_points=0.5,
min_dist_from_sources=0.5,
min_dist_from_receivers=0.5,
)

Generate random valid receiver positions

Use generate_valid_points() to generate 15 random receiver positions in the room model. Pass existing_source_points=source_pos to keep each receiver at least 0.50.5 m from the source positions. The z_range argument constrains the generated receivers to the same height as the sources:

# Generate 15 random valid points inside the room.
n_new_points = 15
receiver_pos = pg.generate_valid_points(
poly_model,
n_new_points,
receivers_ruleset,
existing_source_points=source_pos,
z_range=(source_height, source_height),
)
caution

generate_valid_points() aims to generate the requested number of points. If the specified rules are too strict, the function may return fewer points.

Use random positions in a simulation

Create Treble sources and receivers from the generated random positions:

# Create omni sources from source_pos.
sources = [treble.Source.make_omni(pos, label=f"Src{idx}") for idx, pos in enumerate(source_pos)]

# Create mono receivers from the generated positions.
receivers = [treble.Receiver.make_mono(pos, label=f"Rcv{idx}") for idx, pos in enumerate(receiver_pos)]

Add everything to a simulation definition and plot the setup:

sim = treble.SimulationDefinition(
name="test_sim",
simulation_type=treble.SimulationType.ga,
model=poly_model,
receiver_list=receivers,
source_list=sources,
material_assignment=[
treble.MaterialAssignment(layer, tsdk.material_library.get_by_name("10% Absorption"))
for layer in poly_model.layer_names
],
ir_length=1,
)
sim.plot()

The image below shows the two sound sources in green and the generated receivers in blue.

Two green sound sources and blue generated receiver positions in a polygonal room.

Generate grids

Generate a valid receiver grid

Generate a receiver grid with the following constraints:

  • The horizontal grid resolution is 0.20.2 m.
  • The vertical grid resolution is 0.50.5 m.
  • The grid fills the entire room model along the xx- and yy-axes.
  • Each generated point is at least 0.250.25 m from the room model surfaces.
  • Each generated point is at least 0.50.5 m from the source positions.

Call generate_valid_grid() on a PointsGenerator to generate the receiver positions. The x_res, y_res, and z_res arguments set the spacing between grid points, while existing_points, min_dist_surf, and min_dist_existing_points define the validity rules. The function returns Point3d objects that match the specified constraints:

# Generate a grid of valid receiver positions in the room.
grid_receiver_pos = pg.generate_valid_grid(
model=poly_model,
x_res=0.2,
y_res=0.2,
z_res=0.5,
existing_points=source_pos,
min_dist_surf=0.25,
min_dist_existing_points=0.5,
)

Set grid density by point count

Use n_grid_points_x, n_grid_points_y, and n_grid_points_z instead of x_res, y_res, and z_res when you want to define the number of grid points along each axis. generate_valid_grid() splits each full axis into the requested number of points:

grid_receiver_pos_by_count = pg.generate_valid_grid(
model=poly_model,
n_grid_points_x=25,
n_grid_points_y=25,
n_grid_points_z=6,
existing_points=source_pos,
min_dist_surf=0.25,
min_dist_existing_points=0.5,
)
info

The *_res and n_grid_points_* arguments are mutually exclusive. Define only one of them per axis.

Use grid positions in a simulation

Create Treble sources and receivers from the generated grid coordinates:

# Create omni sources from source_pos.
sources = [treble.Source.make_omni(pos, label=f"Src{idx}") for idx, pos in enumerate(source_pos)]

# Create mono receivers from the generated grid positions.
grid_receivers = [treble.Receiver.make_mono(pos, label=f"Rcv{idx}") for idx, pos in enumerate(grid_receiver_pos)]

Add everything to a SimulationDefinition and plot the setup:

grid_sim = treble.SimulationDefinition(
name="valid_grid_preview",
simulation_type=treble.SimulationType.ga,
model=poly_model,
receiver_list=grid_receivers,
source_list=sources,
material_assignment=[
treble.MaterialAssignment(
layer,
tsdk.material_library.get_by_name("10% Absorption"),
)
for layer in poly_model.layer_names
],
ir_length=1,
)
grid_sim.plot()

The images below show the two sound sources in green and the generated receivers in blue.

Horizontal view3D view
Top view of a polygonal room with green source positions and blue receiver grid positions.Three-dimensional view of a polygonal room with green source positions and blue receiver grid positions.

Limit grid coordinates

Use x_range, y_range, or z_range to limit generated grid points to part of a room model. The following example keeps the same grid resolution and validity rules as the basic grid example, but limits the receiver grid to positions with zz-coordinates between 0.750.75 m and 1.751.75 m:

# Generate valid receiver positions within a limited height range.
grid_receiver_pos_limited = pg.generate_valid_grid(
model=poly_model,
x_res=0.2,
y_res=0.2,
z_res=0.5,
z_range=(0.75, 1.75 + 1e-10),
existing_points=source_pos,
min_dist_surf=0.25,
min_dist_existing_points=0.5,
)

The coordinate range arguments use half-open intervals:

caution

x_range, y_range, and z_range include the lower limit and exclude the upper limit, like numpy.arange(). With z_range=(0.75, 1.75) and z_res=0.5, generate_valid_grid() returns positions at z=0.75z=0.75 m and z=1.25z=1.25 m, but not z=1.75z=1.75 m. Add a small offset to the upper limit when you need to include the final grid plane.

Use this pattern to include the upper limit:

z_lower_limit = 0.75
z_upper_limit = 1.75
z_range = (z_lower_limit, z_upper_limit + 1e-10)