@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,100 +1,40 @@
1
1
  import defaultTemplateJson from '../templates/default-template.json';
2
- import { pointInPolygon, filterItemsInPolygons, filterEntriesByClip, offsetPolygons, projectPolygonsToLocal } from './clipUtils';
3
- import { uniquePositiveDenoms } from './utils';
4
- import { validateLayoutTemplate as validateLayoutTemplateExternal } from './validation';
5
- import { renderViewportElement } from './viewportRender';
6
- import { prepareViewports } from './viewportPrep';
7
- import { renderLabels } from './labelsRender';
8
- import { renderQrElements } from './qrRender';
9
- import { applyLegendFilter, buildLegendGroup } from './legendRender';
10
- import { resolvePresets } from './presets';
11
- import { pushWarning } from './warnings';
12
- import { escapeAttr, escapeText, interpolate, getPathValue } from './renderUtils';
13
- import { validateSectionState } from './sectionValidation';
14
- import { ELEMENT_KIND_VIEWPORT, ELEMENT_KIND_TEXT, ELEMENT_KIND_IMAGE, ELEMENT_KIND_LINE, ELEMENT_KIND_RECT, ELEMENT_KIND_CIRCLE, ELEMENT_KIND_POLYLINE, ELEMENT_KIND_POLYGON, ELEMENT_KIND_SCALE_BAR, ELEMENT_KIND_QR, ELEMENT_KIND_LABELS, ELEMENT_KIND_LEGEND, ELEMENT_KIND_GROUP } from './elementKinds';
15
- import { isRecord, getProp } from './typeGuards';
16
2
  /**
17
- * Validate template, layout data, and section state in a single call.
18
- * Combines `validateLayoutTemplate`, `validateLayoutData`, and `validateSectionState`.
3
+ * High-level facade: renders a complete drawing from a template + section
4
+ * geometry or pre-built models. Start here.
19
5
  */
20
- export const preflight = (template, data, expectedViewportIds, sectionState) => {
21
- const templateValidation = validateLayoutTemplate(template);
22
- const dataValidation = validateLayoutData(data, expectedViewportIds);
23
- const sectionValidation = sectionState ? validateSectionState(sectionState) : { ok: true };
24
- const ok = templateValidation.ok && dataValidation.ok && sectionValidation.ok;
25
- if (ok)
26
- return { ok: true };
27
- return {
28
- ok: false,
29
- templateErrors: templateValidation.ok ? undefined : templateValidation.errors,
30
- dataErrors: dataValidation.ok ? undefined : dataValidation.errors,
31
- sectionErrors: sectionValidation.ok ? undefined : sectionValidation.errors
32
- };
33
- };
6
+ export { renderDrawing, resolveFillColor, BUILTIN_LINE_DEFAULTS, BUILTIN_HIDDEN_DEFAULTS, BUILTIN_LABEL_DEFAULTS, CATEGORY_BUILTIN_OVERRIDES } from './renderDrawing';
7
+ /** Low-level template renderer (prefer {@link renderDrawing}). */
8
+ export { renderLayout } from './renderLayout';
34
9
  /**
35
- * Validate layout data before rendering to catch missing propertySets or items arrays.
10
+ * Full orchestration: products + template rendered sheet. Resolves each
11
+ * viewport's section definition, computes SlabSection/plane sections with a
12
+ * def-keyed cache, and renders. The one-call path for template consumers.
36
13
  */
37
- export const validateLayoutData = (data, expectedViewportIds) => {
38
- const errors = [];
39
- if (!isRecord(data)) {
40
- return { ok: false, errors: ['layout data must be an object'] };
41
- }
42
- expectedViewportIds.forEach((vpId) => {
43
- const viewports = getProp(data, 'viewports');
44
- const viewport = isRecord(viewports) ? getProp(viewports, vpId) : undefined;
45
- const items = getProp(viewport, 'items');
46
- if (!Array.isArray(items)) {
47
- errors.push(`viewports.${vpId}.items is required and must be an array.`);
48
- return;
49
- }
50
- items.forEach((it, idx) => {
51
- if (!isRecord(it)) {
52
- errors.push(`viewports.${vpId}.items[${idx}] must be an object.`);
53
- return;
54
- }
55
- const sets = getProp(it, 'propertySets');
56
- if (!isRecord(sets)) {
57
- errors.push(`viewports.${vpId}.items[${idx}] is missing propertySets.<Set>.<Prop>.value.`);
58
- return;
59
- }
60
- Object.entries(sets).forEach(([setName, props]) => {
61
- if (!isRecord(props)) {
62
- errors.push(`viewports.${vpId}.items[${idx}].propertySets.${setName} must be an object.`);
63
- return;
64
- }
65
- Object.entries(props).forEach(([propName, payload]) => {
66
- if (!isRecord(payload) || !('value' in payload)) {
67
- errors.push(`viewports.${vpId}.items[${idx}].propertySets.${setName}.${propName} must include a value (propertySets.<Set>.<Prop>.value).`);
68
- }
69
- });
70
- });
71
- });
72
- });
73
- return errors.length ? { ok: false, errors } : { ok: true };
74
- };
75
- const validateLayoutTemplate = validateLayoutTemplateExternal;
76
- export { pointInPolygon, filterItemsInPolygons, filterEntriesByClip, offsetPolygons, projectPolygonsToLocal };
14
+ export { renderTemplateSheet, SectionCache } from './renderTemplateSheet';
15
+ /** Pre-render validation: template + layout data + section state in one call. */
16
+ export { preflight, validateLayoutData } from './preflight';
17
+ /** Validate a layout template JSON structure. Returns `{ ok } | { ok: false, errors }`. */
18
+ export { validateLayoutTemplate } from './validation';
19
+ /** @deprecated Use {@link validateLayoutTemplate} — same function, historical alias. */
20
+ export { validateLayoutTemplate as validateLayoutTemplateExternal } from './validation';
77
21
  /** Normalize IFC class names to lowercase for consistent matching. */
78
22
  export { normalizeClass } from './filters';
79
23
  /** Test whether an item matches a class + where filter (used by clip, highlight, labels, legend). */
80
- export { matchesV2Filter } from './filters';
24
+ export { matchesFilter, matchesV2Filter } from './filters';
81
25
  /** Metadata utilities: build lookup maps, resolve IFC class includes, and merge metadata sources. */
82
26
  export { buildMetadataMap, normalizePropertySets, resolveIfcClassName, resolveIfcClassId, resolveIncludeClasses, resolveAllowedClasses, resolveMeta } from './metadata';
83
- /** Validate a layout template JSON structure. Returns `{ ok, errors }` or `{ ok, template }`. */
84
- export { validateLayoutTemplate as validateLayoutTemplateExternal } from './validation';
85
27
  /** Layout utility functions: scale denominators, label sizing, color parsing, and line style application. */
86
28
  export { uniquePositiveDenoms, computeLabelFontSize, colorFromString, applyLineStyleToPaths } from './utils';
29
+ /** SlabSection → SvgModelView adapter (per-product rawPaths for gid features) + productEntries builder. */
30
+ export { slabSectionToModelView, slabPerProductToEntries } from './slabModelView';
31
+ /** The template format's per-viewport section contract: one reader/writer for cutHeight/planeNormal/planePosition/depth. */
32
+ export { resolveViewportSection, writeViewportSection, sectionDefKey, isVariableBinding, sectionUsesVariables, DEFAULT_SECTION_DEPTH } from './sectionDefs';
87
33
  /** Property resolution: traverse nested objects with dot-path strings like `"BIP.spacename"`. */
88
34
  export { getPathValue, resolveProperty, resolvePath, matchesPropertyFilter } from './renderUtils';
89
- const SVG_NS = 'http://www.w3.org/2000/svg';
90
35
  /** Build legend table rows from product items with property formatting. */
91
36
  export { buildLegendRows } from './legendRows';
92
37
  export { normalizeProductEntry, buildViewportItems, sectionStateToModelView } from './itemBuilders';
