@snaptrude/plugin-core 0.9.17 → 0.9.18-networks-20261016.1

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 (213) hide show
  1. package/AGENTS.md +231 -231
  2. package/CHANGELOG.md +408 -408
  3. package/CLAUDE.md +11 -11
  4. package/dist/api/analysis/daylight.d.ts +5 -5
  5. package/dist/api/analysis/solar.d.ts +2 -2
  6. package/dist/api/core/camera/index.d.ts +29 -0
  7. package/dist/api/core/camera/index.d.ts.map +1 -1
  8. package/dist/api/core/geom/create/index.d.ts +7 -1
  9. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  10. package/dist/api/core/geom/update/contour.d.ts +8 -2
  11. package/dist/api/core/geom/update/contour.d.ts.map +1 -1
  12. package/dist/api/core/zoom/index.d.ts +13 -0
  13. package/dist/api/core/zoom/index.d.ts.map +1 -1
  14. package/dist/api/design/bim.d.ts +209 -0
  15. package/dist/api/design/bim.d.ts.map +1 -0
  16. package/dist/api/design/boolean/index.d.ts +2 -0
  17. package/dist/api/design/boolean/index.d.ts.map +1 -1
  18. package/dist/api/design/catalog.d.ts +320 -0
  19. package/dist/api/design/catalog.d.ts.map +1 -0
  20. package/dist/api/design/create/bulk-items.d.ts +65 -25
  21. package/dist/api/design/create/bulk-items.d.ts.map +1 -1
  22. package/dist/api/design/create/index.d.ts +272 -99
  23. package/dist/api/design/create/index.d.ts.map +1 -1
  24. package/dist/api/design/create/opening-fields.d.ts +13 -12
  25. package/dist/api/design/create/opening-fields.d.ts.map +1 -1
  26. package/dist/api/design/delete/index.d.ts +1 -0
  27. package/dist/api/design/delete/index.d.ts.map +1 -1
  28. package/dist/api/design/doors/index.d.ts +18 -24
  29. package/dist/api/design/doors/index.d.ts.map +1 -1
  30. package/dist/api/design/family.d.ts +797 -27
  31. package/dist/api/design/family.d.ts.map +1 -1
  32. package/dist/api/design/index.d.ts +16 -0
  33. package/dist/api/design/index.d.ts.map +1 -1
  34. package/dist/api/design/networks.d.ts +1763 -0
  35. package/dist/api/design/networks.d.ts.map +1 -0
  36. package/dist/api/design/query/index.d.ts +42 -32
  37. package/dist/api/design/query/index.d.ts.map +1 -1
  38. package/dist/api/design/selection/index.d.ts +10 -10
  39. package/dist/api/design/types/index.d.ts +40 -24
  40. package/dist/api/design/types/index.d.ts.map +1 -1
  41. package/dist/api/design/update/index.d.ts +43 -3
  42. package/dist/api/design/update/index.d.ts.map +1 -1
  43. package/dist/api/design/windows/index.d.ts +8 -8
  44. package/dist/api/design/windows/index.d.ts.map +1 -1
  45. package/dist/api/presentation/aiInspiration.d.ts +10 -10
  46. package/dist/api/presentation/annotate.d.ts +38 -22
  47. package/dist/api/presentation/annotate.d.ts.map +1 -1
  48. package/dist/api/presentation/drawingContract.d.ts +32 -0
  49. package/dist/api/presentation/drawingContract.d.ts.map +1 -0
  50. package/dist/api/presentation/drawings.d.ts +3523 -0
  51. package/dist/api/presentation/drawings.d.ts.map +1 -0
  52. package/dist/api/presentation/export.d.ts +23 -5
  53. package/dist/api/presentation/export.d.ts.map +1 -1
  54. package/dist/api/presentation/index.d.ts +32 -12
  55. package/dist/api/presentation/index.d.ts.map +1 -1
  56. package/dist/api/presentation/placedViews.d.ts +106 -6
  57. package/dist/api/presentation/placedViews.d.ts.map +1 -1
  58. package/dist/api/presentation/shapes.d.ts +2 -2
  59. package/dist/api/presentation/sheets.d.ts +175 -19
  60. package/dist/api/presentation/sheets.d.ts.map +1 -1
  61. package/dist/api/presentation/viewPresets.d.ts +222 -0
  62. package/dist/api/presentation/viewPresets.d.ts.map +1 -0
  63. package/dist/api/presentation/views.d.ts +103 -5
  64. package/dist/api/presentation/views.d.ts.map +1 -1
  65. package/dist/api/presentation/walkthroughs.d.ts +6 -6
  66. package/dist/errors/codes.d.ts +3 -1
  67. package/dist/errors/codes.d.ts.map +1 -1
  68. package/dist/index.cjs +3732 -2383
  69. package/dist/index.cjs.map +1 -1
  70. package/dist/index.js +3607 -2383
  71. package/dist/index.js.map +1 -1
  72. package/package.json +42 -42
  73. package/scripts/check-dist-fresh.mjs +22 -22
  74. package/scripts/generate-manifest.mjs +243 -238
  75. package/scripts/generate-manifest.test.mjs +303 -303
  76. package/src/api/analysis/daylight.ts +470 -470
  77. package/src/api/analysis/heatmaps.ts +683 -683
  78. package/src/api/analysis/illuminance.ts +155 -155
  79. package/src/api/analysis/index.ts +61 -61
  80. package/src/api/analysis/shadows.ts +183 -183
  81. package/src/api/analysis/solar.ts +237 -237
  82. package/src/api/analysis/sunlightHours.ts +211 -211
  83. package/src/api/analysis/sunpath.ts +83 -83
  84. package/src/api/analysis/weather.ts +179 -179
  85. package/src/api/core/buildings.ts +485 -485
  86. package/src/api/core/camera/index.ts +696 -667
  87. package/src/api/core/comment/index.ts +376 -376
  88. package/src/api/core/geom/create/index.ts +2139 -2133
  89. package/src/api/core/geom/delete/index.ts +84 -84
  90. package/src/api/core/geom/index.ts +35 -35
  91. package/src/api/core/geom/query/arc.ts +169 -169
  92. package/src/api/core/geom/query/brep.ts +621 -621
  93. package/src/api/core/geom/query/circle.ts +404 -404
  94. package/src/api/core/geom/query/contour.ts +540 -540
  95. package/src/api/core/geom/query/curve.ts +1102 -1102
  96. package/src/api/core/geom/query/edge.ts +130 -130
  97. package/src/api/core/geom/query/face.ts +508 -508
  98. package/src/api/core/geom/query/halfedge.ts +210 -210
  99. package/src/api/core/geom/query/index.ts +63 -63
  100. package/src/api/core/geom/query/profile.ts +473 -473
  101. package/src/api/core/geom/query/vertex.ts +205 -205
  102. package/src/api/core/geom/update/contour.ts +323 -317
  103. package/src/api/core/geom/update/curve.ts +209 -209
  104. package/src/api/core/geom/update/index.ts +29 -29
  105. package/src/api/core/geom/update/profile.ts +379 -379
  106. package/src/api/core/groups.ts +521 -521
  107. package/src/api/core/handles/index.ts +234 -234
  108. package/src/api/core/history/index.ts +84 -84
  109. package/src/api/core/index.ts +99 -99
  110. package/src/api/core/io/export/index.ts +126 -126
  111. package/src/api/core/io/import/index.ts +504 -504
  112. package/src/api/core/io/index.ts +42 -42
  113. package/src/api/core/io/job/index.ts +140 -140
  114. package/src/api/core/io/query/index.ts +71 -71
  115. package/src/api/core/io/terrain/index.ts +360 -360
  116. package/src/api/core/io/underlay/index.ts +705 -705
  117. package/src/api/core/layers.ts +540 -540
  118. package/src/api/core/math/index.ts +20 -20
  119. package/src/api/core/math/quat.ts +172 -172
  120. package/src/api/core/math/vec3.ts +179 -179
  121. package/src/api/core/mode/index.ts +108 -108
  122. package/src/api/core/project/index.ts +479 -479
  123. package/src/api/core/proposals/index.ts +570 -570
  124. package/src/api/core/storeys/index.ts +308 -308
  125. package/src/api/core/storeys/layers.ts +339 -339
  126. package/src/api/core/tags.ts +911 -911
  127. package/src/api/core/units/index.ts +164 -164
  128. package/src/api/core/user.ts +46 -46
  129. package/src/api/core/zoom/index.ts +64 -51
  130. package/src/api/design/bim.ts +121 -0
  131. package/src/api/design/boolean/index.ts +181 -179
  132. package/src/api/design/catalog.ts +203 -0
  133. package/src/api/design/create/bulk-items.ts +294 -254
  134. package/src/api/design/create/grids.ts +137 -137
  135. package/src/api/design/create/index.ts +2148 -1980
  136. package/src/api/design/create/opening-fields.ts +40 -37
  137. package/src/api/design/delete/index.ts +75 -74
  138. package/src/api/design/dimensions.ts +453 -453
  139. package/src/api/design/doors/index.ts +516 -500
  140. package/src/api/design/edit/index.ts +54 -54
  141. package/src/api/design/erase/index.ts +151 -151
  142. package/src/api/design/family.ts +1334 -704
  143. package/src/api/design/furniture/index.ts +319 -319
  144. package/src/api/design/grids.ts +237 -237
  145. package/src/api/design/index.ts +304 -274
  146. package/src/api/design/lock.ts +69 -69
  147. package/src/api/design/materials/index.ts +608 -608
  148. package/src/api/design/networks.ts +667 -0
  149. package/src/api/design/query/geometry/index.ts +319 -319
  150. package/src/api/design/query/grids.ts +197 -197
  151. package/src/api/design/query/index.ts +1236 -1226
  152. package/src/api/design/query/referenceLines.ts +52 -52
  153. package/src/api/design/query/spaces.ts +275 -275
  154. package/src/api/design/selection/index.ts +273 -273
  155. package/src/api/design/shared.ts +40 -40
  156. package/src/api/design/transform/index.ts +254 -254
  157. package/src/api/design/types/index.ts +163 -156
  158. package/src/api/design/update/index.ts +1024 -983
  159. package/src/api/design/visibility.ts +143 -143
  160. package/src/api/design/windows/index.ts +351 -333
  161. package/src/api/entity/buildableEnvelope.ts +322 -322
  162. package/src/api/entity/index.ts +30 -30
  163. package/src/api/entity/referenceLine.ts +235 -235
  164. package/src/api/entity/space.ts +612 -612
  165. package/src/api/entity/story.ts +517 -517
  166. package/src/api/index.ts +47 -47
  167. package/src/api/presentation/aiInspiration.ts +1116 -1116
  168. package/src/api/presentation/annotate.ts +417 -385
  169. package/src/api/presentation/camera.ts +289 -289
  170. package/src/api/presentation/diagrams.ts +194 -194
  171. package/src/api/presentation/drawingContract.ts +70 -0
  172. package/src/api/presentation/drawings.ts +1179 -0
  173. package/src/api/presentation/export.ts +134 -108
  174. package/src/api/presentation/import.ts +159 -159
  175. package/src/api/presentation/index.ts +160 -135
  176. package/src/api/presentation/placedViews.ts +1211 -1120
  177. package/src/api/presentation/sections.ts +420 -420
  178. package/src/api/presentation/shapes.ts +274 -274
  179. package/src/api/presentation/sheets.ts +746 -585
  180. package/src/api/presentation/slideshow.ts +134 -134
  181. package/src/api/presentation/tables.ts +84 -84
  182. package/src/api/presentation/viewPresets.ts +241 -0
  183. package/src/api/presentation/views.ts +713 -673
  184. package/src/api/presentation/walkthroughs.ts +1201 -1201
  185. package/src/api/program/adjacency.ts +164 -164
  186. package/src/api/program/areas.ts +408 -408
  187. package/src/api/program/classification.ts +138 -138
  188. package/src/api/program/cores.ts +83 -83
  189. package/src/api/program/departments.ts +517 -517
  190. package/src/api/program/index.ts +73 -73
  191. package/src/api/program/labels.ts +332 -332
  192. package/src/api/program/layout.ts +422 -422
  193. package/src/api/program/metadata.ts +364 -364
  194. package/src/api/program/metrics.ts +120 -120
  195. package/src/api/program/site.ts +748 -748
  196. package/src/api/program/spreadsheet.ts +2698 -2698
  197. package/src/api/workspace/index.ts +612 -612
  198. package/src/errors/codes.ts +143 -136
  199. package/src/errors/envelope.ts +75 -75
  200. package/src/errors/index.ts +21 -21
  201. package/src/errors/plugin-error.ts +134 -134
  202. package/src/handles.ts +297 -297
  203. package/src/host-utils.ts +67 -67
  204. package/src/index.ts +7 -7
  205. package/src/massParameters.ops.ts +246 -246
  206. package/src/massParameters.ts +427 -427
  207. package/src/types.ts +16 -16
  208. package/test/drawingContract.test.mjs +288 -0
  209. package/test/errors.test.mjs +185 -184
  210. package/test/massParameters.test.mjs +952 -952
  211. package/tsconfig.json +22 -22
  212. package/tsup.config.ts +12 -12
  213. package/api-manifest.json +0 -9711
