@twinfinity/printing 6.0.1-ci.28952-beta → 6.0.2-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 (82) hide show
  1. package/README.md +3 -3
  2. package/dist/BuildInfo.js +3 -3
  3. package/dist/BuildInfo.js.map +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/layout/analyzeTemplate.js.map +1 -1
  6. package/dist/layout/clipUtils.js.map +1 -1
  7. package/dist/layout/colorConstants.js.map +1 -1
  8. package/dist/layout/elementKinds.js.map +1 -1
  9. package/dist/layout/exportDrawing.js.map +1 -1
  10. package/dist/layout/filters.js.map +1 -1
  11. package/dist/layout/geometryConstants.js.map +1 -1
  12. package/dist/layout/index.js.map +1 -1
  13. package/dist/layout/itemBuilders.js.map +1 -1
  14. package/dist/layout/labelsRender.js.map +1 -1
  15. package/dist/layout/legendConstants.js.map +1 -1
  16. package/dist/layout/legendRender.js.map +1 -1
  17. package/dist/layout/legendRows.js.map +1 -1
  18. package/dist/layout/legendUtils.js.map +1 -1
  19. package/dist/layout/metadata.js.map +1 -1
  20. package/dist/layout/model.js.map +1 -1
  21. package/dist/layout/options.js.map +1 -1
  22. package/dist/layout/presets.js.map +1 -1
  23. package/dist/layout/qrRender.js.map +1 -1
  24. package/dist/layout/renderDrawing.js.map +1 -1
  25. package/dist/layout/renderUtils.js.map +1 -1
  26. package/dist/layout/sectionValidation.js.map +1 -1
  27. package/dist/layout/templateVariables.js.map +1 -1
  28. package/dist/layout/typeGuards.js.map +1 -1
  29. package/dist/layout/types.js.map +1 -1
  30. package/dist/layout/utils.js.map +1 -1
  31. package/dist/layout/validation.js.map +1 -1
  32. package/dist/layout/validationHelpers.js.map +1 -1
  33. package/dist/layout/validationUtils.js.map +1 -1
  34. package/dist/layout/viewportPrep.js.map +1 -1
  35. package/dist/layout/viewportRender.js.map +1 -1
  36. package/dist/layout/viewportUtils.js.map +1 -1
  37. package/dist/layout/warnings.js.map +1 -1
  38. package/dist/printToPdf.js.map +1 -1
  39. package/dist/printToSvg.js.map +1 -1
  40. package/dist/sections.js.map +1 -1
  41. package/dist/types.js.map +1 -1
  42. package/package.json +2 -2
  43. package/src/BuildInfo.ts +41 -41
  44. package/src/index.ts +10 -10
  45. package/src/layout/analyzeTemplate.ts +218 -218
  46. package/src/layout/clipUtils.ts +193 -193
  47. package/src/layout/colorConstants.ts +16 -16
  48. package/src/layout/elementKinds.ts +35 -35
  49. package/src/layout/exportDrawing.ts +206 -206
  50. package/src/layout/filters.ts +80 -80
  51. package/src/layout/geometryConstants.ts +11 -11
  52. package/src/layout/index.ts +671 -671
  53. package/src/layout/itemBuilders.ts +159 -159
  54. package/src/layout/labelsRender.ts +170 -170
  55. package/src/layout/legendConstants.ts +11 -11
  56. package/src/layout/legendRender.ts +543 -543
  57. package/src/layout/legendRows.ts +62 -62
  58. package/src/layout/legendUtils.ts +144 -144
  59. package/src/layout/metadata.ts +210 -210
  60. package/src/layout/model.ts +372 -372
  61. package/src/layout/options.ts +17 -17
  62. package/src/layout/presets.ts +51 -51
  63. package/src/layout/qrRender.ts +126 -126
  64. package/src/layout/qrcode.d.ts +2 -2
  65. package/src/layout/renderDrawing.ts +710 -710
  66. package/src/layout/renderUtils.ts +138 -138
  67. package/src/layout/sectionValidation.ts +2 -2
  68. package/src/layout/templateVariables.ts +317 -317
  69. package/src/layout/typeGuards.ts +9 -9
  70. package/src/layout/types.ts +835 -835
  71. package/src/layout/utils.ts +85 -85
  72. package/src/layout/validation.ts +392 -392
  73. package/src/layout/validationHelpers.ts +59 -59
  74. package/src/layout/validationUtils.ts +6 -6
  75. package/src/layout/viewportPrep.ts +351 -351
  76. package/src/layout/viewportRender.ts +442 -442
  77. package/src/layout/viewportUtils.ts +50 -50
  78. package/src/layout/warnings.ts +3 -3
  79. package/src/printToPdf.ts +108 -108
  80. package/src/printToSvg.ts +188 -188
  81. package/src/sections.ts +1019 -1019
  82. package/src/types.ts +49 -49
