@twinfinity/printing 6.1.1-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
@@ -0,0 +1,196 @@
1
+ import type { Vec3, SlabSectionGeometry, PlaneBasis, SectionPath } from '@twinfinity/geometry';
2
+ import type { SvgModelView, ViewportLineStyles, ViewportLineStyle, ProductPolylineEntry } from './types';
3
+ import { resolveCategoryLineStyle } from './renderDrawing';
4
+ import { escapeAttr } from './renderUtils';
5
+
6
+ /** Per-product line attribution (SlabSection with `trackProducts: true`).
7
+ * Structurally typed — any `{ product, cutLines?, visibleLines?, … }` works. */
8
+ export type SlabPerProductEntry = { product: unknown } & { [K in keyof SlabSectionGeometry]?: Vec3[][] };
9
+
10
+ type Pt2 = { x: number; y: number };
11
+ type LineCategory = keyof SlabSectionGeometry;
12
+
13
+ // Paint order (SVG paints later paths on top): hidden categories first, so occluded
14
+ // linework can never cover visible geometry — same order printToSvg uses.
15
+ const CATEGORIES: LineCategory[] = [
16
+ 'hiddenLines',
17
+ 'outlineHidden',
18
+ 'silhouettesHidden',
19
+ 'visibleLines',
20
+ 'outlineVisible',
21
+ 'silhouettesVisible',
22
+ 'featureEdges',
23
+ 'cutLines',
24
+ 'stairArrows'
25
+ ];
26
+
27
+ function resolveStyle(category: LineCategory, lineStyles?: ViewportLineStyles): Required<ViewportLineStyle> {
28
+ return resolveCategoryLineStyle(category, lineStyles);
29
+ }
30
+
31
+ function projectPolyline(poly: Vec3[], basis: PlaneBasis): Pt2[] {
32
+ const ox = basis.origin[0],
33
+ oy = basis.origin[1],
34
+ oz = basis.origin[2];
35
+ const rx = basis.right[0],
36
+ ry = basis.right[1],
37
+ rz = basis.right[2];
38
+ const ux = basis.up[0],
39
+ uy = basis.up[1],
40
+ uz = basis.up[2];
41
+ const pts: Pt2[] = [];
42
+ for (let i = 0; i < poly.length; i++) {
43
+ const p = poly[i];
44
+ const dx = p[0] - ox,
45
+ dy = p[1] - oy,
46
+ dz = p[2] - oz;
47
+ pts.push({ x: dx * rx + dy * ry + dz * rz, y: dx * ux + dy * uy + dz * uz });
48
+ }
49
+ return pts;
50
+ }
51
+
52
+ function fmt(v: number): string {
53
+ return v.toFixed(6).replace(/\.?0+$/, '');
54
+ }
55
+
56
+ function buildPathD(projected: Pt2[][]): string {
57
+ const parts: string[] = [];
58
+ for (const poly of projected) {
59
+ if (poly.length === 0) continue;
60
+ parts.push(`M ${fmt(poly[0].x)} ${fmt(poly[0].y)}`);
61
+ for (let i = 1; i < poly.length; i++) {
62
+ parts.push(`L ${fmt(poly[i].x)} ${fmt(poly[i].y)}`);
63
+ }
64
+ }
65
+ return parts.join(' ');
66
+ }
67
+
68
+ /**
69
+ * Convert SlabSection geometry into an SvgModelView for the print-layout engine
70
+ * (`renderDrawing({ models })`). Returns null when the geometry is empty or
71
+ * degenerate. Use a (0,0,1) up hint when building the plane basis for standard
72
+ * north-up floor plans.
73
+ *
74
+ * When the geometry carries `perProduct` attribution (SlabSection option
75
+ * `trackProducts: true`), the model also gets `rawPaths` with per-product gid
76
+ * metadata — required for the renderer's gid-based features (highlight, clip
77
+ * include/exclude, render-time includeClasses): without rawPaths,
78
+ * filterModelForViewport returns the model untouched and those settings
79
+ * silently do nothing.
80
+ */
81
+ export function slabSectionToModelView(
82
+ geometry: SlabSectionGeometry & { perProduct?: SlabPerProductEntry[] },
83
+ planeBasis: PlaneBasis,
84
+ lineStyles?: ViewportLineStyles
85
+ ): SvgModelView | null {
86
+ const projected: Record<string, Pt2[][]> = {};
87
+ let minX = Infinity,
88
+ maxX = -Infinity;
89
+ let minY = Infinity,
90
+ maxY = -Infinity;
91
+
92
+ for (const cat of CATEGORIES) {
93
+ const polys = geometry[cat];
94
+ const result: Pt2[][] = [];
95
+ for (const poly of polys) {
96
+ if (poly.length === 0) continue;
97
+ const pts = projectPolyline(poly, planeBasis);
98
+ for (const pt of pts) {
99
+ if (pt.x < minX) minX = pt.x;
100
+ if (pt.x > maxX) maxX = pt.x;
101
+ if (pt.y < minY) minY = pt.y;
102
+ if (pt.y > maxY) maxY = pt.y;
103
+ }
104
+ result.push(pts);
105
+ }
106
+ projected[cat] = result;
107
+ }
108
+
109
+ const viewWidth = maxX - minX;
110
+ const viewHeight = maxY - minY;
111
+ if (!Number.isFinite(viewWidth) || viewWidth <= 1e-6 || !Number.isFinite(viewHeight) || viewHeight <= 1e-6) {
112
+ return null;
113
+ }
114
+
115
+ // Map projected (x,y) to SVG coords: SVG_x = x - minX, SVG_y = maxY - y (flip).
116
+ const geometryTransform = `matrix(1 0 0 -1 ${fmt(-minX)} ${fmt(maxY)})`;
117
+
118
+ const svgParts: string[] = [`<g transform="${escapeAttr(geometryTransform)}">`];
119
+ for (const cat of CATEGORIES) {
120
+ const style = resolveStyle(cat, lineStyles);
121
+ if (style.enabled === false) continue;
122
+ const d = buildPathD(projected[cat]);
123
+ if (!d) continue;
124
+ let pathEl = `<path d="${d}" fill="none" stroke="${escapeAttr(style.color)}" stroke-width="${style.widthMm}"`;
125
+ if (style.dash) pathEl += ` stroke-dasharray="${escapeAttr(style.dash)}"`;
126
+ pathEl += ` stroke-linejoin="round" stroke-linecap="round"/>`;
127
+ svgParts.push(pathEl);
128
+ }
129
+ svgParts.push('</g>');
130
+
131
+ // Per-product rawPaths (same projection + styles as the merged groupSvg)
132
+ // so gid-based features can filter/restyle and rebuild the model.
133
+ let rawPaths: SectionPath[] | undefined;
134
+ if (geometry.perProduct?.length) {
135
+ rawPaths = [];
136
+ for (const entry of geometry.perProduct) {
137
+ const gid = (entry.product as { gid?: string } | undefined)?.gid;
138
+ if (!gid) continue;
139
+ for (const cat of CATEGORIES) {
140
+ const style = resolveStyle(cat, lineStyles);
141
+ if (style.enabled === false) continue;
142
+ const polys = entry[cat];
143
+ if (!polys?.length) continue;
144
+ const d = buildPathD(polys.filter((p) => p.length > 0).map((p) => projectPolyline(p, planeBasis)));
145
+ if (!d) continue;
146
+ rawPaths.push({
147
+ key: `${gid}:${cat}`,
148
+ d,
149
+ fill: null,
150
+ fillOpacity: 1,
151
+ stroke: style.color,
152
+ strokeWidth: style.widthMm,
153
+ dash: style.dash || undefined,
154
+ meta: { gid, isSegment: true, category: cat }
155
+ });
156
+ }
157
+ }
158
+ if (rawPaths.length === 0) rawPaths = undefined;
159
+ }
160
+
161
+ return {
162
+ groupSvg: `<g>${svgParts.join('')}</g>`,
163
+ widthModel: viewWidth,
164
+ heightModel: viewHeight,
165
+ transform: {
166
+ viewMinX: minX,
167
+ viewMaxX: maxX,
168
+ viewMinZ: minY,
169
+ viewMaxZ: maxY,
170
+ viewWidth,
171
+ viewHeight
172
+ },
173
+ geometryTransform,
174
+ rawPaths
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Build `options.productEntries` for renderDrawing from SlabSection
180
+ * per-product attribution: each product's cut polylines in world x/z. Labels,
181
+ * legend, clip and highlight items are derived from these entries — the
182
+ * polylines must be in WORLD coordinates (use a zero lateral section origin so
183
+ * the projected space equals world x/z).
184
+ */
185
+ export function slabPerProductToEntries(perProduct: SlabPerProductEntry[]): ProductPolylineEntry[] {
186
+ return perProduct
187
+ .map((entry) => ({
188
+ product: entry.product,
189
+ polylines: (entry.cutLines ?? [])
190
+ .filter((poly) => poly.length > 0)
191
+ .map((poly) => ({
192
+ points: poly.map((pt) => ({ x: pt[0], z: pt[2] }))
193
+ }))
194
+ }))
195
+ .filter((e) => e.polylines.length > 0);
196
+ }
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import type { LayoutTemplate, TemplateVariableDef, TemplateVariableInfo } from './types';
14
+ import { applyPlaceholderFilter } from './renderUtils';
14
15
 
15
16
  /**
16
17
  * Extract all variable definitions from a template.
@@ -263,14 +264,28 @@ export function resolveTemplateValues(
263
264
  * // }
264
265
  * ```
265
266
  */
266
- export function applyTemplateVariables(template: LayoutTemplate, values: Record<string, any>): LayoutTemplate {
267
+ export function applyTemplateVariables(
268
+ template: LayoutTemplate,
269
+ values: Record<string, any>,
270
+ warnings?: string[]
271
+ ): LayoutTemplate {
267
272
  // Deep clone to avoid mutation
268
273
  const resolved: LayoutTemplate = JSON.parse(JSON.stringify(template));
269
274
 
270
- // Recursively replace {{vars.*}} in all string values
275
+ // Recursively replace {{vars.*}} in all string values.
276
+ // A value that is EXACTLY one placeholder (no surrounding text, no
277
+ // format filter) substitutes the variable's RAW value, preserving its
278
+ // type — this is what lets number variables drive numeric template
279
+ // fields like a viewport's cutHeight ("cutHeight": "{{vars.cutHeight}}").
280
+ // Mixed strings keep normal stringified interpolation.
281
+ const WHOLE_VALUE_RE = /^\{\{\s*vars\.(\w+)\s*\}\}$/;
271
282
  function replaceInValue(obj: any): any {
272
283
  if (typeof obj === 'string') {
273
- return interpolateVariables(obj, values);
284
+ const whole = WHOLE_VALUE_RE.exec(obj);
285
+ if (whole && whole[1] in values && typeof values[whole[1]] !== 'string') {
286
+ return values[whole[1]];
287
+ }
288
+ return interpolateVariables(obj, values, warnings);
274
289
  }
275
290
  if (Array.isArray(obj)) {
276
291
  return obj.map(replaceInValue);
@@ -287,8 +302,8 @@ export function applyTemplateVariables(template: LayoutTemplate, values: Record<
287
302
 
288
303
  // Apply to all template properties that might contain variable references
289
304
  resolved.elements = replaceInValue(resolved.elements);
290
- if (resolved.assets) {
291
- resolved.assets = replaceInValue(resolved.assets);
305
+ if (resolved.resources) {
306
+ resolved.resources = replaceInValue(resolved.resources);
292
307
  }
293
308
 
294
309
  return resolved;
@@ -304,14 +319,15 @@ export function applyTemplateVariables(template: LayoutTemplate, values: Record<
304
319
  * @param values - Variable values
305
320
  * @returns String with all {{vars.*}} replaced
306
321
  */
307
- function interpolateVariables(str: string, values: Record<string, any>): string {
308
- return str.replace(/\{\{vars\.(\w+)\}\}/g, (match, varName) => {
322
+ function interpolateVariables(str: string, values: Record<string, any>, warnings?: string[]): string {
323
+ return str.replace(/\{\{\s*vars\.(\w+)\s*(\|[^}]*)?\}\}/g, (match, varName, filterPart) => {
309
324
  if (varName in values) {
310
325
  const value = values[varName];
326
+ const filter = filterPart ? String(filterPart).slice(1).trim() : null;
311
327
  // Convert to string (handles numbers, booleans, etc.)
312
- return String(value);
328
+ return String(applyPlaceholderFilter(value, filter, warnings, str));
313
329
  }
314
- console.warn(`[templateVariables] Variable '${varName}' not found - keeping placeholder`);
330
+ warnings?.push(`Variable '${varName}' has no value placeholder kept.`);
315
331
  return match; // Keep {{vars.varName}} as-is
316
332
  });
317
333
  }