@snaptrude/plugin-core 0.0.0-dev-20260708130115 → 0.0.0-dev-20260827194031

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.
Files changed (261) hide show
  1. package/CHANGELOG.md +152 -1
  2. package/api-manifest.json +3643 -276
  3. package/dist/api/analysis/daylight.d.ts +603 -0
  4. package/dist/api/analysis/daylight.d.ts.map +1 -0
  5. package/dist/api/analysis/heatmaps.d.ts +658 -0
  6. package/dist/api/analysis/heatmaps.d.ts.map +1 -0
  7. package/dist/api/analysis/illuminance.d.ts +145 -0
  8. package/dist/api/analysis/illuminance.d.ts.map +1 -0
  9. package/dist/api/analysis/index.d.ts +59 -0
  10. package/dist/api/analysis/index.d.ts.map +1 -0
  11. package/dist/api/analysis/shadows.d.ts +165 -0
  12. package/dist/api/analysis/shadows.d.ts.map +1 -0
  13. package/dist/api/analysis/solar.d.ts +249 -0
  14. package/dist/api/analysis/solar.d.ts.map +1 -0
  15. package/dist/api/analysis/sunlightHours.d.ts +208 -0
  16. package/dist/api/analysis/sunlightHours.d.ts.map +1 -0
  17. package/dist/api/analysis/sunpath.d.ts +80 -0
  18. package/dist/api/analysis/sunpath.d.ts.map +1 -0
  19. package/dist/api/analysis/weather.d.ts +193 -0
  20. package/dist/api/analysis/weather.d.ts.map +1 -0
  21. package/dist/api/core/camera/index.d.ts +261 -0
  22. package/dist/api/core/camera/index.d.ts.map +1 -0
  23. package/dist/api/core/comment/index.d.ts +105 -2
  24. package/dist/api/core/comment/index.d.ts.map +1 -1
  25. package/dist/api/core/geom/create/index.d.ts +840 -14
  26. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  27. package/dist/api/core/geom/delete/index.d.ts +8 -2
  28. package/dist/api/core/geom/delete/index.d.ts.map +1 -1
  29. package/dist/api/core/geom/query/arc.d.ts +5 -5
  30. package/dist/api/core/geom/query/brep.d.ts +130 -18
  31. package/dist/api/core/geom/query/brep.d.ts.map +1 -1
  32. package/dist/api/core/geom/query/circle.d.ts +18 -18
  33. package/dist/api/core/geom/query/contour.d.ts +20 -20
  34. package/dist/api/core/geom/query/curve.d.ts +49 -49
  35. package/dist/api/core/geom/query/edge.d.ts +5 -5
  36. package/dist/api/core/geom/query/face.d.ts +16 -16
  37. package/dist/api/core/geom/query/halfedge.d.ts +8 -8
  38. package/dist/api/core/geom/query/profile.d.ts +19 -19
  39. package/dist/api/core/geom/query/vertex.d.ts +8 -8
  40. package/dist/api/core/geom/update/contour.d.ts +14 -14
  41. package/dist/api/core/geom/update/curve.d.ts +7 -7
  42. package/dist/api/core/geom/update/profile.d.ts +16 -16
  43. package/dist/api/core/handles/index.d.ts +210 -0
  44. package/dist/api/core/handles/index.d.ts.map +1 -0
  45. package/dist/api/core/index.d.ts +34 -0
  46. package/dist/api/core/index.d.ts.map +1 -1
  47. package/dist/api/core/io/export/index.d.ts +134 -0
  48. package/dist/api/core/io/export/index.d.ts.map +1 -0
  49. package/dist/api/core/io/import/index.d.ts +453 -0
  50. package/dist/api/core/io/import/index.d.ts.map +1 -0
  51. package/dist/api/core/io/index.d.ts +40 -0
  52. package/dist/api/core/io/index.d.ts.map +1 -0
  53. package/dist/api/core/io/job/index.d.ts +139 -0
  54. package/dist/api/core/io/job/index.d.ts.map +1 -0
  55. package/dist/api/core/io/query/index.d.ts +74 -0
  56. package/dist/api/core/io/query/index.d.ts.map +1 -0
  57. package/dist/api/core/io/terrain/index.d.ts +341 -0
  58. package/dist/api/core/io/terrain/index.d.ts.map +1 -0
  59. package/dist/api/core/io/underlay/index.d.ts +805 -0
  60. package/dist/api/core/io/underlay/index.d.ts.map +1 -0
  61. package/dist/api/core/layers.d.ts +7 -7
  62. package/dist/api/core/mode/index.d.ts +99 -0
  63. package/dist/api/core/mode/index.d.ts.map +1 -0
  64. package/dist/api/core/project/index.d.ts +68 -1
  65. package/dist/api/core/project/index.d.ts.map +1 -1
  66. package/dist/api/core/proposals/index.d.ts +531 -0
  67. package/dist/api/core/proposals/index.d.ts.map +1 -0
  68. package/dist/api/core/storeys/index.d.ts +265 -0
  69. package/dist/api/core/storeys/index.d.ts.map +1 -0
  70. package/dist/api/core/tags.d.ts +24 -0
  71. package/dist/api/core/tags.d.ts.map +1 -1
  72. package/dist/api/core/user.d.ts +44 -0
  73. package/dist/api/core/user.d.ts.map +1 -0
  74. package/dist/api/core/zoom/index.d.ts +4 -0
  75. package/dist/api/core/zoom/index.d.ts.map +1 -1
  76. package/dist/api/design/boolean/index.d.ts +4 -4
  77. package/dist/api/design/create/index.d.ts +568 -45
  78. package/dist/api/design/create/index.d.ts.map +1 -1
  79. package/dist/api/design/delete/index.d.ts +3 -0
  80. package/dist/api/design/delete/index.d.ts.map +1 -1
  81. package/dist/api/design/doors/index.d.ts +195 -0
  82. package/dist/api/design/doors/index.d.ts.map +1 -1
  83. package/dist/api/design/edit/index.d.ts +1 -1
  84. package/dist/api/design/erase/index.d.ts +2 -2
  85. package/dist/api/design/family.d.ts +493 -0
  86. package/dist/api/design/family.d.ts.map +1 -0
  87. package/dist/api/design/furniture/index.d.ts +181 -8
  88. package/dist/api/design/furniture/index.d.ts.map +1 -1
  89. package/dist/api/design/index.d.ts +98 -0
  90. package/dist/api/design/index.d.ts.map +1 -1
  91. package/dist/api/design/lock.d.ts +26 -0
  92. package/dist/api/design/lock.d.ts.map +1 -1
  93. package/dist/api/design/materials/index.d.ts +270 -16
  94. package/dist/api/design/materials/index.d.ts.map +1 -1
  95. package/dist/api/design/query/geometry/index.d.ts +112 -0
  96. package/dist/api/design/query/geometry/index.d.ts.map +1 -1
  97. package/dist/api/design/query/index.d.ts +289 -11
  98. package/dist/api/design/query/index.d.ts.map +1 -1
  99. package/dist/api/design/query/referenceLines.d.ts +45 -0
  100. package/dist/api/design/query/referenceLines.d.ts.map +1 -0
  101. package/dist/api/design/query/spaces.d.ts +181 -8
  102. package/dist/api/design/query/spaces.d.ts.map +1 -1
  103. package/dist/api/design/selection/index.d.ts +144 -0
  104. package/dist/api/design/selection/index.d.ts.map +1 -1
  105. package/dist/api/design/transform/index.d.ts +172 -10
  106. package/dist/api/design/transform/index.d.ts.map +1 -1
  107. package/dist/api/design/types/index.d.ts +181 -0
  108. package/dist/api/design/types/index.d.ts.map +1 -0
  109. package/dist/api/design/update/index.d.ts +553 -2
  110. package/dist/api/design/update/index.d.ts.map +1 -1
  111. package/dist/api/design/visibility.d.ts +126 -0
  112. package/dist/api/design/visibility.d.ts.map +1 -0
  113. package/dist/api/design/windows/index.d.ts +113 -2
  114. package/dist/api/design/windows/index.d.ts.map +1 -1
  115. package/dist/api/entity/buildableEnvelope.d.ts +4 -0
  116. package/dist/api/entity/buildableEnvelope.d.ts.map +1 -1
  117. package/dist/api/entity/referenceLine.d.ts +10 -2
  118. package/dist/api/entity/referenceLine.d.ts.map +1 -1
  119. package/dist/api/entity/space.d.ts +21 -21
  120. package/dist/api/entity/story.d.ts +242 -15
  121. package/dist/api/entity/story.d.ts.map +1 -1
  122. package/dist/api/index.d.ts +10 -0
  123. package/dist/api/index.d.ts.map +1 -1
  124. package/dist/api/presentation/aiInspiration.d.ts +25 -25
  125. package/dist/api/presentation/annotate.d.ts +467 -0
  126. package/dist/api/presentation/annotate.d.ts.map +1 -0
  127. package/dist/api/presentation/diagrams.d.ts +111 -8
  128. package/dist/api/presentation/diagrams.d.ts.map +1 -1
  129. package/dist/api/presentation/export.d.ts +108 -0
  130. package/dist/api/presentation/export.d.ts.map +1 -0
  131. package/dist/api/presentation/import.d.ts +55 -4
  132. package/dist/api/presentation/import.d.ts.map +1 -1
  133. package/dist/api/presentation/index.d.ts +58 -1
  134. package/dist/api/presentation/index.d.ts.map +1 -1
  135. package/dist/api/presentation/placedViews.d.ts +1139 -0
  136. package/dist/api/presentation/placedViews.d.ts.map +1 -0
  137. package/dist/api/presentation/shapes.d.ts +481 -0
  138. package/dist/api/presentation/shapes.d.ts.map +1 -0
  139. package/dist/api/presentation/sheets.d.ts +452 -13
  140. package/dist/api/presentation/sheets.d.ts.map +1 -1
  141. package/dist/api/presentation/slideshow.d.ts +125 -0
  142. package/dist/api/presentation/slideshow.d.ts.map +1 -0
  143. package/dist/api/presentation/tables.d.ts +81 -0
  144. package/dist/api/presentation/tables.d.ts.map +1 -0
  145. package/dist/api/presentation/views.d.ts +367 -7
  146. package/dist/api/presentation/views.d.ts.map +1 -1
  147. package/dist/api/program/areas.d.ts +102 -12
  148. package/dist/api/program/areas.d.ts.map +1 -1
  149. package/dist/api/program/cores.d.ts +3 -99
  150. package/dist/api/program/cores.d.ts.map +1 -1
  151. package/dist/api/program/index.d.ts +7 -15
  152. package/dist/api/program/index.d.ts.map +1 -1
  153. package/dist/api/program/layout.d.ts +346 -11
  154. package/dist/api/program/layout.d.ts.map +1 -1
  155. package/dist/api/program/site.d.ts +469 -13
  156. package/dist/api/program/site.d.ts.map +1 -1
  157. package/dist/api/program/spreadsheet.d.ts +365 -41
  158. package/dist/api/program/spreadsheet.d.ts.map +1 -1
  159. package/dist/api/workspace/index.d.ts +505 -0
  160. package/dist/api/workspace/index.d.ts.map +1 -0
  161. package/dist/errors/codes.d.ts +34 -0
  162. package/dist/errors/codes.d.ts.map +1 -0
  163. package/dist/errors/envelope.d.ts +56 -0
  164. package/dist/errors/envelope.d.ts.map +1 -0
  165. package/dist/errors/index.d.ts +6 -0
  166. package/dist/errors/index.d.ts.map +1 -0
  167. package/dist/errors/plugin-error.d.ts +69 -0
  168. package/dist/errors/plugin-error.d.ts.map +1 -0
  169. package/dist/handles.d.ts +97 -25
  170. package/dist/handles.d.ts.map +1 -1
  171. package/dist/host-utils.d.ts +4 -0
  172. package/dist/host-utils.d.ts.map +1 -1
  173. package/dist/index.cjs +4605 -1423
  174. package/dist/index.cjs.map +1 -1
  175. package/dist/index.d.ts +1 -0
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +4191 -1412
  178. package/dist/index.js.map +1 -1
  179. package/package.json +4 -2
  180. package/scripts/generate-manifest.mjs +45 -0
  181. package/scripts/generate-manifest.test.mjs +103 -4
  182. package/src/api/analysis/daylight.ts +470 -0
  183. package/src/api/analysis/heatmaps.ts +683 -0
  184. package/src/api/analysis/illuminance.ts +155 -0
  185. package/src/api/analysis/index.ts +61 -0
  186. package/src/api/analysis/shadows.ts +183 -0
  187. package/src/api/analysis/solar.ts +237 -0
  188. package/src/api/analysis/sunlightHours.ts +211 -0
  189. package/src/api/analysis/sunpath.ts +83 -0
  190. package/src/api/analysis/weather.ts +179 -0
  191. package/src/api/core/camera/index.ts +268 -0
  192. package/src/api/core/comment/index.ts +120 -2
  193. package/src/api/core/geom/create/index.ts +912 -1
  194. package/src/api/core/geom/delete/index.ts +6 -0
  195. package/src/api/core/geom/query/brep.ts +119 -0
  196. package/src/api/core/handles/index.ts +233 -0
  197. package/src/api/core/index.ts +34 -0
  198. package/src/api/core/io/export/index.ts +126 -0
  199. package/src/api/core/io/import/index.ts +496 -0
  200. package/src/api/core/io/index.ts +42 -0
  201. package/src/api/core/io/job/index.ts +140 -0
  202. package/src/api/core/io/query/index.ts +71 -0
  203. package/src/api/core/io/terrain/index.ts +360 -0
  204. package/src/api/core/io/underlay/index.ts +705 -0
  205. package/src/api/core/mode/index.ts +96 -0
  206. package/src/api/core/project/index.ts +62 -1
  207. package/src/api/core/proposals/index.ts +569 -0
  208. package/src/api/core/storeys/index.ts +294 -0
  209. package/src/api/core/tags.ts +27 -0
  210. package/src/api/core/user.ts +46 -0
  211. package/src/api/core/zoom/index.ts +4 -0
  212. package/src/api/design/create/index.ts +670 -30
  213. package/src/api/design/delete/index.ts +3 -0
  214. package/src/api/design/doors/index.ts +208 -0
  215. package/src/api/design/erase/index.ts +1 -1
  216. package/src/api/design/family.ts +544 -0
  217. package/src/api/design/furniture/index.ts +197 -8
  218. package/src/api/design/index.ts +102 -0
  219. package/src/api/design/lock.ts +27 -0
  220. package/src/api/design/materials/index.ts +334 -27
  221. package/src/api/design/query/geometry/index.ts +125 -3
  222. package/src/api/design/query/index.ts +219 -7
  223. package/src/api/design/query/referenceLines.ts +52 -0
  224. package/src/api/design/query/spaces.ts +143 -0
  225. package/src/api/design/selection/index.ts +129 -0
  226. package/src/api/design/transform/index.ts +170 -9
  227. package/src/api/design/types/index.ts +156 -0
  228. package/src/api/design/update/index.ts +631 -3
  229. package/src/api/design/visibility.ts +143 -0
  230. package/src/api/design/windows/index.ts +128 -2
  231. package/src/api/entity/buildableEnvelope.ts +4 -0
  232. package/src/api/entity/referenceLine.ts +8 -0
  233. package/src/api/entity/story.ts +259 -15
  234. package/src/api/index.ts +10 -0
  235. package/src/api/presentation/annotate.ts +385 -0
  236. package/src/api/presentation/diagrams.ts +118 -8
  237. package/src/api/presentation/export.ts +108 -0
  238. package/src/api/presentation/import.ts +51 -4
  239. package/src/api/presentation/index.ts +66 -1
  240. package/src/api/presentation/placedViews.ts +1120 -0
  241. package/src/api/presentation/shapes.ts +274 -0
  242. package/src/api/presentation/sheets.ts +400 -13
  243. package/src/api/presentation/slideshow.ts +134 -0
  244. package/src/api/presentation/tables.ts +84 -0
  245. package/src/api/presentation/views.ts +376 -8
  246. package/src/api/program/areas.ts +88 -15
  247. package/src/api/program/cores.ts +3 -91
  248. package/src/api/program/index.ts +7 -15
  249. package/src/api/program/layout.ts +365 -11
  250. package/src/api/program/site.ts +435 -13
  251. package/src/api/program/spreadsheet.ts +376 -35
  252. package/src/api/workspace/index.ts +563 -0
  253. package/src/errors/codes.ts +136 -0
  254. package/src/errors/envelope.ts +75 -0
  255. package/src/errors/index.ts +21 -0
  256. package/src/errors/plugin-error.ts +134 -0
  257. package/src/handles.ts +123 -13
  258. package/src/host-utils.ts +4 -0
  259. package/src/index.ts +1 -0
  260. package/test/errors.test.mjs +184 -0
  261. package/tsconfig.json +7 -2
