Groups

Placing every object by absolute coordinate is tedious and brittle. Interior design is inherently relational — “the chairs go around the table”, “the desks form a grid”, “the nightstands sit beside the bed”. IDSDL captures this with groups: spatial abstractions that take high-level placement intent and resolve it into concrete coordinates.

Your first group

Every group is a Python context manager. Inside the block you declare placements; you never compute positions:

with scene.RelativeGroup() as seating:
    sofa  = scene.AddAsset("a modern gray sofa")
    table = scene.AddAsset("a wooden coffee table")
    seating.set_anchor(sofa)
    seating.place_on_front(table)
# <- the group compiles here, when the `with` block closes

your first group top-down your first group perspective
The group this snippet builds: the coffee table lands in front of the sofa, and the compiled pair now moves as one unit.

The placement calls are recorded, not executed. When the block exits, the group compiles — placements replay in a sensible order, child groups compile first, the optimizer resolves overlaps — and then freezes into a single rigid unit that behaves like one object: it has its own bounding box, can be rotated and placed, and can be handed to another group as if it were a single asset. That lifecycle (and the hierarchies it enables) is illustrated step by step in Hierarchical Layout & Parent–Child Relationships.

Choosing a group

Ask one question: what does the arrangement relate to?

  • Everything relates to one object (“beside the bed”, “on the sideboard”) → RelativeGroup, and that object is the anchor.
  • Everything surrounds one object (“chairs around the table”) → AroundGroup — also anchored, but placement wraps the anchor.
  • The objects form a pattern among themselves (“rows of desks”) — no anchor → GridGroup, which arranges around its own center.
  • The arrangement is the room itself — walls, doors, named floor and wall positions → RoomGroup, the top-level container everything else lands in.
Group Purpose Key methods
RelativeGroup Place objects relative to an anchor (in front of, beside, behind, on top of). set_anchor, place_on_front/left/right/back, place_on_*_further, place_on_top, place_rug
AroundGroup Arrange objects around an anchor — rings, circles, arcs. set_anchor, place_rectilinear, place_circle, place_arc
GridGroup Arrange objects in regular patterns — rows, grids, borders, arcs. place_row, place_grid, place_rectilinear, place_arc
RoomGroup The top-level container — a room with walls, grid positions, and wall-mounted objects. place_on_center/back/front/..., place_on_*_wall_*, place_walls, place_door, place_window_*

Beyond the four

The core four are just the vocabulary the language ships with. IDSDL is designed to grow new groups, and it has grown them three different ways:

  • A hand-built group that wraps another tool — SentenceASCIIGenerator lays out copies of an object in the shape of text, with a well-prompted language model drawing the glyphs underneath.
  • Groups the agent wrote itself — the motif groups (stacks, pyramids, piles, symmetric pairs, facing rows, concentric rings) were authored by the coding agent following the repo’s add-placement-group skill.
  • Groups built with a human in the loop — richer composites like MirrorStationGroup, WorkstationGroup, and KitchenIslandGroup, created when specific scenes needed arrangements that carry real design judgment.

Every route ends in the same place: a first-class group with the same lifecycle, knobs, and constraint hooks as the core four. Extending the Language tells the full story.

Natural variation: sparsity, randomness, jitter

Every layout group takes two dials, both in [0, 1], both deterministic under the scene seed: sparsity opens up the spacing, and jitter (called randomness on GridGroup/RoomGroup) perturbs position and facing. The 0.0 defaults reproduce the exact deterministic layout, so both are strictly opt-in — and each group applies them within its own contract (a SymmetryGroup pair jitters mirrored — both twins move by the same mirrored draw, staying exactly symmetric about the anchor; stacked crates only slide within the footprint below, a pile ignores jitter; the gradient-solving groups repair any overlap the jitter introduces). The same seeded scene, with only the dial changing:

jitter buys the lived-in, pulled-out-chair look — positions and facing loosen, the arrangement stays a dining table:

dining scene jitter sweep

The same dial on a RelativeGroup bedroom — the bed keeps its two nightstands at the headboard corners (place_on_back_left / place_on_back_right), but each one slides in its slot and turns off square:

bedroom relative-group jitter sweep

sparsity buys space — 0 is the group’s tightest packing, higher values step every placement further out. The same bedroom, sweeping sparsity instead:

bedroom relative-group sparsity sweep

and on a GridGroup classroom it opens the aisles:

classroom scene sparsity sweep

Each layout-group page shows its own configuration swept across these dials, so you can see exactly what each value buys you.


Next: start with RelativeGroup — the anchor concept it introduces carries through every other group page.


Table of contents


This site uses Just the Docs, a documentation theme for Jekyll.