93
- /**
94
- * High-level facade: renders a complete drawing from a template + section geometry.
95
- * Internalizes all data wiring (items, colors, labels, metadata) that `renderLayout` requires manually.
96
- */
97
- export { renderDrawing, resolveFillColor, BUILTIN_LINE_DEFAULTS, BUILTIN_HIDDEN_DEFAULTS, BUILTIN_LABEL_DEFAULTS, CATEGORY_BUILTIN_OVERRIDES, setRenderLayoutBinding } from './renderDrawing';
98
38
  export { EXAMPLE_BUILDING_CLASSES } from './presets';
99
39
  export { analyzeTemplate } from './analyzeTemplate';
100
40
  /** Drawing export utilities: convert SVG markup to PDF or SVG files. */
@@ -103,261 +43,4 @@ export { exportToPdf, exportToSvg } from './exportDrawing';
103
43
  export { getTemplateVariables, getTemplateDefaults, resolveTemplateValues, applyTemplateVariables } from './templateVariables';
104
44
  /** Built-in default layout template (A3 landscape with a single viewport). */
105
45
  export const DEFAULT_LAYOUT_TEMPLATE = defaultTemplateJson;
106
- const dashArray = (dashed) => (dashed ? ' stroke-dasharray="4 2"' : '');
107
- const formatNumber = (value) => {
108
- if (!Number.isFinite(value))
109
- return 0;
110
- return Number.parseFloat(value.toFixed(3));
111
- };
112
- /**
113
- * Render a layout template to SVG.
114
- *
115
- * This is the low-level rendering function. For most use cases, prefer {@link renderDrawing}
116
- * which handles all data wiring automatically.
117
- *
118
- * @param template - Validated layout template (page, elements, styles, presets).
119
- * @param data - Layout data with viewport items, template variables, and legend rows.
120
- * @param models - Per-viewport SVG model views (geometry + transforms).
121
- * @param options - Optional rendering settings (remote assets, scale overrides, metadata).
122
- * @returns SVG markup string, computed viewport scales, and any warnings.
123
- */
124
- export async function renderLayout(template, data, models, options) {
125
- const presets = resolvePresets(template?.presets);
126
- const validation = validateLayoutTemplate(template, {
127
- allowRemoteAssets: options?.allowRemoteAssets,
128
- allowDataUrls: options?.allowDataUrls,
129
- maxViewports: options?.maxViewports
130
- });
131
- if (!validation.ok) {
132
- throw new Error(`Template validation failed: ${validation.errors.map((e) => `${e.path}: ${e.message}`).join('; ')}`);
133
- }
134
- const preferredScales = uniquePositiveDenoms(options?.scaleDenominators);
135
- const page = template.page;
136
- const warnings = [];
137
- // Flatten all elements to collect viewports, qrs, labels, legends regardless of grouping
138
- const allElements = [];
139
- const collectElements = (elements) => {
140
- elements.forEach((el) => {
141
- allElements.push(el);
142
- if (el.kind === ELEMENT_KIND_GROUP) {
143
- collectElements(el.children);
144
- }
145
- });
146
- };
147
- collectElements(template.elements);
148
- const viewports = allElements.filter((el) => el.kind === ELEMENT_KIND_VIEWPORT);
149
- const dataValidation = validateLayoutData(data, viewports.map((v) => v.id));
150
- if (!dataValidation.ok) {
151
- throw new Error(`Layout data validation failed: ${dataValidation.errors.join('; ')}`);
152
- }
153
- const { viewportScales, viewportClipPolygonsLocal, viewportClipPolygonsWorld, viewportClipBounds, viewportClipLabels, viewportClipStyles,
154
- // viewportClipWhere intentionally omitted — not needed for rendering
155
- viewportHighlight, filteredModelsByViewport } = prepareViewports(viewports, models, data, preferredScales, presets, {
156
- metadataById: options?.metadataById,
157
- warnings
158
- });
159
- const viewportData = {};
160
- Object.entries(viewportScales).forEach(([id, scale]) => {
161
- viewportData[id] = {
162
- scaleDenom: scale.denom,
163
- scaleText: `1:${scale.denom}`,
164
- mmPerModelUnit: scale.mmPerModelUnit,
165
- fits: scale.fits
166
- };
167
- });
168
- const dataWithViewport = { ...data, viewport: viewportData };
169
- const sorted = [...template.elements].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
170
- const parts = [];
171
- parts.push(`<svg xmlns="${SVG_NS}" width="${page.widthMm}mm" height="${page.heightMm}mm" viewBox="0 0 ${page.widthMm} ${page.heightMm}">`);
172
- if (page.backgroundColor) {
173
- parts.push(`<rect x="0" y="0" width="${page.widthMm}" height="${page.heightMm}" fill="${escapeAttr(page.backgroundColor)}"/>`);
174
- }
175
- const assetMap = new Map();
176
- (template.assets ?? []).forEach((asset) => assetMap.set(asset.id, asset));
177
- const qrElements = allElements.filter((el) => el.kind === ELEMENT_KIND_QR);
178
- const labelElements = allElements.filter((el) => el.kind === ELEMENT_KIND_LABELS);
179
- const viewportRenderInfo = {};
180
- // Pre-process legends to collect color maps (needed before viewport rendering)
181
- const legendColorMaps = new Map(); // viewport ID -> color map
182
- const legendElements = allElements.filter((el) => el.kind === ELEMENT_KIND_LEGEND);
183
- legendElements.forEach((legend) => {
184
- if (legend.assignColors?.enabled && legend.groupBy) {
185
- const legendItems = getPathValue(data, `viewports.${legend.viewport}.items`);
186
- const rawLegendItems = Array.isArray(legendItems) ? legendItems : [];
187
- const legendDataFiltered = applyLegendFilter(rawLegendItems, legend, viewports, viewportClipPolygonsLocal, viewportRenderInfo);
188
- if (legendDataFiltered && legendDataFiltered.length) {
189
- const vpEl = viewports.find((v) => v.id === legend.viewport);
190
- const fillOpacity = vpEl?.fills?.length ? (vpEl.fills[0].fillOpacity ?? 0.35) : undefined;
191
- const result = buildLegendGroup(legend, template.styles, page, page.marginMm, legendDataFiltered, fillOpacity);
192
- if (result?.colorMap) {
193
- legendColorMaps.set(legend.viewport, result.colorMap);
194
- console.log(`[LEGEND PRE-PROCESS] Stored color map for viewport "${legend.viewport}": ${result.colorMap.size} colors`);
195
- }
196
- }
197
- }
198
- });
199
- sorted.forEach((el) => {
200
- if (el.kind === ELEMENT_KIND_RECT) {
201
- const r = el;
202
- parts.push(`<rect x="${r.xMm}" y="${r.yMm}" width="${r.widthMm}" height="${r.heightMm}"` +
203
- (r.stroke ? ` stroke="${escapeAttr(r.stroke)}"` : '') +
204
- (r.strokeWidthMm ? ` stroke-width="${r.strokeWidthMm}"` : '') +
205
- (r.fill ? ` fill="${escapeAttr(r.fill)}"` : ' fill="none"') +
206
- dashArray(r.dashed) +
207
- ` />`);
208
- }
209
- else if (el.kind === ELEMENT_KIND_LINE) {
210
- const l = el;
211
- parts.push(`<line x1="${l.x1Mm}" y1="${l.y1Mm}" x2="${l.x2Mm}" y2="${l.y2Mm}"` +
212
- (l.stroke ? ` stroke="${escapeAttr(l.stroke)}"` : '') +
213
- (l.strokeWidthMm ? ` stroke-width="${l.strokeWidthMm}"` : '') +
214
- dashArray(l.dashed) +
215
- ` />`);
216
- }
217
- else if (el.kind === ELEMENT_KIND_CIRCLE) {
218
- const c = el;
219
- parts.push(`<circle cx="${c.cxMm}" cy="${c.cyMm}" r="${c.rMm}"` +
220
- (c.stroke ? ` stroke="${escapeAttr(c.stroke)}"` : '') +
221
- (c.strokeWidthMm ? ` stroke-width="${c.strokeWidthMm}"` : '') +
222
- (c.fill ? ` fill="${escapeAttr(c.fill)}"` : ' fill="none"') +
223
- dashArray(c.dashed) +
224
- ` />`);
225
- }
226
- else if (el.kind === ELEMENT_KIND_POLYLINE) {
227
- const pl = el;
228
- const pointsStr = pl.points.map((p) => `${formatNumber(p.xMm)},${formatNumber(p.yMm)}`).join(' ');
229
- parts.push(`<polyline points="${pointsStr}" stroke="${escapeAttr(pl.stroke)}" stroke-width="${pl.strokeWidthMm}" fill="none"${dashArray(pl.dashed)} />`);
230
- }
231
- else if (el.kind === ELEMENT_KIND_POLYGON) {
232
- const pg = el;
233
- const pointsStr = pg.points.map((p) => `${formatNumber(p.xMm)},${formatNumber(p.yMm)}`).join(' ');
234
- parts.push(`<polygon points="${pointsStr}"` +
235
- (pg.stroke ? ` stroke="${escapeAttr(pg.stroke)}"` : '') +
236
- (pg.strokeWidthMm ? ` stroke-width="${pg.strokeWidthMm}"` : '') +
237
- (pg.fill ? ` fill="${escapeAttr(pg.fill)}"` : ' fill="none"') +
238
- dashArray(pg.dashed) +
239
- ` />`);
240
- }
241
- else if (el.kind === ELEMENT_KIND_TEXT) {
242
- const tEl = el;
243
- const styleName = tEl.textStyle;
244
- const style = styleName ? template.styles?.[styleName] : undefined;
245
- if (!style) {
246
- pushWarning(warnings, `Missing text style '${styleName ?? '(none)'}' for element '${tEl.id}'.`);
247
- return;
248
- }
249
- const text = interpolate(tEl.text, dataWithViewport, warnings);
250
- const anchor = tEl.align === 'center' ? 'middle' : tEl.align === 'right' ? 'end' : 'start';
251
- parts.push(`<text x="${tEl.xMm}" y="${tEl.yMm}" font-family="${escapeAttr(style.fontFamily)}" font-size="${style.fontSizeMm}"` +
252
- (style.fontWeight ? ` font-weight="${escapeAttr(style.fontWeight)}"` : '') +
253
- (style.color ? ` fill="${escapeAttr(style.color)}"` : '') +
254
- (style.letterSpacingMm !== undefined ? ` letter-spacing="${style.letterSpacingMm}"` : '') +
255
- (style.lineHeightMm !== undefined ? ` line-height="${style.lineHeightMm}"` : '') +
256
- ` text-anchor="${anchor}">${escapeText(text)}</text>`);
257
- }
258
- else if (el.kind === ELEMENT_KIND_IMAGE) {
259
- const img = el;
260
- const asset = assetMap.get(img.assetId);
261
- if (!asset) {
262
- pushWarning(warnings, `Missing asset '${img.assetId}' for image '${img.id}'.`);
263
- return;
264
- }
265
- if (asset.src) {
266
- parts.push(`<image href="${escapeAttr(asset.src)}" x="${img.xMm}" y="${img.yMm}" width="${img.widthMm}" height="${img.heightMm}"` +
267
- (img.fit === 'cover'
268
- ? ' preserveAspectRatio="xMidYMid slice"'
269
- : img.fit === 'stretch'
270
- ? ' preserveAspectRatio="none"'
271
- : ' preserveAspectRatio="xMidYMid meet"') +
272
- ` />`);
273
- }
274
- else if (asset.content) {
275
- parts.push(`<g transform="translate(${img.xMm} ${img.yMm}) scale(${img.widthMm} ${img.heightMm})">${asset.content}</g>`);
276
- }
277
- }
278
- else if (el.kind === ELEMENT_KIND_SCALE_BAR) {
279
- const sb = el;
280
- // Scale bar declares which viewport it binds to via `viewport`
281
- const boundViewport = sb.viewport ?? viewports[0]?.id ?? null;
282
- if (!boundViewport) {
283
- pushWarning(warnings, `Scale bar '${sb.id}' is not bound to a viewport.`);
284
- return;
285
- }
286
- const scaleInfo = viewportScales[boundViewport];
287
- if (!scaleInfo) {
288
- pushWarning(warnings, `Viewport '${boundViewport}' missing scale info for scale bar '${sb.id}'.`);
289
- return;
290
- }
291
- const lengthMm = sb.maxLengthMm;
292
- const segments = sb.segments ?? 10;
293
- const segmentMm = lengthMm / segments;
294
- const metersPerMm = 1 / scaleInfo.mmPerModelUnit;
295
- const segmentMeters = segmentMm * metersPerMm;
296
- const labelsEvery = Math.max(1, sb.labelEvery ?? 1);
297
- const unitSuffix = sb.unit ? ` ${sb.unit}` : ' m';
298
- const barParts = [];
299
- const sbStroke = sb.stroke ?? '#000';
300
- const sbStrokeWidth = sb.strokeWidthMm ?? 0.5;
301
- for (let i = 0; i <= segments; i++) {
302
- const x = sb.xMm + segmentMm * i;
303
- const isLabel = i % labelsEvery === 0;
304
- const height = isLabel ? 4 : 3;
305
- barParts.push(`<line x1="${x}" y1="${sb.yMm}" x2="${x}" y2="${sb.yMm - height}" stroke="${escapeAttr(sbStroke)}" stroke-width="${sbStrokeWidth}" />`);
306
- if (isLabel && sb.textStyle) {
307
- const style = template.styles?.[sb.textStyle];
308
- if (style) {
309
- const labelValue = (segmentMeters * i).toFixed(2).replace(/\.?0+$/, '');
310
- barParts.push(`<text x="${x}" y="${sb.yMm + style.fontSizeMm + 1}" font-family="${escapeAttr(style.fontFamily)}" font-size="${style.fontSizeMm}" text-anchor="middle">${escapeText(labelValue + unitSuffix)}</text>`);
311
- }
312
- }
313
- }
314
- barParts.push(`<line x1="${sb.xMm}" y1="${sb.yMm}" x2="${sb.xMm + lengthMm}" y2="${sb.yMm}" stroke="${escapeAttr(sbStroke)}" stroke-width="${sbStrokeWidth}" />`);
315
- parts.push(barParts.join(''));
316
- }
317
- else if (el.kind === ELEMENT_KIND_VIEWPORT) {
318
- const vp = el;
319
- const model = filteredModelsByViewport[vp.id] ?? models[vp.id] ?? null;
320
- if (!model) {
321
- pushWarning(warnings, `Viewport '${vp.id}' missing model.`);
322
- }
323
- const vpParts = renderViewportElement(vp, model, dataWithViewport, preferredScales, presets.scales, assetMap, viewportScales, viewportClipPolygonsLocal, viewportClipBounds, viewportClipLabels, viewportClipStyles, viewportHighlight, viewportRenderInfo, warnings, legendColorMaps);
324
- parts.push(...vpParts);
325
- }
326
- else if (el.kind === ELEMENT_KIND_LEGEND) {
327
- const legend = el;
328
- const legendItems = getPathValue(data, `viewports.${legend.viewport}.items`);
329
- const rawLegendItems = Array.isArray(legendItems) ? legendItems : [];
330
- const legendDataFiltered = applyLegendFilter(rawLegendItems, legend, viewports, viewportClipPolygonsLocal, viewportRenderInfo);
331
- if (!legendDataFiltered || !legendDataFiltered.length) {
332
- if (!rawLegendItems.length) {
333
- pushWarning(warnings, `Legend '${legend.id}' has no items from viewport '${legend.viewport}'.`);
334
- }
335
- else {
336
- pushWarning(warnings, `Legend '${legend.id}' filter matched 0 of ${rawLegendItems.length} items (class=${JSON.stringify(legend.class)}, where=${JSON.stringify(legend.where)}).`);
337
- }
338
- }
339
- const vpEl = viewports.find((v) => v.id === legend.viewport);
340
- // Derive swatch opacity from viewport fill rules (first matching rule's opacity)
341
- const fillOpacity = vpEl?.fills?.length ? (vpEl.fills[0].fillOpacity ?? 0.35) : undefined;
342
- const legendResult = buildLegendGroup(legend, template.styles, page, page.marginMm, legendDataFiltered || [], fillOpacity);
343
- if (legendResult) {
344
- parts.push(legendResult.svg);
345
- }
346
- }
347
- });
348
- if (labelElements.length) {
349
- const labelParts = renderLabels(labelElements, data, viewportRenderInfo, viewportClipLabels, viewportClipPolygonsLocal, viewportClipPolygonsWorld, template.styles ?? {}, dataWithViewport, warnings);
350
- parts.push(...labelParts);
351
- }
352
- if (qrElements.length) {
353
- const qrParts = await renderQrElements(qrElements, viewports, viewportScales, models, { qrContext: options?.qrContext }, dataWithViewport, warnings);
354
- parts.push(...qrParts);
355
- }
356
- parts.push('</svg>');
357
- return { svg: parts.join(''), viewportScales, warnings };
358
- }
359
- // Register renderLayout with renderDrawing to break the circular dependency.
360
- // This runs at module evaluation time — safe because renderLayout is defined above.
361
- import { setRenderLayoutBinding as _setBinding } from './renderDrawing';
362
- _setBinding(renderLayout);
363
46
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"/","sources":["layout/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SectionRenderState } from '@twinfinity/geometry';\nimport defaultTemplateJson from '../templates/default-template.json';\nimport {\n pointInPolygon,\n filterItemsInPolygons,\n filterEntriesByClip,\n offsetPolygons,\n projectPolygonsToLocal\n} from './clipUtils';\nimport { uniquePositiveDenoms } from './utils';\nimport { validateLayoutTemplate as validateLayoutTemplateExternal } from './validation';\nimport { renderViewportElement } from './viewportRender';\nimport { prepareViewports } from './viewportPrep';\nimport { renderLabels, ViewportRenderInfo } from './labelsRender';\nimport { renderQrElements } from './qrRender';\nimport { applyLegendFilter, buildLegendGroup } from './legendRender';\nimport { RenderLayoutOptions } from './options';\nimport { resolvePresets } from './presets';\nimport { pushWarning } from './warnings';\nimport { escapeAttr, escapeText, interpolate, getPathValue } from './renderUtils';\nimport { validateSectionState } from './sectionValidation';\nimport {\n ELEMENT_KIND_VIEWPORT,\n ELEMENT_KIND_TEXT,\n ELEMENT_KIND_IMAGE,\n ELEMENT_KIND_LINE,\n ELEMENT_KIND_RECT,\n ELEMENT_KIND_CIRCLE,\n ELEMENT_KIND_POLYLINE,\n ELEMENT_KIND_POLYGON,\n ELEMENT_KIND_SCALE_BAR,\n ELEMENT_KIND_QR,\n ELEMENT_KIND_LABELS,\n ELEMENT_KIND_LEGEND,\n ELEMENT_KIND_GROUP\n} from './elementKinds';\nimport {\n AssetSpec,\n LayoutElement,\n ViewportElement,\n TextElement,\n ImageElement,\n LineElement,\n RectElement,\n CircleElement,\n PolylineElement,\n PolygonElement,\n ScaleBarElement,\n QrElement,\n LabelsElement,\n LegendElement,\n GroupElement,\n LayoutTemplate,\n ModelContext,\n LayoutData,\n ValidationIssue,\n ComputedViewportScale\n} from './types';\n\nexport type LayoutDataValidationResult =\n | { ok: true }\n | {\n ok: false;\n errors: string[];\n };\n\nexport type PreflightResult =\n | { ok: true }\n | {\n ok: false;\n templateErrors?: ValidationIssue[];\n dataErrors?: string[];\n sectionErrors?: string[];\n };\n\nimport { isRecord, getProp } from './typeGuards';\n\n/**\n * Validate template, layout data, and section state in a single call.\n * Combines `validateLayoutTemplate`, `validateLayoutData`, and `validateSectionState`.\n */\nexport const preflight = (\n template: LayoutTemplate,\n data: unknown,\n expectedViewportIds: string[],\n sectionState?: SectionRenderState\n): PreflightResult => {\n const templateValidation = validateLayoutTemplate(template);\n const dataValidation = validateLayoutData(data, expectedViewportIds);\n const sectionValidation = sectionState ? validateSectionState(sectionState) : { ok: true as const };\n\n const ok = templateValidation.ok && dataValidation.ok && sectionValidation.ok;\n if (ok) return { ok: true };\n\n return {\n ok: false,\n templateErrors: templateValidation.ok ? undefined : templateValidation.errors,\n dataErrors: dataValidation.ok ? undefined : dataValidation.errors,\n sectionErrors: sectionValidation.ok ? undefined : sectionValidation.errors\n };\n};\n\n/**\n * Validate layout data before rendering to catch missing propertySets or items arrays.\n */\nexport const validateLayoutData = (data: unknown, expectedViewportIds: string[]): LayoutDataValidationResult => {\n const errors: string[] = [];\n if (!isRecord(data)) {\n return { ok: false, errors: ['layout data must be an object'] };\n }\n expectedViewportIds.forEach((vpId) => {\n const viewports = getProp(data, 'viewports');\n const viewport = isRecord(viewports) ? getProp(viewports, vpId) : undefined;\n const items = getProp(viewport, 'items');\n if (!Array.isArray(items)) {\n errors.push(`viewports.${vpId}.items is required and must be an array.`);\n return;\n }\n items.forEach((it: unknown, idx: number) => {\n if (!isRecord(it)) {\n errors.push(`viewports.${vpId}.items[${idx}] must be an object.`);\n return;\n }\n const sets = getProp(it, 'propertySets');\n if (!isRecord(sets)) {\n errors.push(`viewports.${vpId}.items[${idx}] is missing propertySets.<Set>.<Prop>.value.`);\n return;\n }\n Object.entries(sets).forEach(([setName, props]) => {\n if (!isRecord(props)) {\n errors.push(`viewports.${vpId}.items[${idx}].propertySets.${setName} must be an object.`);\n return;\n }\n Object.entries(props).forEach(([propName, payload]) => {\n if (!isRecord(payload) || !('value' in payload)) {\n errors.push(\n `viewports.${vpId}.items[${idx}].propertySets.${setName}.${propName} must include a value (propertySets.<Set>.<Prop>.value).`\n );\n }\n });\n });\n });\n });\n return errors.length ? { ok: false, errors } : { ok: true };\n};\n\nexport type {\n PageSpec,\n TextStyle,\n StyleMap,\n AssetSpec,\n LayoutElement,\n BaseElement,\n ViewportElement,\n TextElement,\n ImageElement,\n LineElement,\n RectElement,\n CircleElement,\n PolylineElement,\n PolygonElement,\n PointMm,\n ScaleBarElement,\n QrElement,\n LabelsElement,\n LegendElement,\n GroupElement,\n LayoutTemplate,\n LegendAnchor,\n LegendColumn,\n SvgModelView,\n ModelContext,\n LayoutData,\n LayoutDataViewports,\n ViewportItemsMap,\n PagePresetKey,\n PageOrientation,\n ValidationOptions,\n ValidationIssue,\n ValidationResult,\n ComputedViewportScale,\n NormalizedProductItem,\n SectionStateValidationResult,\n LabelStyle,\n WhereCondition,\n ClipConfig,\n HighlightConfig,\n ViewportLineStyle,\n ViewportLineStyles,\n SectionResult,\n FillRule,\n RenderDrawingInput,\n RenderDrawingResult,\n IncludeClassesFilter,\n ResolvedIncludeFilter,\n TemplateVariableDef,\n TemplateVariableInfo\n} from './types';\n\nexport type { RenderLayoutOptions } from './options';\n\nconst validateLayoutTemplate = validateLayoutTemplateExternal;\n\nexport { pointInPolygon, filterItemsInPolygons, filterEntriesByClip, offsetPolygons, projectPolygonsToLocal };\n/** Normalize IFC class names to lowercase for consistent matching. */\nexport { normalizeClass } from './filters';\n/** Test whether an item matches a class + where filter (used by clip, highlight, labels, legend). */\nexport { matchesV2Filter } from './filters';\n/** Metadata utilities: build lookup maps, resolve IFC class includes, and merge metadata sources. */\nexport {\n buildMetadataMap,\n normalizePropertySets,\n resolveIfcClassName,\n resolveIfcClassId,\n resolveIncludeClasses,\n resolveAllowedClasses,\n resolveMeta\n} from './metadata';\n/** Validate a layout template JSON structure. Returns `{ ok, errors }` or `{ ok, template }`. */\nexport { validateLayoutTemplate as validateLayoutTemplateExternal } from './validation';\n/** Layout utility functions: scale denominators, label sizing, color parsing, and line style application. */\nexport { uniquePositiveDenoms, computeLabelFontSize, colorFromString, applyLineStyleToPaths } from './utils';\n/** Property resolution: traverse nested objects with dot-path strings like `\"BIP.spacename\"`. */\nexport { getPathValue, resolveProperty, resolvePath, matchesPropertyFilter } from './renderUtils';\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\n/** Build legend table rows from product items with property formatting. */\nexport { buildLegendRows, type LegendRow, type LegendRowOptions } from './legendRows';\n/** Product normalization, viewport item builders, and section-state-to-SVG-model conversion. */\nexport type { ProductLike, BuildViewportItemsOptions } from './itemBuilders';\nexport { normalizeProductEntry, buildViewportItems, sectionStateToModelView } from './itemBuilders';\n/**\n * High-level facade: renders a complete drawing from a template + section geometry.\n * Internalizes all data wiring (items, colors, labels, metadata) that `renderLayout` requires manually.\n */\nexport {\n renderDrawing,\n resolveFillColor,\n BUILTIN_LINE_DEFAULTS,\n BUILTIN_HIDDEN_DEFAULTS,\n BUILTIN_LABEL_DEFAULTS,\n CATEGORY_BUILTIN_OVERRIDES,\n setRenderLayoutBinding\n} from './renderDrawing';\nexport type { ResolvedLineStyles } from './renderDrawing';\n\nexport { EXAMPLE_BUILDING_CLASSES } from './presets';\n\nexport { analyzeTemplate } from './analyzeTemplate';\nexport type { TemplateRequirements } from './analyzeTemplate';\n\n/** Drawing export utilities: convert SVG markup to PDF or SVG files. */\nexport { exportToPdf, exportToSvg } from './exportDrawing';\nexport type { ExportPdfOptions, ExportSvgOptions } from './exportDrawing';\n\n/** Template variable utilities: extract, validate, and resolve template parameters. */\nexport {\n getTemplateVariables,\n getTemplateDefaults,\n resolveTemplateValues,\n applyTemplateVariables\n} from './templateVariables';\n\n/** Result of {@link renderLayout}: the final SVG markup, per-viewport scale info, and any warnings. */\nexport interface RenderLayoutResult {\n /** Complete SVG markup string ready for embedding or PDF conversion. */\n svg: string;\n /** Per-viewport computed scale information (denominator, mm-per-model-unit, fit status). */\n viewportScales: Record<string, ComputedViewportScale>;\n /** Non-fatal issues encountered during rendering (missing assets, unresolved styles, etc.). */\n warnings: string[];\n}\n\n/** Built-in default layout template (A3 landscape with a single viewport). */\nexport const DEFAULT_LAYOUT_TEMPLATE = defaultTemplateJson as LayoutTemplate;\n\nconst dashArray = (dashed?: boolean): string => (dashed ? ' stroke-dasharray=\"4 2\"' : '');\n\nconst formatNumber = (value: number): number => {\n if (!Number.isFinite(value)) return 0;\n return Number.parseFloat(value.toFixed(3));\n};\n\n/**\n * Render a layout template to SVG.\n *\n * This is the low-level rendering function. For most use cases, prefer {@link renderDrawing}\n * which handles all data wiring automatically.\n *\n * @param template - Validated layout template (page, elements, styles, presets).\n * @param data - Layout data with viewport items, template variables, and legend rows.\n * @param models - Per-viewport SVG model views (geometry + transforms).\n * @param options - Optional rendering settings (remote assets, scale overrides, metadata).\n * @returns SVG markup string, computed viewport scales, and any warnings.\n */\nexport async function renderLayout(\n template: LayoutTemplate,\n data: LayoutData,\n models: ModelContext,\n options?: RenderLayoutOptions\n): Promise<RenderLayoutResult> {\n const presets = resolvePresets(template?.presets);\n\n const validation = validateLayoutTemplate(template, {\n allowRemoteAssets: options?.allowRemoteAssets,\n allowDataUrls: options?.allowDataUrls,\n maxViewports: options?.maxViewports\n });\n if (!validation.ok) {\n throw new Error(\n `Template validation failed: ${validation.errors.map((e: ValidationIssue) => `${e.path}: ${e.message}`).join('; ')}`\n );\n }\n\n const preferredScales = uniquePositiveDenoms(options?.scaleDenominators);\n const page = template.page;\n const warnings: string[] = [];\n\n // Flatten all elements to collect viewports, qrs, labels, legends regardless of grouping\n const allElements: LayoutElement[] = [];\n const collectElements = (elements: LayoutElement[]): void => {\n elements.forEach((el) => {\n allElements.push(el);\n if (el.kind === ELEMENT_KIND_GROUP) {\n collectElements((el as GroupElement).children);\n }\n });\n };\n collectElements(template.elements);\n\n const viewports = allElements.filter((el) => el.kind === ELEMENT_KIND_VIEWPORT) as ViewportElement[];\n\n const dataValidation = validateLayoutData(\n data,\n viewports.map((v) => v.id)\n );\n if (!dataValidation.ok) {\n throw new Error(`Layout data validation failed: ${dataValidation.errors.join('; ')}`);\n }\n\n const {\n viewportScales,\n viewportClipPolygonsLocal,\n viewportClipPolygonsWorld,\n viewportClipBounds,\n viewportClipLabels,\n viewportClipStyles,\n // viewportClipWhere intentionally omitted — not needed for rendering\n viewportHighlight,\n filteredModelsByViewport\n } = prepareViewports(viewports, models, data, preferredScales, presets, {\n metadataById: options?.metadataById,\n warnings\n });\n\n const viewportData: Record<\n string,\n {\n scaleDenom: number;\n scaleText: string;\n mmPerModelUnit: number;\n fits: boolean;\n }\n > = {};\n\n Object.entries(viewportScales).forEach(([id, scale]) => {\n viewportData[id] = {\n scaleDenom: scale.denom,\n scaleText: `1:${scale.denom}`,\n mmPerModelUnit: scale.mmPerModelUnit,\n fits: scale.fits\n };\n });\n\n const dataWithViewport = { ...data, viewport: viewportData };\n\n const sorted = [...template.elements].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));\n\n const parts: string[] = [];\n parts.push(\n `<svg xmlns=\"${SVG_NS}\" width=\"${page.widthMm}mm\" height=\"${page.heightMm}mm\" viewBox=\"0 0 ${page.widthMm} ${page.heightMm}\">`\n );\n\n if (page.backgroundColor) {\n parts.push(\n `<rect x=\"0\" y=\"0\" width=\"${page.widthMm}\" height=\"${page.heightMm}\" fill=\"${escapeAttr(page.backgroundColor)}\"/>`\n );\n }\n\n const assetMap = new Map<string, AssetSpec>();\n (template.assets ?? []).forEach((asset) => assetMap.set(asset.id, asset));\n\n const qrElements = allElements.filter((el) => el.kind === ELEMENT_KIND_QR) as QrElement[];\n const labelElements = allElements.filter((el) => el.kind === ELEMENT_KIND_LABELS) as LabelsElement[];\n const viewportRenderInfo: Record<string, ViewportRenderInfo> = {};\n\n // Pre-process legends to collect color maps (needed before viewport rendering)\n const legendColorMaps = new Map<string, Map<string, string>>(); // viewport ID -> color map\n const legendElements = allElements.filter((el) => el.kind === ELEMENT_KIND_LEGEND) as LegendElement[];\n legendElements.forEach((legend) => {\n if (legend.assignColors?.enabled && legend.groupBy) {\n const legendItems = getPathValue(data, `viewports.${legend.viewport}.items`);\n const rawLegendItems = Array.isArray(legendItems) ? legendItems : [];\n const legendDataFiltered = applyLegendFilter(\n rawLegendItems,\n legend,\n viewports,\n viewportClipPolygonsLocal,\n viewportRenderInfo\n );\n if (legendDataFiltered && legendDataFiltered.length) {\n const vpEl = viewports.find((v) => v.id === legend.viewport);\n const fillOpacity = vpEl?.fills?.length ? (vpEl.fills[0].fillOpacity ?? 0.35) : undefined;\n const result = buildLegendGroup(\n legend,\n template.styles,\n page,\n page.marginMm,\n legendDataFiltered,\n fillOpacity\n );\n if (result?.colorMap) {\n legendColorMaps.set(legend.viewport, result.colorMap);\n console.log(\n `[LEGEND PRE-PROCESS] Stored color map for viewport \"${legend.viewport}\": ${result.colorMap.size} colors`\n );\n }\n }\n }\n });\n\n sorted.forEach((el) => {\n if (el.kind === ELEMENT_KIND_RECT) {\n const r = el as RectElement;\n parts.push(\n `<rect x=\"${r.xMm}\" y=\"${r.yMm}\" width=\"${r.widthMm}\" height=\"${r.heightMm}\"` +\n (r.stroke ? ` stroke=\"${escapeAttr(r.stroke)}\"` : '') +\n (r.strokeWidthMm ? ` stroke-width=\"${r.strokeWidthMm}\"` : '') +\n (r.fill ? ` fill=\"${escapeAttr(r.fill)}\"` : ' fill=\"none\"') +\n dashArray(r.dashed) +\n ` />`\n );\n } else if (el.kind === ELEMENT_KIND_LINE) {\n const l = el as LineElement;\n parts.push(\n `<line x1=\"${l.x1Mm}\" y1=\"${l.y1Mm}\" x2=\"${l.x2Mm}\" y2=\"${l.y2Mm}\"` +\n (l.stroke ? ` stroke=\"${escapeAttr(l.stroke)}\"` : '') +\n (l.strokeWidthMm ? ` stroke-width=\"${l.strokeWidthMm}\"` : '') +\n dashArray(l.dashed) +\n ` />`\n );\n } else if (el.kind === ELEMENT_KIND_CIRCLE) {\n const c = el as CircleElement;\n parts.push(\n `<circle cx=\"${c.cxMm}\" cy=\"${c.cyMm}\" r=\"${c.rMm}\"` +\n (c.stroke ? ` stroke=\"${escapeAttr(c.stroke)}\"` : '') +\n (c.strokeWidthMm ? ` stroke-width=\"${c.strokeWidthMm}\"` : '') +\n (c.fill ? ` fill=\"${escapeAttr(c.fill)}\"` : ' fill=\"none\"') +\n dashArray(c.dashed) +\n ` />`\n );\n } else if (el.kind === ELEMENT_KIND_POLYLINE) {\n const pl = el as PolylineElement;\n const pointsStr = pl.points.map((p) => `${formatNumber(p.xMm)},${formatNumber(p.yMm)}`).join(' ');\n parts.push(\n `<polyline points=\"${pointsStr}\" stroke=\"${escapeAttr(pl.stroke)}\" stroke-width=\"${pl.strokeWidthMm}\" fill=\"none\"${dashArray(pl.dashed)} />`\n );\n } else if (el.kind === ELEMENT_KIND_POLYGON) {\n const pg = el as PolygonElement;\n const pointsStr = pg.points.map((p) => `${formatNumber(p.xMm)},${formatNumber(p.yMm)}`).join(' ');\n parts.push(\n `<polygon points=\"${pointsStr}\"` +\n (pg.stroke ? ` stroke=\"${escapeAttr(pg.stroke)}\"` : '') +\n (pg.strokeWidthMm ? ` stroke-width=\"${pg.strokeWidthMm}\"` : '') +\n (pg.fill ? ` fill=\"${escapeAttr(pg.fill)}\"` : ' fill=\"none\"') +\n dashArray(pg.dashed) +\n ` />`\n );\n } else if (el.kind === ELEMENT_KIND_TEXT) {\n const tEl = el as TextElement;\n const styleName = tEl.textStyle;\n const style = styleName ? template.styles?.[styleName] : undefined;\n if (!style) {\n pushWarning(warnings, `Missing text style '${styleName ?? '(none)'}' for element '${tEl.id}'.`);\n return;\n }\n const text = interpolate(tEl.text, dataWithViewport, warnings);\n const anchor = tEl.align === 'center' ? 'middle' : tEl.align === 'right' ? 'end' : 'start';\n parts.push(\n `<text x=\"${tEl.xMm}\" y=\"${tEl.yMm}\" font-family=\"${escapeAttr(style.fontFamily)}\" font-size=\"${style.fontSizeMm}\"` +\n (style.fontWeight ? ` font-weight=\"${escapeAttr(style.fontWeight)}\"` : '') +\n (style.color ? ` fill=\"${escapeAttr(style.color)}\"` : '') +\n (style.letterSpacingMm !== undefined ? ` letter-spacing=\"${style.letterSpacingMm}\"` : '') +\n (style.lineHeightMm !== undefined ? ` line-height=\"${style.lineHeightMm}\"` : '') +\n ` text-anchor=\"${anchor}\">${escapeText(text)}</text>`\n );\n } else if (el.kind === ELEMENT_KIND_IMAGE) {\n const img = el as ImageElement;\n const asset = assetMap.get(img.assetId);\n if (!asset) {\n pushWarning(warnings, `Missing asset '${img.assetId}' for image '${img.id}'.`);\n return;\n }\n if (asset.src) {\n parts.push(\n `<image href=\"${escapeAttr(asset.src)}\" x=\"${img.xMm}\" y=\"${img.yMm}\" width=\"${img.widthMm}\" height=\"${img.heightMm}\"` +\n (img.fit === 'cover'\n ? ' preserveAspectRatio=\"xMidYMid slice\"'\n : img.fit === 'stretch'\n ? ' preserveAspectRatio=\"none\"'\n : ' preserveAspectRatio=\"xMidYMid meet\"') +\n ` />`\n );\n } else if (asset.content) {\n parts.push(\n `<g transform=\"translate(${img.xMm} ${img.yMm}) scale(${img.widthMm} ${img.heightMm})\">${asset.content}</g>`\n );\n }\n } else if (el.kind === ELEMENT_KIND_SCALE_BAR) {\n const sb = el as ScaleBarElement;\n // Scale bar declares which viewport it binds to via `viewport`\n const boundViewport = sb.viewport ?? viewports[0]?.id ?? null;\n if (!boundViewport) {\n pushWarning(warnings, `Scale bar '${sb.id}' is not bound to a viewport.`);\n return;\n }\n const scaleInfo = viewportScales[boundViewport];\n if (!scaleInfo) {\n pushWarning(warnings, `Viewport '${boundViewport}' missing scale info for scale bar '${sb.id}'.`);\n return;\n }\n const lengthMm = sb.maxLengthMm;\n const segments = sb.segments ?? 10;\n const segmentMm = lengthMm / segments;\n const metersPerMm = 1 / scaleInfo.mmPerModelUnit;\n const segmentMeters = segmentMm * metersPerMm;\n const labelsEvery = Math.max(1, sb.labelEvery ?? 1);\n const unitSuffix = sb.unit ? ` ${sb.unit}` : ' m';\n const barParts: string[] = [];\n const sbStroke = sb.stroke ?? '#000';\n const sbStrokeWidth = sb.strokeWidthMm ?? 0.5;\n\n for (let i = 0; i <= segments; i++) {\n const x = sb.xMm + segmentMm * i;\n const isLabel = i % labelsEvery === 0;\n const height = isLabel ? 4 : 3;\n barParts.push(\n `<line x1=\"${x}\" y1=\"${sb.yMm}\" x2=\"${x}\" y2=\"${sb.yMm - height}\" stroke=\"${escapeAttr(\n sbStroke\n )}\" stroke-width=\"${sbStrokeWidth}\" />`\n );\n if (isLabel && sb.textStyle) {\n const style = template.styles?.[sb.textStyle];\n if (style) {\n const labelValue = (segmentMeters * i).toFixed(2).replace(/\\.?0+$/, '');\n barParts.push(\n `<text x=\"${x}\" y=\"${sb.yMm + style.fontSizeMm + 1}\" font-family=\"${escapeAttr(\n style.fontFamily\n )}\" font-size=\"${style.fontSizeMm}\" text-anchor=\"middle\">${escapeText(labelValue + unitSuffix)}</text>`\n );\n }\n }\n }\n\n barParts.push(\n `<line x1=\"${sb.xMm}\" y1=\"${sb.yMm}\" x2=\"${sb.xMm + lengthMm}\" y2=\"${sb.yMm}\" stroke=\"${escapeAttr(\n sbStroke\n )}\" stroke-width=\"${sbStrokeWidth}\" />`\n );\n\n parts.push(barParts.join(''));\n } else if (el.kind === ELEMENT_KIND_VIEWPORT) {\n const vp = el as ViewportElement;\n const model = filteredModelsByViewport[vp.id] ?? models[vp.id] ?? null;\n if (!model) {\n pushWarning(warnings, `Viewport '${vp.id}' missing model.`);\n }\n const vpParts = renderViewportElement(\n vp,\n model,\n dataWithViewport,\n preferredScales,\n presets.scales,\n assetMap,\n viewportScales,\n viewportClipPolygonsLocal,\n viewportClipBounds,\n viewportClipLabels,\n viewportClipStyles,\n viewportHighlight,\n viewportRenderInfo,\n warnings,\n legendColorMaps\n );\n parts.push(...vpParts);\n } else if (el.kind === ELEMENT_KIND_LEGEND) {\n const legend = el as LegendElement;\n const legendItems = getPathValue(data, `viewports.${legend.viewport}.items`);\n const rawLegendItems = Array.isArray(legendItems) ? legendItems : [];\n const legendDataFiltered = applyLegendFilter(\n rawLegendItems,\n legend,\n viewports,\n viewportClipPolygonsLocal,\n viewportRenderInfo\n );\n if (!legendDataFiltered || !legendDataFiltered.length) {\n if (!rawLegendItems.length) {\n pushWarning(warnings, `Legend '${legend.id}' has no items from viewport '${legend.viewport}'.`);\n } else {\n pushWarning(\n warnings,\n `Legend '${legend.id}' filter matched 0 of ${rawLegendItems.length} items (class=${JSON.stringify(legend.class)}, where=${JSON.stringify(legend.where)}).`\n );\n }\n }\n const vpEl = viewports.find((v) => v.id === legend.viewport);\n // Derive swatch opacity from viewport fill rules (first matching rule's opacity)\n const fillOpacity = vpEl?.fills?.length ? (vpEl.fills[0].fillOpacity ?? 0.35) : undefined;\n const legendResult = buildLegendGroup(\n legend,\n template.styles,\n page,\n page.marginMm,\n legendDataFiltered || [],\n fillOpacity\n );\n if (legendResult) {\n parts.push(legendResult.svg);\n }\n }\n });\n\n if (labelElements.length) {\n const labelParts = renderLabels(\n labelElements,\n data,\n viewportRenderInfo,\n viewportClipLabels,\n viewportClipPolygonsLocal,\n viewportClipPolygonsWorld,\n template.styles ?? {},\n dataWithViewport,\n warnings\n );\n parts.push(...labelParts);\n }\n\n if (qrElements.length) {\n const qrParts = await renderQrElements(\n qrElements,\n viewports,\n viewportScales,\n models,\n { qrContext: options?.qrContext },\n dataWithViewport,\n warnings\n );\n parts.push(...qrParts);\n }\n\n parts.push('</svg>');\n\n return { svg: parts.join(''), viewportScales, warnings };\n}\n\n// Register renderLayout with renderDrawing to break the circular dependency.\n// This runs at module evaluation time — safe because renderLayout is defined above.\nimport { setRenderLayoutBinding as _setBinding } from './renderDrawing';\n_setBinding(renderLayout as any);\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"/","sources":["layout/index.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,oCAAoC,CAAC;AA6DrE;;;GAGG;AACH,OAAO,EACH,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,0BAA0B,EAC7B,MAAM,iBAAiB,CAAC;AAGzB,kEAAkE;AAClE,OAAO,EAAE,YAAY,EAA2B,MAAM,gBAAgB,CAAC;AAEvE;;;;GAIG;AACH,OAAO,EACH,mBAAmB,EACnB,YAAY,EAIf,MAAM,uBAAuB,CAAC;AAE/B,iFAAiF;AACjF,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAyD,MAAM,aAAa,CAAC;AACnH,2FAA2F;AAC3F,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,wFAAwF;AACxF,OAAO,EAAE,sBAAsB,IAAI,8BAA8B,EAAE,MAAM,cAAc,CAAC;AAExF,sEAAsE;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,qGAAqG;AACrG,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE3D,qGAAqG;AACrG,OAAO,EACH,gBAAgB,EAChB,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACd,MAAM,YAAY,CAAC;AAEpB,6GAA6G;AAC7G,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAE7G,2GAA2G;AAC3G,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAA4B,MAAM,iBAAiB,CAAC;AAE5G,4HAA4H;AAC5H,OAAO,EACH,sBAAsB,EACtB,oBAAoB,EACpB,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EAGxB,MAAM,eAAe,CAAC;AAEvB,iGAAiG;AACjG,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAElG,2EAA2E;AAC3E,OAAO,EAAE,eAAe,EAAyC,MAAM,cAAc,CAAC;AAItF,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAEpG,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,wEAAwE;AACxE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG3D,uFAAuF;AACvF,OAAO,EACH,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACzB,MAAM,qBAAqB,CAAC;AAE7B,8EAA8E;AAC9E,MAAM,CAAC,MAAM,uBAAuB,GAAG,mBAAqC,CAAC","sourcesContent":["import defaultTemplateJson from '../templates/default-template.json';\nimport type { LayoutTemplate } from './types';\n\nexport type {\n PageSpec,\n TextStyle,\n StyleMap,\n ResourceSpec,\n LayoutElement,\n BaseElement,\n ViewportElement,\n TextElement,\n ImageElement,\n LineElement,\n RectElement,\n CircleElement,\n PolylineElement,\n PolygonElement,\n PointMm,\n ScaleBarElement,\n QrElement,\n LabelsElement,\n LegendElement,\n LegendLineStyle,\n GroupElement,\n LayoutTemplate,\n LegendAnchor,\n LegendColumn,\n SvgModelView,\n ModelContext,\n LayoutData,\n LayoutDataViewports,\n ViewportItemsMap,\n PagePresetKey,\n PageOrientation,\n ValidationOptions,\n ValidationIssue,\n ValidationResult,\n ComputedViewportScale,\n NormalizedProductItem,\n SectionStateValidationResult,\n LabelStyle,\n WhereCondition,\n VariableBinding,\n ClipConfig,\n HighlightConfig,\n ViewportLineStyle,\n ViewportLineStyles,\n ViewportStairArrowOptions,\n SectionResult,\n FillRule,\n RenderDrawingInput,\n RenderDrawingResult,\n IncludeClassesFilter,\n ResolvedIncludeFilter,\n TemplateVariableDef,\n TemplateVariableInfo\n} from './types';\n\nexport type { RenderLayoutOptions } from './options';\n\n/**\n * High-level facade: renders a complete drawing from a template + section\n * geometry or pre-built models. Start here.\n */\nexport {\n renderDrawing,\n resolveFillColor,\n BUILTIN_LINE_DEFAULTS,\n BUILTIN_HIDDEN_DEFAULTS,\n BUILTIN_LABEL_DEFAULTS,\n CATEGORY_BUILTIN_OVERRIDES\n} from './renderDrawing';\nexport type { ResolvedLineStyles } from './renderDrawing';\n\n/** Low-level template renderer (prefer {@link renderDrawing}). */\nexport { renderLayout, type RenderLayoutResult } from './renderLayout';\n\n/**\n * Full orchestration: products + template → rendered sheet. Resolves each\n * viewport's section definition, computes SlabSection/plane sections with a\n * def-keyed cache, and renders. The one-call path for template consumers.\n */\nexport {\n renderTemplateSheet,\n SectionCache,\n type RenderTemplateSheetInput,\n type RenderedTemplateSheet,\n type ComputedSection\n} from './renderTemplateSheet';\n\n/** Pre-render validation: template + layout data + section state in one call. */\nexport { preflight, validateLayoutData, type PreflightResult, type LayoutDataValidationResult } from './preflight';\n/** Validate a layout template JSON structure. Returns `{ ok } | { ok: false, errors }`. */\nexport { validateLayoutTemplate } from './validation';\n/** @deprecated Use {@link validateLayoutTemplate} — same function, historical alias. */\nexport { validateLayoutTemplate as validateLayoutTemplateExternal } from './validation';\n\n/** Normalize IFC class names to lowercase for consistent matching. */\nexport { normalizeClass } from './filters';\n/** Test whether an item matches a class + where filter (used by clip, highlight, labels, legend). */\nexport { matchesFilter, matchesV2Filter } from './filters';\n\n/** Metadata utilities: build lookup maps, resolve IFC class includes, and merge metadata sources. */\nexport {\n buildMetadataMap,\n normalizePropertySets,\n resolveIfcClassName,\n resolveIfcClassId,\n resolveIncludeClasses,\n resolveAllowedClasses,\n resolveMeta\n} from './metadata';\n\n/** Layout utility functions: scale denominators, label sizing, color parsing, and line style application. */\nexport { uniquePositiveDenoms, computeLabelFontSize, colorFromString, applyLineStyleToPaths } from './utils';\n\n/** SlabSection → SvgModelView adapter (per-product rawPaths for gid features) + productEntries builder. */\nexport { slabSectionToModelView, slabPerProductToEntries, type SlabPerProductEntry } from './slabModelView';\n\n/** The template format's per-viewport section contract: one reader/writer for cutHeight/planeNormal/planePosition/depth. */\nexport {\n resolveViewportSection,\n writeViewportSection,\n sectionDefKey,\n isVariableBinding,\n sectionUsesVariables,\n DEFAULT_SECTION_DEPTH,\n type ResolvedSectionDef,\n type SectionDefaults\n} from './sectionDefs';\n\n/** Property resolution: traverse nested objects with dot-path strings like `\"BIP.spacename\"`. */\nexport { getPathValue, resolveProperty, resolvePath, matchesPropertyFilter } from './renderUtils';\n\n/** Build legend table rows from product items with property formatting. */\nexport { buildLegendRows, type LegendRow, type LegendRowOptions } from './legendRows';\n\n/** Product normalization, viewport item builders, and section-state-to-SVG-model conversion. */\nexport type { ProductLike, BuildViewportItemsOptions } from './itemBuilders';\nexport { normalizeProductEntry, buildViewportItems, sectionStateToModelView } from './itemBuilders';\n\nexport { EXAMPLE_BUILDING_CLASSES } from './presets';\n\nexport { analyzeTemplate } from './analyzeTemplate';\nexport type { TemplateRequirements } from './analyzeTemplate';\n\n/** Drawing export utilities: convert SVG markup to PDF or SVG files. */\nexport { exportToPdf, exportToSvg } from './exportDrawing';\nexport type { ExportPdfOptions, ExportSvgOptions } from './exportDrawing';\n\n/** Template variable utilities: extract, validate, and resolve template parameters. */\nexport {\n getTemplateVariables,\n getTemplateDefaults,\n resolveTemplateValues,\n applyTemplateVariables\n} from './templateVariables';\n\n/** Built-in default layout template (A3 landscape with a single viewport). */\nexport const DEFAULT_LAYOUT_TEMPLATE = defaultTemplateJson as LayoutTemplate;\n"]}
@@ -16,6 +16,17 @@ export interface BuildViewportItemsOptions {
16
16
  * Normalize a single product entry using explicit field paths.
17
17
  */
18
18
  export declare const normalizeProductEntry: (entry: ProductLike, productFields: ProductFieldPaths) => NormalizedProductItem;
19
+ /**
20
+ * Read the plan-projection X/Z of a polyline point, tolerating both shapes the
21
+ * section pipeline emits: `Vec3` arrays `[x, y, z]` (from
22
+ * `computePlaneSectionsForProducts`) and `{ x, z }` objects. Coords are `null`
23
+ * when the point is neither / non-finite. Shared by every consumer so the dual
24
+ * contract is handled in exactly one place.
25
+ */
26
+ export declare const readXZ: (pt: unknown) => {
27
+ x: number | null;
28
+ z: number | null;
29
+ };
19
30
  export declare const computePolylinesCenter: (entry: unknown) => {
20
31
  x: number | null;
21
32
  z: number | null;
@@ -1 +1 @@
1
- {"version":3,"file":"itemBuilders.d.ts","sourceRoot":"/","sources":["layout/itemBuilders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAI/D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI7F,MAAM,WAAW,WAAW;IACxB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,yBAAyB;IACtC,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACrC;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,WAAW,EAAE,eAAe,iBAAiB,KAAG,qBAoD5F,CAAC;AAuBF,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG;IAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CA6B3F,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC3B,SAAS,WAAW,EAAE,EACtB,UAAU,yBAAyB,KACpC,qBAAqB,EAWvB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,kBAAkB,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,YAAY,CAYxG"}
1
+ {"version":3,"file":"itemBuilders.d.ts","sourceRoot":"/","sources":["layout/itemBuilders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAI/D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI7F,MAAM,WAAW,WAAW;IACxB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,CAAC,EAAE,KAAK,CAAC;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,yBAAyB;IACtC,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACrC;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,WAAW,EAAE,eAAe,iBAAiB,KAAG,qBAoD5F,CAAC;AAuBF;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,GAAI,IAAI,OAAO,KAAG;IAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAOxE,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG;IAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CA4B3F,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC3B,SAAS,WAAW,EAAE,EACtB,UAAU,yBAAyB,KACpC,qBAAqB,EAWvB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,kBAAkB,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,YAAY,CAYxG"}
@@ -79,6 +79,21 @@ const getBBoxCenter = (product) => {
79
79
  }
80
80
  return { x: null, z: null };
81
81
  };
82
+ /**
83
+ * Read the plan-projection X/Z of a polyline point, tolerating both shapes the
84
+ * section pipeline emits: `Vec3` arrays `[x, y, z]` (from
85
+ * `computePlaneSectionsForProducts`) and `{ x, z }` objects. Coords are `null`
86
+ * when the point is neither / non-finite. Shared by every consumer so the dual
87
+ * contract is handled in exactly one place.
88
+ */
89
+ export const readXZ = (pt) => {
90
+ const rawX = Array.isArray(pt) ? pt[0] : getProp(pt, 'x');
91
+ const rawZ = Array.isArray(pt) ? pt[2] : getProp(pt, 'z');
92
+ return {
93
+ x: Number.isFinite(rawX) ? rawX : null,
94
+ z: Number.isFinite(rawZ) ? rawZ : null
95
+ };
96
+ };
82
97
  export const computePolylinesCenter = (entry) => {
83
98
  try {
84
99
  const polylines = getProp(entry, 'polylines');
@@ -93,9 +108,8 @@ export const computePolylinesCenter = (entry) => {
93
108
  if (!Array.isArray(points))
94
109
  continue;
95
110
  for (const pt of points) {
96
- const px = getProp(pt, 'x');
97
- const pz = getProp(pt, 'z');
98
- if (Number.isFinite(px) && Number.isFinite(pz)) {
111
+ const { x: px, z: pz } = readXZ(pt);
112
+ if (px !== null && pz !== null) {
99
113
  sumX += px;
100
114
  sumZ += pz;
101
115
  count++;