@snaptrude/plugin-core 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -1
- package/api-manifest.full.json +1350 -89
- package/api-manifest.json +826 -40
- package/dist/api/analysis/daylight.d.ts +603 -0
- package/dist/api/analysis/daylight.d.ts.map +1 -0
- package/dist/api/analysis/heatmaps.d.ts +438 -14
- package/dist/api/analysis/heatmaps.d.ts.map +1 -1
- package/dist/api/analysis/index.d.ts +15 -0
- package/dist/api/analysis/index.d.ts.map +1 -1
- package/dist/api/analysis/solar.d.ts +249 -0
- package/dist/api/analysis/solar.d.ts.map +1 -0
- package/dist/api/analysis/weather.d.ts +193 -0
- package/dist/api/analysis/weather.d.ts.map +1 -0
- package/dist/api/core/camera/index.d.ts +37 -0
- package/dist/api/core/camera/index.d.ts.map +1 -1
- package/dist/api/core/geom/create/index.d.ts +318 -1
- package/dist/api/core/geom/create/index.d.ts.map +1 -1
- package/dist/api/core/index.d.ts +9 -0
- package/dist/api/core/index.d.ts.map +1 -1
- package/dist/api/core/io/export/index.d.ts +3 -1
- package/dist/api/core/io/export/index.d.ts.map +1 -1
- package/dist/api/core/io/import/index.d.ts +61 -0
- package/dist/api/core/io/import/index.d.ts.map +1 -1
- package/dist/api/core/mode/index.d.ts +99 -0
- package/dist/api/core/mode/index.d.ts.map +1 -0
- package/dist/api/core/storeys/index.d.ts +251 -0
- package/dist/api/core/storeys/index.d.ts.map +1 -0
- package/dist/api/core/tags.d.ts +24 -0
- package/dist/api/core/tags.d.ts.map +1 -1
- package/dist/api/core/zoom/index.d.ts +4 -0
- package/dist/api/core/zoom/index.d.ts.map +1 -1
- package/dist/api/design/create/index.d.ts +115 -4
- package/dist/api/design/create/index.d.ts.map +1 -1
- package/dist/api/design/delete/index.d.ts +3 -0
- package/dist/api/design/delete/index.d.ts.map +1 -1
- package/dist/api/design/query/geometry/index.d.ts +112 -0
- package/dist/api/design/query/geometry/index.d.ts.map +1 -1
- package/dist/api/design/query/index.d.ts +4 -0
- package/dist/api/design/query/index.d.ts.map +1 -1
- package/dist/api/design/query/referenceLines.d.ts +45 -0
- package/dist/api/design/query/referenceLines.d.ts.map +1 -0
- package/dist/api/design/query/spaces.d.ts +173 -0
- package/dist/api/design/query/spaces.d.ts.map +1 -1
- package/dist/api/design/transform/index.d.ts +12 -0
- package/dist/api/design/transform/index.d.ts.map +1 -1
- package/dist/api/design/update/index.d.ts +51 -1
- package/dist/api/design/update/index.d.ts.map +1 -1
- package/dist/api/entity/buildableEnvelope.d.ts +4 -0
- package/dist/api/entity/buildableEnvelope.d.ts.map +1 -1
- package/dist/api/entity/referenceLine.d.ts +8 -0
- package/dist/api/entity/referenceLine.d.ts.map +1 -1
- package/dist/api/entity/story.d.ts +20 -0
- package/dist/api/entity/story.d.ts.map +1 -1
- package/dist/api/index.d.ts +5 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/presentation/export.d.ts +4 -0
- package/dist/api/presentation/export.d.ts.map +1 -1
- package/dist/api/presentation/import.d.ts +6 -0
- package/dist/api/presentation/import.d.ts.map +1 -1
- package/dist/api/presentation/index.d.ts +9 -0
- package/dist/api/presentation/index.d.ts.map +1 -1
- package/dist/api/presentation/placedViews.d.ts +370 -0
- package/dist/api/presentation/placedViews.d.ts.map +1 -0
- package/dist/api/presentation/shapes.d.ts +481 -0
- package/dist/api/presentation/shapes.d.ts.map +1 -0
- package/dist/api/program/site.d.ts +94 -0
- package/dist/api/program/site.d.ts.map +1 -1
- package/dist/api/program/spreadsheet.d.ts +260 -28
- package/dist/api/program/spreadsheet.d.ts.map +1 -1
- package/dist/api/workspace/index.d.ts +460 -0
- package/dist/api/workspace/index.d.ts.map +1 -0
- package/dist/index.cjs +2625 -1758
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2515 -1758
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/scripts/generate-manifest.mjs +45 -0
- package/src/api/analysis/daylight.ts +470 -0
- package/src/api/analysis/heatmaps.ts +444 -17
- package/src/api/analysis/index.ts +15 -0
- package/src/api/analysis/solar.ts +237 -0
- package/src/api/analysis/weather.ts +179 -0
- package/src/api/core/camera/index.ts +39 -0
- package/src/api/core/geom/create/index.ts +344 -1
- package/src/api/core/index.ts +9 -0
- package/src/api/core/io/export/index.ts +3 -1
- package/src/api/core/io/import/index.ts +64 -0
- package/src/api/core/mode/index.ts +96 -0
- package/src/api/core/storeys/index.ts +279 -0
- package/src/api/core/tags.ts +27 -0
- package/src/api/core/zoom/index.ts +4 -0
- package/src/api/design/create/index.ts +138 -1
- package/src/api/design/delete/index.ts +3 -0
- package/src/api/design/query/geometry/index.ts +125 -3
- package/src/api/design/query/index.ts +4 -0
- package/src/api/design/query/referenceLines.ts +52 -0
- package/src/api/design/query/spaces.ts +143 -0
- package/src/api/design/transform/index.ts +12 -0
- package/src/api/design/update/index.ts +66 -1
- package/src/api/entity/buildableEnvelope.ts +4 -0
- package/src/api/entity/referenceLine.ts +8 -0
- package/src/api/entity/story.ts +20 -0
- package/src/api/index.ts +5 -0
- package/src/api/presentation/export.ts +4 -0
- package/src/api/presentation/import.ts +6 -0
- package/src/api/presentation/index.ts +9 -0
- package/src/api/presentation/placedViews.ts +363 -0
- package/src/api/presentation/shapes.ts +274 -0
- package/src/api/program/site.ts +95 -0
- package/src/api/program/spreadsheet.ts +251 -6
- package/src/api/workspace/index.ts +516 -0
|
@@ -16,15 +16,25 @@ import { ComponentHandle, Vec3Components } from "../../handles"
|
|
|
16
16
|
* - {@linkcode PluginAnalysisHeatmapsApi.renderGrid} — a coloured grid mesh
|
|
17
17
|
* from point samples in world coordinates (wind, noise, microclimate — any
|
|
18
18
|
* field sampled over an area).
|
|
19
|
+
* - {@linkcode PluginAnalysisHeatmapsApi.renderField} — arbitrary planar
|
|
20
|
+
* polygon cells (or pre-tessellated meshes), one scalar value each — for
|
|
21
|
+
* non-square analysis meshes straight from a solver.
|
|
19
22
|
*
|
|
20
23
|
* Shared semantics:
|
|
21
24
|
*
|
|
22
|
-
* - **
|
|
23
|
-
*
|
|
25
|
+
* - **Named overlays** — every render call targets a named overlay
|
|
26
|
+
* (`options.name`; a shared default overlay when omitted). Rendering an
|
|
27
|
+
* existing name replaces that overlay only; the newly rendered overlay
|
|
28
|
+
* becomes the visible one — **exactly one overlay is visible at a time**.
|
|
29
|
+
* Up to 16 overlays can be registered; manage them (list / show / hide /
|
|
30
|
+
* remove) via {@linkcode PluginAnalysisHeatmapsApi.overlays}.
|
|
24
31
|
* - **Legend** — the host renders a legend card from
|
|
25
32
|
* {@linkcode PluginAnalysisHeatmapOptions} (title, unit, min/max, colour
|
|
26
33
|
* segments). Values outside `[min, max]` **clamp to the end colours** and
|
|
27
34
|
* the legend shows `<` / `>` markers at the clamped ends.
|
|
35
|
+
* - **Hover tooltip** — pass `options.hover: true` to any render call and,
|
|
36
|
+
* while that overlay is visible, hovering a cell shows a tooltip with the
|
|
37
|
+
* cell's `value` (and its `meta` payload, when the cell carries one).
|
|
28
38
|
* - **Ephemeral** — the heatmap is a transient overlay: never persisted,
|
|
29
39
|
* never saved with views, and auto-cleared when a scene-mutating edit
|
|
30
40
|
* invalidates it. Re-render after changing the model.
|
|
@@ -32,6 +42,13 @@ import { ComponentHandle, Vec3Components } from "../../handles"
|
|
|
32
42
|
* Accessed via `snaptrude.analysis.heatmaps`.
|
|
33
43
|
*/
|
|
34
44
|
export abstract class PluginAnalysisHeatmapsApi {
|
|
45
|
+
/**
|
|
46
|
+
* Manage the named heatmap overlays created by the render calls —
|
|
47
|
+
* list / show / hide / remove. See
|
|
48
|
+
* {@linkcode PluginAnalysisHeatmapOverlaysApi}.
|
|
49
|
+
*/
|
|
50
|
+
public abstract overlays: PluginAnalysisHeatmapOverlaysApi
|
|
51
|
+
|
|
35
52
|
constructor() {}
|
|
36
53
|
|
|
37
54
|
/**
|
|
@@ -39,8 +56,9 @@ export abstract class PluginAnalysisHeatmapsApi {
|
|
|
39
56
|
*
|
|
40
57
|
* Each entry maps a space to a scalar value; the value is mapped through
|
|
41
58
|
* the colour ramp (see {@linkcode PluginAnalysisHeatmapOptions}) and the
|
|
42
|
-
* space's top face is painted that single flat colour.
|
|
43
|
-
*
|
|
59
|
+
* space's top face is painted that single flat colour. Renders into the
|
|
60
|
+
* overlay named `options.name` (the default overlay when omitted),
|
|
61
|
+
* replacing that overlay only and making it the visible one. Spaces whose top
|
|
44
62
|
* face is not flat (pitched/sloped tops) are **skipped with a console
|
|
45
63
|
* warning** — the rest of the heatmap still renders.
|
|
46
64
|
*
|
|
@@ -59,6 +77,8 @@ export abstract class PluginAnalysisHeatmapsApi {
|
|
|
59
77
|
* skipped).
|
|
60
78
|
* @throws When `options.min` is greater than `options.max`.
|
|
61
79
|
* @throws When a colour in `options.colors` is not a 6-digit hex colour.
|
|
80
|
+
* @throws When `options.name` is a new overlay name and the 16-overlay
|
|
81
|
+
* cap is reached.
|
|
62
82
|
* @throws When plugin writes are disabled.
|
|
63
83
|
*
|
|
64
84
|
* @examplePrompt Color each room by its occupancy count
|
|
@@ -89,8 +109,9 @@ export abstract class PluginAnalysisHeatmapsApi {
|
|
|
89
109
|
* {@linkcode PluginUnitsApi.convert}) — coloured by mapping the value
|
|
90
110
|
* through the colour ramp
|
|
91
111
|
* (see {@linkcode PluginAnalysisHeatmapOptions}). This is the mode for
|
|
92
|
-
* field data — wind, noise, microclimate — sampled over an area.
|
|
93
|
-
*
|
|
112
|
+
* field data — wind, noise, microclimate — sampled over an area. Renders
|
|
113
|
+
* into the overlay named `options.name` (the default overlay when
|
|
114
|
+
* omitted), replacing that overlay only and making it the visible one.
|
|
94
115
|
*
|
|
95
116
|
* The heatmap is ephemeral: it is auto-cleared by scene-mutating edits
|
|
96
117
|
* and is never persisted or saved with views.
|
|
@@ -108,6 +129,8 @@ export abstract class PluginAnalysisHeatmapsApi {
|
|
|
108
129
|
* @throws When `cells` is empty or `cellSize` is not a positive, finite number.
|
|
109
130
|
* @throws When `options.min` is greater than `options.max`.
|
|
110
131
|
* @throws When a colour in `options.colors` is not a 6-digit hex colour.
|
|
132
|
+
* @throws When `options.name` is a new overlay name and the 16-overlay
|
|
133
|
+
* cap is reached.
|
|
111
134
|
* @throws When plugin writes are disabled.
|
|
112
135
|
*
|
|
113
136
|
* @examplePrompt Overlay my CFD wind simulation results on the site
|
|
@@ -131,15 +154,152 @@ export abstract class PluginAnalysisHeatmapsApi {
|
|
|
131
154
|
): PluginApiReturn<PluginAnalysisHeatmapsRenderResult>
|
|
132
155
|
|
|
133
156
|
/**
|
|
134
|
-
*
|
|
157
|
+
* Render a grid heatmap on an **arbitrarily oriented plane** — a façade,
|
|
158
|
+
* a section cut, any flat surface.
|
|
159
|
+
*
|
|
160
|
+
* The same point-sample model as
|
|
161
|
+
* {@linkcode PluginAnalysisHeatmapsApi.renderGrid}, generalised: cells lie
|
|
162
|
+
* on the plane whose direction is given by `normal` (for a façade, the
|
|
163
|
+
* outward wall normal). Each cell is a square of edge `cellSize` centred
|
|
164
|
+
* on its **world-coordinate** `position` and oriented in that plane; the
|
|
165
|
+
* host derives the in-plane axes from the normal and offsets each cell a
|
|
166
|
+
* hair along it so the heatmap never z-fights the surface it annotates.
|
|
167
|
+
* Cell positions should lie on (or very near) the surface being
|
|
168
|
+
* annotated — the host does not project them. Renders into the overlay
|
|
169
|
+
* named `options.name` (the default overlay when omitted), replacing
|
|
170
|
+
* that overlay only and making it the visible one.
|
|
171
|
+
*
|
|
172
|
+
* This is the mode for per-façade data — solar irradiance, glare, panel
|
|
173
|
+
* yield, wind pressure — sampled over a vertical or sloped face. For
|
|
174
|
+
* horizontal ground fields prefer
|
|
175
|
+
* {@linkcode PluginAnalysisHeatmapsApi.renderGrid}, which is this call
|
|
176
|
+
* with `normal = { x: 0, y: 1, z: 0 }`.
|
|
135
177
|
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* touch the built-in study heatmaps (`sunlightHours` / `illuminance`) —
|
|
139
|
-
* they have their own `reset`.
|
|
178
|
+
* The heatmap is ephemeral: it is auto-cleared by scene-mutating edits
|
|
179
|
+
* and is never persisted or saved with views.
|
|
140
180
|
*
|
|
141
|
-
* @
|
|
142
|
-
*
|
|
181
|
+
* @param cells - One {@linkcode PluginAnalysisHeatmapGridCell} per
|
|
182
|
+
* sample: a world-coordinate `position` on the surface plane (y up)
|
|
183
|
+
* and its scalar `value`.
|
|
184
|
+
* @param cellSize - Edge length of each (square) cell, in the same
|
|
185
|
+
* Snaptrude internal units as `position` (see
|
|
186
|
+
* {@linkcode PluginUnitsApi.convert}). Must be a positive, finite number.
|
|
187
|
+
* @param normal - The plane's normal direction as `{ x, y, z }` (for a
|
|
188
|
+
* façade, the outward wall normal — read it via
|
|
189
|
+
* `core.geom.query.face.getNormal` on the wall's face). Any non-zero
|
|
190
|
+
* length; the host normalises it.
|
|
191
|
+
* @param options - {@linkcode PluginAnalysisHeatmapOptions} — legend
|
|
192
|
+
* title/unit, explicit min/max range (auto-derived from the data when
|
|
193
|
+
* omitted), and the colour ramp.
|
|
194
|
+
* @returns A {@linkcode PluginAnalysisHeatmapsRenderResult} —
|
|
195
|
+
* `{ success: true }` once the heatmap is rendered.
|
|
196
|
+
* @throws When `cells` is empty, `cellSize` is not a positive finite
|
|
197
|
+
* number, or `normal` has zero length.
|
|
198
|
+
* @throws When `options.min` is greater than `options.max`.
|
|
199
|
+
* @throws When a colour in `options.colors` is not a 6-digit hex colour.
|
|
200
|
+
* @throws When `options.name` is a new overlay name and the 16-overlay
|
|
201
|
+
* cap is reached.
|
|
202
|
+
* @throws When plugin writes are disabled.
|
|
203
|
+
*
|
|
204
|
+
* @examplePrompt Paint the solar irradiance results on the south façade
|
|
205
|
+
* @examplePrompt Show my daylight values as a heatmap on this wall
|
|
206
|
+
* @examplePrompt Render panel-level PV yield on the building face
|
|
207
|
+
* @examplePrompt Overlay wind pressure on the tower's west elevation
|
|
208
|
+
*
|
|
209
|
+
* # Example
|
|
210
|
+
* ```ts
|
|
211
|
+
* // Samples lie on a wall plane facing +X; colour each 1-unit cell by irradiance.
|
|
212
|
+
* await snaptrude.analysis.heatmaps.renderSurfaceGrid(
|
|
213
|
+
* samples.map((s) => ({ position: { x: wallX, y: s.y, z: s.z }, value: s.irradiance })),
|
|
214
|
+
* 1,
|
|
215
|
+
* { x: 1, y: 0, z: 0 }, // façade normal
|
|
216
|
+
* { title: "Irradiance", unit: "kWh/m²", colors: ["#0000ff", "#ffff00", "#ff0000"] },
|
|
217
|
+
* )
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
public abstract renderSurfaceGrid(
|
|
221
|
+
cells: PluginAnalysisHeatmapGridCell[],
|
|
222
|
+
cellSize: number,
|
|
223
|
+
normal: Vec3Components,
|
|
224
|
+
options?: PluginAnalysisHeatmapOptions,
|
|
225
|
+
): PluginApiReturn<PluginAnalysisHeatmapsRenderResult>
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Render a heatmap from **arbitrary cell geometry** — planar polygons or
|
|
229
|
+
* pre-tessellated meshes, one scalar value each.
|
|
230
|
+
*
|
|
231
|
+
* The free-form sibling of the grid calls: instead of square cells, each
|
|
232
|
+
* {@linkcode PluginAnalysisHeatmapFieldCell} brings its own footprint in
|
|
233
|
+
* **world coordinates** (y up) — either a planar `polygon` ring (3+
|
|
234
|
+
* points, any orientation; the host triangulates it) or a pre-tessellated
|
|
235
|
+
* `vertices` + `indices` mesh (triangle list). This is the mode for
|
|
236
|
+
* analysis meshes straight from a solver: FEA/CFD cells, Voronoi regions,
|
|
237
|
+
* per-panel façade patches — anything not on a square grid. Each cell's
|
|
238
|
+
* `value` is mapped through the colour scale
|
|
239
|
+
* (see {@linkcode PluginAnalysisHeatmapOptions}, including
|
|
240
|
+
* `options.scale`); an optional `meta` payload is stored with the overlay
|
|
241
|
+
* for later features. Renders into the overlay named `options.name` (the
|
|
242
|
+
* default overlay when omitted), replacing that overlay only and making
|
|
243
|
+
* it the visible one.
|
|
244
|
+
*
|
|
245
|
+
* Degenerate polygon cells (collinear points, zero area) are **skipped
|
|
246
|
+
* with a console warning** — the rest of the heatmap still renders.
|
|
247
|
+
*
|
|
248
|
+
* The heatmap is ephemeral: it is auto-cleared by scene-mutating edits
|
|
249
|
+
* and is never persisted or saved with views.
|
|
250
|
+
*
|
|
251
|
+
* @param cells - One {@linkcode PluginAnalysisHeatmapFieldCell} per cell:
|
|
252
|
+
* a planar `polygon` ring OR a `vertices`/`indices` triangle mesh, plus
|
|
253
|
+
* its scalar `value` and optional `meta`.
|
|
254
|
+
* @param options - {@linkcode PluginAnalysisHeatmapOptions} — legend
|
|
255
|
+
* title/unit, min/max range, colour ramp, and the colour `scale`
|
|
256
|
+
* (bands / threshold / categorical).
|
|
257
|
+
* @returns A {@linkcode PluginAnalysisHeatmapsRenderResult} —
|
|
258
|
+
* `{ success: true }` once the heatmap is rendered.
|
|
259
|
+
* @throws When `cells` is empty, a polygon has fewer than 3 points, a
|
|
260
|
+
* pre-tessellated cell's `indices` length is not a multiple of 3, or an
|
|
261
|
+
* index is out of range of its `vertices`.
|
|
262
|
+
* @throws When every polygon cell was degenerate (nothing renderable).
|
|
263
|
+
* @throws When a `categorical` scale has no class for a cell's value.
|
|
264
|
+
* @throws When `options.min` is greater than `options.max`, or a colour
|
|
265
|
+
* is not a 6-digit hex colour.
|
|
266
|
+
* @throws When `options.name` is a new overlay name and the 16-overlay
|
|
267
|
+
* cap is reached.
|
|
268
|
+
* @throws When plugin writes are disabled.
|
|
269
|
+
*
|
|
270
|
+
* @examplePrompt Overlay my FEA stress results on their solver mesh
|
|
271
|
+
* @examplePrompt Color these Voronoi regions by footfall
|
|
272
|
+
* @examplePrompt Paint each façade panel polygon by its PV yield
|
|
273
|
+
* @examplePrompt Render pass/fail daylight compliance per zone polygon
|
|
274
|
+
*
|
|
275
|
+
* # Example
|
|
276
|
+
* ```ts
|
|
277
|
+
* // Two triangular cells at ground level, coloured pass/fail at 300 lux.
|
|
278
|
+
* await snaptrude.analysis.heatmaps.renderField(
|
|
279
|
+
* [
|
|
280
|
+
* { polygon: [{ x: 0, y: 0, z: 0 }, { x: 4, y: 0, z: 0 }, { x: 0, y: 0, z: 4 }], value: 420 },
|
|
281
|
+
* { polygon: [{ x: 4, y: 0, z: 4 }, { x: 0, y: 0, z: 4 }, { x: 4, y: 0, z: 0 }], value: 180 },
|
|
282
|
+
* ],
|
|
283
|
+
* { title: "Daylight", unit: "lux", scale: { type: "threshold", threshold: 300 } },
|
|
284
|
+
* )
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
public abstract renderField(
|
|
288
|
+
cells: PluginAnalysisHeatmapFieldCell[],
|
|
289
|
+
options?: PluginAnalysisHeatmapOptions,
|
|
290
|
+
): PluginApiReturn<PluginAnalysisHeatmapsRenderResult>
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Clear every plugin heatmap overlay from the scene.
|
|
294
|
+
*
|
|
295
|
+
* Removes **all** registered overlays (equivalent to
|
|
296
|
+
* {@linkcode PluginAnalysisHeatmapOverlaysApi.removeAll}) and closes the
|
|
297
|
+
* legend. A no-op (returns `false`) when no plugin heatmap exists. Does
|
|
298
|
+
* not touch the built-in study heatmaps (`sunlightHours` /
|
|
299
|
+
* `illuminance`) — they have their own `reset`.
|
|
300
|
+
*
|
|
301
|
+
* @returns `true` when at least one overlay was cleared, `false` when
|
|
302
|
+
* none existed.
|
|
143
303
|
* @throws When plugin writes are disabled.
|
|
144
304
|
*
|
|
145
305
|
* @examplePrompt Clear my custom heatmap
|
|
@@ -176,6 +336,138 @@ export abstract class PluginAnalysisHeatmapsApi {
|
|
|
176
336
|
public abstract isActive(): PluginApiReturn<boolean>
|
|
177
337
|
}
|
|
178
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Heatmap overlay management — the named overlays created by the
|
|
341
|
+
* `analysis.heatmaps` render calls.
|
|
342
|
+
*
|
|
343
|
+
* Every render call registers (or replaces) an overlay under
|
|
344
|
+
* `options.name` — a shared default overlay when omitted. Overlays are
|
|
345
|
+
* in-session only: never persisted, never saved with views, and **all**
|
|
346
|
+
* of them are cleared together when a scene-mutating edit invalidates
|
|
347
|
+
* them. At most 16 overlays can be registered at once, and **exactly one
|
|
348
|
+
* overlay is visible at a time** — showing one hides the others, and the
|
|
349
|
+
* legend always describes the visible overlay.
|
|
350
|
+
*
|
|
351
|
+
* Accessed via `snaptrude.analysis.heatmaps.overlays`.
|
|
352
|
+
*/
|
|
353
|
+
export abstract class PluginAnalysisHeatmapOverlaysApi {
|
|
354
|
+
constructor() {}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* List the registered heatmap overlays.
|
|
358
|
+
*
|
|
359
|
+
* Returns every overlay's `name`, `kind` (which render call produced
|
|
360
|
+
* it: `"spaces"`, `"grid"`, `"surfaceGrid"` or `"field"`) and whether it is the
|
|
361
|
+
* currently visible one. At most one overlay has `visible: true`.
|
|
362
|
+
*
|
|
363
|
+
* @returns A {@linkcode PluginAnalysisHeatmapOverlaysListResult} —
|
|
364
|
+
* `{ overlays: [{ name, visible, kind }] }`, empty when nothing is
|
|
365
|
+
* registered.
|
|
366
|
+
*
|
|
367
|
+
* @examplePrompt Which heatmap overlays do I have?
|
|
368
|
+
* @examplePrompt List my rendered analysis overlays
|
|
369
|
+
* @examplePrompt Check which heatmap is currently visible
|
|
370
|
+
*
|
|
371
|
+
* # Example
|
|
372
|
+
* ```ts
|
|
373
|
+
* const { overlays } = await snaptrude.analysis.heatmaps.overlays.list()
|
|
374
|
+
* const visible = overlays.find((o) => o.visible)
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
377
|
+
public abstract list(): PluginApiReturn<PluginAnalysisHeatmapOverlaysListResult>
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Make a registered overlay the visible one.
|
|
381
|
+
*
|
|
382
|
+
* Enables the named overlay's meshes, hides whichever overlay was
|
|
383
|
+
* visible before, and swaps the legend to the shown overlay's. A no-op
|
|
384
|
+
* returning `false` when no overlay of that name exists.
|
|
385
|
+
*
|
|
386
|
+
* @param name - The overlay's name (`options.name` at render time).
|
|
387
|
+
* @returns `true` when the overlay is now visible, `false` when no
|
|
388
|
+
* overlay of that name exists.
|
|
389
|
+
* @throws When plugin writes are disabled.
|
|
390
|
+
*
|
|
391
|
+
* @examplePrompt Switch back to my wind heatmap
|
|
392
|
+
* @examplePrompt Show the occupancy overlay
|
|
393
|
+
* @examplePrompt Flip between my two analysis overlays
|
|
394
|
+
*
|
|
395
|
+
* # Example
|
|
396
|
+
* ```ts
|
|
397
|
+
* await snaptrude.analysis.heatmaps.overlays.show("wind")
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
public abstract show(name: string): PluginApiReturn<boolean>
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Hide an overlay without removing it.
|
|
404
|
+
*
|
|
405
|
+
* Disables the named overlay's meshes; when it was the visible one its
|
|
406
|
+
* legend closes too (nothing visible → no legend). The overlay stays
|
|
407
|
+
* registered and can be brought back with
|
|
408
|
+
* {@linkcode PluginAnalysisHeatmapOverlaysApi.show}. A no-op returning
|
|
409
|
+
* `false` when no overlay of that name exists.
|
|
410
|
+
*
|
|
411
|
+
* @param name - The overlay's name (`options.name` at render time).
|
|
412
|
+
* @returns `true` when the overlay exists (and is now hidden), `false`
|
|
413
|
+
* when no overlay of that name exists.
|
|
414
|
+
* @throws When plugin writes are disabled.
|
|
415
|
+
*
|
|
416
|
+
* @examplePrompt Hide the wind overlay but keep it around
|
|
417
|
+
* @examplePrompt Temporarily hide my heatmap
|
|
418
|
+
* @examplePrompt Turn off the occupancy colours without deleting them
|
|
419
|
+
*
|
|
420
|
+
* # Example
|
|
421
|
+
* ```ts
|
|
422
|
+
* await snaptrude.analysis.heatmaps.overlays.hide("wind")
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
public abstract hide(name: string): PluginApiReturn<boolean>
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Remove an overlay — dispose its meshes and forget it.
|
|
429
|
+
*
|
|
430
|
+
* Frees the overlay's slot toward the 16-overlay cap; when it was the
|
|
431
|
+
* visible one its legend closes. A no-op returning `false` when no
|
|
432
|
+
* overlay of that name exists.
|
|
433
|
+
*
|
|
434
|
+
* @param name - The overlay's name (`options.name` at render time).
|
|
435
|
+
* @returns `true` when the overlay was removed, `false` when no overlay
|
|
436
|
+
* of that name exists.
|
|
437
|
+
* @throws When plugin writes are disabled.
|
|
438
|
+
*
|
|
439
|
+
* @examplePrompt Delete the wind overlay
|
|
440
|
+
* @examplePrompt Remove my occupancy heatmap for good
|
|
441
|
+
* @examplePrompt Free up an overlay slot
|
|
442
|
+
*
|
|
443
|
+
* # Example
|
|
444
|
+
* ```ts
|
|
445
|
+
* await snaptrude.analysis.heatmaps.overlays.remove("wind")
|
|
446
|
+
* ```
|
|
447
|
+
*/
|
|
448
|
+
public abstract remove(name: string): PluginApiReturn<boolean>
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Remove every registered overlay and close the legend.
|
|
452
|
+
*
|
|
453
|
+
* The bulk form of {@linkcode PluginAnalysisHeatmapOverlaysApi.remove};
|
|
454
|
+
* `analysis.heatmaps.reset()` is this call under its historical name.
|
|
455
|
+
*
|
|
456
|
+
* @returns The number of overlays removed (`0` when none existed).
|
|
457
|
+
* @throws When plugin writes are disabled.
|
|
458
|
+
*
|
|
459
|
+
* @examplePrompt Clear all my heatmap overlays
|
|
460
|
+
* @examplePrompt Remove every analysis overlay from the scene
|
|
461
|
+
* @examplePrompt Start over with a clean set of overlays
|
|
462
|
+
*
|
|
463
|
+
* # Example
|
|
464
|
+
* ```ts
|
|
465
|
+
* const removed = await snaptrude.analysis.heatmaps.overlays.removeAll()
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
public abstract removeAll(): PluginApiReturn<number>
|
|
469
|
+
}
|
|
470
|
+
|
|
179
471
|
/**
|
|
180
472
|
* One space's entry in {@linkcode PluginAnalysisHeatmapsApi.renderSpaces}.
|
|
181
473
|
*
|
|
@@ -199,15 +491,115 @@ export type PluginAnalysisHeatmapSpaceEntry = z.infer<
|
|
|
199
491
|
* |---|---|---|
|
|
200
492
|
* | `position` | `{ x, y, z }` | Cell centre in **world coordinates** (y is up) |
|
|
201
493
|
* | `value` | `number` | The scalar value mapped to a colour |
|
|
494
|
+
* | `meta` | `unknown \| undefined` | Optional payload stored with the overlay — shown in the hover tooltip when `options.hover` is on |
|
|
202
495
|
*/
|
|
203
496
|
export const PluginAnalysisHeatmapGridCell = z.object({
|
|
204
497
|
position: Vec3Components,
|
|
205
498
|
value: z.number().finite(),
|
|
499
|
+
meta: z.unknown().optional(),
|
|
206
500
|
})
|
|
207
501
|
export type PluginAnalysisHeatmapGridCell = z.infer<
|
|
208
502
|
typeof PluginAnalysisHeatmapGridCell
|
|
209
503
|
>
|
|
210
504
|
|
|
505
|
+
/** A 6-digit hex colour like `"#0000ff"` (the `#` is optional). */
|
|
506
|
+
const HexColor = z
|
|
507
|
+
.string()
|
|
508
|
+
.regex(/^#?[0-9a-fA-F]{6}$/, 'expected a 6-digit hex colour like "#0000ff"')
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* One cell in {@linkcode PluginAnalysisHeatmapsApi.renderField} — either a
|
|
512
|
+
* planar polygon ring the host triangulates, or a pre-tessellated triangle
|
|
513
|
+
* mesh. All coordinates are **world coordinates** (y up).
|
|
514
|
+
*
|
|
515
|
+
* Polygon form:
|
|
516
|
+
*
|
|
517
|
+
* | Property | Type | Description |
|
|
518
|
+
* |---|---|---|
|
|
519
|
+
* | `polygon` | `{ x, y, z }[]` | Planar ring of 3+ points (any orientation; no self-intersections) |
|
|
520
|
+
* | `value` | `number` | The scalar value mapped to a colour |
|
|
521
|
+
* | `meta` | `unknown \| undefined` | Optional payload stored with the overlay |
|
|
522
|
+
*
|
|
523
|
+
* Pre-tessellated form:
|
|
524
|
+
*
|
|
525
|
+
* | Property | Type | Description |
|
|
526
|
+
* |---|---|---|
|
|
527
|
+
* | `vertices` | `{ x, y, z }[]` | Mesh vertices (3+) |
|
|
528
|
+
* | `indices` | `number[]` | Triangle list into `vertices` — length a multiple of 3, every index in range |
|
|
529
|
+
* | `value` | `number` | The scalar value mapped to a colour |
|
|
530
|
+
* | `meta` | `unknown \| undefined` | Optional payload stored with the overlay |
|
|
531
|
+
*/
|
|
532
|
+
export const PluginAnalysisHeatmapFieldCell = z.union([
|
|
533
|
+
z.object({
|
|
534
|
+
polygon: z.array(Vec3Components).min(3),
|
|
535
|
+
value: z.number().finite(),
|
|
536
|
+
meta: z.unknown().optional(),
|
|
537
|
+
}),
|
|
538
|
+
z.object({
|
|
539
|
+
vertices: z.array(Vec3Components).min(3),
|
|
540
|
+
indices: z.array(z.number().int().nonnegative()).min(3),
|
|
541
|
+
value: z.number().finite(),
|
|
542
|
+
meta: z.unknown().optional(),
|
|
543
|
+
}),
|
|
544
|
+
])
|
|
545
|
+
export type PluginAnalysisHeatmapFieldCell = z.infer<
|
|
546
|
+
typeof PluginAnalysisHeatmapFieldCell
|
|
547
|
+
>
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* The colour scale for a heatmap render — how values become colours and how
|
|
551
|
+
* the legend reads. Three kinds (discriminated by `type`):
|
|
552
|
+
*
|
|
553
|
+
* - **`bands`** — the default: the colour ramp quantised into `count`
|
|
554
|
+
* discrete bands (2–64, default 11). The rendered mesh and legend always
|
|
555
|
+
* use discrete bands; "continuous" is simply a high `count`.
|
|
556
|
+
* - **`threshold`** — binary pass/fail at a cut-off `threshold`: values
|
|
557
|
+
* `>= threshold` get `passColor`, the rest `failColor`; the legend shows
|
|
558
|
+
* the two labelled swatches.
|
|
559
|
+
* - **`categorical`** — exact-match classes: every cell value must equal
|
|
560
|
+
* one class's `value` (anything else is a validation error); the legend
|
|
561
|
+
* shows one labelled swatch per class.
|
|
562
|
+
*
|
|
563
|
+
* | Kind | Property | Type | Description |
|
|
564
|
+
* |---|---|---|---|
|
|
565
|
+
* | `bands` | `count` | `number \| undefined` | Number of discrete bands, 2–64 (default 11) |
|
|
566
|
+
* | `threshold` | `threshold` | `number` | Values `>= threshold` pass |
|
|
567
|
+
* | `threshold` | `passColor` | `string \| undefined` | Hex colour for passing values (host default green) |
|
|
568
|
+
* | `threshold` | `failColor` | `string \| undefined` | Hex colour for failing values (host default red) |
|
|
569
|
+
* | `threshold` | `passLabel` | `string \| undefined` | Legend label for the pass swatch (default `≥ threshold`) |
|
|
570
|
+
* | `threshold` | `failLabel` | `string \| undefined` | Legend label for the fail swatch (default `< threshold`) |
|
|
571
|
+
* | `categorical` | `classes` | `{ value, label, color }[]` | Exact-match classes: scalar `value`, legend `label`, hex `color` |
|
|
572
|
+
*/
|
|
573
|
+
export const PluginAnalysisHeatmapScale = z.discriminatedUnion("type", [
|
|
574
|
+
z.object({
|
|
575
|
+
type: z.literal("bands"),
|
|
576
|
+
count: z.number().int().min(2).max(64).optional(),
|
|
577
|
+
}),
|
|
578
|
+
z.object({
|
|
579
|
+
type: z.literal("threshold"),
|
|
580
|
+
threshold: z.number().finite(),
|
|
581
|
+
passColor: HexColor.optional(),
|
|
582
|
+
failColor: HexColor.optional(),
|
|
583
|
+
passLabel: z.string().optional(),
|
|
584
|
+
failLabel: z.string().optional(),
|
|
585
|
+
}),
|
|
586
|
+
z.object({
|
|
587
|
+
type: z.literal("categorical"),
|
|
588
|
+
classes: z
|
|
589
|
+
.array(
|
|
590
|
+
z.object({
|
|
591
|
+
value: z.number().finite(),
|
|
592
|
+
label: z.string(),
|
|
593
|
+
color: HexColor,
|
|
594
|
+
}),
|
|
595
|
+
)
|
|
596
|
+
.min(1),
|
|
597
|
+
}),
|
|
598
|
+
])
|
|
599
|
+
export type PluginAnalysisHeatmapScale = z.infer<
|
|
600
|
+
typeof PluginAnalysisHeatmapScale
|
|
601
|
+
>
|
|
602
|
+
|
|
211
603
|
/**
|
|
212
604
|
* Display options shared by both heatmap render modes.
|
|
213
605
|
*
|
|
@@ -219,21 +611,24 @@ export type PluginAnalysisHeatmapGridCell = z.infer<
|
|
|
219
611
|
*
|
|
220
612
|
* | Property | Type | Description |
|
|
221
613
|
* |---|---|---|
|
|
614
|
+
* | `name` | `string \| undefined` | Overlay name to render into (see {@linkcode PluginAnalysisHeatmapOverlaysApi}); a shared default overlay when omitted |
|
|
222
615
|
* | `title` | `string \| undefined` | Legend title |
|
|
223
616
|
* | `unit` | `string \| undefined` | Unit label shown on the legend (e.g. `"m/s"`) |
|
|
224
617
|
* | `min` | `number \| undefined` | Value mapped to the first colour stop; data minimum when omitted |
|
|
225
618
|
* | `max` | `number \| undefined` | Value mapped to the last colour stop; data maximum when omitted |
|
|
226
619
|
* | `colors` | `string[] \| undefined` | Ordered 6-digit hex colour stops (e.g. `"#0000ff"`), low → high; host blue → cyan → green → yellow → red default |
|
|
620
|
+
* | `scale` | {@linkcode PluginAnalysisHeatmapScale}` \| undefined` | Colour scale: discrete `bands` (count 2–64, default 11), binary `threshold`, or exact-match `categorical` |
|
|
621
|
+
* | `hover` | `boolean \| undefined` | Show a tooltip with the hovered cell's `value` (+ its `meta`, when given) while this overlay is visible. Default `false` |
|
|
227
622
|
*/
|
|
228
623
|
export const PluginAnalysisHeatmapOptions = z.object({
|
|
624
|
+
name: z.string().min(1).max(64).optional(),
|
|
229
625
|
title: z.string().optional(),
|
|
230
626
|
unit: z.string().optional(),
|
|
231
627
|
min: z.number().finite().optional(),
|
|
232
628
|
max: z.number().finite().optional(),
|
|
233
|
-
colors: z
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
.optional(),
|
|
629
|
+
colors: z.array(HexColor).min(1).optional(),
|
|
630
|
+
scale: PluginAnalysisHeatmapScale.optional(),
|
|
631
|
+
hover: z.boolean().optional(),
|
|
237
632
|
})
|
|
238
633
|
export type PluginAnalysisHeatmapOptions = z.infer<
|
|
239
634
|
typeof PluginAnalysisHeatmapOptions
|
|
@@ -254,3 +649,35 @@ export const PluginAnalysisHeatmapsRenderResult = z.object({
|
|
|
254
649
|
export type PluginAnalysisHeatmapsRenderResult = z.infer<
|
|
255
650
|
typeof PluginAnalysisHeatmapsRenderResult
|
|
256
651
|
>
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* One overlay row in {@linkcode PluginAnalysisHeatmapOverlaysApi.list}.
|
|
655
|
+
*
|
|
656
|
+
* | Property | Type | Description |
|
|
657
|
+
* |---|---|---|
|
|
658
|
+
* | `name` | `string` | The overlay's name (`options.name` at render time) |
|
|
659
|
+
* | `visible` | `boolean` | Whether this is the (single) visible overlay |
|
|
660
|
+
* | `kind` | `"spaces" \| "grid" \| "surfaceGrid" \| "field"` | Which render call produced it |
|
|
661
|
+
*/
|
|
662
|
+
export const PluginAnalysisHeatmapOverlayInfo = z.object({
|
|
663
|
+
name: z.string(),
|
|
664
|
+
visible: z.boolean(),
|
|
665
|
+
kind: z.enum(["spaces", "grid", "surfaceGrid", "field"]),
|
|
666
|
+
})
|
|
667
|
+
export type PluginAnalysisHeatmapOverlayInfo = z.infer<
|
|
668
|
+
typeof PluginAnalysisHeatmapOverlayInfo
|
|
669
|
+
>
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Result of {@linkcode PluginAnalysisHeatmapOverlaysApi.list}.
|
|
673
|
+
*
|
|
674
|
+
* | Property | Type | Description |
|
|
675
|
+
* |---|---|---|
|
|
676
|
+
* | `overlays` | {@linkcode PluginAnalysisHeatmapOverlayInfo}`[]` | Every registered overlay; at most one has `visible: true` |
|
|
677
|
+
*/
|
|
678
|
+
export const PluginAnalysisHeatmapOverlaysListResult = z.object({
|
|
679
|
+
overlays: z.array(PluginAnalysisHeatmapOverlayInfo),
|
|
680
|
+
})
|
|
681
|
+
export type PluginAnalysisHeatmapOverlaysListResult = z.infer<
|
|
682
|
+
typeof PluginAnalysisHeatmapOverlaysListResult
|
|
683
|
+
>
|
|
@@ -3,6 +3,9 @@ import { PluginAnalysisShadowsApi } from "./shadows"
|
|
|
3
3
|
import { PluginAnalysisSunlightHoursApi } from "./sunlightHours"
|
|
4
4
|
import { PluginAnalysisIlluminanceApi } from "./illuminance"
|
|
5
5
|
import { PluginAnalysisHeatmapsApi } from "./heatmaps"
|
|
6
|
+
import { PluginAnalysisWeatherApi } from "./weather"
|
|
7
|
+
import { PluginAnalysisSolarApi } from "./solar"
|
|
8
|
+
import { PluginAnalysisDaylightApi } from "./daylight"
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Sustainability-analysis APIs.
|
|
@@ -21,6 +24,9 @@ import { PluginAnalysisHeatmapsApi } from "./heatmaps"
|
|
|
21
24
|
* - {@linkcode PluginAnalysisApi.sunlightHours} — Direct-sunlight-hours heatmap job (start / poll / cancel / clear)
|
|
22
25
|
* - {@linkcode PluginAnalysisApi.illuminance} — Daylight-illuminance heatmap job, Pro-gated (start / poll / cancel / clear)
|
|
23
26
|
* - {@linkcode PluginAnalysisApi.heatmaps} — Render plugin-computed scalar data as a heatmap (per-space / grid)
|
|
27
|
+
* - {@linkcode PluginAnalysisApi.weather} — Read the site's typical-year weather series (numeric, paged)
|
|
28
|
+
* - {@linkcode PluginAnalysisApi.solar} — Sample solar exposure at caller-chosen points (numeric)
|
|
29
|
+
* - {@linkcode PluginAnalysisApi.daylight} — Standards-grade annual daylight metrics (IES LM-83, numeric)
|
|
24
30
|
*
|
|
25
31
|
* Accessed via `snaptrude.analysis`.
|
|
26
32
|
*/
|
|
@@ -35,6 +41,12 @@ export abstract class PluginAnalysisApi {
|
|
|
35
41
|
public abstract illuminance: PluginAnalysisIlluminanceApi
|
|
36
42
|
/** Plugin-supplied scalar-data heatmaps. See {@linkcode PluginAnalysisHeatmapsApi}. */
|
|
37
43
|
public abstract heatmaps: PluginAnalysisHeatmapsApi
|
|
44
|
+
/** Site typical-year weather series (numeric, paged). See {@linkcode PluginAnalysisWeatherApi}. */
|
|
45
|
+
public abstract weather: PluginAnalysisWeatherApi
|
|
46
|
+
/** Per-point solar exposure sampling (numeric). See {@linkcode PluginAnalysisSolarApi}. */
|
|
47
|
+
public abstract solar: PluginAnalysisSolarApi
|
|
48
|
+
/** IES LM-83 annual daylight metrics (numeric). See {@linkcode PluginAnalysisDaylightApi}. */
|
|
49
|
+
public abstract daylight: PluginAnalysisDaylightApi
|
|
38
50
|
|
|
39
51
|
constructor() {}
|
|
40
52
|
}
|
|
@@ -44,3 +56,6 @@ export * from "./shadows"
|
|
|
44
56
|
export * from "./sunlightHours"
|
|
45
57
|
export * from "./illuminance"
|
|
46
58
|
export * from "./heatmaps"
|
|
59
|
+
export * from "./weather"
|
|
60
|
+
export * from "./solar"
|
|
61
|
+
export * from "./daylight"
|