@@ -1,360 +1,360 @@
1
- import * as z from "zod"
2
- import { PluginApiReturn } from "../../../../types"
3
- import { TerrainHandle } from "../../../../handles"
4
-
5
- /**
6
- * Terrain — inspect and manage the project's site terrain (the singleton created
7
- * by `core.io.import.terrain`).
8
- *
9
- * There is at most one terrain per project. These methods read and edit it: the
10
- * vertical **datum**, elevation/satellite layers, opacity, the cut/fill report,
11
- * and removal. All edits are undoable.
12
- *
13
- * ## Datum
14
- * The **datum** is the terrain's vertical position relative to where it was
15
- * imported. {@link PluginCoreIoTerrainApi.setDatum} shifts the whole terrain
16
- * vertically by a **relative** offset (each call moves it further — calls are
17
- * cumulative, not absolute); {@link PluginCoreIoTerrainApi.getDatum} reads the
18
- * current vertical offset from the imported position.
19
- *
20
- * Accessed via `snaptrude.core.io.terrain`.
21
- */
22
- export abstract class PluginCoreIoTerrainApi {
23
- constructor() {}
24
-
25
- /**
26
- * Whether the project has a site terrain.
27
- *
28
- * @examplePrompt Does this project have site terrain?
29
- * @examplePrompt Is there a terrain imported?
30
- * @examplePrompt Check if the site topography exists
31
- * @examplePrompt Has a terrain been added yet?
32
- *
33
- * # Example
34
- * ```ts
35
- * if (!(await snaptrude.core.io.terrain.exists())) {
36
- * await snaptrude.core.io.import.terrain(40.7128, -74.006, 300, 300)
37
- * }
38
- * ```
39
- */
40
- public abstract exists(): PluginApiReturn<boolean>
41
-
42
- /**
43
- * Resolve the project's terrain handle, or `null` if there is none. The same
44
- * handle {@link core.io.import.terrain} returns — use it to obtain the handle
45
- * without re-importing.
46
- *
47
- * @examplePrompt Get the terrain handle
48
- * @examplePrompt Give me the site terrain
49
- * @examplePrompt Fetch the current terrain
50
- * @examplePrompt Return the topography handle if it exists
51
- */
52
- public abstract get(): PluginApiReturn<TerrainHandle | null>
53
-
54
- /**
55
- * Read the terrain's current vertical position (world Y, internal units).
56
- * `null` if there is no terrain. Import places the terrain so its highest
57
- * point sits at `y = 0`, so a never-shifted terrain reads a negative
58
- * baseline, not `0`; each {@link PluginCoreIoTerrainApi.setDatum} shift
59
- * moves this value.
60
- *
61
- * @examplePrompt What's the terrain's datum?
62
- * @examplePrompt Read the current site elevation reference
63
- * @examplePrompt Get the terrain vertical offset
64
- * @examplePrompt How far has the datum been shifted?
65
- */
66
- public abstract getDatum(): PluginApiReturn<number | null>
67
-
68
- /**
69
- * Shift the terrain's **datum** — move the whole terrain vertically DOWN by
70
- * `offset` (internal units) from its current position; pass a negative value to
71
- * raise it. Undoable.
72
- *
73
- * **Relative and cumulative**, not absolute: `setDatum(5)` called twice shifts
74
- * the terrain down by 10 total, it does not settle at a fixed level. Read the
75
- * terrain's resulting vertical position (world Y — not the accumulated
76
- * shift) back with {@link PluginCoreIoTerrainApi.getDatum}.
77
- *
78
- * @param offset - How far to shift the terrain down, in internal units
79
- * (negative raises it).
80
- * @throws if writes are disabled, there is no terrain, or the terrain is
81
- * **locked** (unlock it in the app first).
82
- *
83
- * @examplePrompt Shift the terrain datum down by 12
84
- * @examplePrompt Lower the site terrain by 10
85
- * @examplePrompt Raise the terrain a little
86
- * @examplePrompt Nudge the terrain datum down
87
- *
88
- * # Example
89
- * ```ts
90
- * await snaptrude.core.io.terrain.setDatum(12) // shift terrain down by 12 (relative)
91
- * ```
92
- */
93
- public abstract setDatum(offset: number): PluginApiReturn<void>
94
-
95
- /**
96
- * Delete the site terrain. Undoable.
97
- *
98
- * @throws if writes are disabled, there is no terrain, or the terrain is
99
- * **locked** (unlock it in the app first).
100
- *
101
- * @examplePrompt Delete the site terrain
102
- * @examplePrompt Remove the topography
103
- * @examplePrompt Clear the imported site
104
- * @examplePrompt Get rid of the terrain
105
- */
106
- public abstract delete(): PluginApiReturn<void>
107
-
108
- /**
109
- * The terrain cut/fill earthwork report — cut, fill, and net volumes — or
110
- * `null` if there is no terrain.
111
- *
112
- * @examplePrompt What are the cut and fill volumes for the site?
113
- * @examplePrompt Get the terrain earthwork report
114
- * @examplePrompt How much cut and fill does the grading need?
115
- * @examplePrompt Show the net volume of the terrain edits
116
- *
117
- * # Example
118
- * ```ts
119
- * const r = await snaptrude.core.io.terrain.getReport()
120
- * if (r) console.log(`cut ${r.cutVolume}, fill ${r.fillVolume}, net ${r.netVolume}`)
121
- * ```
122
- */
123
- public abstract getReport(): PluginApiReturn<TerrainReport | null>
124
-
125
- /** Whether terrain elevation (DEM height) is on. `null` if no terrain. */
126
- public abstract isElevationEnabled(): PluginApiReturn<boolean | null>
127
- /**
128
- * Turn terrain elevation (DEM height) ON — a real 3D topography surface. Undoable.
129
- * @throws if writes are disabled, there is no terrain, or the terrain is
130
- * **locked** (unlock it in the app first).
131
- * @examplePrompt Turn on terrain elevation
132
- * @examplePrompt Enable the site heightmap
133
- * @examplePrompt Make the terrain 3D
134
- * @examplePrompt Show the topography relief
135
- */
136
- public abstract enableElevation(): PluginApiReturn<void>
137
- /**
138
- * Turn terrain elevation OFF — flatten to a plane. Undoable.
139
- * @throws if writes are disabled, there is no terrain, or the terrain is
140
- * **locked** (unlock it in the app first).
141
- * @examplePrompt Flatten the terrain
142
- * @examplePrompt Turn off site elevation
143
- * @examplePrompt Disable the heightmap
144
- * @examplePrompt Make the terrain flat
145
- */
146
- public abstract disableElevation(): PluginApiReturn<void>
147
-
148
- /** Whether satellite imagery is draped on the terrain. `null` if no terrain. */
149
- public abstract isSatelliteEnabled(): PluginApiReturn<boolean | null>
150
- /**
151
- * Turn satellite imagery ON. Undoable.
152
- * @throws if writes are disabled, there is no terrain, or the terrain is
153
- * **locked** (unlock it in the app first).
154
- * @examplePrompt Show satellite imagery on the site
155
- * @examplePrompt Enable the satellite texture
156
- * @examplePrompt Turn on aerial imagery
157
- * @examplePrompt Drape satellite over the terrain
158
- */
159
- public abstract enableSatellite(): PluginApiReturn<void>
160
- /**
161
- * Turn satellite imagery OFF. Undoable.
162
- * @throws if writes are disabled, there is no terrain, or the terrain is
163
- * **locked** (unlock it in the app first).
164
- * @examplePrompt Hide the satellite imagery
165
- * @examplePrompt Turn off the aerial texture
166
- * @examplePrompt Disable satellite on the site
167
- * @examplePrompt Remove the satellite drape
168
- */
169
- public abstract disableSatellite(): PluginApiReturn<void>
170
-
171
- /**
172
- * Replace the terrain surface with a caller-supplied triangle mesh. Undoable
173
- * (one step), and the replacement persists with the project. The returned
174
- * promise resolves only after the surface is fully applied and recorded —
175
- * it is safe to toggle terrain resolution or read the surface back the
176
- * moment it resolves.
177
- *
178
- * `positions` is a flat `[x, y, z, …]` array in **world space** — the same
179
- * frame `design.query.geometry.getTriangulatedMeshes` reads — so a
180
- * read-modify-write round trip needs no conversion. `indices` is a flat
181
- * triangle list into `positions` (three indices per triangle). A
182
- * predominantly down-facing surface is flipped automatically (reported in
183
- * `warnings`), and zero-area sliver triangles are tolerated and counted.
184
- *
185
- * **What survives the replacement:** the datum, geolocation, opacity, lock
186
- * state, and the elevation/satellite toggles. The map/satellite texture is
187
- * re-projected onto the new surface from above (best effort — alignment is
188
- * approximate, reported in `warnings`). **What resets:** cut/fill history
189
- * and the earthwork report — the replaced surface starts with a clean
190
- * grading slate.
191
- *
192
- * Limits: at most 500,000 triangles and 500,000 vertices; every coordinate
193
- * must be finite and within the scene bound.
194
- *
195
- * @param positions - Flat `[x, y, z, …]` world-space vertex positions.
196
- * @param indices - Flat triangle list into `positions`.
197
- * @param units - `"world"` (default): coordinates are raw internal units;
198
- * `"meters"`: coordinates are metres and are converted on the way in.
199
- * @param options - Optional {@linkcode PluginTerrainReplaceMeshOptions}:
200
- * optimistic-concurrency expectations (`expectedTerrain`,
201
- * `expectedModelRevision`), `preserve` opt-outs, idempotent replay via
202
- * `clientMutationId`, and the `coordinateSpace`/`baseline` declarations.
203
- * @throws if writes are disabled, there is no terrain (import one first),
204
- * the terrain is **locked**, the mesh fails validation (non-finite or
205
- * out-of-bound coordinates, bad indices, degenerate-only geometry, over
206
- * the size caps), `expectedTerrain` does not match the live terrain, or
207
- * `expectedModelRevision` is stale.
208
- *
209
- * @examplePrompt Replace the terrain with this surveyed mesh
210
- * @examplePrompt Flatten a building pad into the site surface
211
- * @examplePrompt Load a custom DEM surface onto the terrain
212
- * @examplePrompt Regrade the terrain from these points
213
- *
214
- * # Example
215
- * ```ts
216
- * // Projects with no terrain yet: import first, then replace.
217
- * if (!(await snaptrude.core.io.terrain.exists())) {
218
- * await snaptrude.core.io.import.terrain(40.7128, -74.006, 300, 300)
219
- * }
220
- * const result = await snaptrude.core.io.terrain.replaceMesh(
221
- * positions, // [x0, y0, z0, x1, y1, z1, …] — world space
222
- * indices, // [a0, b0, c0, a1, b1, c1, …]
223
- * "meters",
224
- * {
225
- * coordinateSpace: "world",
226
- * expectedTerrain: await snaptrude.core.io.terrain.get(),
227
- * baseline: "make-replacement-original",
228
- * preserve: { datum: true, geolocation: true, satellite: true, material: true },
229
- * clientMutationId: "survey-2026-08-05-r1",
230
- * },
231
- * )
232
- * // result.modelRevision — pass back as expectedModelRevision on the next call
233
- * ```
234
- */
235
- public abstract replaceMesh(
236
- positions: number[],
237
- indices: number[],
238
- units?: "world" | "meters",
239
- options?: PluginTerrainReplaceMeshOptions,
240
- ): PluginApiReturn<PluginTerrainReplaceMeshResult>
241
-
242
- /** Read the terrain opacity, `0`..`1`, or `null` if no terrain. */
243
- public abstract getOpacity(): PluginApiReturn<number | null>
244
- /**
245
- * Set the terrain opacity (`0` transparent .. `1` opaque). Undoable.
246
- * @param opacity - Target opacity, `0`..`1`.
247
- * @throws if writes are disabled, there is no terrain, or the terrain is
248
- * **locked** (unlock it in the app first).
249
- * @examplePrompt Fade the terrain to 50%
250
- * @examplePrompt Make the site semi-transparent
251
- * @examplePrompt Set terrain opacity to 0.3
252
- * @examplePrompt Dim the topography
253
- *
254
- * # Example
255
- * ```ts
256
- * await snaptrude.core.io.terrain.setOpacity(0.5)
257
- * ```
258
- */
259
- public abstract setOpacity(opacity: number): PluginApiReturn<void>
260
- }
261
-
262
- /**
263
- * Terrain cut/fill earthwork report. Volumes are in the **project's display
264
- * volume unit** (what the UI shows), not internal units.
265
- *
266
- * | Property | Type | Description |
267
- * |---|---|---|
268
- * | `cutVolume` | `number` | Total excavated (cut) volume |
269
- * | `fillVolume` | `number` | Total added (fill) volume |
270
- * | `netVolume` | `number` | Net volume (`fill − cut`) |
271
- */
272
- export const TerrainReport = z.object({
273
- cutVolume: z.number(),
274
- fillVolume: z.number(),
275
- netVolume: z.number(),
276
- })
277
- export type TerrainReport = z.infer<typeof TerrainReport>
278
-
279
- /**
280
- * Result of {@link PluginCoreIoTerrainApi.replaceMesh}.
281
- *
282
- * | Property | Type | Description |
283
- * |---|---|---|
284
- * | `terrain` | `TerrainHandle` | The terrain (unchanged singleton handle) |
285
- * | `modelRevision` | `number` | Session-scoped terrain-mesh revision after this replace — pass back as `expectedModelRevision` for compare-and-swap semantics |
286
- * | `vertexCount` | `number` | Vertices in the applied mesh |
287
- * | `triangleCount` | `number` | Triangles in the applied mesh |
288
- * | `warnings` | `string[]` | Non-fatal notes: winding flipped, sliver triangles, approximate texture re-projection |
289
- */
290
- export type PluginTerrainReplaceMeshResult = {
291
- terrain: TerrainHandle
292
- modelRevision: number
293
- vertexCount: number
294
- triangleCount: number
295
- warnings: string[]
296
- }
297
-
298
- /**
299
- * Options for {@link PluginCoreIoTerrainApi.replaceMesh}.
300
- *
301
- * | Property | Type | Description |
302
- * |---|---|---|
303
- * | `coordinateSpace` | `"world"`? | Declares the input frame. `world` is the only supported space (the same frame `getTriangulatedMeshes` reads) |
304
- * | `expectedTerrain` | `TerrainHandle`? | Optimistic check: throws `HANDLE_INVALID` if the live terrain is not this handle (session-scoped — handles do not survive reloads) |
305
- * | `expectedModelRevision` | `number`? | Optimistic check against the session terrain-mesh revision (starts at `0`, increments per successful `replaceMesh`; the result returns the new value). Stale → `PRECONDITION_FAILED` with the current revision in `details` |
306
- * | `baseline` | `"make-replacement-original"`? | Declares replacement semantics. This is the only (and default) behavior: the replacement becomes the persisted surface; the app's terrain Reset restores the original map heightmap as the escape hatch |
307
- * | `preserve` | object? | Opt-outs, all defaulting `true`. `datum: false` resets the terrain transform (authored coordinates become the local frame). `satellite: false` switches the drape off after the replace. `material: false` skips texture re-projection — the whole surface takes the ground material. `geolocation: false` is rejected (`VALIDATION`): geolocation is always preserved in this version |
308
- * | `clientMutationId` | `string`? | Session-scoped idempotency: a repeated call with the same id returns the original result without re-executing |
309
- */
310
- export const PluginTerrainReplaceMeshOptions = z
311
- .object({
312
- coordinateSpace: z.literal("world").optional(),
313
- expectedTerrain: z.string().min(1).optional(),
314
- expectedModelRevision: z.number().int().nonnegative().optional(),
315
- baseline: z.literal("make-replacement-original").optional(),
316
- preserve: z
317
- .object({
318
- datum: z.boolean().optional(),
319
- geolocation: z.boolean().optional(),
320
- satellite: z.boolean().optional(),
321
- material: z.boolean().optional(),
322
- })
323
- .strict()
324
- .optional(),
325
- clientMutationId: z.string().min(1).max(128).optional(),
326
- })
327
- .strict()
328
- export type PluginTerrainReplaceMeshOptions = z.infer<typeof PluginTerrainReplaceMeshOptions>
329
-
330
- /**
331
- * Arguments for {@link PluginCoreIoTerrainApi.replaceMesh}. The caps are the
332
- * plugin surface's explicit payload ceiling: 500k vertices / 500k triangles.
333
- */
334
- export const PluginTerrainReplaceMeshArgs = z.object({
335
- positions: z
336
- .array(z.number().finite())
337
- .min(9)
338
- .max(1_500_000)
339
- .refine((a) => a.length % 3 === 0, {
340
- message: "positions must be a flat [x, y, z, …] array (length a multiple of 3)",
341
- }),
342
- indices: z
343
- .array(z.number().int().nonnegative())
344
- .min(3)
345
- .max(1_500_000)
346
- .refine((a) => a.length % 3 === 0, {
347
- message: "indices must be a flat triangle list (length a multiple of 3)",
348
- }),
349
- units: z.enum(["world", "meters"]).optional(),
350
- options: PluginTerrainReplaceMeshOptions.optional(),
351
- })
352
- export type PluginTerrainReplaceMeshArgs = z.infer<typeof PluginTerrainReplaceMeshArgs>
353
-
354
- /** Arguments for {@link PluginCoreIoTerrainApi.setDatum}. */
355
- export const PluginTerrainSetDatumArgs = z.object({ offset: z.number().finite() })
356
- export type PluginTerrainSetDatumArgs = z.infer<typeof PluginTerrainSetDatumArgs>
357
-
358
- /** Arguments for {@link PluginCoreIoTerrainApi.setOpacity}. */
359
- export const PluginTerrainSetOpacityArgs = z.object({ opacity: z.number().min(0).max(1) })
360
- export type PluginTerrainSetOpacityArgs = z.infer<typeof PluginTerrainSetOpacityArgs>
1
+ import * as z from "zod"
2
+ import { PluginApiReturn } from "../../../../types"
3
+ import { TerrainHandle } from "../../../../handles"
4
+
5
+ /**
6
+ * Terrain — inspect and manage the project's site terrain (the singleton created
7
+ * by `core.io.import.terrain`).
8
+ *
9
+ * There is at most one terrain per project. These methods read and edit it: the
10
+ * vertical **datum**, elevation/satellite layers, opacity, the cut/fill report,
11
+ * and removal. All edits are undoable.
12
+ *
13
+ * ## Datum
14
+ * The **datum** is the terrain's vertical position relative to where it was
15
+ * imported. {@link PluginCoreIoTerrainApi.setDatum} shifts the whole terrain
16
+ * vertically by a **relative** offset (each call moves it further — calls are
17
+ * cumulative, not absolute); {@link PluginCoreIoTerrainApi.getDatum} reads the
18
+ * current vertical offset from the imported position.
19
+ *
20
+ * Accessed via `snaptrude.core.io.terrain`.
21
+ */
22
+ export abstract class PluginCoreIoTerrainApi {
23
+ constructor() {}
24
+
25
+ /**
26
+ * Whether the project has a site terrain.
27
+ *
28
+ * @examplePrompt Does this project have site terrain?
29
+ * @examplePrompt Is there a terrain imported?
30
+ * @examplePrompt Check if the site topography exists
31
+ * @examplePrompt Has a terrain been added yet?
32
+ *
33
+ * # Example
34
+ * ```ts
35
+ * if (!(await snaptrude.core.io.terrain.exists())) {
36
+ * await snaptrude.core.io.import.terrain(40.7128, -74.006, 300, 300)
37
+ * }
38
+ * ```
39
+ */
40
+ public abstract exists(): PluginApiReturn<boolean>
41
+
42
+ /**
43
+ * Resolve the project's terrain handle, or `null` if there is none. The same
44
+ * handle {@link core.io.import.terrain} returns — use it to obtain the handle
45
+ * without re-importing.
46
+ *
47
+ * @examplePrompt Get the terrain handle
48
+ * @examplePrompt Give me the site terrain
49
+ * @examplePrompt Fetch the current terrain
50
+ * @examplePrompt Return the topography handle if it exists
51
+ */
52
+ public abstract get(): PluginApiReturn<TerrainHandle | null>
53
+
54
+ /**
55
+ * Read the terrain's current vertical position (world Y, internal units).
56
+ * `null` if there is no terrain. Import places the terrain so its highest
57
+ * point sits at `y = 0`, so a never-shifted terrain reads a negative
58
+ * baseline, not `0`; each {@link PluginCoreIoTerrainApi.setDatum} shift
59
+ * moves this value.
60
+ *
61
+ * @examplePrompt What's the terrain's datum?
62
+ * @examplePrompt Read the current site elevation reference
63
+ * @examplePrompt Get the terrain vertical offset
64
+ * @examplePrompt How far has the datum been shifted?
65
+ */
66
+ public abstract getDatum(): PluginApiReturn<number | null>
67
+
68
+ /**
69
+ * Shift the terrain's **datum** — move the whole terrain vertically DOWN by
70
+ * `offset` (internal units) from its current position; pass a negative value to
71
+ * raise it. Undoable.
72
+ *
73
+ * **Relative and cumulative**, not absolute: `setDatum(5)` called twice shifts
74
+ * the terrain down by 10 total, it does not settle at a fixed level. Read the
75
+ * terrain's resulting vertical position (world Y — not the accumulated
76
+ * shift) back with {@link PluginCoreIoTerrainApi.getDatum}.
77
+ *
78
+ * @param offset - How far to shift the terrain down, in internal units
79
+ * (negative raises it).
80
+ * @throws if writes are disabled, there is no terrain, or the terrain is
81
+ * **locked** (unlock it in the app first).
82
+ *
83
+ * @examplePrompt Shift the terrain datum down by 12
84
+ * @examplePrompt Lower the site terrain by 10
85
+ * @examplePrompt Raise the terrain a little
86
+ * @examplePrompt Nudge the terrain datum down
87
+ *
88
+ * # Example
89
+ * ```ts
90
+ * await snaptrude.core.io.terrain.setDatum(12) // shift terrain down by 12 (relative)
91
+ * ```
92
+ */
93
+ public abstract setDatum(offset: number): PluginApiReturn<void>
94
+
95
+ /**
96
+ * Delete the site terrain. Undoable.
97
+ *
98
+ * @throws if writes are disabled, there is no terrain, or the terrain is
99
+ * **locked** (unlock it in the app first).
100
+ *
101
+ * @examplePrompt Delete the site terrain
102
+ * @examplePrompt Remove the topography
103
+ * @examplePrompt Clear the imported site
104
+ * @examplePrompt Get rid of the terrain
105
+ */
106
+ public abstract delete(): PluginApiReturn<void>
107
+
108
+ /**
109
+ * The terrain cut/fill earthwork report — cut, fill, and net volumes — or
110
+ * `null` if there is no terrain.
111
+ *
112
+ * @examplePrompt What are the cut and fill volumes for the site?
113
+ * @examplePrompt Get the terrain earthwork report
114
+ * @examplePrompt How much cut and fill does the grading need?
115
+ * @examplePrompt Show the net volume of the terrain edits
116
+ *
117
+ * # Example
118
+ * ```ts
119
+ * const r = await snaptrude.core.io.terrain.getReport()
120
+ * if (r) console.log(`cut ${r.cutVolume}, fill ${r.fillVolume}, net ${r.netVolume}`)
121
+ * ```
122
+ */
123
+ public abstract getReport(): PluginApiReturn<TerrainReport | null>
124
+
125
+ /** Whether terrain elevation (DEM height) is on. `null` if no terrain. */
126
+ public abstract isElevationEnabled(): PluginApiReturn<boolean | null>
127
+ /**
128
+ * Turn terrain elevation (DEM height) ON — a real 3D topography surface. Undoable.
129
+ * @throws if writes are disabled, there is no terrain, or the terrain is
130
+ * **locked** (unlock it in the app first).
131
+ * @examplePrompt Turn on terrain elevation
132
+ * @examplePrompt Enable the site heightmap
133
+ * @examplePrompt Make the terrain 3D
134
+ * @examplePrompt Show the topography relief
135
+ */
136
+ public abstract enableElevation(): PluginApiReturn<void>
137
+ /**
138
+ * Turn terrain elevation OFF — flatten to a plane. Undoable.
139
+ * @throws if writes are disabled, there is no terrain, or the terrain is
140
+ * **locked** (unlock it in the app first).
141
+ * @examplePrompt Flatten the terrain
142
+ * @examplePrompt Turn off site elevation
143
+ * @examplePrompt Disable the heightmap
144
+ * @examplePrompt Make the terrain flat
145
+ */
146
+ public abstract disableElevation(): PluginApiReturn<void>
147
+
148
+ /** Whether satellite imagery is draped on the terrain. `null` if no terrain. */
149
+ public abstract isSatelliteEnabled(): PluginApiReturn<boolean | null>
150
+ /**
151
+ * Turn satellite imagery ON. Undoable.
152
+ * @throws if writes are disabled, there is no terrain, or the terrain is
153
+ * **locked** (unlock it in the app first).
154
+ * @examplePrompt Show satellite imagery on the site
155
+ * @examplePrompt Enable the satellite texture
156
+ * @examplePrompt Turn on aerial imagery
157
+ * @examplePrompt Drape satellite over the terrain
158
+ */
159
+ public abstract enableSatellite(): PluginApiReturn<void>
160
+ /**
161
+ * Turn satellite imagery OFF. Undoable.
162
+ * @throws if writes are disabled, there is no terrain, or the terrain is
163
+ * **locked** (unlock it in the app first).
164
+ * @examplePrompt Hide the satellite imagery
165
+ * @examplePrompt Turn off the aerial texture
166
+ * @examplePrompt Disable satellite on the site
167
+ * @examplePrompt Remove the satellite drape
168
+ */
169
+ public abstract disableSatellite(): PluginApiReturn<void>
170
+
171
+ /**
172
+ * Replace the terrain surface with a caller-supplied triangle mesh. Undoable
173
+ * (one step), and the replacement persists with the project. The returned
174
+ * promise resolves only after the surface is fully applied and recorded —
175
+ * it is safe to toggle terrain resolution or read the surface back the
176
+ * moment it resolves.
177
+ *
178
+ * `positions` is a flat `[x, y, z, …]` array in **world space** — the same
179
+ * frame `design.query.geometry.getTriangulatedMeshes` reads — so a
180
+ * read-modify-write round trip needs no conversion. `indices` is a flat
181
+ * triangle list into `positions` (three indices per triangle). A
182
+ * predominantly down-facing surface is flipped automatically (reported in
183
+ * `warnings`), and zero-area sliver triangles are tolerated and counted.
184
+ *
185
+ * **What survives the replacement:** the datum, geolocation, opacity, lock
186
+ * state, and the elevation/satellite toggles. The map/satellite texture is
187
+ * re-projected onto the new surface from above (best effort — alignment is
188
+ * approximate, reported in `warnings`). **What resets:** cut/fill history
189
+ * and the earthwork report — the replaced surface starts with a clean
190
+ * grading slate.
191
+ *
192
+ * Limits: at most 500,000 triangles and 500,000 vertices; every coordinate
193
+ * must be finite and within the scene bound.
194
+ *
195
+ * @param positions - Flat `[x, y, z, …]` world-space vertex positions.
196
+ * @param indices - Flat triangle list into `positions`.
197
+ * @param units - `"world"` (default): coordinates are raw internal units;
198
+ * `"meters"`: coordinates are metres and are converted on the way in.
199
+ * @param options - Optional {@linkcode PluginTerrainReplaceMeshOptions}:
200
+ * optimistic-concurrency expectations (`expectedTerrain`,
201
+ * `expectedModelRevision`), `preserve` opt-outs, idempotent replay via
202
+ * `clientMutationId`, and the `coordinateSpace`/`baseline` declarations.
203
+ * @throws if writes are disabled, there is no terrain (import one first),
204
+ * the terrain is **locked**, the mesh fails validation (non-finite or
205
+ * out-of-bound coordinates, bad indices, degenerate-only geometry, over
206
+ * the size caps), `expectedTerrain` does not match the live terrain, or
207
+ * `expectedModelRevision` is stale.
208
+ *
209
+ * @examplePrompt Replace the terrain with this surveyed mesh
210
+ * @examplePrompt Flatten a building pad into the site surface
211
+ * @examplePrompt Load a custom DEM surface onto the terrain
212
+ * @examplePrompt Regrade the terrain from these points
213
+ *
214
+ * # Example
215
+ * ```ts
216
+ * // Projects with no terrain yet: import first, then replace.
217
+ * if (!(await snaptrude.core.io.terrain.exists())) {
218
+ * await snaptrude.core.io.import.terrain(40.7128, -74.006, 300, 300)
219
+ * }
220
+ * const result = await snaptrude.core.io.terrain.replaceMesh(
221
+ * positions, // [x0, y0, z0, x1, y1, z1, …] — world space
222
+ * indices, // [a0, b0, c0, a1, b1, c1, …]
223
+ * "meters",
224
+ * {
225
+ * coordinateSpace: "world",
226
+ * expectedTerrain: await snaptrude.core.io.terrain.get(),
227
+ * baseline: "make-replacement-original",
228
+ * preserve: { datum: true, geolocation: true, satellite: true, material: true },
229
+ * clientMutationId: "survey-2026-08-05-r1",
230
+ * },
231
+ * )
232
+ * // result.modelRevision — pass back as expectedModelRevision on the next call
233
+ * ```
234
+ */
235
+ public abstract replaceMesh(
236
+ positions: number[],
237
+ indices: number[],
238
+ units?: "world" | "meters",
239
+ options?: PluginTerrainReplaceMeshOptions,
240
+ ): PluginApiReturn<PluginTerrainReplaceMeshResult>
241
+
242
+ /** Read the terrain opacity, `0`..`1`, or `null` if no terrain. */
243
+ public abstract getOpacity(): PluginApiReturn<number | null>
244
+ /**
245
+ * Set the terrain opacity (`0` transparent .. `1` opaque). Undoable.
246
+ * @param opacity - Target opacity, `0`..`1`.
247
+ * @throws if writes are disabled, there is no terrain, or the terrain is
248
+ * **locked** (unlock it in the app first).
249
+ * @examplePrompt Fade the terrain to 50%
250
+ * @examplePrompt Make the site semi-transparent
251
+ * @examplePrompt Set terrain opacity to 0.3
252
+ * @examplePrompt Dim the topography
253
+ *
254
+ * # Example
255
+ * ```ts
256
+ * await snaptrude.core.io.terrain.setOpacity(0.5)
257
+ * ```
258
+ */
259
+ public abstract setOpacity(opacity: number): PluginApiReturn<void>
260
+ }
261
+
262
+ /**
263
+ * Terrain cut/fill earthwork report. Volumes are in the **project's display
264
+ * volume unit** (what the UI shows), not internal units.
265
+ *
266
+ * | Property | Type | Description |
267
+ * |---|---|---|
268
+ * | `cutVolume` | `number` | Total excavated (cut) volume |
269
+ * | `fillVolume` | `number` | Total added (fill) volume |
270
+ * | `netVolume` | `number` | Net volume (`fill − cut`) |
271
+ */
272
+ export const TerrainReport = z.object({
273
+ cutVolume: z.number(),
274
+ fillVolume: z.number(),
275
+ netVolume: z.number(),
276
+ })
277
+ export type TerrainReport = z.infer<typeof TerrainReport>
278
+
279
+ /**
280
+ * Result of {@link PluginCoreIoTerrainApi.replaceMesh}.
281
+ *
282
+ * | Property | Type | Description |
283
+ * |---|---|---|
284
+ * | `terrain` | `TerrainHandle` | The terrain (unchanged singleton handle) |
285
+ * | `modelRevision` | `number` | Session-scoped terrain-mesh revision after this replace — pass back as `expectedModelRevision` for compare-and-swap semantics |
286
+ * | `vertexCount` | `number` | Vertices in the applied mesh |
287
+ * | `triangleCount` | `number` | Triangles in the applied mesh |
288
+ * | `warnings` | `string[]` | Non-fatal notes: winding flipped, sliver triangles, approximate texture re-projection |
289
+ */
290
+ export type PluginTerrainReplaceMeshResult = {
291
+ terrain: TerrainHandle
292
+ modelRevision: number
293
+ vertexCount: number
294
+ triangleCount: number
295
+ warnings: string[]
296
+ }
297
+
298
+ /**
299
+ * Options for {@link PluginCoreIoTerrainApi.replaceMesh}.
300
+ *
301
+ * | Property | Type | Description |
302
+ * |---|---|---|
303
+ * | `coordinateSpace` | `"world"`? | Declares the input frame. `world` is the only supported space (the same frame `getTriangulatedMeshes` reads) |
304
+ * | `expectedTerrain` | `TerrainHandle`? | Optimistic check: throws `HANDLE_INVALID` if the live terrain is not this handle (session-scoped — handles do not survive reloads) |
305
+ * | `expectedModelRevision` | `number`? | Optimistic check against the session terrain-mesh revision (starts at `0`, increments per successful `replaceMesh`; the result returns the new value). Stale → `PRECONDITION_FAILED` with the current revision in `details` |
306
+ * | `baseline` | `"make-replacement-original"`? | Declares replacement semantics. This is the only (and default) behavior: the replacement becomes the persisted surface; the app's terrain Reset restores the original map heightmap as the escape hatch |
307
+ * | `preserve` | object? | Opt-outs, all defaulting `true`. `datum: false` resets the terrain transform (authored coordinates become the local frame). `satellite: false` switches the drape off after the replace. `material: false` skips texture re-projection — the whole surface takes the ground material. `geolocation: false` is rejected (`VALIDATION`): geolocation is always preserved in this version |
308
+ * | `clientMutationId` | `string`? | Session-scoped idempotency: a repeated call with the same id returns the original result without re-executing |
309
+ */
310
+ export const PluginTerrainReplaceMeshOptions = z
311
+ .object({
312
+ coordinateSpace: z.literal("world").optional(),
313
+ expectedTerrain: z.string().min(1).optional(),
314
+ expectedModelRevision: z.number().int().nonnegative().optional(),
315
+ baseline: z.literal("make-replacement-original").optional(),
316
+ preserve: z
317
+ .object({
318
+ datum: z.boolean().optional(),
319
+ geolocation: z.boolean().optional(),
320
+ satellite: z.boolean().optional(),
321
+ material: z.boolean().optional(),
322
+ })
323
+ .strict()
324
+ .optional(),
325
+ clientMutationId: z.string().min(1).max(128).optional(),
326
+ })
327
+ .strict()
328
+ export type PluginTerrainReplaceMeshOptions = z.infer<typeof PluginTerrainReplaceMeshOptions>
329
+
330
+ /**
331
+ * Arguments for {@link PluginCoreIoTerrainApi.replaceMesh}. The caps are the
332
+ * plugin surface's explicit payload ceiling: 500k vertices / 500k triangles.
333
+ */
334
+ export const PluginTerrainReplaceMeshArgs = z.object({
335
+ positions: z
336
+ .array(z.number().finite())
337
+ .min(9)
338
+ .max(1_500_000)
339
+ .refine((a) => a.length % 3 === 0, {
340
+ message: "positions must be a flat [x, y, z, …] array (length a multiple of 3)",
341
+ }),
342
+ indices: z
343
+ .array(z.number().int().nonnegative())
344
+ .min(3)
345
+ .max(1_500_000)
346
+ .refine((a) => a.length % 3 === 0, {
347
+ message: "indices must be a flat triangle list (length a multiple of 3)",
348
+ }),
349
+ units: z.enum(["world", "meters"]).optional(),
350
+ options: PluginTerrainReplaceMeshOptions.optional(),
351
+ })
352
+ export type PluginTerrainReplaceMeshArgs = z.infer<typeof PluginTerrainReplaceMeshArgs>
353
+
354
+ /** Arguments for {@link PluginCoreIoTerrainApi.setDatum}. */
355
+ export const PluginTerrainSetDatumArgs = z.object({ offset: z.number().finite() })
356
+ export type PluginTerrainSetDatumArgs = z.infer<typeof PluginTerrainSetDatumArgs>
357
+
358
+ /** Arguments for {@link PluginCoreIoTerrainApi.setOpacity}. */
359
+ export const PluginTerrainSetOpacityArgs = z.object({ opacity: z.number().min(0).max(1) })
360
+ export type PluginTerrainSetOpacityArgs = z.infer<typeof PluginTerrainSetOpacityArgs>