# Configuration and Plan

## Plan Objects

### Object position

Objects are positioned in the room with the bottom center of the objects' bounding box. When some components are added to a configurable object in a room, the bounding box of the object changes, resulting in a displacement of the original components of the object. To compensate for this, the position of the root component of the object is retained.

| original object                                                      | modified object                                                      | compensated position                                                 |
| -------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- |
| ![configurable object position in plan](/files/IuTFHApybHpoLoLZi7aI) | ![configurable object position in plan](/files/IWbibRCXLw77RjRwdagM) | ![configurable object position in plan](/files/O8upByqF2ZpaOBmutzHu) |

### Snapping

Snapping of an object in the plan is done with the following stages. If the information to perform a stage is not available, the next stage is used. If a stage leads to a snapping, the following stages are not executed and the snapping is finished.

1. Snap Vector
2. Bounding geometry
3. Bounding box for measurement
4. Bounding box of the geometry

### Box for measurement

Usually the bottom center of the bounding box is used to position the object in the plan. If the object is a configurable object, and the bounding box for measurement is set, the bottom center of the bounding box for measurement is used to position the object in the plan.

For example, the bounding box for the measurement can be set to the carcass of a cabinet so that the cabinet is positioned in plan with the bottom center of the carcass, regardless of the components that are added to the cabinet.

![configurable object position in plan](/files/92iUTrMwRJDew0j1RXnb)

### Bounding geometry

Objects are snapped by the projected rectangle of the bounding box of the geometry or bounding box for measurement in the ground plane. If a bounding geometry is defined, the bounding geometry is used to snap the object in the plan. The bounding geometry is projected in the ground plan and the resulting 2D contour is used to snap the objects.

![object snapping bounding geometry](/files/BFSbgf2nT7u2UQXQWKnE)