@@ -1,170 +1,170 @@
1
- import { LabelsElement, LayoutData } from './types';
2
- import { matchesV2Filter } from './filters';
3
- import { pointInPolygon } from './clipUtils';
4
- import { getPathValue, resolvePath, escapeAttr, escapeText } from './renderUtils';
5
-
6
- import { isRecord, getProp } from './typeGuards';
7
-
8
- export interface ViewportRenderInfo {
9
- mmPerModel: number;
10
- contentWidthMm: number;
11
- contentHeightMm: number;
12
- offsetX: number;
13
- offsetY: number;
14
- rotationDeg: number;
15
- transform: {
16
- viewMinX: number;
17
- viewMaxX: number;
18
- viewMinZ: number;
19
- viewMaxZ: number;
20
- viewWidth: number;
21
- viewHeight: number;
22
- };
23
- centerX: number;
24
- centerY: number;
25
- centerModelX: number;
26
- centerModelY: number;
27
- }
28
-
29
- export const renderLabels = (
30
- labelElements: LabelsElement[],
31
- data: LayoutData,
32
- viewportRenderInfo: Record<string, ViewportRenderInfo>,
33
- viewportClipLabels: Record<string, boolean>,
34
- viewportClipPolygonsLocal: Record<string, Array<Array<{ x: number; y: number }>>>,
35
- viewportClipPolygonsWorld: Record<string, Array<Array<{ x: number; z: number }>>>,
36
- styles: Record<string, unknown>,
37
- dataWithViewport: unknown,
38
- warnings: string[]
39
- ): string[] => {
40
- const parts: string[] = [];
41
-
42
- for (const dl of labelElements) {
43
- const vpInfo = viewportRenderInfo[dl.viewport];
44
- if (!vpInfo) {
45
- warnings.push(`Labels '${dl.id}' missing viewport '${dl.viewport}'.`);
46
- continue;
47
- }
48
- if (vpInfo.rotationDeg !== 0) {
49
- warnings.push(`Labels '${dl.id}' skipped because rotation is not supported.`);
50
- continue;
51
- }
52
- if (!vpInfo.transform) {
53
- warnings.push(`Labels '${dl.id}' skipped because model transform is missing.`);
54
- continue;
55
- }
56
- // Items come from the bound viewport's data
57
- const items = getPathValue(data, `viewports.${dl.viewport}.items`);
58
- if (!Array.isArray(items) || !items.length) {
59
- continue;
60
- }
61
- // Filter by class + where
62
- let filteredItems =
63
- dl.class || dl.where ? items.filter((item) => matchesV2Filter(item, dl.class, dl.where)) : items;
64
-
65
- console.log(`[Labels ${dl.id}] Total items:`, items.length, 'After class+where filter:', filteredItems.length);
66
-
67
- // If matchClipFilter is true, filter by spatial position inside clip polygon
68
- if (dl.matchClipFilter && viewportClipPolygonsWorld[dl.viewport]?.length) {
69
- const clipPolygons = viewportClipPolygonsWorld[dl.viewport];
70
- console.log(`[Labels ${dl.id}] Applying matchClipFilter (spatial) with ${clipPolygons.length} polygons`);
71
- const beforeClipFilter = filteredItems.length;
72
- filteredItems = filteredItems.filter((item) => {
73
- const ix = getProp(item, 'x');
74
- const iz = getProp(item, 'z');
75
- if (!Number.isFinite(ix) || !Number.isFinite(iz)) return false;
76
- return clipPolygons.some((poly) => {
77
- // pointInPolygon expects y coordinate, but we use z for world space
78
- const polyWith2D = poly.map((p) => ({ x: p.x, y: p.z }));
79
- return pointInPolygon(polyWith2D, ix as number, iz as number);
80
- });
81
- });
82
- console.log(
83
- `[Labels ${dl.id}] After matchClipFilter (spatial):`,
84
- filteredItems.length,
85
- '(was:',
86
- beforeClipFilter,
87
- ')'
88
- );
89
-
90
- // Debug: log first few items to see their properties
91
- if (filteredItems.length === 0 && beforeClipFilter > 0) {
92
- console.log('[Labels] Sample item that was filtered out:', items[0]);
93
- }
94
- }
95
-
96
- if (!filteredItems.length) {
97
- console.log(`[Labels ${dl.id}] No items left after filtering, skipping.`);
98
- continue;
99
- }
100
- const styleName = dl.textStyle;
101
- const style = styleName && isRecord(styles) ? getProp(styles, styleName) : undefined;
102
- if (!isRecord(style)) {
103
- warnings.push(`Labels '${dl.id}' missing textStyle '${styleName ?? '(none)'}'.`);
104
- continue;
105
- }
106
- const anchor = dl.align === 'center' ? 'middle' : dl.align === 'right' ? 'end' : 'start';
107
- const t = vpInfo.transform;
108
-
109
- let renderedCount = 0;
110
- let spatiallyFilteredCount = 0;
111
-
112
- filteredItems.forEach((item) => {
113
- // Hardcoded x/z for BIM floor plane convention
114
- const xVal = getProp(item, 'x');
115
- const yVal = getProp(item, 'z');
116
- if (!Number.isFinite(xVal as number) || !Number.isFinite(yVal as number)) {
117
- return;
118
- }
119
- const viewX = (xVal as number) - t.viewMinX;
120
- const viewY = t.viewMaxZ - (yVal as number);
121
- const centerModelX = vpInfo.centerModelX;
122
- const centerModelY = vpInfo.centerModelY;
123
- const xMm = vpInfo.centerX + (viewX - centerModelX) * vpInfo.mmPerModel;
124
- const yMm = vpInfo.centerY + (viewY - centerModelY) * vpInfo.mmPerModel;
125
-
126
- if (viewportClipLabels[dl.viewport] && viewportClipPolygonsLocal[dl.viewport]?.length) {
127
- const inside = viewportClipPolygonsLocal[dl.viewport].some((poly) =>
128
- pointInPolygon(poly, viewX, viewY)
129
- );
130
- if (!inside) {
131
- spatiallyFilteredCount++;
132
- return;
133
- }
134
- }
135
-
136
- renderedCount++;
137
-
138
- const text = escapeText(
139
- dl.text && typeof dl.text === 'string'
140
- ? dl.text.replace(/\{\{\s*([^}]+?)\s*\}\}/g, (_m, path) => {
141
- const val = resolvePath(item, path) ?? getPathValue(dataWithViewport, path);
142
- return val === undefined || val === null || typeof val === 'object' ? '' : String(val);
143
- })
144
- : ''
145
- );
146
-
147
- const fontFamily = getProp(style, 'fontFamily');
148
- const fontSizeMm = getProp(style, 'fontSizeMm');
149
- const fontWeight = getProp(style, 'fontWeight');
150
- const color = getProp(style, 'color');
151
- const letterSpacingMm = getProp(style, 'letterSpacingMm');
152
- const lineHeightMm = getProp(style, 'lineHeightMm');
153
-
154
- parts.push(
155
- `<text x="${xMm}" y="${yMm}" font-family="${escapeAttr(String(fontFamily))}" font-size="${fontSizeMm}"` +
156
- (fontWeight ? ` font-weight="${escapeAttr(String(fontWeight))}"` : '') +
157
- (color ? ` fill="${escapeAttr(String(color))}"` : '') +
158
- (letterSpacingMm !== undefined ? ` letter-spacing="${letterSpacingMm}"` : '') +
159
- (lineHeightMm !== undefined ? ` line-height="${lineHeightMm}"` : '') +
160
- ` text-anchor="${anchor}">${text}</text>`
161
- );
162
- });
163
-
164
- console.log(
165
- `[Labels ${dl.id}] Rendered ${renderedCount} labels. Spatially filtered out: ${spatiallyFilteredCount}`
166
- );
167
- }
168
-
169
- return parts;
170
- };
1
+ import { LabelsElement, LayoutData } from './types';
2
+ import { matchesV2Filter } from './filters';
3
+ import { pointInPolygon } from './clipUtils';
4
+ import { getPathValue, resolvePath, escapeAttr, escapeText } from './renderUtils';
5
+
6
+ import { isRecord, getProp } from './typeGuards';
7
+
8
+ export interface ViewportRenderInfo {
9
+ mmPerModel: number;
10
+ contentWidthMm: number;
11
+ contentHeightMm: number;
12
+ offsetX: number;
13
+ offsetY: number;
14
+ rotationDeg: number;
15
+ transform: {
16
+ viewMinX: number;
17
+ viewMaxX: number;
18
+ viewMinZ: number;
19
+ viewMaxZ: number;
20
+ viewWidth: number;
21
+ viewHeight: number;
22
+ };
23
+ centerX: number;
24
+ centerY: number;
25
+ centerModelX: number;
26
+ centerModelY: number;
27
+ }
28
+
29
+ export const renderLabels = (
30
+ labelElements: LabelsElement[],
31
+ data: LayoutData,
32
+ viewportRenderInfo: Record<string, ViewportRenderInfo>,
33
+ viewportClipLabels: Record<string, boolean>,
34
+ viewportClipPolygonsLocal: Record<string, Array<Array<{ x: number; y: number }>>>,
35
+ viewportClipPolygonsWorld: Record<string, Array<Array<{ x: number; z: number }>>>,
36
+ styles: Record<string, unknown>,
37
+ dataWithViewport: unknown,
38
+ warnings: string[]
39
+ ): string[] => {
40
+ const parts: string[] = [];
41
+
42
+ for (const dl of labelElements) {
43
+ const vpInfo = viewportRenderInfo[dl.viewport];
44
+ if (!vpInfo) {
45
+ warnings.push(`Labels '${dl.id}' missing viewport '${dl.viewport}'.`);
46
+ continue;
47
+ }
48
+ if (vpInfo.rotationDeg !== 0) {
49
+ warnings.push(`Labels '${dl.id}' skipped because rotation is not supported.`);
50
+ continue;
51
+ }
52
+ if (!vpInfo.transform) {
53
+ warnings.push(`Labels '${dl.id}' skipped because model transform is missing.`);
54
+ continue;
55
+ }
56
+ // Items come from the bound viewport's data
57
+ const items = getPathValue(data, `viewports.${dl.viewport}.items`);
58
+ if (!Array.isArray(items) || !items.length) {
59
+ continue;
60
+ }
61
+ // Filter by class + where
62
+ let filteredItems =
63
+ dl.class || dl.where ? items.filter((item) => matchesV2Filter(item, dl.class, dl.where)) : items;
64
+
65
+ console.log(`[Labels ${dl.id}] Total items:`, items.length, 'After class+where filter:', filteredItems.length);
66
+
67
+ // If matchClipFilter is true, filter by spatial position inside clip polygon
68
+ if (dl.matchClipFilter && viewportClipPolygonsWorld[dl.viewport]?.length) {
69
+ const clipPolygons = viewportClipPolygonsWorld[dl.viewport];
70
+ console.log(`[Labels ${dl.id}] Applying matchClipFilter (spatial) with ${clipPolygons.length} polygons`);
71
+ const beforeClipFilter = filteredItems.length;
72
+ filteredItems = filteredItems.filter((item) => {
73
+ const ix = getProp(item, 'x');
74
+ const iz = getProp(item, 'z');
75
+ if (!Number.isFinite(ix) || !Number.isFinite(iz)) return false;
76
+ return clipPolygons.some((poly) => {
77
+ // pointInPolygon expects y coordinate, but we use z for world space
78
+ const polyWith2D = poly.map((p) => ({ x: p.x, y: p.z }));
79
+ return pointInPolygon(polyWith2D, ix as number, iz as number);
80
+ });
81
+ });
82
+ console.log(
83
+ `[Labels ${dl.id}] After matchClipFilter (spatial):`,
84
+ filteredItems.length,
85
+ '(was:',
86
+ beforeClipFilter,
87
+ ')'
88
+ );
89
+
90
+ // Debug: log first few items to see their properties
91
+ if (filteredItems.length === 0 && beforeClipFilter > 0) {
92
+ console.log('[Labels] Sample item that was filtered out:', items[0]);
93
+ }
94
+ }
95
+
96
+ if (!filteredItems.length) {
97
+ console.log(`[Labels ${dl.id}] No items left after filtering, skipping.`);
98
+ continue;
99
+ }
100
+ const styleName = dl.textStyle;
101
+ const style = styleName && isRecord(styles) ? getProp(styles, styleName) : undefined;
102
+ if (!isRecord(style)) {
103
+ warnings.push(`Labels '${dl.id}' missing textStyle '${styleName ?? '(none)'}'.`);
104
+ continue;
105
+ }
106
+ const anchor = dl.align === 'center' ? 'middle' : dl.align === 'right' ? 'end' : 'start';
107
+ const t = vpInfo.transform;
108
+
109
+ let renderedCount = 0;
110
+ let spatiallyFilteredCount = 0;
111
+
112
+ filteredItems.forEach((item) => {
113
+ // Hardcoded x/z for BIM floor plane convention
114
+ const xVal = getProp(item, 'x');
115
+ const yVal = getProp(item, 'z');
116
+ if (!Number.isFinite(xVal as number) || !Number.isFinite(yVal as number)) {
117
+ return;
118
+ }
119
+ const viewX = (xVal as number) - t.viewMinX;
120
+ const viewY = t.viewMaxZ - (yVal as number);
121
+ const centerModelX = vpInfo.centerModelX;
122
+ const centerModelY = vpInfo.centerModelY;
123
+ const xMm = vpInfo.centerX + (viewX - centerModelX) * vpInfo.mmPerModel;
124
+ const yMm = vpInfo.centerY + (viewY - centerModelY) * vpInfo.mmPerModel;
125
+
126
+ if (viewportClipLabels[dl.viewport] && viewportClipPolygonsLocal[dl.viewport]?.length) {
127
+ const inside = viewportClipPolygonsLocal[dl.viewport].some((poly) =>
128
+ pointInPolygon(poly, viewX, viewY)
129
+ );
130
+ if (!inside) {
131
+ spatiallyFilteredCount++;
132
+ return;
133
+ }
134
+ }
135
+
136
+ renderedCount++;
137
+
138
+ const text = escapeText(
139
+ dl.text && typeof dl.text === 'string'
140
+ ? dl.text.replace(/\{\{\s*([^}]+?)\s*\}\}/g, (_m, path) => {
141
+ const val = resolvePath(item, path) ?? getPathValue(dataWithViewport, path);
142
+ return val === undefined || val === null || typeof val === 'object' ? '' : String(val);
143
+ })
144
+ : ''
145
+ );
146
+
147
+ const fontFamily = getProp(style, 'fontFamily');
148
+ const fontSizeMm = getProp(style, 'fontSizeMm');
149
+ const fontWeight = getProp(style, 'fontWeight');
150
+ const color = getProp(style, 'color');
151
+ const letterSpacingMm = getProp(style, 'letterSpacingMm');
152
+ const lineHeightMm = getProp(style, 'lineHeightMm');
153
+
154
+ parts.push(
155
+ `<text x="${xMm}" y="${yMm}" font-family="${escapeAttr(String(fontFamily))}" font-size="${fontSizeMm}"` +
156
+ (fontWeight ? ` font-weight="${escapeAttr(String(fontWeight))}"` : '') +
157
+ (color ? ` fill="${escapeAttr(String(color))}"` : '') +
158
+ (letterSpacingMm !== undefined ? ` letter-spacing="${letterSpacingMm}"` : '') +
159
+ (lineHeightMm !== undefined ? ` line-height="${lineHeightMm}"` : '') +
160
+ ` text-anchor="${anchor}">${text}</text>`
161
+ );
162
+ });
163
+
164
+ console.log(
165
+ `[Labels ${dl.id}] Rendered ${renderedCount} labels. Spatially filtered out: ${spatiallyFilteredCount}`
166
+ );
167
+ }
168
+
169
+ return parts;
170
+ };
@@ -1,11 +1,11 @@
1
- export const PX_PER_MM = 96 / 25.4;
2
- export const DEFAULT_LEGEND_SWATCH_WIDTH_MM = 8;
3
- export const DEFAULT_LEGEND_CELL_PADDING_MM = 2;
4
- export const DEFAULT_LEGEND_BORDER_WIDTH_MM = 0.25;
5
- export const DEFAULT_LEGEND_GRID_WIDTH_MM = 0.25;
6
- export const DEFAULT_LEGEND_HEADER_WIDTH_MM = 0.65;
7
- export const DEFAULT_LEGEND_OFFSET_MM = 5;
8
- export const LEGEND_TEXT_WIDTH_FACTOR = 0.7;
9
- export const LEGEND_MIN_CONTENT_WIDTH_EM = 4;
10
- export const LEGEND_TEXT_Y_OFFSET_FACTOR = 0.3;
11
- export const MAX_LEGEND_ROWS = 200;
1
+ export const PX_PER_MM = 96 / 25.4;
2
+ export const DEFAULT_LEGEND_SWATCH_WIDTH_MM = 8;
3
+ export const DEFAULT_LEGEND_CELL_PADDING_MM = 2;
4
+ export const DEFAULT_LEGEND_BORDER_WIDTH_MM = 0.25;
5
+ export const DEFAULT_LEGEND_GRID_WIDTH_MM = 0.25;
6
+ export const DEFAULT_LEGEND_HEADER_WIDTH_MM = 0.65;
7
+ export const DEFAULT_LEGEND_OFFSET_MM = 5;
8
+ export const LEGEND_TEXT_WIDTH_FACTOR = 0.7;
9
+ export const LEGEND_MIN_CONTENT_WIDTH_EM = 4;
10
+ export const LEGEND_TEXT_Y_OFFSET_FACTOR = 0.3;
11
+ export const MAX_LEGEND_ROWS = 200;