@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
@@ -8,32 +8,77 @@ import { ComponentHandle, MaterialHandle } from "../../../handles"
8
8
  * Materials are {@linkcode MaterialHandle}s (entity-style, name-addressed). Reads return
9
9
  * a {@linkcode MaterialHandle}/{@linkcode PluginMaterialInfo}; `apply`/`reset` mutate the
10
10
  * scene (undoable via `save`, default `true`). `create` mints a material from a
11
- * {@linkcode PluginMaterialSpec} (idempotent by name).
11
+ * {@linkcode PluginMaterialSpec} (idempotent by name). `list` reads the materials loaded
12
+ * in the project; `listPresets` reads the browsable preset/library catalog (handle-less
13
+ * {@linkcode PluginPresetMaterial} records that become materials via `create`/`apply`).
12
14
  *
13
- * Face-targeted apply, per-face reads (`listByFace`), and `copy` (needs an undo
14
- * wrapper) land in a later pass.
15
+ * Per-face assignment {@linkcode PluginDesignMaterialsApi.applyToFaces} /
16
+ * {@linkcode PluginDesignMaterialsApi.resetFaces} plus per-face reads
17
+ * ({@linkcode PluginDesignMaterialsApi.getByFace} / {@linkcode PluginDesignMaterialsApi.listByFace})
18
+ * — targets **BREP faces by durable face index** and is **BREP-only** (furniture,
19
+ * doors/windows, and Revit imports are rejected) and **visual-only** in v1 (the
20
+ * per-face finish is not carried into BIM schedules / cost layers). A face index
21
+ * is durable only while the component's BREP topology (face count/order) is unchanged.
22
+ *
23
+ * `copy` (needs an undo wrapper) lands in a later pass.
15
24
  */
