From bdc1e6b17fbe22062a7c6291263ebdbaaf0bcdc0 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 02:38:38 +0000 Subject: [PATCH 1/2] Add expanded clone mode reference documentation New dedicated page documenting all four street-generated-clones placement modes (fixed, random, single, fit) with ASCII diagrams, property tables, usage examples, and a comparison table. Addresses contributor confusion about mode differences, especially between random (centerpoint-based) and fit (dimension-aware edge-to-edge) placement. Cross-references added from existing docs. https://claude.ai/code/session_01WCrEmcJ4vwoPJimGdKgpdv --- docs/managed-street/clone-modes.md | 260 ++++++++++++++++++ .../component-street-segment.md | 11 +- .../managed-street-json-format.md | 11 +- .../managed-street/overview-managed-street.md | 1 + 4 files changed, 275 insertions(+), 8 deletions(-) create mode 100644 docs/managed-street/clone-modes.md diff --git a/docs/managed-street/clone-modes.md b/docs/managed-street/clone-modes.md new file mode 100644 index 000000000..b0876efe3 --- /dev/null +++ b/docs/managed-street/clone-modes.md @@ -0,0 +1,260 @@ +--- +sidebar_position: 3.5 +--- + +# Clone Mode Reference + +The `street-generated-clones` component places 3D models along the Z-axis (length) of a street segment. The segment extends from `+length/2` to `-length/2`. Four placement modes are available, each designed for different use cases. + +## `mode: "fixed"` — Equal-Spaced Slots + +Places clones at regular intervals along the segment. The number of clones is determined automatically: `numClones = floor(length / spacing)`. The `cycleOffset` property (0–1, default 0.5) shifts the starting position as a fraction of `spacing`. + +Models are placed at their centerpoint regardless of model dimensions — a 5m-wide building and a 20m-wide building both get the same slot size. + +``` +spacing = 15m, cycleOffset = 0.5 +Segment (length = 60m): +| | ++------------------------------------------------------------+ +| X X X X | +| ^ ^ ^ ^ | +| 7.5m from 22.5m 37.5m 52.5m | +| start | ++------------------------------------------------------------+ + +X = model centerpoint +Slot size is always `spacing`, regardless of model size. +Models may overlap if model width > spacing. +Models may have gaps if model width < spacing. + +Example with mixed model sizes (same fixed spacing): ++------------------------------------------------------------+ +| [=====] [==] [=========] [===] | +| X X X X | ++------------------------------------------------------------+ + ←15m→ ←15m→ ←15m→ ←15m→ + Note: models are centered on X, gaps/overlaps vary +``` + +### Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| spacing | number | - | Distance between model centerpoints in meters | +| cycleOffset | number | 0.5 | Starting offset as a fraction of `spacing` (0–1) | + +### Use case + +Regularly spaced objects like street trees, lamp posts, bollards — objects where uniform centerpoint distance matters more than edge-to-edge tiling. + +### Example + +```javascript +{ + mode: "fixed", + modelsArray: "tree3", + spacing: 15 +} +``` + +--- + +## `mode: "random"` — Random Subset of Fixed Grid Positions + +Creates a grid of evenly-spaced candidate positions (same grid as `fixed` mode), then **shuffles** the grid using a seeded Fisher-Yates shuffle and takes the first `count` positions. This means: + +- The minimum distance between any two clones is guaranteed to be `>= spacing` (since they come from a grid) +- Positions are randomly distributed but constrained to grid points +- **Model dimensions are NOT considered** — placement is by centerpoint only +- Multiple `modelsArray` entries are selected randomly per clone (seeded RNG) + +``` +spacing = 10m, count = 3, length = 60m +Grid positions (7 slots): +| -25 -15 -5 5 15 25 35 | ++--------------------------------------------+ +| o o o o o o o | o = grid slot ++--------------------------------------------+ + +After shuffle, pick first 3: ++--------------------------------------------+ +| X X X | X = placed clone ++--------------------------------------------+ + -15 5 25 + +Models placed at centerpoints — no awareness of model width: ++--------------------------------------------+ +| [========] [==] [==========] | +| X X X | ++--------------------------------------------+ + Buildings may overhang or have gaps because + placement is by centerpoint, not by edges. +``` + +### Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| spacing | number | - | Grid interval / minimum distance between centerpoints | +| count | number | - | How many clones to place from the grid | +| seed | number | - | Seed for deterministic random results | + +### Use case + +Scattered objects like parked cars, pedestrians, random vegetation — things that should appear irregularly distributed with guaranteed minimum separation. **NOT suitable for buildings** that need edge-to-edge alignment (use `fit` mode instead). + +### Example + +```javascript +{ + mode: "random", + modelsArray: "sedan-rig, box-truck-rig, self-driving-waymo-car, suv-rig, motorbike", + spacing: 7.3, + count: 4 +} +``` + +--- + +## `mode: "single"` — One Clone + +Places exactly one model. Position is controlled by the `justify` property. + +``` +justify = "start": ++------------------------------------------------------------+ +| X←padding | ++------------------------------------------------------------+ + +justify = "middle" (default): ++------------------------------------------------------------+ +| X | ++------------------------------------------------------------+ + +justify = "end": ++------------------------------------------------------------+ +| padding→X | ++------------------------------------------------------------+ +``` + +### Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| justify | string | "middle" | Position along the segment: `start`, `middle`, or `end` | +| padding | number | 4 | Distance from the edge in meters (used with `start` and `end`) | + +### Use case + +Placing a single landmark, sign, or feature element on a segment. + +### Example + +```javascript +{ + mode: "single", + modelsArray: "bus-stop", + justify: "start", + padding: 4 +} +``` + +--- + +## `mode: "fit"` — Dimension-Aware Tiling + +The only mode that uses model dimensions. Uses a hardcoded lookup table of `buildingWidths` (Z-axis extent) and `buildingDepths` (X-axis extent) for each supported model ID. Models are tiled **end-to-end** along the segment: + +1. Start at `+length/2` +2. Look up the next model's width from the table (default 10m if unknown) +3. Check if it fits in the remaining space +4. Place it so its edge (not center) aligns with the previous model's edge + `spacing` gap +5. Advance by `buildingWidth + spacing` +6. Cycle through `modelsArray` sequentially (index % length) + +The `justifyWidth` property aligns buildings along the X-axis (cross-street direction) using their depth: +- `left`: building's right edge aligns with segment's left edge +- `right`: building's left edge aligns with segment's right edge +- `center`: uses `positionX` as-is + +``` +modelsArray = [A (8m wide), B (12m wide), C (6m wide)], spacing = 1m + ++------------------------------------------------------------+ +|[==A==] [====B====] [=C=] [==A==] [====B====] [=C=] | ++------------------------------------------------------------+ + ← 8m →1← 12m →1←6m→1← 8m →1← 12m →1←6m→ + ↑ spacing gaps + +Edge-to-edge tiling: each building's edges touch the gap, +not centered on uniform grid points. No overlap, no uneven gaps. + +justifyWidth effect (cross-section, X-axis): + Segment Width + ←─────────────────────→ +left: |[Building] | building hugs left edge +center: | [Building] | building centered +right: | [Building]| building hugs right edge +``` + +### Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| modelsArray | string | - | Comma-separated model names, cycled sequentially | +| spacing | number | - | Gap between model edges in meters (not centerpoint distance) | +| justifyWidth | string | "center" | Cross-street alignment: `left`, `center`, or `right` | + +### Use case + +Buildings, walls, fences — anything that should tile seamlessly along a street edge with no overlaps or irregular gaps. + +### Example + +```javascript +{ + mode: "fit", + modelsArray: "SM3D_Bld_Mixed_4fl, SM3D_Bld_Mixed_5fl, SM3D_Bld_Mixed_4fl_2", + spacing: 0, + justifyWidth: "left", + facing: 270 +} +``` + +--- + +## Mode Comparison + +| | Dimension-Aware? | Position Method | Model Selection | Spacing Meaning | +|---|---|---|---|---| +| **fixed** | No | Regular grid | Random\* | Centerpoint distance | +| **random** | No | Shuffled grid | Random\* | Min centerpoint distance | +| **single** | No | `justify` prop | First | N/A | +| **fit** | Yes | Edge-to-edge | Sequential | Gap between edges | + +\* When `modelsArray` has multiple entries and RNG is active. + +## Common Properties (All Modes) + +These properties are available across all clone modes: + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| mode | string | - | Clone placement mode: `fixed`, `random`, `single`, or `fit` | +| modelsArray | string | - | Comma-separated list of model short-names | +| facing | number | 0 | Y-axis rotation in degrees | +| randomFacing | boolean | false | Apply random rotation to each clone | +| positionX | number | 0 | X-axis offset from segment center | +| positionY | number | 0 | Y-axis (vertical) offset | + +## Choosing the Right Mode + +- **Street trees, lamp posts, bollards** at regular intervals: use `fixed` +- **Vehicles, pedestrians, scattered objects** with irregular placement: use `random` +- **A single sign, landmark, or bus stop**: use `single` +- **Buildings, walls, fences** that need to tile without gaps: use `fit` + +:::caution +`random` mode does not consider model dimensions. If you use it with buildings, models will be placed by centerpoint and may overlap or leave irregular gaps. Use `fit` mode for buildings. +::: diff --git a/docs/managed-street/component-street-segment.md b/docs/managed-street/component-street-segment.md index cd88ad106..2c60c036f 100644 --- a/docs/managed-street/component-street-segment.md +++ b/docs/managed-street/component-street-segment.md @@ -128,17 +128,20 @@ The building segment supports multiple preset variants via the `variant` propert #### Fit Mode for Clones -Building segments use a special `fit` mode for the `street-generated-clones` component that intelligently places models based on their actual dimensions: +Building segments use `fit` mode for the `street-generated-clones` component — the only mode that is aware of model dimensions. It tiles models edge-to-edge using a lookup table of known building widths and depths. -**Properties:** +For full documentation of all clone modes (`fixed`, `random`, `single`, `fit`) including diagrams and property tables, see the [Clone Mode Reference](./clone-modes.md). + +**Key `fit` mode properties:** - `mode: 'fit'` - Places models continuously along the segment -- `spacing` - Gap between models in meters (can be negative for overlapping) -- `justifyWidth` - Horizontal alignment: 'left', 'center', or 'right' +- `spacing` - Gap between model edges in meters (not centerpoint distance; can be 0 for seamless tiling) +- `justifyWidth` - Cross-street alignment: `left`, `center`, or `right` The component automatically: - Uses building width and depth from a lookup table - Calculates positions to fit models end-to-end - Adjusts horizontal position based on `justifyWidth` and segment `side` +- Cycles through `modelsArray` sequentially - Stops when remaining space is insufficient **Supported building dimensions** are defined for all StreetPlan models, brownstone buildings, suburban houses, and arcade buildings. See the source code for the complete list. diff --git a/docs/managed-street/managed-street-json-format.md b/docs/managed-street/managed-street-json-format.md index 303bb60c2..ea7797cfb 100644 --- a/docs/managed-street/managed-street-json-format.md +++ b/docs/managed-street/managed-street-json-format.md @@ -90,10 +90,13 @@ clones: [{ ``` #### Clone Modes -- **`random`**: Randomly places `count` models with minimum `spacing` between them -- **`fixed`**: Places models at regular `spacing` intervals with optional `cycleOffset` -- **`single`**: Places a single model (use `justify` property for position) -- **`fit`**: Intelligently places models based on their actual dimensions, fitting them end-to-end along the segment. Ideal for buildings. + +See the [Clone Mode Reference](./clone-modes.md) for detailed documentation with diagrams. + +- **`fixed`**: Places models at regular `spacing` intervals with optional `cycleOffset`. Spacing is between centerpoints. +- **`random`**: Randomly places `count` models from a grid with minimum `spacing` between centerpoints. +- **`single`**: Places a single model (use `justify` property for position). +- **`fit`**: Dimension-aware mode that tiles models end-to-end based on their actual widths. Ideal for buildings, walls, and fences. Common model arrays: - Vehicles: `"sedan-rig, box-truck-rig, self-driving-waymo-car, suv-rig, motorbike"` diff --git a/docs/managed-street/overview-managed-street.md b/docs/managed-street/overview-managed-street.md index d43172629..8535f9e33 100644 --- a/docs/managed-street/overview-managed-street.md +++ b/docs/managed-street/overview-managed-street.md @@ -14,6 +14,7 @@ Users can create streets with the new Managed Street format using the 3DStreet E - [Managed Street Component](./component-managed-street.md) - [Street Segment Component](./component-street-segment.md) +- [Clone Mode Reference](./clone-modes.md) - [Managed Street JSON Format](./managed-street-json-format.md) # Further Research From 4225a927956c553fe4eac950eaedeb9fd598eb26 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 02:46:36 +0000 Subject: [PATCH 2/2] Clarify fixed vs random relationship and broaden fit mode language - Explain that fixed and random share the same grid; fixed fills all slots while random fills a subset. Both randomly select models. - Add "How fixed and random relate" comparison section - Broaden fit mode language to reference walls, fences, seawalls alongside buildings - Remove building-specific caution callout https://claude.ai/code/session_01WCrEmcJ4vwoPJimGdKgpdv --- docs/managed-street/clone-modes.md | 86 ++++++++++++++++++------------ 1 file changed, 52 insertions(+), 34 deletions(-) diff --git a/docs/managed-street/clone-modes.md b/docs/managed-street/clone-modes.md index b0876efe3..56f7e1e59 100644 --- a/docs/managed-street/clone-modes.md +++ b/docs/managed-street/clone-modes.md @@ -6,11 +6,13 @@ sidebar_position: 3.5 The `street-generated-clones` component places 3D models along the Z-axis (length) of a street segment. The segment extends from `+length/2` to `-length/2`. Four placement modes are available, each designed for different use cases. -## `mode: "fixed"` — Equal-Spaced Slots +## `mode: "fixed"` — Fill Every Grid Slot -Places clones at regular intervals along the segment. The number of clones is determined automatically: `numClones = floor(length / spacing)`. The `cycleOffset` property (0–1, default 0.5) shifts the starting position as a fraction of `spacing`. +Places a clone at every slot in an evenly-spaced grid along the segment. The number of clones is determined automatically: `numClones = floor(length / spacing)`. The `cycleOffset` property (0–1, default 0.5) shifts the starting position as a fraction of `spacing`. -Models are placed at their centerpoint regardless of model dimensions — a 5m-wide building and a 20m-wide building both get the same slot size. +Models are placed at their centerpoint regardless of model dimensions. When `modelsArray` contains multiple entries, each clone gets a **randomly selected** model (using seeded RNG) — so despite the name "fixed," only the *positions* are fixed. Model selection is random, the same as `random` mode. + +> **`fixed` vs `random`:** These two modes share the same underlying grid. `fixed` fills *every* grid slot. `random` fills only a *subset* — it shuffles the same grid and takes the first `count` positions. Both randomly select which model to place from `modelsArray`. See [How `fixed` and `random` relate](#how-fixed-and-random-relate) for details. ``` spacing = 15m, cycleOffset = 0.5 @@ -60,14 +62,13 @@ Regularly spaced objects like street trees, lamp posts, bollards — objects whe --- -## `mode: "random"` — Random Subset of Fixed Grid Positions +## `mode: "random"` — Fill Some Grid Slots -Creates a grid of evenly-spaced candidate positions (same grid as `fixed` mode), then **shuffles** the grid using a seeded Fisher-Yates shuffle and takes the first `count` positions. This means: +Creates the same evenly-spaced grid as `fixed` mode, then **shuffles** the grid using a seeded Fisher-Yates shuffle and takes the first `count` positions. The result is a sparse, irregular distribution with a guaranteed minimum separation between clones. -- The minimum distance between any two clones is guaranteed to be `>= spacing` (since they come from a grid) -- Positions are randomly distributed but constrained to grid points -- **Model dimensions are NOT considered** — placement is by centerpoint only -- Multiple `modelsArray` entries are selected randomly per clone (seeded RNG) +- Positions are randomly selected from the grid, so minimum distance is always `>= spacing` +- Model dimensions are not considered — placement is by centerpoint only +- Model selection from `modelsArray` is random per clone (same as `fixed` mode) ``` spacing = 10m, count = 3, length = 60m @@ -82,14 +83,6 @@ After shuffle, pick first 3: | X X X | X = placed clone +--------------------------------------------+ -15 5 25 - -Models placed at centerpoints — no awareness of model width: -+--------------------------------------------+ -| [========] [==] [==========] | -| X X X | -+--------------------------------------------+ - Buildings may overhang or have gaps because - placement is by centerpoint, not by edges. ``` ### Properties @@ -102,7 +95,7 @@ Models placed at centerpoints — no awareness of model width: ### Use case -Scattered objects like parked cars, pedestrians, random vegetation — things that should appear irregularly distributed with guaranteed minimum separation. **NOT suitable for buildings** that need edge-to-edge alignment (use `fit` mode instead). +Scattered objects like parked cars, pedestrians, random vegetation — things that should appear irregularly distributed with guaranteed minimum separation. ### Example @@ -164,18 +157,20 @@ Placing a single landmark, sign, or feature element on a segment. ## `mode: "fit"` — Dimension-Aware Tiling -The only mode that uses model dimensions. Uses a hardcoded lookup table of `buildingWidths` (Z-axis extent) and `buildingDepths` (X-axis extent) for each supported model ID. Models are tiled **end-to-end** along the segment: +The only mode that uses model dimensions. Uses a lookup table of known widths (Z-axis extent) and depths (X-axis extent) for supported model IDs. Models are tiled **end-to-end** along the segment with no overlaps or irregular gaps: 1. Start at `+length/2` 2. Look up the next model's width from the table (default 10m if unknown) 3. Check if it fits in the remaining space 4. Place it so its edge (not center) aligns with the previous model's edge + `spacing` gap -5. Advance by `buildingWidth + spacing` +5. Advance by `modelWidth + spacing` 6. Cycle through `modelsArray` sequentially (index % length) -The `justifyWidth` property aligns buildings along the X-axis (cross-street direction) using their depth: -- `left`: building's right edge aligns with segment's left edge -- `right`: building's left edge aligns with segment's right edge +Unlike `fixed` and `random`, model selection is **sequential** — models cycle through `modelsArray` in order rather than being randomly picked. + +The `justifyWidth` property aligns models along the X-axis (cross-street direction) using their depth: +- `left`: model's right edge aligns with segment's left edge +- `right`: model's left edge aligns with segment's right edge - `center`: uses `positionX` as-is ``` @@ -187,15 +182,15 @@ modelsArray = [A (8m wide), B (12m wide), C (6m wide)], spacing = 1m ← 8m →1← 12m →1←6m→1← 8m →1← 12m →1←6m→ ↑ spacing gaps -Edge-to-edge tiling: each building's edges touch the gap, +Edge-to-edge tiling: each model's edges touch the gap, not centered on uniform grid points. No overlap, no uneven gaps. justifyWidth effect (cross-section, X-axis): Segment Width ←─────────────────────→ -left: |[Building] | building hugs left edge -center: | [Building] | building centered -right: | [Building]| building hugs right edge +left: |[Model] | model hugs left edge +center: | [Model] | model centered +right: | [Model]| model hugs right edge ``` ### Properties @@ -208,11 +203,12 @@ right: | [Building]| building hugs right edge ### Use case -Buildings, walls, fences — anything that should tile seamlessly along a street edge with no overlaps or irregular gaps. +Buildings, walls, fences, seawalls — anything that should tile seamlessly along a street edge with no overlaps or irregular gaps. -### Example +### Examples ```javascript +// Buildings tiled along a street edge { mode: "fit", modelsArray: "SM3D_Bld_Mixed_4fl, SM3D_Bld_Mixed_5fl, SM3D_Bld_Mixed_4fl_2", @@ -220,6 +216,13 @@ Buildings, walls, fences — anything that should tile seamlessly along a street justifyWidth: "left", facing: 270 } + +// Fence sections tiled end-to-end +{ + mode: "fit", + modelsArray: "fence", + spacing: 0 +} ``` --- @@ -248,13 +251,28 @@ These properties are available across all clone modes: | positionX | number | 0 | X-axis offset from segment center | | positionY | number | 0 | Y-axis (vertical) offset | +## How `fixed` and `random` Relate + +These two modes are closely related — they share the same underlying grid mechanism: + +| | `fixed` | `random` | +|---|---|---| +| **Slots filled** | All of them | A subset (`count` out of total) | +| **Position order** | Sequential (slot 0, 1, 2...) | Shuffled (random grid positions) | +| **Starting offset** | Controlled by `cycleOffset` | Starts at `-length/2 + spacing/2` (no `cycleOffset`) | +| **Model selection** | Random from `modelsArray` | Random from `modelsArray` | + +If you set `count` equal to the total number of grid slots, `random` would place the same number of clones as `fixed` — just in shuffled positions and without `cycleOffset` support. + +A simple way to think about it: +- **`fixed`** = fill every grid slot +- **`random`** = fill some grid slots (randomly chosen) + +Both modes randomly select *which model* goes in each slot from `modelsArray`. Neither mode is aware of model dimensions. + ## Choosing the Right Mode - **Street trees, lamp posts, bollards** at regular intervals: use `fixed` - **Vehicles, pedestrians, scattered objects** with irregular placement: use `random` - **A single sign, landmark, or bus stop**: use `single` -- **Buildings, walls, fences** that need to tile without gaps: use `fit` - -:::caution -`random` mode does not consider model dimensions. If you use it with buildings, models will be placed by centerpoint and may overlap or leave irregular gaps. Use `fit` mode for buildings. -::: +- **Buildings, walls, fences, seawalls** that need to tile without gaps: use `fit`