[**Example - snapping with bounding geometry**](https://www.roomle.com/t/cp/?moc=true\&catalogRootTag=roomle_script_documentation_root\&configuratorId=roomleplay\&id=ps_uvnfvdkcf75sphphkbn47wj679au2jd)

```json
{
    "id": "roomle_script_documentation:trapezoid_with_bounding_geometry",
     "geometry": "
        AddPrism(1000, Vector2f[{ -500, 500}, {500, 500}, {1077.35, -500}, { -1077.35, -500}]);
        SetObjSurface('roomle_script_documentation:magenta');
    ",
    "boundingGeometry": "
        AddPrism(1000, Vector2f[{ -500, 500}, {500, 500}, {1077.35, -500}, { -1077.35, -500}]);
    "
}
```

### Snap Vector

#### What Are Snapping Vectors?

Snap vectors are directed line segments on an object. They describe where the object can snap, which kind of edge or connector is available there, and which other snap vector types are compatible with it. When two compatible snap vectors geometrically coincide, the planner can snap the moving object into that position. Objects can be snapped left to right, back to back, bottom to top, or by special connector types.

#### What Snapping Vectors Enable

* **Snapping:** When dragging an object near another object, the planner can snap it to the nearest compatible edge or connector.
* **Object alignment:** Compatible vectors define the exact position and orientation for side-by-side, back-to-back and bottom-to-top placement.
* **Stacking:** Top-to-bottom vector pairs allow objects to be placed on top of each other.
* **Collision prevention:** Complete snap vector sets and `collisionbox` entries help the planner detect occupied space more precisely.
* **Dynamic configuration:** Because snap vector properties can be expressions, configurable objects can enable, disable or move snap vectors when their shape changes.

#### Automatic Snapping Vector Generation

To simplify catalog creation, Roomle can automatically generate snapping vectors for your configurable objects if they meet specific configuration criteria. This ensures standard snapping properties work out-of-the-box without requiring you to manually define them in RoomleScript.

**How Roomle Decides to Generate Automatic Vectors**

1. **No Explicit Snap Vectors:** Automatic generation only takes place if **none** of the sub-components in the object have manual snap vectors defined in their script/JSON configuration. If even a single component defines manual snap vectors, automatic generation is completely disabled for the entire object.
2. **Proper Type of Geometry:** The object must not consist solely of primitive/collision bounding shapes (`BOUNDS` type geometry).
3. **Presence of Docking Points:** The system checks the location of the component's **docking points** (connections where components can attach, such as parent/child dockings) to understand the object's physical function. A docking point is associated with a side of the object if it is located very close to it (within a 10% distance tolerance of that side's size).

**Supported Automatic Layouts**

Based on which sides of the object contain physical docking points, Roomle automatically determines the matching shape and generates the correct set of snapping vectors for you:

* **In-Line / Straight Objects (e.g., standard shelves, cabinets):**
  * **Trigger:** Docking points are present on the **Left** and **Right** sides, but none are on the **Front** or **Back** sides.
  * **Generated Vectors:** A standard set of **6 vectors** on the top and bottom rows of the Left, Right, and Back sides.
* **L-Shape / Right-Corner Objects:**
  * **Trigger:** Docking points are present on the **Left** and **Front** sides, but none are on the **Right** or **Back** sides.
  * **Generated Vectors:** A complete corner set of **8 vectors** (Left, Right, Left-Back, and Right-Back) suited for a right-turning corner component.
* **L-Shape / Left-Corner Objects:**
  * **Trigger:** Docking points are present on the **Right** and **Front** sides, but none are on the **Left** or **Back** sides.
  * **Generated Vectors:** A mirrored corner set of **8 vectors** suited for a left-turning corner component.

If the docking points of the component do not match any of these three patterns (for example, if they exist on all four sides, or only on the front), automatic snapping vectors will not be generated, and you must define them manually.

#### Defining Snapping Vectors

A snap vector consists of `positionFrom`, `positionTo`, `type` and `enabled`. The `positionFrom` and `positionTo` values define the start and end point of the vector in the component coordinate system. The direction from `positionFrom` to `positionTo` matters because it is used for orientation and alignment. The `type` defines the vector type. The `enabled` property defines if the vector is active. `positionFrom`, `positionTo`, `type` and `enabled` can be constants or expression scripts. The scripts are re-evaluated when a configuration changes.

The allowed types are `"righttop"`, `"backtop"`, `"lefttop"`, `"rightbottom"`, `"backbottom"`, `"leftbottom"`, `"leftbacktop"`, `"rightbacktop"`, `"leftbackbottom"`, `"rightbackbottom"`, `"plug"`, `"socket"`, `"collisionbox"`, `"verticalleftback"`, `"verticalleftfront"`, `"verticalrightback"` and `"verticalrightfront"`.

| Type                   | Description                 | Typical use                                          |
| ---------------------- | --------------------------- | ---------------------------------------------------- |
| `"righttop"`           | Right side, top row         | Top edge of the right side                           |
| `"rightbottom"`        | Right side, bottom row      | Bottom edge of the right side                        |
| `"lefttop"`            | Left side, top row          | Top edge of the left side                            |
| `"leftbottom"`         | Left side, bottom row       | Bottom edge of the left side                         |
| `"backtop"`            | Back side, top row          | Top edge of the back side                            |
| `"backbottom"`         | Back side, bottom row       | Bottom edge of the back side                         |
| `"leftbacktop"`        | Left back side, top row     | Back-top edge of the left arm of a corner object     |
| `"leftbackbottom"`     | Left back side, bottom row  | Back-bottom edge of the left arm of a corner object  |
| `"rightbacktop"`       | Right back side, top row    | Back-top edge of the right arm of a corner object    |
| `"rightbackbottom"`    | Right back side, bottom row | Back-bottom edge of the right arm of a corner object |
| `"plug"`               | Connector plug              | Point-to-point connector snapping                    |
| `"socket"`             | Connector socket            | Point-to-point connector snapping                    |
| `"collisionbox"`       | Explicit collision box      | Collision volume, not a snapping pair                |
| `"verticalleftback"`   | Vertical left-back edge     | Upright docking edge                                 |
| `"verticalleftfront"`  | Vertical left-front edge    | Upright docking edge                                 |
| `"verticalrightback"`  | Vertical right-back edge    | Upright docking edge                                 |
| `"verticalrightfront"` | Vertical right-front edge   | Upright docking edge                                 |

#### Compatible snap vector pairs

Side-by-side snapping is possible when compatible edge vectors coincide geometrically. The most common pairs are:

| Object A vector    | Object B vector     | Description                                            |
| ------------------ | ------------------- | ------------------------------------------------------ |
| `"righttop"`       | `"lefttop"`         | Right side of A meets left side of B on the top row    |
| `"rightbottom"`    | `"leftbottom"`      | Right side of A meets left side of B on the bottom row |
| `"backbottom"`     | `"backbottom"`      | Back sides meet on the bottom row                      |
| `"backbottom"`     | `"rightbackbottom"` | Back side meets the right back edge of a corner object |
| `"backbottom"`     | `"leftbackbottom"`  | Back side meets the left back edge of a corner object  |
| `"leftbackbottom"` | `"rightbackbottom"` | Back edges of corner objects meet                      |

Top-to-bottom snapping is used for stacking objects:

| Lower object vector | Upper object vector | Description                                                       |
| ------------------- | ------------------- | ----------------------------------------------------------------- |
| `"lefttop"`         | `"leftbottom"`      | Left side, top of the lower object to bottom of the upper object  |
| `"righttop"`        | `"rightbottom"`     | Right side, top of the lower object to bottom of the upper object |
| `"backtop"`         | `"backbottom"`      | Back side, top of the lower object to bottom of the upper object  |

Back-to-back pairs are reverse pairs. The two vectors face each other in opposite directions, and the planner reverses the moving vector for alignment. This is used by `"backtop"`, `"backbottom"`, `"rightbacktop"` and `"rightbackbottom"` combinations. `"leftbacktop"` and `"leftbackbottom"` are not part of the reverse-pair set.

#### Complete snap vector sets

The planner can use snap vectors to derive a precise collision area for the object. For a regular rectangular object, a complete set contains at least one top vector, one bottom vector, one back vector, one left vector and one right vector. A typical rectangular object therefore defines six vectors: left/right top and bottom, plus back top and bottom.

Corner objects can define a complete corner set with eight vectors: `"lefttop"`, `"righttop"`, `"leftbottom"`, `"rightbottom"`, `"leftbacktop"`, `"rightbacktop"`, `"leftbackbottom"` and `"rightbackbottom"`. This lets the planner derive two collision boxes for the two arms of an L-shaped object instead of one large bounding box.

If the set is incomplete, snapping can still work, but collision checks during snapping may be less precise. Use `"collisionbox"` when the occupied space of the object cannot be described well enough by the regular snap vectors.

Snap vectors for regular objects:

| horizontal vectors                                              | vertical and horizontal vectors                               |
| --------------------------------------------------------------- | ------------------------------------------------------------- |
| ![regular horizontal snap vectors](/files/HPmCcIvo22YZkhGCjZZn) | ![regular vertical snap vectors](/files/ryaBPpAlKKzg3E25md1k) |

Snap vectors for corner objects:

| horizontal corner vectors                                      | vertical and horizontal cornervectors                        |
| -------------------------------------------------------------- | ------------------------------------------------------------ |
| ![corner horizontal snap vectors](/files/q9S1tFys3P1fwaoQtcMB) | ![corner vertical snap vectors](/files/iaEi2xzvWv5SfP8w5AhK) |

[**Example - snap vectors**](https://www.roomle.com/t/cp/?moc=true\&catalogRootTag=roomle_script_documentation_root\&configuratorId=roomleplay\&id=ps_uvqvmce0p136ju88zmo7rfz6gl0f6f1)

Backside snap vector example:

```json
{
    "id": "roomle_script_documentation:backside_snap_vector",
    "geometry": "
        AddPrism(100, Vector2f[{-404.5, -250}, {404.5, -250}, {404.5, 250}, {-404.5, 250}]);
        SetObjSurface('roomle_script_documentation:wood_oak_veneer2');
    ",
    "planInteraction": {
        "snapVectors": [
            {
                "type": "backbottom",
                "positionFrom": "{-404.5, -250, 0}",
                "positionTo": "{404.5, -250, 0}",
                "enabled": true
            }
        ]
    }
}
```

Corner snap vector example:

```json
{
    "id": "roomle_script_documentation:corner_snap_vector",
    "geometry": "
        AddPrism(100, Vector2f[{-500, -500}, {500, -500}, {500, 0}, {0, 0}, {0, 500}, {-500, 500}]);
        SetObjSurface('roomle_script_documentation:wood_oak_veneer2');
    ",
    "planInteraction": {
        "snapVectors": [
            {
                "type": "rightbackbottom",
                "positionFrom": "{-500, -500, 0}",
                "positionTo": "{500, -500, 0}",
                "enabled": true
            },
            {
                "type": "leftbackbottom",
                "positionFrom": "{-500, -500, 0}",
                "positionTo": "{-500, 500, 0}",
                "enabled": true
            }
        ]
    }
}
```

Curved shape with snap vector:

```json
{
    "id": "roomle_script_documentation:curved_geometry_snap_vector",
    "geometry": "
        AddPrism(height, Vector2f[{338.5, -304.9}, {569, 252.4}, {443.2, 292.9}, {338.0, 321.2}, {231.3, 341.5}, {122.8, 354.3}, {0.2, 359.2}, { -122.8, 354.3}, { -231.3, 341.5}, { -338.6, 321.0}, { -444.1, 292.7}, { -571.5, 252.4}, { -340.6, -304.9}, { -240.2, -264.6}, { -157.7, -246.4}, { -73.8, -235.8}, {0, -232.9}, {74.0, -235.8}, {157.7, -246.4}, {240.3, -264.7}]);
        SetObjSurface('roomle_script_documentation:wood_maple_veneer');
    ",
    "planInteraction": {
        "snapVectors": [
            {
                "type": "rightbottom",
                "positionFrom": "{338.5, -304.9, 0}",
                "positionTo": "{569, 252.4, 0}",
                "enabled": true
            },
            {
                "type": "leftbottom",
                "positionFrom": "{-340.6, -304.9, 0}",
                "positionTo": "{-571.5, 252.4, 0}",
                "enabled": true
            },
            {
                "type": "leftbottom",
                "positionTo": "{338.5, -304.9, 0}",
                "positionFrom": "{569, 252.4, 0}",
                "enabled": true
            },
            {
                "type": "rightbottom",
                "positionTo": "{-340.6, -304.9, 0}",
                "positionFrom": "{-571.5, 252.4, 0}",
                "enabled": true
            }
        ]
    }
}
```

#### Plug and socket

`"plug"`, `"socket"`

The `plug` and `socket` snap vector types act as a complementary pair—working like a male and female connector. They are used to precisely lock two objects together at defined coordinates (`positionFrom` and `positionTo`).

* **Plug:** Defines the protruding connection point on an object.
* **Socket:** Defines the receiving connection point on another object.

An object with a `plug` snap vector is designed to smoothly snap directly into a compatible `socket` snap vector on another object. This is especially useful for modular items, electrical components, or precise node-to-node connections.

Plug

[**Example - plug and socketsnap vectors**](https://www.roomle.com/t/bo-test/?moc=true\&catalogRootTag=roomle_script_documentation_root\&configuratorId=roomleplay\&id=ps_dyurnehkurjguejio5s05ie8cvigqnh)

```json
"planInteraction": {
    "snapVectors": [
        {
            "type": "plug",
            "positionFrom": "{0, -250, 0}",
            "positionTo": "{0, 250, 0}",
            "enabled": true
        }
    ]
}
```

Socket

```json
"planInteraction": {
    "snapVectors": [
        {
            "type": "socket",
            "positionFrom": "{-350, 750, 0}",
            "positionTo": "{-350, 250, 0}",
            "enabled": true
        }
    ]
}
```

#### Collision box

`"collisionbox"`

The `collisionbox` snap vector type defines an explicit axis-aligned collision box for an object. Unlike the regular snap vector types, it is not used to snap two vectors together. Instead, the two vector positions are interpreted as opposite corners of a box that is used for collision checks and assembled object bounds in the planner.

Use `collisionbox` when the regular snap vectors do not describe the occupied space of the object accurately enough, for example for L-shaped objects, T-shaped objects or objects with overhangs. Multiple `collisionbox` snap vectors can be defined if the object needs more than one collision volume. When `collisionbox` entries are present, the planner uses the union of these boxes for collision checks. `collisionbox` entries do not participate in snap pair matching.

![collision box](/files/l1ercIFEkVFl7KwVMOy0)

```json
"planInteraction": {
    "snapVectors": [
        {
            "type": "collisionbox",
            "positionFrom": "{0, 0, 0}",
            "positionTo": "{200, 500, 900}",
            "enabled": true
        },
        {
            "type": "collisionbox",
            "positionFrom": "{0, 0, 900}",
            "positionTo": "{500, 1000, 950}",
            "enabled": true
        }
    ]
}
```

#### Vertical snapping vectors

`"verticalleftback"`, `"verticalleftfront"`, `"verticalrightback"`, `"verticalrightfront"`

Vertical snap vectors define upright docking edges. They are useful when objects should connect along a vertical edge instead of only along a horizontal top or bottom edge. The `positionFrom` and `positionTo` values should describe the lower and upper point of the same vertical edge.

The matching pairs are `verticalleftback` with `verticalrightback` and `verticalleftfront` with `verticalrightfront`. The `left` and `right` part defines the side of the object that can dock to the opposite side of another object. The `back` and `front` part identifies which vertical edge is used.

```json
"planInteraction": {
    "snapVectors": [
        {
            "type": "verticalleftback",
            "positionFrom": "{-500, -250, 0}",
            "positionTo": "{-500, -250, 900}",
            "enabled": true
        },
        {
            "type": "verticalrightback",
            "positionFrom": "{500, -250, 0}",
            "positionTo": "{500, -250, 900}",
            "enabled": true
        }
    ]
}
```

#### Snap vector authoring tips

Define matching top and bottom vectors where possible. For example, if an object has `"lefttop"`, define the corresponding `"leftbottom"` on the same side. This enables side-by-side snapping, top-to-bottom stacking and more precise collision handling.

Define back vectors when an object should snap to walls, wall corners or another object's back side. `"backtop"` and `"backbottom"` provide the planner with both alignment information and a more complete object outline.

Keep vector directions consistent with the examples. The direction from `positionFrom` to `positionTo` affects how the planner orients the moving object during snapping, especially for back-to-back and corner cases.

Use `enabled` expressions to switch snap vectors on or off when the configurable object's shape changes. If a parameter removes a side, extension, connector or collision volume, the corresponding snap vector should be disabled as well.

### Snap Level

Snap vectors are additional information that is used to snap objects to walls at certain levels. The levels define the heights at which an object is snapped. One level can be defined as the `"default"` level and is then used as the initially selected level when an object is inserted into the plan. If the levels are `"fixedSnapLevels"`, the object cannot be placed off the defined levels, but can only jump from one level to another. The default value for the `"fixedSnapLevels"`, `"default"` and `"fixed"` properties is `false` and the default value for the "enabled" propety is `true`. The default value for the "level" is 0. If more than one level is specified as default level, the behavior is undefined. `"fixedSnapLevels"`, `"level"`, `"default"`, `"enabled"` are either constants or expression scripts. The scripts are re-evaluated when a configuration is changed. Levels can only be defined on the basis of a component, although levels must actually be defined on the basis of objects. So if an object consists of multiple components, the level information is read from the root component.

e.g.

[**Example - snap levels**](https://www.roomle.com/t/cp/?moc=true\&catalogRootTag=roomle_script_documentation_root\&configuratorId=roomleplay\&id=ps_uvwj2blys80rd4qf2y0828us6sbqx8v)

```json
{
    //...
    "fixedSnapLevels": true,
    "planInteraction": {
        "snapLevels": [
            {
                "level": 1000,
                "default": true,
                "enabled": true
            },
            {
                "level": 1600,
                "enabled": true
            }
        ]
    }
}
```

This defines the snap levels 1000 and 1800 for the object, with 1000 being the default level.

### Intersect with walls

In the case of walls, this is a 2-step process. In the plan component itself the state `intersectWithWalls` needs to be set. If this state is not set, the PlanComponent and its underlying SubParts are not checked for intersections with walls. How to intersect the geometry with walls in detail must be defined with the special geometry function `IntersectWithWalls`.

[**Example - cut off by walls**](https://www.roomle.com/t/cp/?moc=true\&catalogRootTag=roomle_script_documentation_root\&configuratorId=roomleplay\&id=ps_w0qm4invn2hhfzxhqyleuisqv63dwzv)

```json
{
    "id": "roomle_script_documentation:worktop_cut_off_by_walls",
    "onUpdate": "
        setBoxForMeasurement(Vector3f{1000, 600, 900}, Vector3f{0, 0, 0});
    ",
    "geometry": "
        AddPlainCube(Vector3f{1000, 600, 850});
        SetObjSurface('roomle_script_documentation:gray');
        AddPlainCube(Vector3f{1400, 1000, 50});
        MoveMatrixBy(Vector3f{ -200, -200, 850});
        SetObjSurface('roomle_script_documentation:texture_4x4');
        IntersectWithWalls();
    ",
    "planInteraction": {
        "intersectWithWalls": "true"
    }
}
```

## Construction objects

### Door arches

In the Roomle Room Planner door arches (opening arches) get drawn in the 2D view for doors and windows. The sizes of these arches are calculated via the size of the object they belong to. The default opening direction is clockwise to the left, that means to the bottom left (left hinged) when you look on the component from above and have not rotated or flipped it.

It is possible to modify these arches by flipping it on the x or y axis. For this there internal parameters available: `rml_flipX` and `rml_flipY`. By setting these to `true` or `false` you can flip the door arch of the object.

It is also possible to define door arches yourself inside the geometry script via the `AddDoorArch(DOOR_CENTER, DOOR_WIDTH, OPENING_DIRECTION)` function:

| Functionname | Parameters                                                    | Description                       |
| ------------ | ------------------------------------------------------------- | --------------------------------- |
| AddDoorArch  | doorCenter: float, doorWidth: float, \*openingDirection: enum | Adds a door arch to the component |

The `doorCenter` property defines the X-position of the door arch center and so affects where the door arch should be placed. The `doorWidth` defines the width (size) of the door arch. The `openingDirection` is an enum value and defines the opening direction (and hinge position) of the door arch. This property is optional and defaults to `LEFT_CLOCKWISE` if not provided.

The available enum values for the opening direction are:

* `LEFT_CLOCKWISE`
* `LEFT_COUNTERCLOCKWISE`
* `RIGHT_CLOCKWISE`
* `RIGHT_COUNTERCLOCKWISE`

<figure><img src="/files/ur4VDpxRhEkn1n6kpxZP" alt=""><figcaption><p>Opening direction enum values for door arches</p></figcaption></figure>

Keep in mind that these opening directions are also affected by the flip properties. So if you have for example a door arch that has an opening direction of `LEFT_CLOCKWISE`, so it opens to the bottom left (left hinged), and you set `rml_flipX` to `true`, the opening direction for this arch changes to `RIGHT_COUNTERCLOCKWISE`, so it opens to the bottom right (right hinged). Flip properties always apply to ALL door arches of the component.

If an object consists of mulitple components, like sub components or docked componenents, all door arches of all components get collected and combined in the object.

All geometry functions, like `moveMatrixBy(...)` and all this stuff also works on door arches. The arches basically behave like if they are a geometry like a cube or similar and so also work exactly the same. You can also put them in object groups and do all the fancy stuff, like already mentioned, they basically behave like all geometries but with the difference that they are only shown inside the planner in 2D view.

A complete configuration could look like this:

```json
{
    "id": "catalog_id:normal_door",
    "geometry": "
        AddCube(Vector3f{400, 10, 2000});
         SetObjSurface('isdt:yellow');
        AddDoorArch(100, 200, LEFT_CLOCKWISE);
        AddDoorArch(300, 200, RIGHT_COUNTERCLOCKWISE);
    "
}
```

This creates a cube with two door arches in the planner 2D view.

### Construction object position

By default, a construction object is positioned in the wall so that the center of the bounding box of the geometry is on the wall axis. The position of the object can be changed by defining the bounding box for measurement. If this is defined, the construction object is positioned so that the center of the bounding box for measurement is on the wall axis.

![construction object position](/files/MGTEBcLeGpjrsM1Elg4b)

**The bounding box for the measurement is not aligned with one side of the wall, but the center of the bounding box for the measurement is placed on the wall axis.**\
It may seem like it would be more logical to align the box on the side of the wall. However, it is not possible to define on which side of the wall. What appears logical to the human eye cannot be easily recognized by the system on the basis of a geometry divided into triangles.

### Flip object

[**Example - flip-x, flip-y**](https://www.roomle.com/t/cp/?moc=true\&catalogRootTag=roomle_script_documentation_root\&configuratorId=roomleplay\&id=ps_uw6y994pah5pr8bsz9kb0fplaxp83qi)

Flip able window template:

```json
{
    "id": "roomle_script_documentation:construction_window_1",
     "parameters": [
        { "key": "width", "type": "Decimal", "defaultValue": 600 },
        { "key": "height", "type": "Decimal", "defaultValue": 800 },
        {
            "key": "rml_flipX", "type": "Boolean", "defaultValue": false,
            "valueObjects": [ { "value": false }, { "value": true } ],
            "visible": true, "visibleInPartList": true, "global": true, "visibleAsGlobal": true, "visibleInPlanner": true
        },
        {
            "key": "rml_flipY", "type": "Boolean", "defaultValue": false,
            "valueObjects": [ { "value": false }, { "value": true } ],
            "visible": true, "visibleInPartList": true, "global": true, "visibleAsGlobal": true, "visibleInPlanner": true
        }
    ],
    "onUpdate": "
        setBoxForMeasurement({width, 120, height},{rml_flipX ? -width : 0, -60, 0});
    ",
    "geometry": "
        BeginObjGroup('window');
        /* ... */
        EndObjGroup();
        ScaleMatrixBy({rml_flipX ? -1 : 1, rml_flipY ? -1 : 1 , 1});
    "
}
```

### Wall thickness

If the window is attached to a wall, the thickness of the wall can be queried using the `getObjectProperty` function, passing the `wallThickness` argument. If the window is not attached to a wall, the returned wall thickness is `0` or the default value.

```js
_.wallThicknessOrZero = getObjectProperty('wallThickness');
_.wallThicknessOr120 = getObjectProperty('wallThickness', 120);
```

[**Example - wall thickness**](https://www.roomle.com/t/cp/?moc=true\&catalogRootTag=roomle_script_documentation_root\&configuratorId=roomleplay\&id=ps_v5928rhecy9rnghova6cz5rzbeso4z8)

Template for flip able window considering wall thickness:

```json
{
    "id": "roomle_script_documentation:window_flip_x_flip_y",
    "parameters": [
        { "key": "width", "type": "Decimal", "defaultValue": 600 },
        { "key": "height", "type": "Decimal", "defaultValue": 800 },
        { "key": "depth", "type": "Decimal", "defaultValue": 60 },
        {
            "key": "rml_flipX", "type": "Boolean", "defaultValue": false,
            "valueObjects": [ { "value": false }, { "value": true } ],
            "visible": true, "visibleInPartList": true, "global": true, "visibleAsGlobal": true, "visibleInPlanner": true
        },
        {
            "key": "rml_flipY", "type": "Boolean", "defaultValue": false,
            "valueObjects": [ { "value": false }, { "value": true } ],
            "visible": true, "visibleInPartList": true, "global": true, "visibleAsGlobal": true, "visibleInPlanner": true
        }
    ],
    "onUpdate": "
        _.wallThickness = getObjectProperty('wallThickness', 120);
        setBoxForMeasurement({width, _.wallThickness, height},{(rml_flipX ? -width : 0), (depth/2-_.wallThickness), 0});
    ",
    "geometry": "
        BeginObjGroup('window');
        /* ... */
        EndObjGroup();
        MoveMatrixBy({0, -depth/2, 0});
        ScaleMatrixBy({rml_flipX ? -1 : 1, rml_flipY ? -1 : 1 , 1});
    "
}
```

### Bounding geometry

With the boundary geometry, the cut-out shape of the wall can be defined.

![construction object bounding geometry](/files/Q0mPTZsWJbDGXSbkwkml)

The boundary geometry must be a prism with the outline contour of the construction object and must be thicker than the wall.

[**Example - construction object with bounding geometry**](https://www.roomle.com/t/cp/?moc=true\&catalogRootTag=roomle_script_documentation_root\&configuratorId=roomleplay\&id=ps_uwhnuix9lggxw5l02windrgurjxgc6j)

```json
{
    "id": "roomle_script_documentation:window_with_bounding_geometry",
    "parameters": [
        {
            "key": "shape", "type": "String", "defaultValue": "rhomb", 
            "validValues": [ "rhomb", "circle" ]
        },
        {
            "key": "width", "type": "Decimal", "defaultValue": 1200 
        }
    ],
    "geometry": "
        if (shape == 'rhomb') {
            AddPrism(40, [{ -width/2, 0}, {0, -width/2}, {width/2, 0}, {0, width/2}]);
            MoveMatrixBy({-width/2, -width/2, -20});
        } else if (shape == 'circle') {
            AddCylinder(width/2, width/2, 40, 64);
            MoveMatrixBy({0, 0, -20});
        }
        RotateMatrixBy({1, 0, 0}, {0, 0, 0}, -90);
        SetObjSurface('roomle_script_documentation:gray_transparent');
    ",
    "boundingGeometry": "
        if (shape == 'rhomb') {
            AddPrism(1000, [{ -width/2, 0}, {0, -width/2}, {width/2, 0}, {0, width/2}]);
            MoveMatrixBy({-width/2, -width/2, -500});
        } else if (shape == 'circle') {
            AddCylinder(width/2, width/2, 1000, 64);
            MoveMatrixBy({0, 0, -500});
        }
        RotateMatrixBy({1, 0, 0}, {0, 0, 0}, -90);
    "
}
```

By combining bounding geometry and box for measurement, it is even possible to create wall niches.

[**Example - wall niche**](https://www.roomle.com/t/cp/?moc=true\&catalogRootTag=roomle_script_documentation_root\&configuratorId=roomleplay\&id=ps_i3qyl3h6mj5u7v5ijcgen78f3xhx8l2)

```json
{
    "id": "roomle_script_documentation:niche_shelf",
    "parameters": [
        { "key": "material", "type": "Material", "defaultValue": "roomle_script_documentation:wood_oak_veneer" },
        { "key": "width", "type": "Decimal", "defaultValue": 600 },
        { "key": "height", "type": "Decimal", "defaultValue": 800 },
        { "key": "depth", "type": "Decimal", "defaultValue": 60 },
        { "key": "shelves", "type": "Integer", "defaultValue": 2 }
    ],
    "onUpdate": "
        _.wallThickness = getObjectProperty('wallThickness', 120);
        setBoxForMeasurement({width, _.wallThickness, height},{0, (depth-_.wallThickness), 0});
    ",
    "geometry": "
        for (_.i = 0; _.i < shelves; _.i++) {
            AddCube({width, depth, 30});
            MoveMatrixBy({0, 0, height * (_.i + 1) / (shelves + 1) - 15});
            SetObjSurface(material);
        }
    ",
    "boundingGeometry": "
        _.wallThickness = getObjectProperty('wallThickness', 120);
        AddPlainCube({width, 1000, height});
        "
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.roomle.com/rubens/content-creation/roomlescript-reference/configurator-planner-interaction.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
