$npx -y skills add AlpacaLabsLLC/skills-for-architects --skill zoning-envelopeGenerate interactive 3D zoning envelope viewers from zoning analysis reports. Use when the user asks to "visualize the zoning envelope" or see buildable massing in 3D, typically after /zoning-analysis-nyc. Requires a zoning analysis report as input.
| 1 | # /zoning-envelope — 3D Zoning Envelope Viewer |
| 2 | |
| 3 | Generate an interactive 3D axonometric zoning envelope viewer as a single HTML file. Uses Three.js with OrbitControls, loaded from a CDN — opens in any modern browser, no install or build step, but an internet connection is required the first time the file is viewed (the Three.js modules are fetched from jsdelivr, not embedded; offline the page loads but the 3D scene will not render). |
| 4 | |
| 5 | **Requires a zoning analysis report** generated by `/zoning-analysis-nyc`. This skill is a renderer — it does not perform zoning calculations. |
| 6 | |
| 7 | ## Project Dossier |
| 8 | |
| 9 | If `PROJECT.md` exists in the working directory, read it before fetching — lot geometry and bulk controls may already be on file from /zoning-analysis-nyc. After completing, append a one-line envelope summary and the viewer file path to its **Zoning** section. Update values in place (the dossier holds current state, not history), every entry with a source and date. No `PROJECT.md`? Skip silently — or mention `/project-dossier init` if the user is clearly starting a project. |
| 10 | |
| 11 | ## Usage |
| 12 | |
| 13 | ``` |
| 14 | /zoning-envelope path/to/zoning-analysis.md |
| 15 | /zoning-envelope 250 hudson |
| 16 | /zoning-envelope |
| 17 | ``` |
| 18 | |
| 19 | ## Step 1: Find the Report |
| 20 | |
| 21 | ### If a `.md` path is provided |
| 22 | Read the file directly. |
| 23 | |
| 24 | ### If a search term is provided (address, etc.) |
| 25 | Search for matching zoning analysis reports in the current working directory. |
| 26 | |
| 27 | Use Glob + Grep to find reports matching the search term. If multiple matches, show the options and ask the user to pick one. |
| 28 | |
| 29 | ### If no argument is provided |
| 30 | Search for the most recently modified `zoning-analysis-*.md` file in the current working directory. If found, confirm with the user. If not found, tell the user: |
| 31 | |
| 32 | > No zoning analysis report found. Run `/zoning-analysis-nyc` first, then come back with `/zoning-envelope`. |
| 33 | |
| 34 | ### If no Envelope Data block is found |
| 35 | If the report exists but lacks the `## Envelope Data` JSON block (older report format), tell the user: |
| 36 | |
| 37 | > This report was generated before the Envelope Data format was added. Re-run the zoning analysis to get an updated report, or I can attempt to parse the tables (results may be approximate). |
| 38 | |
| 39 | ## Step 2: Parse Envelope Data |
| 40 | |
| 41 | Read the `## Envelope Data` JSON block from the report — a fenced code block containing: |
| 42 | |
| 43 | ```json |
| 44 | { |
| 45 | "lot_poly": [[x, y], ...], |
| 46 | "unit": "ft", |
| 47 | "setbacks": { "front": 6, "rear": 3, "lateral1": 3, "lateral2": 2 }, |
| 48 | "volumes": [ |
| 49 | { "type": "base", "inset": 20, "h_bottom": 0, "h_top": 85, "label": "base" }, |
| 50 | { "type": "tower", "inset": 10, "h_bottom": 85, "h_top": 290, "label": "tower" } |
| 51 | ], |
| 52 | "height_cap": 290, |
| 53 | "info": { "title": "...", "zone": "...", "id": "...", "area": "..." }, |
| 54 | "stats": { "key": "value", ... }, |
| 55 | "scenarios": null |
| 56 | } |
| 57 | ``` |
| 58 | |
| 59 | ## Step 3: Normalize to Envelope Model |
| 60 | |
| 61 | From the parsed JSON, build the internal model: |
| 62 | |
| 63 | - `LOT_POLY` — the lot boundary polygon in local units |
| 64 | - `UNIT` — "ft" |
| 65 | - `VOLUMES` — array of volumes to extrude, each with inset distance, height range, label |
| 66 | - `HEIGHT_CAP` — max height for the amber cap plane |
| 67 | - `INFO` — title, zone, id, area for the overlay panels |
| 68 | - `STATS` — key/value pairs for the parameters panel |
| 69 | - `SCENARIOS` — if present, multi-scenario toggle data |
| 70 | |
| 71 | **Compute inset polygons** using the `insetPolygon(poly, distance)` function. For multi-volume envelopes (base + tower), compute the tower inset **from the base inset** (cumulative), not from the lot polygon — so the tower is always smaller than the base. |
| 72 | |
| 73 | **Compute volumes** by extruding inset polygons between height intervals. |
| 74 | |
| 75 | ## Step 3: Generate HTML |
| 76 | |
| 77 | Build a self-contained HTML file following the design system below. |
| 78 | |
| 79 | ### Design System |
| 80 | |
| 81 | | Element | Color | Opacity | |
| 82 | |---------|-------|---------| |
| 83 | | Background | `#f5f3ef` | 1.0 | |
| 84 | | Ground (lot) | `#dcd7cd` | 0.5 | |
| 85 | | Lot boundary | `#2c2c2c` | 1.0 | |
| 86 | | Setback zones | `#c85a50` | 0.2 | |
| 87 | | Base volume faces | `#6ba0c5` | 0.08–0.10 | |
| 88 | | Base volume edges | `#6ba0c5` | 0.30–0.35 | |
| 89 | | Tower/upper volume | `#6ba0c5` | 0.05 | |
| 90 | | Height cap / sky plane | `#e8a849` | 0.10–0.15 | |
| 91 | | Labels | `#333333` | 1.0 (canvas sprites) | |
| 92 | | Grid | `#d0ccc4` | 0.15 | |
| 93 | |
| 94 | **Typography:** Helvetica Neue, 11px for overlay panels, canvas sprites for 3D labels. |
| 95 | |
| 96 | **Layout:** |
| 97 | - Top-left: Title + address/zone |
| 98 | - Top-right: Parameters panel (stats) |
| 99 | - Bottom-left: Color legend |
| 100 | - Bottom-right: Controls hint |
| 101 | |
| 102 | All materials: `transparent: true`, `depthWrite: false`, `side: DoubleSide`. |
| 103 | |
| 104 | ### CDN Import Map |
| 105 | |
| 106 | ```html |
| 107 | <script type="importmap"> |
| 108 | { "imports": { |
| 109 | "three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js", |
| 110 | "three/addons/": "https://cdn.js |