@@ -0,0 +1,683 @@
1
+ import * as z from "zod"
2
+ import { PluginApiReturn } from "../../types"
3
+ import { ComponentHandle, Vec3Components } from "../../handles"
4
+
5
+ /**
6
+ * Analysis heatmaps — render plugin-computed scalar data as a heatmap.
7
+ *
8
+ * Unlike the built-in studies ({@linkcode PluginAnalysisSunlightHoursApi},
9
+ * {@linkcode PluginAnalysisIlluminanceApi}) where the host computes the
10
+ * values, here the **plugin brings its own numbers** and the host renders
11
+ * them. Two render modes:
12
+ *
13
+ * - {@linkcode PluginAnalysisHeatmapsApi.renderSpaces} — one flat colour per
14
+ * space, painted on the space's top face (occupancy, energy use, any
15
+ * per-room metric).
16
+ * - {@linkcode PluginAnalysisHeatmapsApi.renderGrid} — a coloured grid mesh
17
+ * from point samples in world coordinates (wind, noise, microclimate — any
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.
22
+ *
23
+ * Shared semantics:
24
+ *
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}.
31
+ * - **Legend** — the host renders a legend card from
32
+ * {@linkcode PluginAnalysisHeatmapOptions} (title, unit, min/max, colour
33
+ * segments). Values outside `[min, max]` **clamp to the end colours** and
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).
38
+ * - **Ephemeral** — the heatmap is a transient overlay: never persisted,
39
+ * never saved with views, and auto-cleared when a scene-mutating edit
40
+ * invalidates it. Re-render after changing the model.
41
+ *
42
+ * Accessed via `snaptrude.analysis.heatmaps`.
43
+ */
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
+
52
+ constructor() {}
53
+
54
+ /**
55
+ * Render a per-space heatmap — one flat colour per space's top face.
56
+ *
57
+ * Each entry maps a space to a scalar value; the value is mapped through
58
+ * the colour ramp (see {@linkcode PluginAnalysisHeatmapOptions}) and the
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
62
+ * face is not flat (pitched/sloped tops) are **skipped with a console
63
+ * warning** — the rest of the heatmap still renders.
64
+ *
65
+ * The heatmap is ephemeral: it is auto-cleared by scene-mutating edits
66
+ * and is never persisted or saved with views.
67
+ *
68
+ * @param entries - One {@linkcode PluginAnalysisHeatmapSpaceEntry} per
69
+ * space: the space's handle and its scalar value.
70
+ * @param options - {@linkcode PluginAnalysisHeatmapOptions} — legend
71
+ * title/unit, explicit min/max range (auto-derived from the data when
72
+ * omitted), and the colour ramp.
73
+ * @returns A {@linkcode PluginAnalysisHeatmapsRenderResult} —
74
+ * `{ success: true }` once the heatmap is rendered.
75
+ * @throws When `entries` is empty or a handle does not resolve to a space.
76
+ * @throws When no space had a flat top face to paint (every entry was
77
+ * skipped).
78
+ * @throws When `options.min` is greater than `options.max`.
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.
82
+ * @throws When plugin writes are disabled.
83
+ *
84
+ * @examplePrompt Color each room by its occupancy count
85
+ * @examplePrompt Show my energy simulation results as a heatmap per space
86
+ * @examplePrompt Paint the rooms red to blue by temperature
87
+ *
88
+ * # Example
89
+ * ```ts
90
+ * const spaces = await snaptrude.design.query.listSpaces()
91
+ * await snaptrude.analysis.heatmaps.renderSpaces(
92
+ * spaces.map((space) => ({ space, value: occupancy[space] ?? 0 })),
93
+ * { title: "Occupancy", unit: "people", min: 0, max: 50 },
94
+ * )
95
+ * ```
96
+ */
97
+ public abstract renderSpaces(
98
+ entries: PluginAnalysisHeatmapSpaceEntry[],
99
+ options?: PluginAnalysisHeatmapOptions,
100
+ ): PluginApiReturn<PluginAnalysisHeatmapsRenderResult>
101
+
102
+ /**
103
+ * Render a grid heatmap — a coloured mesh from point samples.
104
+ *
105
+ * Each cell is a scalar sample at a **world-coordinate** position
106
+ * (**y is up** — a ground-level field sits at `y = 0`); the host builds
107
+ * one square cell of edge `cellSize` per sample — in the same Snaptrude
108
+ * internal units as `position` (convert real-world lengths via
109
+ * {@linkcode PluginUnitsApi.convert}) — coloured by mapping the value
110
+ * through the colour ramp
111
+ * (see {@linkcode PluginAnalysisHeatmapOptions}). This is the mode for
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.
115
+ *
116
+ * The heatmap is ephemeral: it is auto-cleared by scene-mutating edits
117
+ * and is never persisted or saved with views.
118
+ *
119
+ * @param cells - One {@linkcode PluginAnalysisHeatmapGridCell} per
120
+ * sample: a world-coordinate `position` (y up) and its scalar `value`.
121
+ * @param cellSize - Edge length of each (square) cell, in the same
122
+ * Snaptrude internal units as `position` (see
123
+ * {@linkcode PluginUnitsApi.convert}). Must be a positive, finite number.
124
+ * @param options - {@linkcode PluginAnalysisHeatmapOptions} — legend
125
+ * title/unit, explicit min/max range (auto-derived from the data when
126
+ * omitted), and the colour ramp.
127
+ * @returns A {@linkcode PluginAnalysisHeatmapsRenderResult} —
128
+ * `{ success: true }` once the heatmap is rendered.
129
+ * @throws When `cells` is empty or `cellSize` is not a positive, finite number.
130
+ * @throws When `options.min` is greater than `options.max`.
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.
134
+ * @throws When plugin writes are disabled.
135
+ *
136
+ * @examplePrompt Overlay my CFD wind simulation results on the site
137
+ * @examplePrompt Render a noise-level grid around the building
138
+ * @examplePrompt Show these sampled microclimate values as a colored grid
139
+ *
140
+ * # Example
141
+ * ```ts
142
+ * // World coordinates are y-up: a ground-level field varies in x/z at y = 0.
143
+ * await snaptrude.analysis.heatmaps.renderGrid(
144
+ * samples.map((s) => ({ position: { x: s.x, y: 0, z: s.z }, value: s.windSpeed })),
145
+ * 2, // 2-unit square cells
146
+ * { title: "Wind speed", unit: "m/s", colors: ["#0000ff", "#00ff00", "#ff0000"] },
147
+ * )
148
+ * ```
149
+ */
150
+ public abstract renderGrid(
151
+ cells: PluginAnalysisHeatmapGridCell[],
152
+ cellSize: number,
153
+ options?: PluginAnalysisHeatmapOptions,
154
+ ): PluginApiReturn<PluginAnalysisHeatmapsRenderResult>
155
+
156
+ /**
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 }`.
177
+ *
178
+ * The heatmap is ephemeral: it is auto-cleared by scene-mutating edits
179
+ * and is never persisted or saved with views.
180
+ *
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.
303
+ * @throws When plugin writes are disabled.
304
+ *
305
+ * @examplePrompt Clear my custom heatmap
306
+ * @examplePrompt Remove the wind overlay from the model
307
+ * @examplePrompt Reset the plugin heatmap view
308
+ *
309
+ * # Example
310
+ * ```ts
311
+ * await snaptrude.analysis.heatmaps.reset()
312
+ * ```
313
+ */
314
+ public abstract reset(): PluginApiReturn<boolean>
315
+
316
+ /**
317
+ * Is a plugin heatmap currently showing?
318
+ *
319
+ * `true` between a successful render and the next
320
+ * {@linkcode PluginAnalysisHeatmapsApi.reset} — or the scene-mutating
321
+ * edit that auto-clears it (heatmaps are ephemeral).
322
+ *
323
+ * @returns `true` when a plugin heatmap is rendered on the scene.
324
+ *
325
+ * @examplePrompt Is my heatmap still showing?
326
+ * @examplePrompt Check whether the custom heatmap is active
327
+ * @examplePrompt Do I need to re-render the wind overlay?
328
+ *
329
+ * # Example
330
+ * ```ts
331
+ * if (!(await snaptrude.analysis.heatmaps.isActive())) {
332
+ * await snaptrude.analysis.heatmaps.renderGrid(cells, 2)
333
+ * }
334
+ * ```
335
+ */
336
+ public abstract isActive(): PluginApiReturn<boolean>
337
+ }
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
+
471
+ /**
472
+ * One space's entry in {@linkcode PluginAnalysisHeatmapsApi.renderSpaces}.
473
+ *
474
+ * | Property | Type | Description |
475
+ * |---|---|---|
476
+ * | `space` | {@linkcode ComponentHandle} | Handle of the space to colour |
477
+ * | `value` | `number` | The scalar value mapped to a colour |
478
+ */
479
+ export const PluginAnalysisHeatmapSpaceEntry = z.object({
480
+ space: ComponentHandle,
481
+ value: z.number().finite(),
482
+ })
483
+ export type PluginAnalysisHeatmapSpaceEntry = z.infer<
484
+ typeof PluginAnalysisHeatmapSpaceEntry
485
+ >
486
+
487
+ /**
488
+ * One grid sample in {@linkcode PluginAnalysisHeatmapsApi.renderGrid}.
489
+ *
490
+ * | Property | Type | Description |
491
+ * |---|---|---|
492
+ * | `position` | `{ x, y, z }` | Cell centre in **world coordinates** (y is up) |
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 |
495
+ */
496
+ export const PluginAnalysisHeatmapGridCell = z.object({
497
+ position: Vec3Components,
498
+ value: z.number().finite(),
499
+ meta: z.unknown().optional(),
500
+ })
501
+ export type PluginAnalysisHeatmapGridCell = z.infer<
502
+ typeof PluginAnalysisHeatmapGridCell
503
+ >
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
+
603
+ /**
604
+ * Display options shared by both heatmap render modes.
605
+ *
606
+ * The colour ramp maps values to colours: `colors` is an **ordered list of
607
+ * hex colour stops, low → high** (e.g. `["#0000ff", "#ff0000"]`). When
608
+ * omitted, the host's default blue → cyan → green → yellow → red ramp is used. Values outside
609
+ * `[min, max]` clamp to the end colours; the legend marks the clamped ends
610
+ * with `<` / `>`. `min`/`max` are auto-derived from the data when omitted.
611
+ *
612
+ * | Property | Type | Description |
613
+ * |---|---|---|
614
+ * | `name` | `string \| undefined` | Overlay name to render into (see {@linkcode PluginAnalysisHeatmapOverlaysApi}); a shared default overlay when omitted |
615
+ * | `title` | `string \| undefined` | Legend title |
616
+ * | `unit` | `string \| undefined` | Unit label shown on the legend (e.g. `"m/s"`) |
617
+ * | `min` | `number \| undefined` | Value mapped to the first colour stop; data minimum when omitted |
618
+ * | `max` | `number \| undefined` | Value mapped to the last colour stop; data maximum when omitted |
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` |
622
+ */
623
+ export const PluginAnalysisHeatmapOptions = z.object({
624
+ name: z.string().min(1).max(64).optional(),
625
+ title: z.string().optional(),
626
+ unit: z.string().optional(),
627
+ min: z.number().finite().optional(),
628
+ max: z.number().finite().optional(),
629
+ colors: z.array(HexColor).min(1).optional(),
630
+ scale: PluginAnalysisHeatmapScale.optional(),
631
+ hover: z.boolean().optional(),
632
+ })
633
+ export type PluginAnalysisHeatmapOptions = z.infer<
634
+ typeof PluginAnalysisHeatmapOptions
635
+ >
636
+
637
+ /**
638
+ * Result of {@linkcode PluginAnalysisHeatmapsApi.renderSpaces} /
639
+ * {@linkcode PluginAnalysisHeatmapsApi.renderGrid} — render failures throw,
640
+ * so a returned value always carries `success: true`.
641
+ *
642
+ * | Property | Type | Description |
643
+ * |---|---|---|
644
+ * | `success` | `true` | The heatmap is rendered on the scene |
645
+ */
646
+ export const PluginAnalysisHeatmapsRenderResult = z.object({
647
+ success: z.literal(true),
648
+ })
649
+ export type PluginAnalysisHeatmapsRenderResult = z.infer<
650
+ typeof PluginAnalysisHeatmapsRenderResult
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
+ >