16
25
  export abstract class PluginDesignMaterialsApi {
17
26
  constructor() {}
18
27
 
19
28
  /**
20
- * List the project materials.
21
- * @param scope Which library to list (optional)
29
+ * List the materials loaded in the project — every named material in the live
30
+ * scene (applied finishes, plugin-created materials, and type defaults in use).
31
+ * For the browsable preset/library catalog (finishes not yet in the project),
32
+ * use {@linkcode PluginDesignMaterialsApi.listPresets}.
33
+ *
22
34
  * @returns The materials as {@linkcode MaterialHandle}`[]`
23
35
  *
24
36
  * @examplePrompt List all the materials in this project
25
37
  * @examplePrompt What materials do I have available to apply?
26
- * @examplePrompt Show me the preset materials library
27
38
  * @examplePrompt Which finishes exist in this model?
28
39
  * @examplePrompt Get every material in the project palette
29
40
  *
30
41
  * # Example
31
42
  * ```ts
32
- * const presets = await snaptrude.design.materials.list("preset")
33
- * console.log(presets.length, "preset materials")
43
+ * const materials = await snaptrude.design.materials.list()
44
+ * console.log(materials.length, "materials in the project")
34
45
  * ```
35
46
  */
36
- public abstract list(scope?: "project" | "preset"): PluginApiReturn<MaterialHandle[]>
47
+ public abstract list(): PluginApiReturn<MaterialHandle[]>
48
+
49
+ /**
50
+ * List the preset material library — the catalog the app's material browser
51
+ * shows (the built-in finishes plus the workspace's uploaded materials),
52
+ * fetched from the material library service. Library entries are **not**
53
+ * scene materials and carry no {@linkcode MaterialHandle}; each is a named
54
+ * texture record. To use one, feed it to
55
+ * {@linkcode PluginDesignMaterialsApi.create} /
56
+ * {@linkcode PluginDesignMaterialsApi.apply} as a
57
+ * {@linkcode PluginMaterialSpec}: `{ name, textureUrl, materialType: category }`.
58
+ *
59
+ * @returns The library as {@linkcode PluginPresetMaterial}`[]` (`[]` when the
60
+ * library service is unreachable)
61
+ *
62
+ * @examplePrompt Show me the preset materials library
63
+ * @examplePrompt What wood finishes are available in the material library?
64
+ * @examplePrompt Browse the material catalog and apply a brick texture
65
+ * @examplePrompt Which preset materials can I choose from?
66
+ *
67
+ * # Example
68
+ * ```ts
69
+ * const presets = await snaptrude.design.materials.listPresets()
70
+ * const brick = presets.find((p) => p.category.toLowerCase().includes("brick"))
71
+ * if (brick) {
72
+ * const walls = await snaptrude.design.query.listWalls()
73
+ * await snaptrude.design.materials.apply(walls, {
74
+ * name: brick.name,
75
+ * textureUrl: brick.textureUrl,
76
+ * materialType: brick.category,
77
+ * })
78
+ * }
79
+ * ```
80
+ */
81
+ public abstract listPresets(): PluginApiReturn<PluginPresetMaterial[]>
37
82
 
38
83
  /**
39
84
  * Read a material's descriptor — its name, color, opacity, and texture
@@ -54,7 +99,43 @@ export abstract class PluginDesignMaterialsApi {
54
99
  * if (info) console.log(info.name, info.colorHex, info.alpha)
55
100
  * ```
56
101
  */
57
- public abstract getInfo(material: MaterialHandle): PluginApiReturn<PluginMaterialInfo | null>
102
+ public abstract getInfo(
103
+ material: MaterialHandle,
104
+ ): PluginApiReturn<PluginMaterialInfo | null>
105
+
106
+ /**
107
+ * Read the material applied to a component **as a whole** — the eyedropper. For a
108
+ * uniformly-painted component this is that single material; for a per-face-painted
109
+ * component it is the material on the component's base/first face. Use
110
+ * {@linkcode PluginDesignMaterialsApi.getByFace} /
111
+ * {@linkcode PluginDesignMaterialsApi.listByFace} for the per-face breakdown.
112
+ *
113
+ * @param component The component to sample
114
+ * @returns The whole-object material as a {@linkcode PluginComponentMaterial} — its
115
+ * handle (chain it into `apply` / `getInfo`), engine id, name, and whether the
116
+ * component carries its type `default` or an explicitly `applied` finish — or `null`
117
+ * if the component carries no material
118
+ *
119
+ * @examplePrompt What material is on this wall?
120
+ * @examplePrompt Eyedrop the finish of the selected object
121
+ * @examplePrompt Which material does this slab use?
122
+ * @examplePrompt Read the material on this column so I can paint others with it
123
+ * @examplePrompt Is this component painted, and with what?
124
+ *
125
+ * # Example
126
+ * ```ts
127
+ * const [component] = await snaptrude.design.selection.get()
128
+ * const sampled = await snaptrude.design.materials.get(component)
129
+ * if (sampled) {
130
+ * console.log(sampled.name, sampled.source) // e.g. "Brick Red" "applied"
131
+ * const info = await snaptrude.design.materials.getInfo(sampled.material)
132
+ * console.log(info?.colorHex)
133
+ * }
134
+ * ```
135
+ */
136
+ public abstract get(
137
+ component: ComponentHandle,
138
+ ): PluginApiReturn<PluginComponentMaterial | null>
58
139
 
59
140
  /**
60
141
  * Apply a material to one or more components — an existing project material by
@@ -83,7 +164,7 @@ export abstract class PluginDesignMaterialsApi {
83
164
  public abstract apply(
84
165
  targets: ComponentHandle[],
85
166
  material: MaterialHandle | PluginMaterialSpec,
86
- save?: boolean
167
+ save?: boolean,
87
168
  ): PluginApiReturn<PluginMaterialResult>
88
169
 
89
170
  /**
@@ -109,9 +190,123 @@ export abstract class PluginDesignMaterialsApi {
109
190
  */
110
191
  public abstract reset(
111
192
  components: ComponentHandle[],
112
- save?: boolean
193
+ save?: boolean,
194
+ ): PluginApiReturn<PluginMaterialResult>
195
+
196
+ /**
197
+ * Paint specific **BREP faces** of a component with a material — an existing
198
+ * project material by handle, or a new one described inline as a
199
+ * {@linkcode PluginMaterialSpec}. Faces are addressed by **durable BREP face
200
+ * index** (their 0-based position in the component's BREP face list).
201
+ * Undoable when `save` is true (the default).
202
+ *
203
+ * Per-face materials are **BREP-only** (walls, floors, slabs, roofs, masses,
204
+ * columns, beams; furniture, doors/windows, and Revit imports are rejected) and
205
+ * **visual-only in v1** — the finish is not carried into BIM schedules / cost
206
+ * layers. A face index is durable only while the BREP topology (face count/order)
207
+ * is unchanged; a face index at or beyond the face count is rejected.
208
+ *
209
+ * @param component The (BREP) component to paint
210
+ * @param faces BREP face indices to paint (nonnegative integers)
211
+ * @param material Material handle, or an inline {@linkcode PluginMaterialSpec}
212
+ * @param save Commit as an undoable command (default `true`)
213
+ * @returns The number of faces painted
214
+ *
215
+ * @examplePrompt Paint the top face of this slab red
216
+ * @examplePrompt Apply the brick material to faces 0 and 2 of this wall
217
+ * @examplePrompt Put a wood finish on just the front face of this column
218
+ * @examplePrompt Colour face 4 of the selected mass with #ff8800
219
+ *
220
+ * # Example
221
+ * ```ts
222
+ * const [wall] = await snaptrude.design.query.listWalls({ storeys: [1] })
223
+ * // faces are addressed by durable 0-based BREP face index
224
+ * const { count } = await snaptrude.design.materials.applyToFaces(wall, [0, 1], { name: "Accent", color: "#b04a3a" })
225
+ * console.log("painted", count, "faces")
226
+ * ```
227
+ */
228
+ public abstract applyToFaces(
229
+ component: ComponentHandle,
230
+ faces: number[],
231
+ material: MaterialHandle | PluginMaterialSpec,
232
+ save?: boolean,
233
+ ): PluginApiReturn<PluginMaterialResult>
234
+
235
+ /**
236
+ * Reset specific BREP faces of a component back to the component/type default,
237
+ * removing any per-face finish. The inverse of
238
+ * {@linkcode PluginDesignMaterialsApi.applyToFaces}; undoable when `save` is true
239
+ * (the default). BREP-only (see `applyToFaces`).
240
+ *
241
+ * @param component The (BREP) component
242
+ * @param faces BREP face indices to reset (nonnegative integers)
243
+ * @param save Commit as an undoable command (default `true`)
244
+ * @returns The number of faces reset
245
+ *
246
+ * @examplePrompt Remove the paint from the top face of this slab
247
+ * @examplePrompt Reset faces 0 and 2 of this wall to their default material
248
+ * @examplePrompt Clear the custom finish on the front face of this column
249
+ *
250
+ * # Example
251
+ * ```ts
252
+ * const [wall] = await snaptrude.design.query.listWalls({ storeys: [1] })
253
+ * const { count } = await snaptrude.design.materials.resetFaces(wall, [0, 1])
254
+ * console.log("reset", count, "faces")
255
+ * ```
256
+ */
257
+ public abstract resetFaces(
258
+ component: ComponentHandle,
259
+ faces: number[],
260
+ save?: boolean,
113
261
  ): PluginApiReturn<PluginMaterialResult>
114
262
 
263
+ /**
264
+ * Read the material applied to a single BREP face.
265
+ *
266
+ * @param component The (BREP) component
267
+ * @param face The BREP face index (a nonnegative integer)
268
+ * @returns The face's material as a {@linkcode MaterialHandle}, or `null` if the
269
+ * face carries no explicit per-face material (or the component has no BREP)
270
+ *
271
+ * @examplePrompt What material is on the top face of this slab?
272
+ * @examplePrompt Get the finish applied to face 2 of this wall
273
+ * @examplePrompt Which material is painted on the front face of this column?
274
+ *
275
+ * # Example
276
+ * ```ts
277
+ * const [wall] = await snaptrude.design.query.listWalls()
278
+ * const material = await snaptrude.design.materials.getByFace(wall, 0)
279
+ * if (material) console.log(await snaptrude.design.materials.getInfo(material))
280
+ * ```
281
+ */
282
+ public abstract getByFace(
283
+ component: ComponentHandle,
284
+ face: number,
285
+ ): PluginApiReturn<MaterialHandle | null>
286
+
287
+ /**
288
+ * List the per-face materials of a component — one entry per BREP face, in face
289
+ * index order. A face carrying no explicit per-face material reports `null`.
290
+ *
291
+ * @param component The (BREP) component
292
+ * @returns The per-face materials as {@linkcode PluginFaceMaterial}`[]` (`[]` for a
293
+ * component with no BREP)
294
+ *
295
+ * @examplePrompt List the material on every face of this wall
296
+ * @examplePrompt Show me which faces of this slab are painted and with what
297
+ * @examplePrompt Break down the per-face finishes of the selected mass
298
+ *
299
+ * # Example
300
+ * ```ts
301
+ * const [wall] = await snaptrude.design.query.listWalls()
302
+ * const faces = await snaptrude.design.materials.listByFace(wall)
303
+ * for (const f of faces) console.log(f.faceIndex, f.material ?? "(default)")
304
+ * ```
305
+ */
306
+ public abstract listByFace(
307
+ component: ComponentHandle,
308
+ ): PluginApiReturn<PluginFaceMaterial[]>
309
+
115
310
  /**
116
311
  * Create a new material in the project from a spec — a color, texture URL,
117
312
  * opacity, and/or name. Idempotent by name: creating with the name of an
@@ -132,7 +327,9 @@ export abstract class PluginDesignMaterialsApi {
132
327
  * await snaptrude.design.materials.apply(walls, glass)
133
328
  * ```
134
329
  */
135
- public abstract create(spec: PluginMaterialSpec): PluginApiReturn<MaterialHandle>
330
+ public abstract create(
331
+ spec: PluginMaterialSpec,
332
+ ): PluginApiReturn<MaterialHandle>
136
333
 
137
334
  /**
138
335
  * Get the default material for a component's type.
@@ -151,7 +348,9 @@ export abstract class PluginDesignMaterialsApi {
151
348
  * if (defaultMaterial) console.log(await snaptrude.design.materials.getInfo(defaultMaterial))
152
349
  * ```
153
350
  */
154
- public abstract getDefault(component: ComponentHandle): PluginApiReturn<MaterialHandle | null>
351
+ public abstract getDefault(
352
+ component: ComponentHandle,
353
+ ): PluginApiReturn<MaterialHandle | null>
155
354
 
156
355
  /**
157
356
  * Test whether a component carries its (type) default material.
@@ -170,7 +369,9 @@ export abstract class PluginDesignMaterialsApi {
170
369
  * console.log(untouched ? "still default" : "has been painted")
171
370
  * ```
172
371
  */
173
- public abstract isDefault(component: ComponentHandle): PluginApiReturn<boolean>
372
+ public abstract isDefault(
373
+ component: ComponentHandle,
374
+ ): PluginApiReturn<boolean>
174
375
 
175
376
  /**
176
377
  * Test whether a component has a single (uniform) material across all faces.
@@ -189,7 +390,9 @@ export abstract class PluginDesignMaterialsApi {
189
390
  * console.log(uniform ? "one material all over" : "painted per face")
190
391
  * ```
191
392
  */
192
- public abstract isUniform(component: ComponentHandle): PluginApiReturn<boolean>
393
+ public abstract isUniform(
394
+ component: ComponentHandle,
395
+ ): PluginApiReturn<boolean>
193
396
 
194
397
  /**
195
398
  * Test whether a material has a texture.
@@ -257,18 +460,68 @@ export type PluginMaterialSpec = z.infer<typeof PluginMaterialSpec>
257
460
  export const PluginMaterialResult = z.object({ count: z.number() })
258
461
  export type PluginMaterialResult = z.infer<typeof PluginMaterialResult>
259
462
 
260
- /** Arguments for {@linkcode PluginDesignMaterialsApi.list}. */
261
- export const PluginDesignMaterialsListArgs = z.object({
262
- scope: z.enum(["project", "preset"]).optional(),
463
+ /**
464
+ * A single component face's material assignment (value record — not a handle).
465
+ *
466
+ * | Property | Type | Description |
467
+ * |---|---|---|
468
+ * | `faceIndex` | `number` | Durable BREP face index (0-based position in the face list) |
469
+ * | `material` | {@linkcode MaterialHandle} \| `null` | The applied material, or `null` for the default |
470
+ */
471
+ export const PluginFaceMaterial = z.object({
472
+ faceIndex: z.number().int().nonnegative(),
473
+ material: MaterialHandle.nullable(),
474
+ })
475
+ export type PluginFaceMaterial = z.infer<typeof PluginFaceMaterial>
476
+
477
+ /**
478
+ * The material on a component **as a whole** — the eyedropper read returned by
479
+ * {@linkcode PluginDesignMaterialsApi.get} (value record, plus a reusable handle).
480
+ *
481
+ * | Property | Type | Description |
482
+ * |---|---|---|
483
+ * | `material` | {@linkcode MaterialHandle} | The whole-object material — chain into `apply` / `getInfo` |
484
+ * | `id` | `string` | Engine material id |
485
+ * | `name` | `string` | Material name (== the handle token) |
486
+ * | `source` | `"default" \| "applied"` | Whether it is the component's type default or an explicitly-applied finish |
487
+ */
488
+ export const PluginComponentMaterial = z.object({
489
+ material: MaterialHandle,
490
+ id: z.string(),
491
+ name: z.string(),
492
+ source: z.enum(["default", "applied"]),
493
+ })
494
+ export type PluginComponentMaterial = z.infer<typeof PluginComponentMaterial>
495
+
496
+ /**
497
+ * A preset material library entry (value record — NOT a handle), returned by
498
+ * {@linkcode PluginDesignMaterialsApi.listPresets}.
499
+ *
500
+ * | Property | Type | Description |
501
+ * |---|---|---|
502
+ * | `name` | `string` | Display name of the library entry |
503
+ * | `category` | `string` | Library category (camelCase, e.g. `"wood"`, `"colors"`, `"brick"`) |
504
+ * | `textureUrl` | `string` | Texture/swatch image URL — feed to {@linkcode PluginMaterialSpec}`.textureUrl` |
505
+ */
506
+ export const PluginPresetMaterial = z.object({
507
+ name: z.string(),
508
+ category: z.string(),
509
+ textureUrl: z.string(),
263
510
  })
264
- export type PluginDesignMaterialsListArgs = z.infer<typeof PluginDesignMaterialsListArgs>
511
+ export type PluginPresetMaterial = z.infer<typeof PluginPresetMaterial>
265
512
 
266
513
  /** Arguments for {@linkcode PluginDesignMaterialsApi.getInfo} / `hasTexture`. */
267
- export const PluginDesignMaterialsGetInfoArgs = z.object({ material: MaterialHandle })
268
- export type PluginDesignMaterialsGetInfoArgs = z.infer<typeof PluginDesignMaterialsGetInfoArgs>
514
+ export const PluginDesignMaterialsGetInfoArgs = z.object({
515
+ material: MaterialHandle,
516
+ })
517
+ export type PluginDesignMaterialsGetInfoArgs = z.infer<
518
+ typeof PluginDesignMaterialsGetInfoArgs
519
+ >
269
520
 
270
521
  /** Arguments for component-scoped predicates (`getDefault`/`isDefault`/`isUniform`). */
271
- export const PluginDesignMaterialsComponentArgs = z.object({ component: ComponentHandle })
522
+ export const PluginDesignMaterialsComponentArgs = z.object({
523
+ component: ComponentHandle,
524
+ })
272
525
  export type PluginDesignMaterialsComponentArgs = z.infer<
273
526
  typeof PluginDesignMaterialsComponentArgs
274
527
  >
@@ -287,15 +540,69 @@ export const PluginDesignMaterialsApplyArgs = z.object({
287
540
  material: z.union([MaterialHandle, PluginMaterialSpec]),
288
541
  save: z.boolean().optional(),
289
542
  })
290
- export type PluginDesignMaterialsApplyArgs = z.infer<typeof PluginDesignMaterialsApplyArgs>
543
+ export type PluginDesignMaterialsApplyArgs = z.infer<
544
+ typeof PluginDesignMaterialsApplyArgs
545
+ >
291
546
 
292
547
  /** Arguments for {@linkcode PluginDesignMaterialsApi.reset}. */
293
548
  export const PluginDesignMaterialsResetArgs = z.object({
294
549
  components: z.array(ComponentHandle),
295
550
  save: z.boolean().optional(),
296
551
  })
297
- export type PluginDesignMaterialsResetArgs = z.infer<typeof PluginDesignMaterialsResetArgs>
552
+ export type PluginDesignMaterialsResetArgs = z.infer<
553
+ typeof PluginDesignMaterialsResetArgs
554
+ >
298
555
 
299
556
  /** Arguments for {@linkcode PluginDesignMaterialsApi.create}. */
300
- export const PluginDesignMaterialsCreateArgs = z.object({ spec: PluginMaterialSpec })
301
- export type PluginDesignMaterialsCreateArgs = z.infer<typeof PluginDesignMaterialsCreateArgs>
557
+ export const PluginDesignMaterialsCreateArgs = z.object({
558
+ spec: PluginMaterialSpec,
559
+ })
560
+ export type PluginDesignMaterialsCreateArgs = z.infer<
561
+ typeof PluginDesignMaterialsCreateArgs
562
+ >
563
+
564
+ /**
565
+ * Arguments for {@linkcode PluginDesignMaterialsApi.applyToFaces}.
566
+ *
567
+ * | Property | Type | Description |
568
+ * |---|---|---|
569
+ * | `component` | {@linkcode ComponentHandle} | The (BREP) component to paint |
570
+ * | `faces` | `number[]` | BREP face indices to paint (nonnegative integers) |
571
+ * | `material` | {@linkcode MaterialHandle} \| {@linkcode PluginMaterialSpec} | Material handle, or an inline spec |
572
+ * | `save` | `boolean` | Commit as an undoable command (default `true`) |
573
+ */
574
+ export const PluginDesignMaterialsApplyToFacesArgs = z.object({
575
+ component: ComponentHandle,
576
+ faces: z.array(z.number().int().nonnegative()),
577
+ material: z.union([MaterialHandle, PluginMaterialSpec]),
578
+ save: z.boolean().optional(),
579
+ })
580
+ export type PluginDesignMaterialsApplyToFacesArgs = z.infer<
581
+ typeof PluginDesignMaterialsApplyToFacesArgs
582
+ >
583
+
584
+ /** Arguments for {@linkcode PluginDesignMaterialsApi.resetFaces}. */
585
+ export const PluginDesignMaterialsResetFacesArgs = z.object({
586
+ component: ComponentHandle,
587
+ faces: z.array(z.number().int().nonnegative()),
588
+ save: z.boolean().optional(),
589
+ })
590
+ export type PluginDesignMaterialsResetFacesArgs = z.infer<
591
+ typeof PluginDesignMaterialsResetFacesArgs
592
+ >
593
+
594
+ /** Arguments for {@linkcode PluginDesignMaterialsApi.getByFace}. */
595
+ export const PluginDesignMaterialsGetByFaceArgs = z.object({
596
+ component: ComponentHandle,
597
+ face: z.number().int().nonnegative(),
598
+ })
599
+ export type PluginDesignMaterialsGetByFaceArgs = z.infer<
600
+ typeof PluginDesignMaterialsGetByFaceArgs
601
+ >
602
+
603
+ /** Arguments for {@linkcode PluginDesignMaterialsApi.listByFace} (component only). */
604
+ export const PluginDesignMaterialsListByFaceArgs =
605
+ PluginDesignMaterialsComponentArgs
606
+ export type PluginDesignMaterialsListByFaceArgs = z.infer<
607
+ typeof PluginDesignMaterialsListByFaceArgs
608
+ >
@@ -18,6 +18,9 @@ import {
18
18
  * {@linkcode ContourHandle} for its footprint outline;
19
19
  * {@linkcode PluginDesignQueryGeometryApi.getCenterline} mints a
20
20
  * {@linkcode CurveHandle} for a wall's core curve.
21
+ * {@linkcode PluginDesignQueryGeometryApi.getTriangulatedMeshes} is the
22
+ * handle-free exception: it returns the triangulated render meshes as plain
23
+ * world-space arrays.
21
24
  *
22
25
  * Returned handles are session-ephemeral and go stale on undo/redo/re-topologize
23
26
  * — re-fetch a fresh one after edits.
@@ -48,7 +51,7 @@ export abstract class PluginDesignQueryGeometryApi {
48
51
  * ```
49
52
  */
50
53
  public abstract getBrep(
51
- component: ComponentHandle
54
+ component: ComponentHandle,
52
55
  ): PluginApiReturn<BrepHandle | null>
53
56
 
54
57
  /**
@@ -80,7 +83,7 @@ export abstract class PluginDesignQueryGeometryApi {
80
83
  * ```
81
84
  */
82
85
  public abstract getBottomContour(
83
- component: ComponentHandle
86
+ component: ComponentHandle,
84
87
  ): PluginApiReturn<ContourHandle | null>
85
88
 
86
89
  /**
@@ -116,8 +119,67 @@ export abstract class PluginDesignQueryGeometryApi {
116
119
  * ```
117
120
  */
118
121
  public abstract getCenterline(
119
- component: ComponentHandle
122
+ component: ComponentHandle,
120
123
  ): PluginApiReturn<CurveHandle | null>
124
+
125
+ /**
126
+ * Get the **triangulated render meshes** of scene components as plain,
127
+ * serializable arrays — flat world-space vertex `positions` (`[x0, y0, z0,
128
+ * x1, …]`) and triangle `indices` (three per triangle), one record per
129
+ * requested component.
130
+ *
131
+ * Unlike the other `geometry.*` reads this does NOT mint kernel handles — the
132
+ * raw arrays cross the boundary directly, ready for export, custom analysis,
133
+ * or feeding an external renderer. Positions are **always world-space**
134
+ * (instance/transform baked in; there is no local-coordinates mode) in plan
135
+ * units — no conversion needed. Raw doubles, unrounded.
136
+ *
137
+ * With `includeMaterialIds: true` each record also carries `materialIds`: one
138
+ * entry **per triangle** (`indices.length / 3` entries), the material *name*
139
+ * painted on that triangle, or `null` for unpainted/default faces.
140
+ *
141
+ * With `includeFaceIndices: true` each record also carries `faceIds`: one
142
+ * entry **per triangle** (input order), the B-rep face index owning that
143
+ * triangle (same ids as {@linkcode getBrep} faces), or `null` where there is
144
+ * no provenance. Non-B-rep meshes (Revit imports, generic models, furniture)
145
+ * yield all-`null` rather than an error.
146
+ *
147
+ * A resolvable component with no readable mesh geometry yields
148
+ * `{ id, positions: [], indices: [] }` — nothing is silently omitted.
149
+ *
150
+ * The call is capped at **500 000 triangles total**; over the cap it throws a
151
+ * `VALIDATION` error whose message includes the actual total — split the
152
+ * component list into smaller batches and call again.
153
+ *
154
+ * @param components - The scene components to triangulate
155
+ * @param options - `includeMaterialIds` adds the per-triangle material names;
156
+ * `includeFaceIndices` adds the per-triangle B-rep face indices
157
+ * @returns `{ meshes }` — one {@linkcode PluginTriangulatedMesh} per requested
158
+ * component, in input order
159
+ * @throws `HANDLE_INVALID` if any component handle is gone/forged;
160
+ * `VALIDATION` if the combined mesh exceeds 500 000 triangles
161
+ *
162
+ * @examplePrompt Export the selected masses as a triangle mesh
163
+ * @examplePrompt Get the raw vertices and triangles of this wall in world coordinates
164
+ * @examplePrompt Give me the triangulated geometry of every space with its materials
165
+ * @examplePrompt Compute the surface area of this roof from its triangles
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * const spaces = await snaptrude.design.query.listSpaces()
170
+ * const { meshes } = await snaptrude.design.query.geometry.getTriangulatedMeshes(spaces, {
171
+ * includeMaterialIds: true,
172
+ * })
173
+ * for (const mesh of meshes) {
174
+ * const triangleCount = mesh.indices.length / 3
175
+ * console.log(mesh.id, triangleCount, mesh.materialIds?.[0])
176
+ * }
177
+ * ```
178
+ */
179
+ public abstract getTriangulatedMeshes(
180
+ components: ComponentHandle[],
181
+ options?: PluginDesignQueryGeometryGetTriangulatedMeshesArgs["options"],
182
+ ): PluginApiReturn<PluginDesignQueryGeometryGetTriangulatedMeshesResult>
121
183
  }
122
184
 
123
185
  /**
@@ -164,3 +226,63 @@ export const PluginDesignQueryGeometryGetCenterlineArgs = z.object({
164
226
  export type PluginDesignQueryGeometryGetCenterlineArgs = z.infer<
165
227
  typeof PluginDesignQueryGeometryGetCenterlineArgs
166
228
  >
229
+
230
+ /**
231
+ * One component's triangulated world-space mesh, as plain serializable arrays
232
+ * (no handles).
233
+ *
234
+ * | Property | Type | Description |
235
+ * |---|---|---|
236
+ * | `id` | `string` | The component's id (its {@linkcode ComponentHandle} token) |
237
+ * | `positions` | `number[]` | Flat world-space vertex coordinates `[x0, y0, z0, x1, …]`, plan units, raw doubles |
238
+ * | `indices` | `number[]` | Triangle vertex indices, three per triangle, into `positions / 3` |
239
+ * | `materialIds` | `(string \| null)[]` _(optional)_ | One entry per triangle: the painted material's name, `null` for unpainted/default faces. Present only when requested via `includeMaterialIds` |
240
+ * | `faceIds` | `(number \| null)[]` _(optional)_ | One entry per triangle: the B-rep face index owning it (same ids as {@linkcode PluginDesignQueryGeometryApi.getBrep} faces), `null` where there is no provenance (non-B-rep mesh). Present only when requested via `includeFaceIndices` |
241
+ */
242
+ export const PluginTriangulatedMesh = z.object({
243
+ id: z.string(),
244
+ positions: z.array(z.number()),
245
+ indices: z.array(z.number()),
246
+ materialIds: z.array(z.string().nullable()).optional(),
247
+ faceIds: z.array(z.number().nullable()).optional(),
248
+ })
249
+
250
+ export type PluginTriangulatedMesh = z.infer<typeof PluginTriangulatedMesh>
251
+
252
+ /**
253
+ * Arguments for {@linkcode PluginDesignQueryGeometryApi.getTriangulatedMeshes}.
254
+ *
255
+ * | Property | Type | Description |
256
+ * |---|---|---|
257
+ * | `components` | {@linkcode ComponentHandle}`[]` | The scene components to triangulate |
258
+ * | `options.includeMaterialIds` | `boolean` _(optional)_ | Also return the per-triangle material names |
259
+ * | `options.includeFaceIndices` | `boolean` _(optional)_ | Also return the per-triangle B-rep face indices |
260
+ */
261
+ export const PluginDesignQueryGeometryGetTriangulatedMeshesArgs = z.object({
262
+ components: z.array(ComponentHandle),
263
+ options: z
264
+ .object({
265
+ includeMaterialIds: z.boolean().optional(),
266
+ includeFaceIndices: z.boolean().optional(),
267
+ })
268
+ .optional(),
269
+ })
270
+
271
+ export type PluginDesignQueryGeometryGetTriangulatedMeshesArgs = z.infer<
272
+ typeof PluginDesignQueryGeometryGetTriangulatedMeshesArgs
273
+ >
274
+
275
+ /**
276
+ * Result of {@linkcode PluginDesignQueryGeometryApi.getTriangulatedMeshes}.
277
+ *
278
+ * | Property | Type | Description |
279
+ * |---|---|---|
280
+ * | `meshes` | {@linkcode PluginTriangulatedMesh}`[]` | One record per requested component, in input order |
281
+ */
282
+ export const PluginDesignQueryGeometryGetTriangulatedMeshesResult = z.object({
283
+ meshes: z.array(PluginTriangulatedMesh),
284
+ })
285
+
286
+ export type PluginDesignQueryGeometryGetTriangulatedMeshesResult = z.infer<
287
+ typeof PluginDesignQueryGeometryGetTriangulatedMeshesResult
288
+ >