@twinfinity/printing 6.1.0-beta → 6.1.2-ci.30738-beta

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 (157) hide show
  1. package/dist/BuildInfo.js +3 -3
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/layout/analyzeTemplate.d.ts +4 -4
  7. package/dist/layout/analyzeTemplate.d.ts.map +1 -1
  8. package/dist/layout/analyzeTemplate.js +18 -16
  9. package/dist/layout/analyzeTemplate.js.map +1 -1
  10. package/dist/layout/classClosure.d.ts +41 -0
  11. package/dist/layout/classClosure.d.ts.map +1 -0
  12. package/dist/layout/classClosure.js +143 -0
  13. package/dist/layout/classClosure.js.map +1 -0
  14. package/dist/layout/clipUtils.d.ts +11 -0
  15. package/dist/layout/clipUtils.d.ts.map +1 -1
  16. package/dist/layout/clipUtils.js +67 -6
  17. package/dist/layout/clipUtils.js.map +1 -1
  18. package/dist/layout/filters.d.ts +14 -8
  19. package/dist/layout/filters.d.ts.map +1 -1
  20. package/dist/layout/filters.js +28 -3
  21. package/dist/layout/filters.js.map +1 -1
  22. package/dist/layout/index.d.ts +24 -57
  23. package/dist/layout/index.d.ts.map +1 -1
  24. package/dist/layout/index.js +20 -337
  25. package/dist/layout/index.js.map +1 -1
  26. package/dist/layout/itemBuilders.d.ts +11 -0
  27. package/dist/layout/itemBuilders.d.ts.map +1 -1
  28. package/dist/layout/itemBuilders.js +17 -3
  29. package/dist/layout/itemBuilders.js.map +1 -1
  30. package/dist/layout/labelsRender.d.ts.map +1 -1
  31. package/dist/layout/labelsRender.js +15 -27
  32. package/dist/layout/labelsRender.js.map +1 -1
  33. package/dist/layout/legendRender.d.ts +18 -6
  34. package/dist/layout/legendRender.d.ts.map +1 -1
  35. package/dist/layout/legendRender.js +49 -79
  36. package/dist/layout/legendRender.js.map +1 -1
  37. package/dist/layout/metadata.d.ts.map +1 -1
  38. package/dist/layout/metadata.js +26 -5
  39. package/dist/layout/metadata.js.map +1 -1
  40. package/dist/layout/model.d.ts +4 -3
  41. package/dist/layout/model.d.ts.map +1 -1
  42. package/dist/layout/model.js +17 -57
  43. package/dist/layout/model.js.map +1 -1
  44. package/dist/layout/options.d.ts +1 -2
  45. package/dist/layout/options.d.ts.map +1 -1
  46. package/dist/layout/options.js.map +1 -1
  47. package/dist/layout/preflight.d.ts +32 -0
  48. package/dist/layout/preflight.d.ts.map +1 -0
  49. package/dist/layout/preflight.js +69 -0
  50. package/dist/layout/preflight.js.map +1 -0
  51. package/dist/layout/qrRender.d.ts.map +1 -1
  52. package/dist/layout/qrRender.js +11 -10
  53. package/dist/layout/qrRender.js.map +1 -1
  54. package/dist/layout/renderDrawing.d.ts +28 -17
  55. package/dist/layout/renderDrawing.d.ts.map +1 -1
  56. package/dist/layout/renderDrawing.js +107 -88
  57. package/dist/layout/renderDrawing.js.map +1 -1
  58. package/dist/layout/renderLayout.d.ts +25 -0
  59. package/dist/layout/renderLayout.d.ts.map +1 -0
  60. package/dist/layout/renderLayout.js +315 -0
  61. package/dist/layout/renderLayout.js.map +1 -0
  62. package/dist/layout/renderTemplateSheet.d.ts +99 -0
  63. package/dist/layout/renderTemplateSheet.d.ts.map +1 -0
  64. package/dist/layout/renderTemplateSheet.js +296 -0
  65. package/dist/layout/renderTemplateSheet.js.map +1 -0
  66. package/dist/layout/renderUtils.d.ts +19 -0
  67. package/dist/layout/renderUtils.d.ts.map +1 -1
  68. package/dist/layout/renderUtils.js +47 -4
  69. package/dist/layout/renderUtils.js.map +1 -1
  70. package/dist/layout/sectionDefs.d.ts +65 -0
  71. package/dist/layout/sectionDefs.d.ts.map +1 -0
  72. package/dist/layout/sectionDefs.js +88 -0
  73. package/dist/layout/sectionDefs.js.map +1 -0
  74. package/dist/layout/slabModelView.d.ts +34 -0
  75. package/dist/layout/slabModelView.d.ts.map +1 -0
  76. package/dist/layout/slabModelView.js +175 -0
  77. package/dist/layout/slabModelView.js.map +1 -0
  78. package/dist/layout/templateVariables.d.ts +1 -1
  79. package/dist/layout/templateVariables.d.ts.map +1 -1
  80. package/dist/layout/templateVariables.js +21 -9
  81. package/dist/layout/templateVariables.js.map +1 -1
  82. package/dist/layout/types.d.ts +164 -83
  83. package/dist/layout/types.d.ts.map +1 -1
  84. package/dist/layout/types.js.map +1 -1
  85. package/dist/layout/validation.d.ts.map +1 -1
  86. package/dist/layout/validation.js +78 -33
  87. package/dist/layout/validation.js.map +1 -1
  88. package/dist/layout/viewportPrep.d.ts +8 -6
  89. package/dist/layout/viewportPrep.d.ts.map +1 -1
  90. package/dist/layout/viewportPrep.js +35 -23
  91. package/dist/layout/viewportPrep.js.map +1 -1
  92. package/dist/layout/viewportRender.d.ts +9 -7
  93. package/dist/layout/viewportRender.d.ts.map +1 -1
  94. package/dist/layout/viewportRender.js +93 -126
  95. package/dist/layout/viewportRender.js.map +1 -1
  96. package/dist/layout/viewportUtils.d.ts +20 -0
  97. package/dist/layout/viewportUtils.d.ts.map +1 -1
  98. package/dist/layout/viewportUtils.js +107 -4
  99. package/dist/layout/viewportUtils.js.map +1 -1
  100. package/dist/printToPdf.d.ts +7 -1
  101. package/dist/printToPdf.d.ts.map +1 -1
  102. package/dist/printToPdf.js +81 -14
  103. package/dist/printToPdf.js.map +1 -1
  104. package/dist/printToSvg.d.ts.map +1 -1
  105. package/dist/printToSvg.js +7 -3
  106. package/dist/printToSvg.js.map +1 -1
  107. package/dist/schemaUtils.d.ts +9 -0
  108. package/dist/schemaUtils.d.ts.map +1 -0
  109. package/dist/schemaUtils.js +14 -0
  110. package/dist/schemaUtils.js.map +1 -0
  111. package/dist/templates/default-template.json +30 -8
  112. package/dist/types.d.ts +2 -1
  113. package/dist/types.d.ts.map +1 -1
  114. package/dist/types.js +10 -1
  115. package/dist/types.js.map +1 -1
  116. package/package.json +8 -3
  117. package/src/index.ts +4 -0
  118. package/src/layout/analyzeTemplate.ts +21 -19
  119. package/src/layout/classClosure.ts +142 -0
  120. package/src/layout/clipUtils.ts +65 -6
  121. package/src/layout/filters.ts +31 -3
  122. package/src/layout/index.ts +61 -571
  123. package/src/layout/itemBuilders.ts +20 -5
  124. package/src/layout/labelsRender.ts +23 -40
  125. package/src/layout/legendRender.ts +64 -84
  126. package/src/layout/metadata.ts +26 -5
  127. package/src/layout/model.ts +21 -66
  128. package/src/layout/options.ts +1 -2
  129. package/src/layout/preflight.ts +96 -0
  130. package/src/layout/qrRender.ts +11 -10
  131. package/src/layout/renderDrawing.ts +132 -110
  132. package/src/layout/renderLayout.ts +483 -0
  133. package/src/layout/renderTemplateSheet.ts +435 -0
  134. package/src/layout/renderUtils.ts +53 -4
  135. package/src/layout/sectionDefs.ts +131 -0
  136. package/src/layout/slabModelView.ts +196 -0
  137. package/src/layout/templateVariables.ts +25 -9
  138. package/src/layout/types.ts +160 -70
  139. package/src/layout/validation.ts +98 -32
  140. package/src/layout/viewportPrep.ts +47 -29
  141. package/src/layout/viewportRender.ts +114 -141
  142. package/src/layout/viewportUtils.ts +118 -5
  143. package/src/printToPdf.ts +84 -15
  144. package/src/printToSvg.ts +7 -3
  145. package/src/schemaUtils.ts +13 -0
  146. package/src/templates/default-template.json +30 -8
  147. package/src/types.ts +12 -2
  148. package/templates/default-a0-landscape.json +171 -66
  149. package/templates/default-a0-portrait.json +159 -60
  150. package/templates/default-a1-landscape.json +135 -48
  151. package/templates/default-a1-portrait.json +123 -42
  152. package/templates/default-a3-landscape.json +87 -24
  153. package/templates/default-a3-portrait.json +99 -30
  154. package/templates/default-a4-landscape.json +75 -18
  155. package/templates/default-a4-portrait.json +75 -18
  156. package/templates/llm-authoring-guide.md +247 -0
  157. package/templates/template-schema.json +295 -120
@@ -1,8 +1,8 @@
1
- import { sectionStateToModelView, computePolylinesCenter } from './itemBuilders';
1
+ import { sectionStateToModelView, computePolylinesCenter, readXZ } from './itemBuilders';
2
2
  import { buildMetadataMap, normalizePropertySets, resolveIfcClassName, resolveIfcClassId } from './metadata';
3
3
  import { applyLineStyleToPaths } from './utils';
4
4
  import { analyzeTemplate } from './analyzeTemplate';
5
- import { matchesV2Filter } from './filters';
5
+ import { matchesFilter, matchesProductClass, normalizeValue } from './filters';
6
6
  import { getProp } from './typeGuards';
7
7
  // These were previously lazy-loaded via require() from @measurement-tools/* packages.
8
8
  // Now they are part of @twinfinity/printing (this package) and @twinfinity/geometry.
@@ -75,30 +75,27 @@ function getGeometry() {
75
75
  function getSampleColor() {
76
76
  return _geometry.sampleProductColorRgba;
77
77
  }
78
- // Late-binding for renderLayout to break circular dependency.
79
- // renderDrawing is imported by index.ts which defines renderLayout.
80
- // The binding is set by index.ts after definition via setRenderLayoutBinding().
81
- let _renderLayout = null;
82
- /** Called by index.ts to register renderLayout without a circular import. */
83
- export function setRenderLayoutBinding(fn) {
84
- _renderLayout = fn;
85
- }
86
- function getRenderLayout() {
87
- if (!_renderLayout) {
88
- throw new Error('renderLayout not initialized. Ensure @twinfinity/printing index is imported before calling renderDrawing.');
89
- }
90
- return _renderLayout;
91
- }
78
+ import { renderLayout } from './renderLayout';
92
79
  // ---------------------------------------------------------------------------
93
80
  // Public API
94
81
  // ---------------------------------------------------------------------------
95
82
  /**
96
- * High-level entry point that converts raw plane-section results and a layout
97
- * template into a ready-to-print SVG string.
83
+ * High-level entry point that renders a layout template to a ready-to-print
84
+ * SVG string.
85
+ *
86
+ * The inputs form a 2×2 matrix — geometry × item data, each either raw or
87
+ * pre-built:
88
+ *
89
+ * | input | what it is |
90
+ * |---------------------------|--------------------------------------------------|
91
+ * | `sections` | RAW geometry: plane-section results (one shared or per viewport id). |
92
+ * | `models` | PRE-BUILT geometry: `SvgModelView`s per viewport id (e.g. from `slabSectionToModelView`). Needs `rawPaths` with `meta.gid` for highlight/clip/includeClasses. |
93
+ * | `options.productEntries` | RAW item data: per-product world-x/z polylines (`slabPerProductToEntries`); one flat list shared by all viewports or a per-viewport record. Feeds labels/legend/clip/highlight items. |
94
+ * | `options.viewportItems` | PRE-BUILT item data per viewport id (overrides `productEntries`). |
98
95
  *
99
- * Internalizes all data wiring (section SVG, item extraction, color sampling,
100
- * metadata building, legend/label data) that previously required ~350 lines of
101
- * app-side boilerplate.
96
+ * Prerequisite: products must have property sets loaded
97
+ * (`await api.ifc.loadPropertySets()` on core BIM objects) without them
98
+ * every `where` filter matches nothing.
102
99
  *
103
100
  * ```ts
104
101
  * const section = computePlaneSectionsForProducts(products, plane, { includeInvisible: true });
@@ -106,9 +103,8 @@ function getRenderLayout() {
106
103
  * template,
107
104
  * sections: { main: section },
108
105
  * options: {
109
- * allowRemoteAssets: true,
110
- * qrContext: { host, containerName, planeHeight },
111
- * productFields: { idPath: "propertySets.BIP.spacenumber.value", ... },
106
+ * allowRemoteResources: true,
107
+ * qrContext: { host, containerName },
112
108
  * roomLabels: { labelPath: "BIP.spacename", classFilter: "space" },
113
109
  * },
114
110
  * });
@@ -126,9 +122,8 @@ export async function renderDrawing(input) {
126
122
  // Add variable resolution warnings to output
127
123
  varWarnings.forEach((w) => warnings.push(`[Variables] ${w}`));
128
124
  // Apply variables to template (substitute {{vars.*}} references)
129
- template = applyTemplateVariables(template, values);
125
+ template = applyTemplateVariables(template, values, warnings);
130
126
  }
131
- const renderLayout = getRenderLayout();
132
127
  // ---- Collect viewport elements from template ----
133
128
  const viewports = template.elements.filter((el) => el.kind === 'viewport');
134
129
  if (!viewports.length) {
@@ -136,19 +131,28 @@ export async function renderDrawing(input) {
136
131
  }
137
132
  // ---- Fast path: pre-built models provided directly ----
138
133
  if (input.models) {
134
+ if (options?.roomLabels) {
135
+ warnings.push('options.roomLabels is ignored on the models path — use a template labels element instead.');
136
+ }
137
+ // productEntries: one flat list for all viewports, or per-viewport record.
138
+ const entriesInput = options?.productEntries;
139
+ const entriesFor = (vpId) => Array.isArray(entriesInput) ? entriesInput : (entriesInput?.[vpId] ?? []);
140
+ const allEntries = Array.isArray(entriesInput)
141
+ ? entriesInput
142
+ : Object.values(entriesInput ?? {}).flat();
139
143
  // Build items: explicit viewportItems take priority, then auto-build from productEntries
140
144
  let viewportsData;
141
145
  if (options?.viewportItems) {
142
146
  const emptyDefaults = Object.fromEntries(viewports.map((vp) => [vp.id, { items: [] }]));
143
147
  viewportsData = { ...emptyDefaults, ...options.viewportItems };
144
148
  }
145
- else if (options?.productEntries?.length) {
149
+ else if (allEntries.length) {
146
150
  // Auto-build items per viewport from raw product entries
147
151
  const sampleColor = getSampleColor();
148
152
  viewportsData = {};
149
153
  for (const vp of viewports) {
150
154
  viewportsData[vp.id] = {
151
- items: buildItemsFromEntries(options.productEntries, vp.fills, sampleColor)
155
+ items: buildItemsFromEntries(entriesFor(vp.id), vp.fills, sampleColor)
152
156
  };
153
157
  }
154
158
  }
@@ -176,14 +180,22 @@ export async function renderDrawing(input) {
176
180
  }
177
181
  }
178
182
  }
179
- if (reqs.usesRemoteAssets && !template.allowRemoteAssets && !options?.allowRemoteAssets) {
180
- warnings.push('Template uses remote assets but allowRemoteAssets is not enabled.');
183
+ else if (reqs.propertyPaths.length > 0 ||
184
+ template.elements.some((el) => el.kind === 'labels' || el.kind === 'legend')) {
185
+ // Item-driven features with zero items is almost always a missing
186
+ // productEntries/viewportItems input, not an intentionally empty sheet.
187
+ warnings.push('Template uses item data (labels/legend/where-filters) but no items were provided — pass options.productEntries or options.viewportItems.');
188
+ }
189
+ if (reqs.usesRemoteResources && !template.allowRemoteResources && !options?.allowRemoteResources) {
190
+ warnings.push('Template uses remote resources but allowRemoteResources is not enabled.');
181
191
  }
182
192
  // Build metadata from product entries (same as standard path)
183
- const metadataEntries = options?.productEntries ?? [];
184
- const metadataById = buildMetadataMap(metadataEntries);
193
+ // Caller-supplied metadata first: entries only cover cut-bearing products, but
194
+ // include filtering must classify products whose geometry never crosses the cut
195
+ // (furniture below a plan cut) - renderTemplateSheet supplies the full map.
196
+ const metadataById = { ...(options?.metadataById ?? {}), ...buildMetadataMap(allEntries) };
185
197
  const result = await renderLayout(template, layoutData, input.models, {
186
- allowRemoteAssets: template.allowRemoteAssets ?? options?.allowRemoteAssets,
198
+ allowRemoteResources: template.allowRemoteResources ?? options?.allowRemoteResources,
187
199
  qrContext: options?.qrContext,
188
200
  metadataById
189
201
  });
@@ -210,7 +222,6 @@ export async function renderDrawing(input) {
210
222
  // ---- Build section state + model view per viewport ----
211
223
  const models = {};
212
224
  const entriesByViewport = {};
213
- const sectionStateCache = new Map();
214
225
  for (const vp of viewports) {
215
226
  const sectionResult = sections[vp.id];
216
227
  if (!sectionResult?.perProduct?.length) {
@@ -218,38 +229,38 @@ export async function renderDrawing(input) {
218
229
  continue;
219
230
  }
220
231
  entriesByViewport[vp.id] = sectionResult.perProduct;
221
- // Dedup: skip if already built for this viewport key
222
- if (sectionStateCache.has(vp.id)) {
223
- const cached = sectionStateCache.get(vp.id);
224
- if (cached) {
225
- models[vp.id] = sectionStateToModelView(cached);
226
- }
227
- continue;
228
- }
229
232
  const resolved = resolveLineStyles(vp);
230
233
  // Estimate label font size from template style and section bounds
231
234
  const labelFontSize = estimateLabelFontSize(template, vp, sectionResult.perProduct);
232
- // Extract clip polygon from section results if clip config is present
235
+ // Extract clip polygon from section results if clip config is present.
236
+ // Matching semantics MUST mirror matchesFilter (viewportPrep/labels/
237
+ // legend all use it on the same clip config): class optional,
238
+ // Ifc prefix optional, case-insensitive values, arrays mean any-of.
233
239
  let clipPolygon;
234
- if (vp.clip?.where && vp.clip.class) {
235
- const clipClass = Array.isArray(vp.clip.class) ? vp.clip.class : [vp.clip.class];
240
+ if (vp.clip?.where) {
241
+ const clipClass = vp.clip.class ? (Array.isArray(vp.clip.class) ? vp.clip.class : [vp.clip.class]) : null;
236
242
  const clipEntries = sectionResult.perProduct.filter((entry) => {
237
243
  const product = entry.product;
238
- const matchesClass = clipClass.some((c) => product.class?.is?.(c));
239
- if (!matchesClass)
244
+ // Shared helper rather than product.class.is(): that compares with ===, so a clip
245
+ // filter of "SPACE" or "IFCWALL" selected nothing while labels and legend — reading
246
+ // the same clip config through matchesFilter — matched it. The clip silently did
247
+ // not apply, with no warning anywhere.
248
+ if (!matchesProductClass(product, clipClass ?? undefined))
240
249
  return false;
241
- // Match where condition using T8-aware property resolution
242
250
  for (const [path, expected] of Object.entries(vp.clip.where)) {
243
- const actualValue = resolveProductProperty(product, path);
244
- if (actualValue === undefined)
245
- return false;
246
- if (actualValue !== expected)
251
+ const actual = normalizeValue(resolveProductProperty(product, path));
252
+ const matches = Array.isArray(expected)
253
+ ? expected.some((v) => normalizeValue(v) === actual)
254
+ : actual === normalizeValue(expected);
255
+ if (!matches)
247
256
  return false;
248
257
  }
249
258
  return true;
250
259
  });
251
260
  if (clipEntries.length) {
252
- clipPolygon = clipEntries.flatMap((entry) => (entry.polylines || []).map((pl) => (pl.points || []).map((p) => ({ x: p.x, z: p.z }))));
261
+ clipPolygon = clipEntries.flatMap((entry) => (entry.polylines || []).map((pl) => (pl.points || [])
262
+ .map((p) => readXZ(p))
263
+ .filter((p) => p.x !== null && p.z !== null)));
253
264
  }
254
265
  }
255
266
  // Convert planeNormal array to object with x, y, z if present
@@ -272,7 +283,6 @@ export async function renderDrawing(input) {
272
283
  clipPolygon,
273
284
  planeNormal
274
285
  });
275
- sectionStateCache.set(vp.id, state);
276
286
  if (!state) {
277
287
  warnings.push(`No geometry for viewport '${vp.id}'.`);
278
288
  continue;
@@ -315,14 +325,17 @@ export async function renderDrawing(input) {
315
325
  }
316
326
  // ---- Build metadata from all section entries ----
317
327
  const allEntries = Object.values(entriesByViewport).flat();
318
- const metadataById = buildMetadataMap(allEntries);
328
+ // Caller-supplied metadata first: entries only cover cut-bearing products, but
329
+ // include filtering must classify products whose geometry never crosses the cut
330
+ // (furniture below a plan cut) - renderTemplateSheet supplies the full map.
331
+ const metadataById = { ...(options?.metadataById ?? {}), ...buildMetadataMap(allEntries) };
319
332
  // ---- Assemble layout data ----
320
333
  const layoutData = {
321
334
  viewports: viewportsData
322
335
  };
323
336
  // ---- Render ----
324
337
  const result = await renderLayout(template, layoutData, models, {
325
- allowRemoteAssets: template.allowRemoteAssets ?? options?.allowRemoteAssets,
338
+ allowRemoteResources: template.allowRemoteResources ?? options?.allowRemoteResources,
326
339
  qrContext: options?.qrContext,
327
340
  metadataById
328
341
  });
@@ -388,9 +401,8 @@ function estimateLabelFontSize(template, vp, perProduct) {
388
401
  if (!polyline.points)
389
402
  continue;
390
403
  for (const pt of polyline.points) {
391
- const px = getProp(pt, 'x');
392
- const pz = getProp(pt, 'z');
393
- if (Number.isFinite(px) && Number.isFinite(pz)) {
404
+ const { x: px, z: pz } = readXZ(pt);
405
+ if (px !== null && pz !== null) {
394
406
  if (px < minX)
395
407
  minX = px;
396
408
  if (px > maxX)
@@ -446,56 +458,63 @@ export const BUILTIN_HIDDEN_DEFAULTS = {
446
458
  export const CATEGORY_BUILTIN_OVERRIDES = {
447
459
  cutLines: { widthMm: 0.02 },
448
460
  hiddenLines: BUILTIN_HIDDEN_DEFAULTS,
461
+ // Occluded silhouettes follow the hidden drafting convention out of the box.
462
+ silhouettesHidden: BUILTIN_HIDDEN_DEFAULTS,
449
463
  outlineHidden: BUILTIN_HIDDEN_DEFAULTS
450
464
  };
465
+ /** Visibility group of each category; keyed to the `defaultVisibleLines`/`defaultHiddenLines` template keys. `stairArrows` is annotation, not geometry, and belongs to neither group. */
466
+ const CATEGORY_VISIBILITY_GROUP = {
467
+ cutLines: 'defaultVisibleLines',
468
+ visibleLines: 'defaultVisibleLines',
469
+ silhouettesVisible: 'defaultVisibleLines',
470
+ silhouettesHidden: 'defaultHiddenLines',
471
+ featureEdges: 'defaultVisibleLines',
472
+ outlineVisible: 'defaultVisibleLines',
473
+ hiddenLines: 'defaultHiddenLines',
474
+ outlineHidden: 'defaultHiddenLines'
475
+ };
451
476
  /**
452
- * Resolve the effective line style for every category.
477
+ * Resolve the effective line style for one category — the single source of
478
+ * truth used by both the slab plan path (slabModelView) and the drawing path.
453
479
  *
454
- * Priority: category entry in `lineStyles` → `lineStyles.default`
455
- * → `lineWidthMm`/`lineColor` shorthand → built-in defaults.
480
+ * Cascade, weakest first: built-in defaults → `default` → built-in category
481
+ * defaults → `defaultVisibleLines`/`defaultHiddenLines` (the category's
482
+ * visibility group) → the category's own key.
483
+ */
484
+ export function resolveCategoryLineStyle(category, lineStyles) {
485
+ const group = CATEGORY_VISIBILITY_GROUP[category];
486
+ return {
487
+ ...BUILTIN_LINE_DEFAULTS,
488
+ ...(lineStyles?.default ?? {}),
489
+ ...(CATEGORY_BUILTIN_OVERRIDES[category] ?? {}),
490
+ ...((group && lineStyles?.[group]) ?? {}),
491
+ ...(lineStyles?.[category] ?? {})
492
+ };
493
+ }
494
+ /**
495
+ * Resolve the effective line style for every category via
496
+ * {@link resolveCategoryLineStyle} (shared with the slab plan path).
456
497
  */
457
498
  function resolveLineStyles(vp) {
458
499
  const ls = vp.lineStyles;
459
- // Base default: lineStyles.default → shorthand → built-in
460
500
  const base = {
461
501
  ...BUILTIN_LINE_DEFAULTS,
462
- ...(vp.lineWidthMm !== undefined ? { widthMm: vp.lineWidthMm } : {}),
463
- ...(vp.lineColor !== undefined ? { color: vp.lineColor } : {}),
464
502
  ...(ls?.default ?? {})
465
503
  };
466
504
  const categories = [
467
505
  'cutLines',
468
506
  'visibleLines',
469
507
  'hiddenLines',
470
- 'silhouettes',
508
+ 'silhouettesVisible',
509
+ 'silhouettesHidden',
471
510
  'featureEdges',
472
511
  'stairArrows',
473
512
  'outlineVisible',
474
513
  'outlineHidden'
475
514
  ];
476
- // Category group mapping: individual categories inherit from their group style
477
- const categoryGroups = {
478
- cutLines: 'visibleLines',
479
- visibleLines: 'visibleLines',
480
- silhouettes: 'visibleLines',
481
- featureEdges: 'visibleLines',
482
- outlineVisible: 'visibleLines',
483
- hiddenLines: 'hiddenLines',
484
- outlineHidden: 'hiddenLines',
485
- stairArrows: 'stairArrows'
486
- };
487
515
  const byCategory = {};
488
516
  for (const cat of categories) {
489
- const builtinOverride = CATEGORY_BUILTIN_OVERRIDES[cat];
490
- const groupName = categoryGroups[cat];
491
- const groupStyle = ls?.[groupName]; // Group style (visibleLines, hiddenLines, stairArrows)
492
- const categoryOverride = ls?.[cat]; // Individual category settings (enable/disable)
493
- byCategory[cat] = {
494
- ...base,
495
- ...(builtinOverride ?? {}),
496
- ...(groupStyle ?? {}), // Apply group style first
497
- ...(categoryOverride ?? {}) // Then individual overrides (enable/disable)
498
- };
517
+ byCategory[cat] = resolveCategoryLineStyle(cat, ls);
499
518
  }
500
519
  return { default: base, byCategory };
501
520
  }
@@ -510,7 +529,7 @@ export function resolveFillColor(item, product, fills, sampleColor) {
510
529
  if (!fills?.length)
511
530
  return undefined;
512
531
  for (const rule of fills) {
513
- if (!matchesV2Filter(item, rule.class, rule.where))
532
+ if (!matchesFilter(item, rule.class, rule.where))
514
533
  continue;
515
534
  if (rule.fill === 'sample') {
516
535
  const sampled = sampleColor(product);
@@ -1 +1 @@
1
- {"version":3,"file":"renderDrawing.js","sourceRoot":"/","sources":["layout/renderDrawing.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7G,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAa5C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAuCvC,sFAAsF;AACtF,qFAAqF;AACrF,OAAO,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAElF,uFAAuF;AACvF,MAAM,SAAS,GAAqB;IAChC,2BAA2B,EAAE,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE;QAGjD,OAAO,2BAA2B,CAAC,UAAqB,EAAE;YACtD,GAAG,OAAO;YACV,KAAK,EAAE,OAAO,CAAC,KAAsB;YACrC,WAAW,EAAE,OAAO,CAAC,WAAkC;YACvD,WAAW,EAAE,OAAO,CAAC,WAAkC;SAC1D,CAAC,CAAC;IACP,CAAC;CACJ,CAAC;AACF,SAAS,WAAW;IAChB,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,wEAAwE;AACxE,iFAAiF;AACjF,gFAAgF;AAChF,MAAM,SAAS,GAAqB;IAChC,sBAAsB,EAAE,CAAC,OAAgB,EAA4B,EAAE;QAOnE,MAAM,CAAC,GAAG,OAAkC,CAAC;QAC7C,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,EAAE,aAAa,CAA2B,CAAC;QACzE,IAAI,CAAC,MAAM,EAAE,MAAM;YAAE,OAAO,SAAS,CAAC;QAEtC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;YACxB,IAAI,CAAC;gBACD,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;oBACzC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC9B,CAAC;qBAAM,IAAI,OAAO,IAAI,CAAC,iBAAiB,KAAK,UAAU,EAAE,CAAC;oBACtD,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;oBACpC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;oBACpC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;oBACpC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;gBACxC,CAAC;qBAAM,CAAC;oBACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;oBACzB,MAAM,CAAC,GAAG,KAAK,EAAE,YAAY,IAAI,KAAK,EAAE,KAAK,CAAC;oBAC9C,IAAI,CAAC,EAAE,CAAC;wBACJ,4DAA4D;wBAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;wBAC1C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;wBACvC,OAAO;4BACH,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE;4BAC5D,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;yBAClD,CAAC;oBACN,CAAC;oBACD,SAAS;gBACb,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACL,SAAS;YACb,CAAC;YACD,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;gBAAE,SAAS;YACvD,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC;QAC7E,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IACD,eAAe,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,4DAA4D;CACzF,CAAC;AACF,SAAS,WAAW;IAChB,OAAO,SAAS,CAAC;AACrB,CAAC;AACD,SAAS,cAAc;IACnB,OAAO,SAAS,CAAC,sBAAsB,CAAC;AAC5C,CAAC;AAED,8DAA8D;AAC9D,oEAAoE;AACpE,gFAAgF;AAChF,IAAI,aAAa,GAOJ,IAAI,CAAC;AAElB,6EAA6E;AAC7E,MAAM,UAAU,sBAAsB,CAAC,EAAqC;IACxE,aAAa,GAAG,EAAE,CAAC;AACvB,CAAC;AAED,SAAS,eAAe;IACpB,IAAI,CAAC,aAAa,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACX,2GAA2G,CAC9G,CAAC;IACN,CAAC;IACD,OAAO,aAAa,CAAC;AACzB,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAyB;IACzD,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,oDAAoD;IACpD,kGAAkG;IAClG,IAAI,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;IAC9B,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QACrB,MAAM,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QAC9F,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;QAE9F,6CAA6C;QAC7C,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC;QAE9D,iEAAiE;QACjE,QAAQ,GAAG,sBAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;IAEvC,oDAAoD;IACpD,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,UAAU,CAAsB,CAAC;IAChG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,mCAAmC,CAAC,EAAE,CAAC;IACxE,CAAC;IAED,0DAA0D;IAC1D,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACf,yFAAyF;QACzF,IAAI,aAAiE,CAAC;QAEtE,IAAI,OAAO,EAAE,aAAa,EAAE,CAAC;YACzB,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CACpC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAA6B,EAAE,CAAC,CAAC,CAC3E,CAAC;YACF,aAAa,GAAG,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;QACnE,CAAC;aAAM,IAAI,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC;YACzC,yDAAyD;YACzD,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;YACrC,aAAa,GAAG,EAAE,CAAC;YACnB,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;gBACzB,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG;oBACnB,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC;iBAC9E,CAAC;YACN,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,aAAa,GAAG,MAAM,CAAC,WAAW,CAC9B,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAA6B,EAAE,CAAC,CAAC,CAC3E,CAAC;QACN,CAAC;QAED,MAAM,UAAU,GAAG,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;QAEhD,iFAAiF;QACjF,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QAEvC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtB,QAAQ,CAAC,IAAI,CAAC,sBAAsB,IAAI,wCAAwC,CAAC,CAAC;YACtF,CAAC;QACL,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACtE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;oBAAE,SAAS;gBAC/B,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC;gBAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC;gBACrG,IAAI,CAAC,KAAK,EAAE,CAAC;oBACT,QAAQ,CAAC,IAAI,CAAC,iCAAiC,IAAI,4BAA4B,CAAC,CAAC;gBACrF,CAAC;YACL,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,gBAAgB,IAAI,CAAC,QAAQ,CAAC,iBAAiB,IAAI,CAAC,OAAO,EAAE,iBAAiB,EAAE,CAAC;YACtF,QAAQ,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;QACvF,CAAC;QAED,8DAA8D;QAC9D,MAAM,eAAe,GAAG,OAAO,EAAE,cAAc,IAAI,EAAE,CAAC;QACtD,MAAM,YAAY,GAAG,gBAAgB,CAAC,eAAe,CAAC,CAAC;QAEvD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE;YAClE,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB,IAAI,OAAO,EAAE,iBAAiB;YAC3E,SAAS,EAAE,OAAO,EAAE,SAAS;YAC7B,YAAY;SACf,CAAC,CAAC;QAEH,OAAO;YACH,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,QAAQ,EAAE,CAAC,GAAG,QAAQ,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;SAC9C,CAAC;IACN,CAAC;IAED,6DAA6D;IAC7D,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,6CAA6C,CAAC,EAAE,CAAC;IAClF,CAAC;IAED,MAAM,EAAE,2BAA2B,EAAE,GAAG,WAAW,EAAE,CAAC;IACtD,MAAM,EAAE,sBAAsB,EAAE,eAAe,EAAE,GAAG,WAAW,EAAE,CAAC;IAElE,kEAAkE;IAClE,MAAM,QAAQ,GAAG,CAAC,CAAE,KAAK,CAAC,QAA0B,CAAC,UAAU,CAAC;IAChE,MAAM,QAAQ,GAAkC,QAAQ;QACpD,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,QAAyB,CAAC,CAAC,CAAC;QACrF,CAAC,CAAE,KAAK,CAAC,QAA0C,CAAC;IAExD,gEAAgE;IAChE,0EAA0E;IAC1E,MAAM,wBAAwB,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IACtF,MAAM,YAAY,GAAG,wBAAwB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC;IAEhF,0DAA0D;IAC1D,MAAM,MAAM,GAAiC,EAAE,CAAC;IAChD,MAAM,iBAAiB,GAAgD,EAAE,CAAC;IAC1E,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAqC,CAAC;IAEvE,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QACzB,MAAM,aAAa,GAAG,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;YACrC,QAAQ,CAAC,IAAI,CAAC,iCAAiC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;YAC1D,SAAS;QACb,CAAC;QAED,iBAAiB,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC;QAEpD,qDAAqD;QACrD,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAC5C,IAAI,MAAM,EAAE,CAAC;gBACT,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;YACpD,CAAC;YACD,SAAS;QACb,CAAC;QAED,MAAM,QAAQ,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;QAEvC,kEAAkE;QAClE,MAAM,aAAa,GAAG,qBAAqB,CAAC,QAAQ,EAAE,EAAE,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;QAEpF,sEAAsE;QACtE,IAAI,WAA+D,CAAC;QACpE,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAClC,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjF,MAAM,WAAW,GAAG,aAAa,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,KAAU,EAAE,EAAE;gBAC/D,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC9B,MAAM,YAAY,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3E,IAAI,CAAC,YAAY;oBAAE,OAAO,KAAK,CAAC;gBAChC,2DAA2D;gBAC3D,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAK,CAAC,KAAM,CAAC,EAAE,CAAC;oBAC7D,MAAM,WAAW,GAAG,sBAAsB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;oBAC1D,IAAI,WAAW,KAAK,SAAS;wBAAE,OAAO,KAAK,CAAC;oBAC5C,IAAI,WAAW,KAAK,QAAQ;wBAAE,OAAO,KAAK,CAAC;gBAC/C,CAAC;gBACD,OAAO,IAAI,CAAC;YAChB,CAAC,CAAC,CAAC;YACH,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;gBACrB,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,KAAU,EAAE,EAAE,CAC7C,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAO,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CACpG,CAAC;YACN,CAAC;QACL,CAAC;QAED,8DAA8D;QAC9D,IAAI,WAAW,GAAY,SAAS,CAAC;QACrC,IAAI,EAAE,CAAC,WAAW,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjF,WAAW,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QACvF,CAAC;QAGD,MAAM,KAAK,GAA+B,2BAA2B,CAAC,aAAa,CAAC,UAAU,EAAE;YAC5F,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO;YACrC,aAAa;YACb,KAAK,EAAE,EAAE,CAAC,KAAK;YACf,WAAW,EAAE,IAAI;YACjB,aAAa,EAAE,CAAC,CAAC,YAAY;YAC7B,WAAW,EAAE,YAAY;gBACrB,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,CACnB,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE;oBAChC,SAAS,EAAE,YAAY,CAAC,SAAS;oBACjC,WAAW,EAAE,YAAY,CAAC,WAAW;iBACxC,CAAC;gBACR,CAAC,CAAC,SAAS;YACf,WAAW;YACX,WAAW;SACd,CAAC,CAAC;QAEH,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAEpC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,QAAQ,CAAC,IAAI,CAAC,6BAA6B,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;YACtD,SAAS;QACb,CAAC;QAED,kDAAkD;QAClD,IAAI,UAAU,GAAwB,KAAK,CAAC;QAC5C,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC;QACtC,IAAI,YAAY,CAAC,KAAK,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;YAC7C,MAAM,KAAK,GAA8C,EAAE,CAAC;YAC5D,IAAI,YAAY,CAAC,KAAK;gBAAE,KAAK,CAAC,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC;YAC1D,IAAI,YAAY,CAAC,OAAO;gBAAE,KAAK,CAAC,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC;YACnE,UAAU,GAAG;gBACT,GAAG,KAAK;gBACR,KAAK,EAAE,qBAAqB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC;aACnD,CAAC;YACF,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACjB,UAAU,CAAC,QAAQ,GAAG,qBAAqB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YACvE,CAAC;QACL,CAAC;QAED,MAAM,UAAU,GAA2B,QAAQ,CAAC,MAAM,EAAE,IAAI;YAC5D,CAAC,CAAC;gBACI,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,sBAAsB,CAAC,IAAI;gBAC/D,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,sBAAsB,CAAC,UAAU;gBAChF,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,IAAI,sBAAsB,CAAC,UAAU;aACnF;YACH,CAAC,CAAC,SAAS,CAAC;QAEhB,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,uBAAuB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACpE,CAAC;IAED,qCAAqC;IACrC,MAAM,aAAa,GAAuD,EAAE,CAAC;IAE7E,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,iBAAiB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;YACrC,SAAS;QACb,CAAC;QAED,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,qBAAqB,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,EAAE,sBAAsB,CAAC,EAAE,CAAC;IACvG,CAAC;IAED,oDAAoD;IACpD,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3D,MAAM,YAAY,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAElD,iCAAiC;IACjC,MAAM,UAAU,GAAG;QACf,SAAS,EAAE,aAAa;KAC3B,CAAC;IAEF,mBAAmB;IACnB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE;QAC5D,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB,IAAI,OAAO,EAAE,iBAAiB;QAC3E,SAAS,EAAE,OAAO,EAAE,SAAS;QAC7B,YAAY;KACf,CAAC,CAAC;IAEH,OAAO;QACH,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,QAAQ,EAAE,CAAC,GAAG,QAAQ,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;KAC9C,CAAC;AACN,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;;GAKG;AACH,SAAS,qBAAqB,CAC1B,OAAwD,EACxD,KAA6B,EAC7B,WAAyE;IAEzE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACzB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9B,MAAM,YAAY,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,MAAM,CAAuB,CAAC;QAC5D,MAAM,WAAW,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC3G,MAAM,IAAI,GAA0B;YAChC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;YAClC,QAAQ,EAAE,mBAAmB,CAAC,OAAO,CAAC;YACtC,UAAU,EAAE,iBAAiB,CAAC,OAAO,CAAC;YACtC,YAAY;YACZ,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG;YAClC,WAAW,EAAE,WAAW;YACxB,IAAI,EAAE,IAAI;YACV,CAAC,EAAE,MAAM,CAAC,CAAC;YACX,CAAC,EAAE,MAAM,CAAC,CAAC;YACX,IAAI,EAAE,WAAW;YACjB,SAAS,EAAE,KAAK,CAAC,SAAgE;SACpF,CAAC;QAEF,IAAI,KAAK,EAAE,MAAM,IAAI,WAAW,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;YAClE,IAAI,KAAK,EAAE,CAAC;gBACR,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;YACvB,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAC1B,QAAwB,EACxB,EAAmB,EACnB,UAAuC;IAEvC,MAAM,kBAAkB,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,IAAI,GAAG,CAAC;IAEpE,0BAA0B;IAC1B,IAAI,IAAI,GAAG,QAAQ,CAAC;IACpB,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC;IACrB,IAAI,IAAI,GAAG,QAAQ,CAAC;IACpB,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC;IACrB,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC7B,IAAI,CAAC,KAAK,CAAC,SAAS;YAAE,SAAS;QAC/B,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,QAAQ,CAAC,MAAM;gBAAE,SAAS;YAC/B,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;gBAC/B,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBAC5B,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBAC5B,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC7C,IAAK,EAAa,GAAG,IAAI;wBAAE,IAAI,GAAG,EAAY,CAAC;oBAC/C,IAAK,EAAa,GAAG,IAAI;wBAAE,IAAI,GAAG,EAAY,CAAC;oBAC/C,IAAK,EAAa,GAAG,IAAI;wBAAE,IAAI,GAAG,EAAY,CAAC;oBAC/C,IAAK,EAAa,GAAG,IAAI;wBAAE,IAAI,GAAG,EAAY,CAAC;gBACnD,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;IAC/B,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;QACvG,OAAO,kBAAkB,CAAC,CAAC,yBAAyB;IACxD,CAAC;IAED,6DAA6D;IAC7D,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,GAAG,UAAU,CAAC;IACvC,MAAM,MAAM,GAAG,EAAE,CAAC,QAAQ,GAAG,WAAW,CAAC;IACzC,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;QAC1D,OAAO,kBAAkB,CAAC;IAC9B,CAAC;IAED,OAAO,kBAAkB,GAAG,cAAc,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E,iFAAiF;AACjF,MAAM,CAAC,MAAM,sBAAsB,GAAG;IAClC,IAAI,EAAE,SAAS;IACf,UAAU,EAAE,iDAAiD;IAC7D,UAAU,EAAE,KAAK;CACX,CAAC;AAEX,8EAA8E;AAC9E,mCAAmC;AACnC,8EAA8E;AAE9E,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAgC;IAC9D,OAAO,EAAE,IAAI,EAAE,iEAAiE;IAChF,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,IAAI;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAgC;IAChE,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,IAAI;CAChB,CAAC;AAYF,4EAA4E;AAC5E,MAAM,CAAC,MAAM,0BAA0B,GAA8D;IACjG,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;IAC3B,WAAW,EAAE,uBAAuB;IACpC,aAAa,EAAE,uBAAuB;CACzC,CAAC;AAOF;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,EAAmB;IAC1C,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC;IAEzB,0DAA0D;IAC1D,MAAM,IAAI,GAAgC;QACtC,GAAG,qBAAqB;QACxB,GAAG,CAAC,EAAE,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,GAAG,CAAC,EAAE,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,EAAE,EAAE,OAAO,IAAI,EAAE,CAAC;KACM,CAAC;IAEjC,MAAM,UAAU,GAAmB;QAC/B,UAAU;QACV,cAAc;QACd,aAAa;QACb,aAAa;QACb,cAAc;QACd,aAAa;QACb,gBAAgB;QAChB,eAAe;KAClB,CAAC;IAEF,+EAA+E;IAC/E,MAAM,cAAc,GAAuC;QACvD,QAAQ,EAAE,cAAc;QACxB,YAAY,EAAE,cAAc;QAC5B,WAAW,EAAE,cAAc;QAC3B,YAAY,EAAE,cAAc;QAC5B,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,aAAa;QAC1B,aAAa,EAAE,aAAa;QAC5B,WAAW,EAAE,aAAa;KAC7B,CAAC;IAEF,MAAM,UAAU,GAAG,EAAuD,CAAC;IAC3E,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC3B,MAAM,eAAe,GAAG,0BAA0B,CAAC,GAAG,CAAC,CAAC;QACxD,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,UAAU,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,uDAAuD;QAC3F,MAAM,gBAAgB,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,gDAAgD;QAEpF,UAAU,CAAC,GAAG,CAAC,GAAG;YACd,GAAG,IAAI;YACP,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC;YAC1B,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,0BAA0B;YACjD,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,6CAA6C;SAC7C,CAAC;IACrC,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AACzC,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC5B,IAA2B,EAC3B,OAAgB,EAChB,KAA6B,EAC7B,WAA2D;IAE3D,IAAI,CAAC,KAAK,EAAE,MAAM;QAAE,OAAO,SAAS,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC;YAAE,SAAS;QAC7D,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YACrC,IAAI,OAAO,EAAE,CAAC;gBACV,OAAO;oBACH,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC;oBACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC;oBACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC;oBACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,GAAG,GAAG,CAAC;iBAClC,CAAC;YACN,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,CAAC,2BAA2B;IACtC,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,GAAW;IAC/B,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAChC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IAClG,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IACpH,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,OAAO;YACH,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;YAC/B,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;YAC/B,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;YAC/B,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;SAClC,CAAC;IACN,CAAC;IACD,yBAAyB;IACzB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["import type { SectionRenderState, SectionPath, SectionLabel } from '@twinfinity/geometry';\nimport { sectionStateToModelView, computePolylinesCenter } from './itemBuilders';\nimport { buildMetadataMap, normalizePropertySets, resolveIfcClassName, resolveIfcClassId } from './metadata';\nimport { applyLineStyleToPaths } from './utils';\nimport { analyzeTemplate } from './analyzeTemplate';\nimport { matchesV2Filter } from './filters';\nimport type {\n LayoutTemplate,\n ViewportElement,\n ViewportLineStyle,\n FillRule,\n SvgModelView,\n SectionResult,\n RenderDrawingInput,\n RenderDrawingResult,\n NormalizedProductItem,\n LabelStyle\n} from './types';\nimport { getProp } from './typeGuards';\n\n// ---------------------------------------------------------------------------\n// Local type declarations for optional peer-dependency APIs.\n// Declared inline to avoid pulling @babylonjs/core and @twinfinity/core into\n// print-layout's compile-time dependency graph.\n// ---------------------------------------------------------------------------\n\ninterface SampledColor {\n color: { r: number; g: number; b: number };\n alpha: number;\n}\n\ninterface ExternalSections {\n buildSectionStateForEntries: (\n perProduct: unknown[],\n options: {\n viewport?: { viewMinX: number; viewMaxX: number; viewMinZ: number; viewMaxZ: number };\n strokeWidth?: number;\n labelFontSize?: number;\n fills?: Array<{ class?: string | string[]; fillOpacity?: number; matchClipFilter?: boolean }>;\n includeMeta?: boolean;\n includeLabels?: boolean;\n buildLabels?: (products: unknown[], fontSize: number) => unknown[];\n clipPolygon?: Array<Array<{ x: number; z: number }>>;\n planeNormal?: unknown;\n }\n ) => (SectionRenderState & { rawPaths?: SectionPath[]; rawLabels?: SectionLabel[] }) | null;\n}\n\ninterface ExternalGeometry {\n sampleProductColorRgba: (product: unknown) => SampledColor | undefined;\n buildRoomLabels: (\n entries: unknown[],\n fontSize: number,\n options: { labelPath: string; classFilter?: string }\n ) => SectionLabel[];\n}\n\n// These were previously lazy-loaded via require() from @measurement-tools/* packages.\n// Now they are part of @twinfinity/printing (this package) and @twinfinity/geometry.\nimport { buildSectionStateForEntries, resolveProductProperty } from '../sections';\n\n// Wrap into the ExternalSections shape for backward compat with the rest of this file.\nconst _sections: ExternalSections = {\n buildSectionStateForEntries: (perProduct, options) => {\n type Entries = Parameters<typeof buildSectionStateForEntries>[0];\n type Opts = Parameters<typeof buildSectionStateForEntries>[1];\n return buildSectionStateForEntries(perProduct as Entries, {\n ...options,\n fills: options.fills as Opts['fills'],\n buildLabels: options.buildLabels as Opts['buildLabels'],\n planeNormal: options.planeNormal as Opts['planeNormal']\n });\n }\n};\nfunction getSections(): ExternalSections {\n return _sections;\n}\n\n// sampleProductColorRgba: duck-type access to product mesh style color.\n// buildRoomLabels: requires product-geometry which this package can't depend on.\n// Consumers must provide buildRoomLabels via options if room labels are needed.\nconst _geometry: ExternalGeometry = {\n sampleProductColorRgba: (product: unknown): SampledColor | undefined => {\n /** Duck-typed mesh with optional color-access methods. */\n interface DuckMesh {\n copyColorTo?: (out: Uint8Array, offset: number) => void;\n getColorComponent?: (index: number) => number;\n style?: { surfaceColor?: readonly number[]; color?: readonly number[] };\n }\n const p = product as Record<string, unknown>;\n const meshes = (p?.meshes ?? p?.productMeshes) as DuckMesh[] | undefined;\n if (!meshes?.length) return undefined;\n\n const rgba = new Uint8Array(4);\n for (const mesh of meshes) {\n try {\n if (typeof mesh.copyColorTo === 'function') {\n mesh.copyColorTo(rgba, 0);\n } else if (typeof mesh.getColorComponent === 'function') {\n rgba[0] = mesh.getColorComponent(0);\n rgba[1] = mesh.getColorComponent(1);\n rgba[2] = mesh.getColorComponent(2);\n rgba[3] = mesh.getColorComponent(3);\n } else {\n const style = mesh.style;\n const c = style?.surfaceColor ?? style?.color;\n if (c) {\n // surfaceColor values may be 0–1 or 0–255; normalize to 0–1\n const maxVal = Math.max(c[0], c[1], c[2]);\n const scale = maxVal > 1 ? 1 / 255 : 1;\n return {\n color: { r: c[0] * scale, g: c[1] * scale, b: c[2] * scale },\n alpha: (c[3] ?? (maxVal > 1 ? 255 : 1)) * scale\n };\n }\n continue;\n }\n } catch {\n continue;\n }\n const [r, g, b, a] = rgba;\n if (a === 0 && r === 0 && g === 0 && b === 0) continue;\n return { color: { r: r / 255, g: g / 255, b: b / 255 }, alpha: a / 255 };\n }\n return undefined;\n },\n buildRoomLabels: () => [] // Requires product-geometry — consumer provides via options\n};\nfunction getGeometry(): ExternalGeometry {\n return _geometry;\n}\nfunction getSampleColor(): (product: unknown) => SampledColor | undefined {\n return _geometry.sampleProductColorRgba;\n}\n\n// Late-binding for renderLayout to break circular dependency.\n// renderDrawing is imported by index.ts which defines renderLayout.\n// The binding is set by index.ts after definition via setRenderLayoutBinding().\nlet _renderLayout:\n | ((\n template: LayoutTemplate,\n data: any,\n models: Record<string, SvgModelView>,\n options?: any\n ) => Promise<{ svg: string; viewportScales: any; warnings: string[] }>)\n | null = null;\n\n/** Called by index.ts to register renderLayout without a circular import. */\nexport function setRenderLayoutBinding(fn: NonNullable<typeof _renderLayout>): void {\n _renderLayout = fn;\n}\n\nfunction getRenderLayout(): NonNullable<typeof _renderLayout> {\n if (!_renderLayout) {\n throw new Error(\n 'renderLayout not initialized. Ensure @twinfinity/printing index is imported before calling renderDrawing.'\n );\n }\n return _renderLayout;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * High-level entry point that converts raw plane-section results and a layout\n * template into a ready-to-print SVG string.\n *\n * Internalizes all data wiring (section → SVG, item extraction, color sampling,\n * metadata building, legend/label data) that previously required ~350 lines of\n * app-side boilerplate.\n *\n * ```ts\n * const section = computePlaneSectionsForProducts(products, plane, { includeInvisible: true });\n * const { svg, warnings } = await renderDrawing({\n * template,\n * sections: { main: section },\n * options: {\n * allowRemoteAssets: true,\n * qrContext: { host, containerName, planeHeight },\n * productFields: { idPath: \"propertySets.BIP.spacenumber.value\", ... },\n * roomLabels: { labelPath: \"BIP.spacename\", classFilter: \"space\" },\n * },\n * });\n * ```\n */\nexport async function renderDrawing(input: RenderDrawingInput): Promise<RenderDrawingResult> {\n const { options } = input;\n const warnings: string[] = [];\n\n // ---- Resolve template variables (if present) ----\n // This happens FIRST to ensure all {{vars.*}} references are resolved before validation/rendering\n let template = input.template;\n if (template.variables) {\n const { resolveTemplateValues, applyTemplateVariables } = await import('./templateVariables');\n const { values, warnings: varWarnings } = resolveTemplateValues(template, options?.variables);\n\n // Add variable resolution warnings to output\n varWarnings.forEach((w) => warnings.push(`[Variables] ${w}`));\n\n // Apply variables to template (substitute {{vars.*}} references)\n template = applyTemplateVariables(template, values);\n }\n\n const renderLayout = getRenderLayout();\n\n // ---- Collect viewport elements from template ----\n const viewports = template.elements.filter((el) => el.kind === 'viewport') as ViewportElement[];\n if (!viewports.length) {\n return { svg: '', warnings: ['No viewport elements in template.'] };\n }\n\n // ---- Fast path: pre-built models provided directly ----\n if (input.models) {\n // Build items: explicit viewportItems take priority, then auto-build from productEntries\n let viewportsData: Record<string, { items: NormalizedProductItem[] }>;\n\n if (options?.viewportItems) {\n const emptyDefaults = Object.fromEntries(\n viewports.map((vp) => [vp.id, { items: [] as NormalizedProductItem[] }])\n );\n viewportsData = { ...emptyDefaults, ...options.viewportItems };\n } else if (options?.productEntries?.length) {\n // Auto-build items per viewport from raw product entries\n const sampleColor = getSampleColor();\n viewportsData = {};\n for (const vp of viewports) {\n viewportsData[vp.id] = {\n items: buildItemsFromEntries(options.productEntries, vp.fills, sampleColor)\n };\n }\n } else {\n viewportsData = Object.fromEntries(\n viewports.map((vp) => [vp.id, { items: [] as NormalizedProductItem[] }])\n );\n }\n\n const layoutData = { viewports: viewportsData };\n\n // Early input analysis: warn about mismatches between template and provided data\n const reqs = analyzeTemplate(template);\n\n for (const vpId of reqs.viewportIds) {\n if (!input.models[vpId]) {\n warnings.push(`Template viewport '${vpId}' has no model — it will render empty.`);\n }\n }\n\n const allItems = Object.values(viewportsData).flatMap((v) => v.items);\n if (allItems.length > 0) {\n for (const path of reqs.propertyPaths) {\n const parts = path.split('.');\n if (parts.length < 2) continue;\n const [setName, propName] = parts;\n const hasIt = allItems.some((item) => item.propertySets?.[setName]?.[propName]?.value !== undefined);\n if (!hasIt) {\n warnings.push(`Template references property '${path}' but no items provide it.`);\n }\n }\n }\n\n if (reqs.usesRemoteAssets && !template.allowRemoteAssets && !options?.allowRemoteAssets) {\n warnings.push('Template uses remote assets but allowRemoteAssets is not enabled.');\n }\n\n // Build metadata from product entries (same as standard path)\n const metadataEntries = options?.productEntries ?? [];\n const metadataById = buildMetadataMap(metadataEntries);\n\n const result = await renderLayout(template, layoutData, input.models, {\n allowRemoteAssets: template.allowRemoteAssets ?? options?.allowRemoteAssets,\n qrContext: options?.qrContext,\n metadataById\n });\n\n return {\n svg: result.svg,\n warnings: [...warnings, ...result.warnings]\n };\n }\n\n // ---- Standard path: build models from section results ----\n if (!input.sections) {\n return { svg: '', warnings: ['Either sections or models must be provided.'] };\n }\n\n const { buildSectionStateForEntries } = getSections();\n const { sampleProductColorRgba, buildRoomLabels } = getGeometry();\n\n // Normalize sections: single SectionResult → map to all viewports\n const isSingle = !!(input.sections as SectionResult).perProduct;\n const sections: Record<string, SectionResult> = isSingle\n ? Object.fromEntries(viewports.map((vp) => [vp.id, input.sections as SectionResult]))\n : (input.sections as Record<string, SectionResult>);\n\n // Auto-detect whether the template has its own labels elements.\n // If so, do NOT embed labels in the section SVG (would cause duplicates).\n const hasTemplateLabelElements = template.elements.some((el) => el.kind === 'labels');\n const roomLabelCfg = hasTemplateLabelElements ? undefined : options?.roomLabels;\n\n // ---- Build section state + model view per viewport ----\n const models: Record<string, SvgModelView> = {};\n const entriesByViewport: Record<string, SectionResult['perProduct']> = {};\n const sectionStateCache = new Map<string, SectionRenderState | null>();\n\n for (const vp of viewports) {\n const sectionResult = sections[vp.id];\n if (!sectionResult?.perProduct?.length) {\n warnings.push(`No section data for viewport '${vp.id}'.`);\n continue;\n }\n\n entriesByViewport[vp.id] = sectionResult.perProduct;\n\n // Dedup: skip if already built for this viewport key\n if (sectionStateCache.has(vp.id)) {\n const cached = sectionStateCache.get(vp.id);\n if (cached) {\n models[vp.id] = sectionStateToModelView(cached);\n }\n continue;\n }\n\n const resolved = resolveLineStyles(vp);\n\n // Estimate label font size from template style and section bounds\n const labelFontSize = estimateLabelFontSize(template, vp, sectionResult.perProduct);\n\n // Extract clip polygon from section results if clip config is present\n let clipPolygon: Array<Array<{ x: number; z: number }>> | undefined;\n if (vp.clip?.where && vp.clip.class) {\n const clipClass = Array.isArray(vp.clip.class) ? vp.clip.class : [vp.clip.class];\n const clipEntries = sectionResult.perProduct.filter((entry: any) => {\n const product = entry.product;\n const matchesClass = clipClass.some((c: string) => product.class?.is?.(c));\n if (!matchesClass) return false;\n // Match where condition using T8-aware property resolution\n for (const [path, expected] of Object.entries(vp.clip!.where!)) {\n const actualValue = resolveProductProperty(product, path);\n if (actualValue === undefined) return false;\n if (actualValue !== expected) return false;\n }\n return true;\n });\n if (clipEntries.length) {\n clipPolygon = clipEntries.flatMap((entry: any) =>\n (entry.polylines || []).map((pl: any) => (pl.points || []).map((p: any) => ({ x: p.x, z: p.z })))\n );\n }\n }\n\n // Convert planeNormal array to object with x, y, z if present\n let planeNormal: unknown = undefined;\n if (vp.planeNormal && Array.isArray(vp.planeNormal) && vp.planeNormal.length === 3) {\n planeNormal = { x: vp.planeNormal[0], y: vp.planeNormal[1], z: vp.planeNormal[2] };\n }\n\n type SectionStateWithRaw = SectionRenderState & { rawPaths?: SectionPath[]; rawLabels?: SectionLabel[] };\n const state: SectionStateWithRaw | null = buildSectionStateForEntries(sectionResult.perProduct, {\n strokeWidth: resolved.default.widthMm,\n labelFontSize,\n fills: vp.fills,\n includeMeta: true,\n includeLabels: !!roomLabelCfg,\n buildLabels: roomLabelCfg\n ? (products, fontSize) =>\n buildRoomLabels(products, fontSize, {\n labelPath: roomLabelCfg.labelPath,\n classFilter: roomLabelCfg.classFilter\n })\n : undefined,\n clipPolygon,\n planeNormal\n });\n\n sectionStateCache.set(vp.id, state);\n\n if (!state) {\n warnings.push(`No geometry for viewport '${vp.id}'.`);\n continue;\n }\n\n // Apply line style overrides from viewport config\n let finalState: SectionStateWithRaw = state;\n const defaultStyle = resolved.default;\n if (defaultStyle.color || defaultStyle.widthMm) {\n const style: { stroke?: string; strokeWidth?: number } = {};\n if (defaultStyle.color) style.stroke = defaultStyle.color;\n if (defaultStyle.widthMm) style.strokeWidth = defaultStyle.widthMm;\n finalState = {\n ...state,\n paths: applyLineStyleToPaths(state.paths, style)\n };\n if (state.rawPaths) {\n finalState.rawPaths = applyLineStyleToPaths(state.rawPaths, style);\n }\n }\n\n const labelStyle: LabelStyle | undefined = template.styles?.body\n ? {\n fill: template.styles.body.color ?? BUILTIN_LABEL_DEFAULTS.fill,\n fontFamily: template.styles.body.fontFamily ?? BUILTIN_LABEL_DEFAULTS.fontFamily,\n fontWeight: template.styles.body.fontWeight ?? BUILTIN_LABEL_DEFAULTS.fontWeight\n }\n : undefined;\n\n models[vp.id] = sectionStateToModelView(finalState, labelStyle);\n }\n\n // ---- Build items per viewport ----\n const viewportsData: Record<string, { items: NormalizedProductItem[] }> = {};\n\n for (const vp of viewports) {\n const entries = entriesByViewport[vp.id];\n if (!entries) {\n viewportsData[vp.id] = { items: [] };\n continue;\n }\n\n viewportsData[vp.id] = { items: buildItemsFromEntries(entries, vp.fills, sampleProductColorRgba) };\n }\n\n // ---- Build metadata from all section entries ----\n const allEntries = Object.values(entriesByViewport).flat();\n const metadataById = buildMetadataMap(allEntries);\n\n // ---- Assemble layout data ----\n const layoutData = {\n viewports: viewportsData\n };\n\n // ---- Render ----\n const result = await renderLayout(template, layoutData, models, {\n allowRemoteAssets: template.allowRemoteAssets ?? options?.allowRemoteAssets,\n qrContext: options?.qrContext,\n metadataById\n });\n\n return {\n svg: result.svg,\n warnings: [...warnings, ...result.warnings]\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Build NormalizedProductItems from per-product entries.\n *\n * Shared between the standard path (plane sections) and the models path\n * (slab sections / pre-built models with productEntries).\n */\nfunction buildItemsFromEntries(\n entries: Array<{ product: unknown; polylines: unknown }>,\n fills: FillRule[] | undefined,\n sampleColor: ((product: unknown) => SampledColor | undefined) | undefined\n): NormalizedProductItem[] {\n return entries.map((entry) => {\n const product = entry.product;\n const propertySets = normalizePropertySets(product);\n const gid = getProp(product, 'gid') ?? getProp(product, 'id');\n const center = computePolylinesCenter(entry);\n const name = getProp(product, 'name') as string | undefined;\n const displayName = typeof name === 'string' && name.trim().length ? name.trim() : gid ? String(gid) : '-';\n const item: NormalizedProductItem = {\n gid: gid ? String(gid) : undefined,\n ifcClass: resolveIfcClassName(product),\n ifcClassId: resolveIfcClassId(product),\n propertySets,\n productId: gid ? String(gid) : '-',\n productName: displayName,\n area: null,\n x: center.x,\n z: center.z,\n text: displayName,\n polylines: entry.polylines as Array<{ points?: Array<{ x: number; z: number }> }>\n };\n\n if (fills?.length && sampleColor) {\n const color = resolveFillColor(item, product, fills, sampleColor);\n if (color) {\n item.color = color;\n }\n }\n\n return item;\n });\n}\n\n/**\n * Estimate label font size in model units from the template body style and the\n * approximate viewport-to-world scale ratio.\n */\nfunction estimateLabelFontSize(\n template: LayoutTemplate,\n vp: ViewportElement,\n perProduct: SectionResult['perProduct']\n): number {\n const templateFontSizeMm = template.styles?.body?.fontSizeMm ?? 3.5;\n\n // Quick world-bounds scan\n let minX = Infinity;\n let maxX = -Infinity;\n let minZ = Infinity;\n let maxZ = -Infinity;\n for (const entry of perProduct) {\n if (!entry.polylines) continue;\n for (const polyline of entry.polylines) {\n if (!polyline.points) continue;\n for (const pt of polyline.points) {\n const px = getProp(pt, 'x');\n const pz = getProp(pt, 'z');\n if (Number.isFinite(px) && Number.isFinite(pz)) {\n if ((px as number) < minX) minX = px as number;\n if ((px as number) > maxX) maxX = px as number;\n if ((pz as number) < minZ) minZ = pz as number;\n if ((pz as number) > maxZ) maxZ = pz as number;\n }\n }\n }\n }\n\n const worldWidth = maxX - minX;\n const worldHeight = maxZ - minZ;\n if (!Number.isFinite(worldWidth) || worldWidth <= 0 || !Number.isFinite(worldHeight) || worldHeight <= 0) {\n return templateFontSizeMm; // Fallback: 1:1 estimate\n }\n\n // Approximate mm-per-model-unit from the viewport dimensions\n const scaleX = vp.widthMm / worldWidth;\n const scaleY = vp.heightMm / worldHeight;\n const mmPerModelUnit = Math.min(scaleX, scaleY);\n\n if (!Number.isFinite(mmPerModelUnit) || mmPerModelUnit <= 0) {\n return templateFontSizeMm;\n }\n\n return templateFontSizeMm / mmPerModelUnit;\n}\n\n// ---------------------------------------------------------------------------\n// Label style defaults\n// ---------------------------------------------------------------------------\n\n/** Built-in label style defaults when template body styles are partially set. */\nexport const BUILTIN_LABEL_DEFAULTS = {\n fill: '#374151',\n fontFamily: '\"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif',\n fontWeight: '300'\n} as const;\n\n// ---------------------------------------------------------------------------\n// Line style defaults & resolution\n// ---------------------------------------------------------------------------\n\n/** Built-in defaults when neither lineStyles nor lineWidthMm/lineColor are set. */\nexport const BUILTIN_LINE_DEFAULTS: Required<ViewportLineStyle> = {\n widthMm: 0.35, // Reasonable default: 0.35mm line width (was 0.01mm - too thin!)\n color: '#1f2937',\n dash: '',\n enabled: true\n};\n\nexport const BUILTIN_HIDDEN_DEFAULTS: Required<ViewportLineStyle> = {\n widthMm: 0.005,\n color: '#9ca3af',\n dash: '4 2',\n enabled: true\n};\n\ntype LineCategory =\n | 'cutLines'\n | 'visibleLines'\n | 'hiddenLines'\n | 'silhouettes'\n | 'featureEdges'\n | 'stairArrows'\n | 'outlineVisible'\n | 'outlineHidden';\n\n/** Category-specific built-in defaults (merged over the global default). */\nexport const CATEGORY_BUILTIN_OVERRIDES: Partial<Record<LineCategory, Partial<ViewportLineStyle>>> = {\n cutLines: { widthMm: 0.02 },\n hiddenLines: BUILTIN_HIDDEN_DEFAULTS,\n outlineHidden: BUILTIN_HIDDEN_DEFAULTS\n};\n\nexport interface ResolvedLineStyles {\n default: Required<ViewportLineStyle>;\n byCategory: Record<LineCategory, Required<ViewportLineStyle>>;\n}\n\n/**\n * Resolve the effective line style for every category.\n *\n * Priority: category entry in `lineStyles` → `lineStyles.default`\n * → `lineWidthMm`/`lineColor` shorthand → built-in defaults.\n */\nfunction resolveLineStyles(vp: ViewportElement): ResolvedLineStyles {\n const ls = vp.lineStyles;\n\n // Base default: lineStyles.default → shorthand → built-in\n const base: Required<ViewportLineStyle> = {\n ...BUILTIN_LINE_DEFAULTS,\n ...(vp.lineWidthMm !== undefined ? { widthMm: vp.lineWidthMm } : {}),\n ...(vp.lineColor !== undefined ? { color: vp.lineColor } : {}),\n ...(ls?.default ?? {})\n } as Required<ViewportLineStyle>;\n\n const categories: LineCategory[] = [\n 'cutLines',\n 'visibleLines',\n 'hiddenLines',\n 'silhouettes',\n 'featureEdges',\n 'stairArrows',\n 'outlineVisible',\n 'outlineHidden'\n ];\n\n // Category group mapping: individual categories inherit from their group style\n const categoryGroups: Record<LineCategory, LineCategory> = {\n cutLines: 'visibleLines',\n visibleLines: 'visibleLines',\n silhouettes: 'visibleLines',\n featureEdges: 'visibleLines',\n outlineVisible: 'visibleLines',\n hiddenLines: 'hiddenLines',\n outlineHidden: 'hiddenLines',\n stairArrows: 'stairArrows'\n };\n\n const byCategory = {} as Record<LineCategory, Required<ViewportLineStyle>>;\n for (const cat of categories) {\n const builtinOverride = CATEGORY_BUILTIN_OVERRIDES[cat];\n const groupName = categoryGroups[cat];\n const groupStyle = ls?.[groupName]; // Group style (visibleLines, hiddenLines, stairArrows)\n const categoryOverride = ls?.[cat]; // Individual category settings (enable/disable)\n\n byCategory[cat] = {\n ...base,\n ...(builtinOverride ?? {}),\n ...(groupStyle ?? {}), // Apply group style first\n ...(categoryOverride ?? {}) // Then individual overrides (enable/disable)\n } as Required<ViewportLineStyle>;\n }\n\n return { default: base, byCategory };\n}\n\n// ---------------------------------------------------------------------------\n// Fill rule helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve fill color for an item based on viewport fill rules.\n * Returns an RGBA tuple [r, g, b, a] (0–255) or undefined if no rule matches.\n */\nexport function resolveFillColor(\n item: NormalizedProductItem,\n product: unknown,\n fills: FillRule[] | undefined,\n sampleColor: (product: unknown) => SampledColor | undefined\n): [number, number, number, number] | undefined {\n if (!fills?.length) return undefined;\n for (const rule of fills) {\n if (!matchesV2Filter(item, rule.class, rule.where)) continue;\n if (rule.fill === 'sample') {\n const sampled = sampleColor(product);\n if (sampled) {\n return [\n Math.round(sampled.color.r * 255),\n Math.round(sampled.color.g * 255),\n Math.round(sampled.color.b * 255),\n Math.round(sampled.alpha * 255)\n ];\n }\n } else {\n return parseHexToRgba(rule.fill);\n }\n break; // first matching rule wins\n }\n return undefined;\n}\n\n/**\n * Parse a CSS hex color string to an RGBA tuple (0–255).\n */\nfunction parseHexToRgba(hex: string): [number, number, number, number] {\n const h = hex.replace(/^#/, '');\n if (h.length === 3) {\n return [parseInt(h[0] + h[0], 16), parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16), 255];\n }\n if (h.length === 6) {\n return [parseInt(h.substring(0, 2), 16), parseInt(h.substring(2, 4), 16), parseInt(h.substring(4, 6), 16), 255];\n }\n if (h.length === 8) {\n return [\n parseInt(h.substring(0, 2), 16),\n parseInt(h.substring(2, 4), 16),\n parseInt(h.substring(4, 6), 16),\n parseInt(h.substring(6, 8), 16)\n ];\n }\n // Fallback: opaque black\n return [0, 0, 0, 255];\n}\n"]}
1
+ {"version":3,"file":"renderDrawing.js","sourceRoot":"/","sources":["layout/renderDrawing.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SectionRenderState, SectionPath, SectionLabel } from '@twinfinity/geometry';\nimport { sectionStateToModelView, computePolylinesCenter, readXZ } from './itemBuilders';\nimport { buildMetadataMap, normalizePropertySets, resolveIfcClassName, resolveIfcClassId } from './metadata';\nimport { applyLineStyleToPaths } from './utils';\nimport { analyzeTemplate } from './analyzeTemplate';\nimport { matchesFilter, matchesProductClass, normalizeValue } from './filters';\nimport type {\n LayoutTemplate,\n ViewportElement,\n ViewportLineStyle,\n ViewportLineStyles,\n FillRule,\n SvgModelView,\n SectionResult,\n RenderDrawingInput,\n RenderDrawingResult,\n NormalizedProductItem,\n ProductPolylineEntry,\n LabelStyle\n} from './types';\nimport { getProp } from './typeGuards';\n\n// ---------------------------------------------------------------------------\n// Local type declarations for optional peer-dependency APIs.\n// Declared inline to avoid pulling @babylonjs/core and @twinfinity/core into\n// print-layout's compile-time dependency graph.\n// ---------------------------------------------------------------------------\n\ninterface SampledColor {\n color: { r: number; g: number; b: number };\n alpha: number;\n}\n\ninterface ExternalSections {\n buildSectionStateForEntries: (\n perProduct: unknown[],\n options: {\n viewport?: { viewMinX: number; viewMaxX: number; viewMinZ: number; viewMaxZ: number };\n strokeWidth?: number;\n labelFontSize?: number;\n fills?: Array<{ class?: string | string[]; fillOpacity?: number; matchClipFilter?: boolean }>;\n includeMeta?: boolean;\n includeLabels?: boolean;\n buildLabels?: (products: unknown[], fontSize: number) => unknown[];\n clipPolygon?: Array<Array<{ x: number; z: number }>>;\n planeNormal?: unknown;\n }\n ) => (SectionRenderState & { rawPaths?: SectionPath[]; rawLabels?: SectionLabel[] }) | null;\n}\n\ninterface ExternalGeometry {\n sampleProductColorRgba: (product: unknown) => SampledColor | undefined;\n buildRoomLabels: (\n entries: unknown[],\n fontSize: number,\n options: { labelPath: string; classFilter?: string }\n ) => SectionLabel[];\n}\n\n// These were previously lazy-loaded via require() from @measurement-tools/* packages.\n// Now they are part of @twinfinity/printing (this package) and @twinfinity/geometry.\nimport { buildSectionStateForEntries, resolveProductProperty } from '../sections';\n\n// Wrap into the ExternalSections shape for backward compat with the rest of this file.\nconst _sections: ExternalSections = {\n buildSectionStateForEntries: (perProduct, options) => {\n type Entries = Parameters<typeof buildSectionStateForEntries>[0];\n type Opts = Parameters<typeof buildSectionStateForEntries>[1];\n return buildSectionStateForEntries(perProduct as Entries, {\n ...options,\n fills: options.fills as Opts['fills'],\n buildLabels: options.buildLabels as Opts['buildLabels'],\n planeNormal: options.planeNormal as Opts['planeNormal']\n });\n }\n};\nfunction getSections(): ExternalSections {\n return _sections;\n}\n\n// sampleProductColorRgba: duck-type access to product mesh style color.\n// buildRoomLabels: requires product-geometry which this package can't depend on.\n// Consumers must provide buildRoomLabels via options if room labels are needed.\nconst _geometry: ExternalGeometry = {\n sampleProductColorRgba: (product: unknown): SampledColor | undefined => {\n /** Duck-typed mesh with optional color-access methods. */\n interface DuckMesh {\n copyColorTo?: (out: Uint8Array, offset: number) => void;\n getColorComponent?: (index: number) => number;\n style?: { surfaceColor?: readonly number[]; color?: readonly number[] };\n }\n const p = product as Record<string, unknown>;\n const meshes = (p?.meshes ?? p?.productMeshes) as DuckMesh[] | undefined;\n if (!meshes?.length) return undefined;\n\n const rgba = new Uint8Array(4);\n for (const mesh of meshes) {\n try {\n if (typeof mesh.copyColorTo === 'function') {\n mesh.copyColorTo(rgba, 0);\n } else if (typeof mesh.getColorComponent === 'function') {\n rgba[0] = mesh.getColorComponent(0);\n rgba[1] = mesh.getColorComponent(1);\n rgba[2] = mesh.getColorComponent(2);\n rgba[3] = mesh.getColorComponent(3);\n } else {\n const style = mesh.style;\n const c = style?.surfaceColor ?? style?.color;\n if (c) {\n // surfaceColor values may be 0–1 or 0–255; normalize to 0–1\n const maxVal = Math.max(c[0], c[1], c[2]);\n const scale = maxVal > 1 ? 1 / 255 : 1;\n return {\n color: { r: c[0] * scale, g: c[1] * scale, b: c[2] * scale },\n alpha: (c[3] ?? (maxVal > 1 ? 255 : 1)) * scale\n };\n }\n continue;\n }\n } catch {\n continue;\n }\n const [r, g, b, a] = rgba;\n if (a === 0 && r === 0 && g === 0 && b === 0) continue;\n return { color: { r: r / 255, g: g / 255, b: b / 255 }, alpha: a / 255 };\n }\n return undefined;\n },\n buildRoomLabels: () => [] // Requires product-geometry — consumer provides via options\n};\nfunction getGeometry(): ExternalGeometry {\n return _geometry;\n}\nfunction getSampleColor(): (product: unknown) => SampledColor | undefined {\n return _geometry.sampleProductColorRgba;\n}\n\nimport { renderLayout } from './renderLayout';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * High-level entry point that renders a layout template to a ready-to-print\n * SVG string.\n *\n * The inputs form a 2×2 matrix — geometry × item data, each either raw or\n * pre-built:\n *\n * | input | what it is |\n * |---------------------------|--------------------------------------------------|\n * | `sections` | RAW geometry: plane-section results (one shared or per viewport id). |\n * | `models` | PRE-BUILT geometry: `SvgModelView`s per viewport id (e.g. from `slabSectionToModelView`). Needs `rawPaths` with `meta.gid` for highlight/clip/includeClasses. |\n * | `options.productEntries` | RAW item data: per-product world-x/z polylines (`slabPerProductToEntries`); one flat list shared by all viewports or a per-viewport record. Feeds labels/legend/clip/highlight items. |\n * | `options.viewportItems` | PRE-BUILT item data per viewport id (overrides `productEntries`). |\n *\n * Prerequisite: products must have property sets loaded\n * (`await api.ifc.loadPropertySets()` on core BIM objects) — without them\n * every `where` filter matches nothing.\n *\n * ```ts\n * const section = computePlaneSectionsForProducts(products, plane, { includeInvisible: true });\n * const { svg, warnings } = await renderDrawing({\n * template,\n * sections: { main: section },\n * options: {\n * allowRemoteResources: true,\n * qrContext: { host, containerName },\n * roomLabels: { labelPath: \"BIP.spacename\", classFilter: \"space\" },\n * },\n * });\n * ```\n */\nexport async function renderDrawing(input: RenderDrawingInput): Promise<RenderDrawingResult> {\n const { options } = input;\n const warnings: string[] = [];\n\n // ---- Resolve template variables (if present) ----\n // This happens FIRST to ensure all {{vars.*}} references are resolved before validation/rendering\n let template = input.template;\n if (template.variables) {\n const { resolveTemplateValues, applyTemplateVariables } = await import('./templateVariables');\n const { values, warnings: varWarnings } = resolveTemplateValues(template, options?.variables);\n\n // Add variable resolution warnings to output\n varWarnings.forEach((w) => warnings.push(`[Variables] ${w}`));\n\n // Apply variables to template (substitute {{vars.*}} references)\n template = applyTemplateVariables(template, values, warnings);\n }\n\n // ---- Collect viewport elements from template ----\n const viewports = template.elements.filter((el) => el.kind === 'viewport') as ViewportElement[];\n if (!viewports.length) {\n return { svg: '', warnings: ['No viewport elements in template.'] };\n }\n\n // ---- Fast path: pre-built models provided directly ----\n if (input.models) {\n if (options?.roomLabels) {\n warnings.push('options.roomLabels is ignored on the models path — use a template labels element instead.');\n }\n // productEntries: one flat list for all viewports, or per-viewport record.\n const entriesInput = options?.productEntries;\n const entriesFor = (vpId: string): ProductPolylineEntry[] =>\n Array.isArray(entriesInput) ? entriesInput : (entriesInput?.[vpId] ?? []);\n const allEntries: ProductPolylineEntry[] = Array.isArray(entriesInput)\n ? entriesInput\n : Object.values(entriesInput ?? {}).flat();\n\n // Build items: explicit viewportItems take priority, then auto-build from productEntries\n let viewportsData: Record<string, { items: NormalizedProductItem[] }>;\n\n if (options?.viewportItems) {\n const emptyDefaults = Object.fromEntries(\n viewports.map((vp) => [vp.id, { items: [] as NormalizedProductItem[] }])\n );\n viewportsData = { ...emptyDefaults, ...options.viewportItems };\n } else if (allEntries.length) {\n // Auto-build items per viewport from raw product entries\n const sampleColor = getSampleColor();\n viewportsData = {};\n for (const vp of viewports) {\n viewportsData[vp.id] = {\n items: buildItemsFromEntries(entriesFor(vp.id), vp.fills, sampleColor)\n };\n }\n } else {\n viewportsData = Object.fromEntries(\n viewports.map((vp) => [vp.id, { items: [] as NormalizedProductItem[] }])\n );\n }\n\n const layoutData = { viewports: viewportsData };\n\n // Early input analysis: warn about mismatches between template and provided data\n const reqs = analyzeTemplate(template);\n\n for (const vpId of reqs.viewportIds) {\n if (!input.models[vpId]) {\n warnings.push(`Template viewport '${vpId}' has no model — it will render empty.`);\n }\n }\n\n const allItems = Object.values(viewportsData).flatMap((v) => v.items);\n if (allItems.length > 0) {\n for (const path of reqs.propertyPaths) {\n const parts = path.split('.');\n if (parts.length < 2) continue;\n const [setName, propName] = parts;\n const hasIt = allItems.some((item) => item.propertySets?.[setName]?.[propName]?.value !== undefined);\n if (!hasIt) {\n warnings.push(`Template references property '${path}' but no items provide it.`);\n }\n }\n } else if (\n reqs.propertyPaths.length > 0 ||\n template.elements.some((el) => el.kind === 'labels' || el.kind === 'legend')\n ) {\n // Item-driven features with zero items is almost always a missing\n // productEntries/viewportItems input, not an intentionally empty sheet.\n warnings.push(\n 'Template uses item data (labels/legend/where-filters) but no items were provided — pass options.productEntries or options.viewportItems.'\n );\n }\n\n if (reqs.usesRemoteResources && !template.allowRemoteResources && !options?.allowRemoteResources) {\n warnings.push('Template uses remote resources but allowRemoteResources is not enabled.');\n }\n\n // Build metadata from product entries (same as standard path)\n // Caller-supplied metadata first: entries only cover cut-bearing products, but\n // include filtering must classify products whose geometry never crosses the cut\n // (furniture below a plan cut) - renderTemplateSheet supplies the full map.\n const metadataById = { ...(options?.metadataById ?? {}), ...buildMetadataMap(allEntries) };\n\n const result = await renderLayout(template, layoutData, input.models, {\n allowRemoteResources: template.allowRemoteResources ?? options?.allowRemoteResources,\n qrContext: options?.qrContext,\n metadataById\n });\n\n return {\n svg: result.svg,\n warnings: [...warnings, ...result.warnings]\n };\n }\n\n // ---- Standard path: build models from section results ----\n if (!input.sections) {\n return { svg: '', warnings: ['Either sections or models must be provided.'] };\n }\n\n const { buildSectionStateForEntries } = getSections();\n const { sampleProductColorRgba, buildRoomLabels } = getGeometry();\n\n // Normalize sections: single SectionResult → map to all viewports\n const isSingle = !!(input.sections as SectionResult).perProduct;\n const sections: Record<string, SectionResult> = isSingle\n ? Object.fromEntries(viewports.map((vp) => [vp.id, input.sections as SectionResult]))\n : (input.sections as Record<string, SectionResult>);\n\n // Auto-detect whether the template has its own labels elements.\n // If so, do NOT embed labels in the section SVG (would cause duplicates).\n const hasTemplateLabelElements = template.elements.some((el) => el.kind === 'labels');\n const roomLabelCfg = hasTemplateLabelElements ? undefined : options?.roomLabels;\n\n // ---- Build section state + model view per viewport ----\n const models: Record<string, SvgModelView> = {};\n const entriesByViewport: Record<string, SectionResult['perProduct']> = {};\n\n for (const vp of viewports) {\n const sectionResult = sections[vp.id];\n if (!sectionResult?.perProduct?.length) {\n warnings.push(`No section data for viewport '${vp.id}'.`);\n continue;\n }\n\n entriesByViewport[vp.id] = sectionResult.perProduct;\n\n const resolved = resolveLineStyles(vp);\n\n // Estimate label font size from template style and section bounds\n const labelFontSize = estimateLabelFontSize(template, vp, sectionResult.perProduct);\n\n // Extract clip polygon from section results if clip config is present.\n // Matching semantics MUST mirror matchesFilter (viewportPrep/labels/\n // legend all use it on the same clip config): class optional,\n // Ifc prefix optional, case-insensitive values, arrays mean any-of.\n let clipPolygon: Array<Array<{ x: number; z: number }>> | undefined;\n if (vp.clip?.where) {\n const clipClass = vp.clip.class ? (Array.isArray(vp.clip.class) ? vp.clip.class : [vp.clip.class]) : null;\n const clipEntries = sectionResult.perProduct.filter((entry: any) => {\n const product = entry.product;\n // Shared helper rather than product.class.is(): that compares with ===, so a clip\n // filter of \"SPACE\" or \"IFCWALL\" selected nothing while labels and legend — reading\n // the same clip config through matchesFilter — matched it. The clip silently did\n // not apply, with no warning anywhere.\n if (!matchesProductClass(product, clipClass ?? undefined)) return false;\n for (const [path, expected] of Object.entries(vp.clip!.where!)) {\n const actual = normalizeValue(resolveProductProperty(product, path));\n const matches = Array.isArray(expected)\n ? expected.some((v) => normalizeValue(v) === actual)\n : actual === normalizeValue(expected);\n if (!matches) return false;\n }\n return true;\n });\n if (clipEntries.length) {\n clipPolygon = clipEntries.flatMap((entry: any) =>\n (entry.polylines || []).map((pl: any) =>\n (pl.points || [])\n .map((p: any) => readXZ(p))\n .filter(\n (p: { x: number | null; z: number | null }): p is { x: number; z: number } =>\n p.x !== null && p.z !== null\n )\n )\n );\n }\n }\n\n // Convert planeNormal array to object with x, y, z if present\n let planeNormal: unknown = undefined;\n if (vp.planeNormal && Array.isArray(vp.planeNormal) && vp.planeNormal.length === 3) {\n planeNormal = { x: vp.planeNormal[0], y: vp.planeNormal[1], z: vp.planeNormal[2] };\n }\n\n type SectionStateWithRaw = SectionRenderState & { rawPaths?: SectionPath[]; rawLabels?: SectionLabel[] };\n const state: SectionStateWithRaw | null = buildSectionStateForEntries(sectionResult.perProduct, {\n strokeWidth: resolved.default.widthMm,\n labelFontSize,\n fills: vp.fills,\n includeMeta: true,\n includeLabels: !!roomLabelCfg,\n buildLabels: roomLabelCfg\n ? (products, fontSize) =>\n buildRoomLabels(products, fontSize, {\n labelPath: roomLabelCfg.labelPath,\n classFilter: roomLabelCfg.classFilter\n })\n : undefined,\n clipPolygon,\n planeNormal\n });\n\n if (!state) {\n warnings.push(`No geometry for viewport '${vp.id}'.`);\n continue;\n }\n\n // Apply line style overrides from viewport config\n let finalState: SectionStateWithRaw = state;\n const defaultStyle = resolved.default;\n if (defaultStyle.color || defaultStyle.widthMm) {\n const style: { stroke?: string; strokeWidth?: number } = {};\n if (defaultStyle.color) style.stroke = defaultStyle.color;\n if (defaultStyle.widthMm) style.strokeWidth = defaultStyle.widthMm;\n finalState = {\n ...state,\n paths: applyLineStyleToPaths(state.paths, style)\n };\n if (state.rawPaths) {\n finalState.rawPaths = applyLineStyleToPaths(state.rawPaths, style);\n }\n }\n\n const labelStyle: LabelStyle | undefined = template.styles?.body\n ? {\n fill: template.styles.body.color ?? BUILTIN_LABEL_DEFAULTS.fill,\n fontFamily: template.styles.body.fontFamily ?? BUILTIN_LABEL_DEFAULTS.fontFamily,\n fontWeight: template.styles.body.fontWeight ?? BUILTIN_LABEL_DEFAULTS.fontWeight\n }\n : undefined;\n\n models[vp.id] = sectionStateToModelView(finalState, labelStyle);\n }\n\n // ---- Build items per viewport ----\n const viewportsData: Record<string, { items: NormalizedProductItem[] }> = {};\n\n for (const vp of viewports) {\n const entries = entriesByViewport[vp.id];\n if (!entries) {\n viewportsData[vp.id] = { items: [] };\n continue;\n }\n\n viewportsData[vp.id] = { items: buildItemsFromEntries(entries, vp.fills, sampleProductColorRgba) };\n }\n\n // ---- Build metadata from all section entries ----\n const allEntries = Object.values(entriesByViewport).flat();\n // Caller-supplied metadata first: entries only cover cut-bearing products, but\n // include filtering must classify products whose geometry never crosses the cut\n // (furniture below a plan cut) - renderTemplateSheet supplies the full map.\n const metadataById = { ...(options?.metadataById ?? {}), ...buildMetadataMap(allEntries) };\n\n // ---- Assemble layout data ----\n const layoutData = {\n viewports: viewportsData\n };\n\n // ---- Render ----\n const result = await renderLayout(template, layoutData, models, {\n allowRemoteResources: template.allowRemoteResources ?? options?.allowRemoteResources,\n qrContext: options?.qrContext,\n metadataById\n });\n\n return {\n svg: result.svg,\n warnings: [...warnings, ...result.warnings]\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Build NormalizedProductItems from per-product entries.\n *\n * Shared between the standard path (plane sections) and the models path\n * (slab sections / pre-built models with productEntries).\n */\nfunction buildItemsFromEntries(\n entries: Array<{ product: unknown; polylines: unknown }>,\n fills: FillRule[] | undefined,\n sampleColor: ((product: unknown) => SampledColor | undefined) | undefined\n): NormalizedProductItem[] {\n return entries.map((entry) => {\n const product = entry.product;\n const propertySets = normalizePropertySets(product);\n const gid = getProp(product, 'gid') ?? getProp(product, 'id');\n const center = computePolylinesCenter(entry);\n const name = getProp(product, 'name') as string | undefined;\n const displayName = typeof name === 'string' && name.trim().length ? name.trim() : gid ? String(gid) : '-';\n const item: NormalizedProductItem = {\n gid: gid ? String(gid) : undefined,\n ifcClass: resolveIfcClassName(product),\n ifcClassId: resolveIfcClassId(product),\n propertySets,\n productId: gid ? String(gid) : '-',\n productName: displayName,\n area: null,\n x: center.x,\n z: center.z,\n text: displayName,\n polylines: entry.polylines as Array<{ points?: Array<{ x: number; z: number }> }>\n };\n\n if (fills?.length && sampleColor) {\n const color = resolveFillColor(item, product, fills, sampleColor);\n if (color) {\n item.color = color;\n }\n }\n\n return item;\n });\n}\n\n/**\n * Estimate label font size in model units from the template body style and the\n * approximate viewport-to-world scale ratio.\n */\nfunction estimateLabelFontSize(\n template: LayoutTemplate,\n vp: ViewportElement,\n perProduct: SectionResult['perProduct']\n): number {\n const templateFontSizeMm = template.styles?.body?.fontSizeMm ?? 3.5;\n\n // Quick world-bounds scan\n let minX = Infinity;\n let maxX = -Infinity;\n let minZ = Infinity;\n let maxZ = -Infinity;\n for (const entry of perProduct) {\n if (!entry.polylines) continue;\n for (const polyline of entry.polylines) {\n if (!polyline.points) continue;\n for (const pt of polyline.points) {\n const { x: px, z: pz } = readXZ(pt);\n if (px !== null && pz !== null) {\n if (px < minX) minX = px;\n if (px > maxX) maxX = px;\n if (pz < minZ) minZ = pz;\n if (pz > maxZ) maxZ = pz;\n }\n }\n }\n }\n\n const worldWidth = maxX - minX;\n const worldHeight = maxZ - minZ;\n if (!Number.isFinite(worldWidth) || worldWidth <= 0 || !Number.isFinite(worldHeight) || worldHeight <= 0) {\n return templateFontSizeMm; // Fallback: 1:1 estimate\n }\n\n // Approximate mm-per-model-unit from the viewport dimensions\n const scaleX = vp.widthMm / worldWidth;\n const scaleY = vp.heightMm / worldHeight;\n const mmPerModelUnit = Math.min(scaleX, scaleY);\n\n if (!Number.isFinite(mmPerModelUnit) || mmPerModelUnit <= 0) {\n return templateFontSizeMm;\n }\n\n return templateFontSizeMm / mmPerModelUnit;\n}\n\n// ---------------------------------------------------------------------------\n// Label style defaults\n// ---------------------------------------------------------------------------\n\n/** Built-in label style defaults when template body styles are partially set. */\nexport const BUILTIN_LABEL_DEFAULTS = {\n fill: '#374151',\n fontFamily: '\"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif',\n fontWeight: '300'\n} as const;\n\n// ---------------------------------------------------------------------------\n// Line style defaults & resolution\n// ---------------------------------------------------------------------------\n\n/** Built-in defaults when neither lineStyles nor lineWidthMm/lineColor are set. */\nexport const BUILTIN_LINE_DEFAULTS: Required<ViewportLineStyle> = {\n widthMm: 0.35, // Reasonable default: 0.35mm line width (was 0.01mm - too thin!)\n color: '#1f2937',\n dash: '',\n enabled: true\n};\n\nexport const BUILTIN_HIDDEN_DEFAULTS: Required<ViewportLineStyle> = {\n widthMm: 0.005,\n color: '#9ca3af',\n dash: '4 2',\n enabled: true\n};\n\nexport type LineCategory =\n | 'cutLines'\n | 'visibleLines'\n | 'hiddenLines'\n | 'silhouettesVisible'\n | 'silhouettesHidden'\n | 'featureEdges'\n | 'stairArrows'\n | 'outlineVisible'\n | 'outlineHidden';\n\n/** Category-specific built-in defaults (merged over the global default). */\nexport const CATEGORY_BUILTIN_OVERRIDES: Partial<Record<LineCategory, Partial<ViewportLineStyle>>> = {\n cutLines: { widthMm: 0.02 },\n hiddenLines: BUILTIN_HIDDEN_DEFAULTS,\n // Occluded silhouettes follow the hidden drafting convention out of the box.\n silhouettesHidden: BUILTIN_HIDDEN_DEFAULTS,\n outlineHidden: BUILTIN_HIDDEN_DEFAULTS\n};\n\n/** Visibility group of each category; keyed to the `defaultVisibleLines`/`defaultHiddenLines` template keys. `stairArrows` is annotation, not geometry, and belongs to neither group. */\nconst CATEGORY_VISIBILITY_GROUP: Partial<Record<LineCategory, 'defaultVisibleLines' | 'defaultHiddenLines'>> = {\n cutLines: 'defaultVisibleLines',\n visibleLines: 'defaultVisibleLines',\n silhouettesVisible: 'defaultVisibleLines',\n silhouettesHidden: 'defaultHiddenLines',\n featureEdges: 'defaultVisibleLines',\n outlineVisible: 'defaultVisibleLines',\n hiddenLines: 'defaultHiddenLines',\n outlineHidden: 'defaultHiddenLines'\n};\n\n/**\n * Resolve the effective line style for one category — the single source of\n * truth used by both the slab plan path (slabModelView) and the drawing path.\n *\n * Cascade, weakest first: built-in defaults → `default` → built-in category\n * defaults → `defaultVisibleLines`/`defaultHiddenLines` (the category's\n * visibility group) → the category's own key.\n */\nexport function resolveCategoryLineStyle(\n category: LineCategory,\n lineStyles?: ViewportLineStyles\n): Required<ViewportLineStyle> {\n const group = CATEGORY_VISIBILITY_GROUP[category];\n return {\n ...BUILTIN_LINE_DEFAULTS,\n ...(lineStyles?.default ?? {}),\n ...(CATEGORY_BUILTIN_OVERRIDES[category] ?? {}),\n ...((group && lineStyles?.[group]) ?? {}),\n ...(lineStyles?.[category] ?? {})\n } as Required<ViewportLineStyle>;\n}\n\nexport interface ResolvedLineStyles {\n default: Required<ViewportLineStyle>;\n byCategory: Record<LineCategory, Required<ViewportLineStyle>>;\n}\n\n/**\n * Resolve the effective line style for every category via\n * {@link resolveCategoryLineStyle} (shared with the slab plan path).\n */\nfunction resolveLineStyles(vp: ViewportElement): ResolvedLineStyles {\n const ls = vp.lineStyles;\n\n const base: Required<ViewportLineStyle> = {\n ...BUILTIN_LINE_DEFAULTS,\n ...(ls?.default ?? {})\n } as Required<ViewportLineStyle>;\n\n const categories: LineCategory[] = [\n 'cutLines',\n 'visibleLines',\n 'hiddenLines',\n 'silhouettesVisible',\n 'silhouettesHidden',\n 'featureEdges',\n 'stairArrows',\n 'outlineVisible',\n 'outlineHidden'\n ];\n\n const byCategory = {} as Record<LineCategory, Required<ViewportLineStyle>>;\n for (const cat of categories) {\n byCategory[cat] = resolveCategoryLineStyle(cat, ls);\n }\n\n return { default: base, byCategory };\n}\n\n// ---------------------------------------------------------------------------\n// Fill rule helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve fill color for an item based on viewport fill rules.\n * Returns an RGBA tuple [r, g, b, a] (0–255) or undefined if no rule matches.\n */\nexport function resolveFillColor(\n item: NormalizedProductItem,\n product: unknown,\n fills: FillRule[] | undefined,\n sampleColor: (product: unknown) => SampledColor | undefined\n): [number, number, number, number] | undefined {\n if (!fills?.length) return undefined;\n for (const rule of fills) {\n if (!matchesFilter(item, rule.class, rule.where)) continue;\n if (rule.fill === 'sample') {\n const sampled = sampleColor(product);\n if (sampled) {\n return [\n Math.round(sampled.color.r * 255),\n Math.round(sampled.color.g * 255),\n Math.round(sampled.color.b * 255),\n Math.round(sampled.alpha * 255)\n ];\n }\n } else {\n return parseHexToRgba(rule.fill);\n }\n break; // first matching rule wins\n }\n return undefined;\n}\n\n/**\n * Parse a CSS hex color string to an RGBA tuple (0–255).\n */\nfunction parseHexToRgba(hex: string): [number, number, number, number] {\n const h = hex.replace(/^#/, '');\n if (h.length === 3) {\n return [parseInt(h[0] + h[0], 16), parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16), 255];\n }\n if (h.length === 6) {\n return [parseInt(h.substring(0, 2), 16), parseInt(h.substring(2, 4), 16), parseInt(h.substring(4, 6), 16), 255];\n }\n if (h.length === 8) {\n return [\n parseInt(h.substring(0, 2), 16),\n parseInt(h.substring(2, 4), 16),\n parseInt(h.substring(4, 6), 16),\n parseInt(h.substring(6, 8), 16)\n ];\n }\n // Fallback: opaque black\n return [0, 0, 0, 255];\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import { RenderLayoutOptions } from './options';
2
+ import { LayoutTemplate, ModelContext, LayoutData, ComputedViewportScale } from './types';
3
+ /** Result of {@link renderLayout}: the final SVG markup, per-viewport scale info, and any warnings. */
4
+ export interface RenderLayoutResult {
5
+ /** Complete SVG markup string ready for embedding or PDF conversion. */
6
+ svg: string;
7
+ /** Per-viewport computed scale information (denominator, mm-per-model-unit, fit status). */
8
+ viewportScales: Record<string, ComputedViewportScale>;
9
+ /** Non-fatal issues encountered during rendering (missing resources, unresolved styles, etc.). */
10
+ warnings: string[];
11
+ }
12
+ /**
13
+ * Render a layout template to SVG.
14
+ *
15
+ * This is the low-level rendering function. For most use cases, prefer {@link renderDrawing}
16
+ * which handles all data wiring automatically.
17
+ *
18
+ * @param template - Validated layout template (page, elements, styles, presets).
19
+ * @param data - Layout data with viewport items, template variables, and legend rows.
20
+ * @param models - Per-viewport SVG model views (geometry + transforms).
21
+ * @param options - Optional rendering settings (remote resources, scale overrides, metadata).
22
+ * @returns SVG markup string, computed viewport scales, and any warnings.
23
+ */
24
+ export declare function renderLayout(template: LayoutTemplate, data: LayoutData, models: ModelContext, options?: RenderLayoutOptions): Promise<RenderLayoutResult>;
25
+ //# sourceMappingURL=renderLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderLayout.d.ts","sourceRoot":"/","sources":["layout/renderLayout.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAuBhD,OAAO,EAgBH,cAAc,EACd,YAAY,EACZ,UAAU,EAEV,qBAAqB,EACxB,MAAM,SAAS,CAAC;AAIjB,uGAAuG;AACvG,MAAM,WAAW,kBAAkB;IAC/B,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,4FAA4F;IAC5F,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IACtD,kGAAkG;IAClG,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAC9B,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,mBAAmB,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CAgZ7B"}