@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,159 +1,159 @@
1
- import type { SectionRenderState } from '@twinfinity/geometry';
2
- import { normalizePropertySets } from './metadata';
3
- import { getPathValue } from './renderUtils';
4
- import { buildModelViewFromRaw } from './model';
5
- import { ProductFieldPaths, NormalizedProductItem, SvgModelView, LabelStyle } from './types';
6
-
7
- import { getProp } from './typeGuards';
8
-
9
- export interface ProductLike {
10
- product?: any;
11
- polylines?: Array<{ points?: Array<{ x: number; z: number }> }>;
12
- }
13
-
14
- export interface BuildViewportItemsOptions {
15
- productFields?: ProductFieldPaths;
16
- }
17
-
18
- /**
19
- * Normalize a single product entry using explicit field paths.
20
- */
21
- export const normalizeProductEntry = (entry: ProductLike, productFields: ProductFieldPaths): NormalizedProductItem => {
22
- if (!productFields?.idPath || !productFields?.namePath || !productFields?.areaPath) {
23
- throw new Error(
24
- 'productFields must include idPath, namePath, and areaPath using propertySets.<Set>.<Prop>.value.'
25
- );
26
- }
27
- const product = getProp(entry, 'product') ?? entry;
28
- const propertySets = normalizePropertySets(product);
29
- const getVal = (path: string): unknown => getPathValue({ propertySets }, path);
30
- const idVal = getVal(productFields.idPath) ?? getProp(product, 'gid');
31
- const nameVal = getVal(productFields.namePath);
32
- const areaVal = getVal(productFields.areaPath);
33
- // class.type = IfcClassType ("space", "walls", etc.) — used by template filters
34
- // class.id = PredefinedIfcClassId ("IfcSpace", "IfcWall", etc.) — used as fallback
35
- // classPath from propertySets may contain a PredefinedIfcClassId
36
- const classPathVal = productFields.classPath ? getVal(productFields.classPath) : null;
37
- const productClass = getProp(product, 'class');
38
- const classType = getProp(productClass, 'type') as string | null; // IfcClassType
39
- const classId = getProp(productClass, 'id') as string | null; // PredefinedIfcClassId
40
- // ifcClass = IfcClassType category; ifcClassId = PredefinedIfcClassId
41
- const ifcClass = (classType ?? null) as string | null;
42
- const ifcClassId = (classPathVal ?? classId ?? null) as string | null;
43
- // Try multiple sources for coordinates:
44
- // 1. BBox center from product
45
- // 2. Polylines center from entry
46
- // 3. Direct x/z on entry or product
47
- let { x, z } = getBBoxCenter(product);
48
- if (x === null || z === null) {
49
- const polyCenter = computePolylinesCenter(entry);
50
- x = polyCenter.x ?? x;
51
- z = polyCenter.z ?? z;
52
- }
53
- if (x === null || z === null) {
54
- const directX = getProp(entry, 'x') ?? getProp(product, 'x');
55
- const directZ = getProp(entry, 'z') ?? getProp(product, 'z');
56
- if (Number.isFinite(directX)) x = directX as number;
57
- if (Number.isFinite(directZ)) z = directZ as number;
58
- }
59
- const rawPolylines = getProp(entry, 'polylines');
60
- return {
61
- gid: getProp(product, 'gid') as string | undefined,
62
- ifcClass,
63
- ifcClassId,
64
- propertySets,
65
- productId: idVal ? String(idVal) : '-',
66
- productName: typeof nameVal === 'string' && nameVal.trim().length ? nameVal.trim() : '-',
67
- area: typeof areaVal === 'number' && Number.isFinite(areaVal) ? areaVal : null,
68
- x,
69
- z,
70
- text: typeof nameVal === 'string' && nameVal.trim().length ? nameVal.trim() : '-',
71
- polylines: Array.isArray(rawPolylines) ? rawPolylines : undefined
72
- };
73
- };
74
-
75
- const getBBoxCenter = (product: unknown): { x: number | null; z: number | null } => {
76
- try {
77
- const boundingInfo = getProp(product, 'boundingInfo');
78
- const boundingInfoResult = typeof boundingInfo === 'function' ? boundingInfo() : null;
79
- const bbox = getProp(boundingInfoResult, 'boundingBox');
80
- const min = getProp(bbox, 'minimumWorld') ?? getProp(bbox, 'minimum');
81
- const max = getProp(bbox, 'maximumWorld') ?? getProp(bbox, 'maximum');
82
- const minX = getProp(min, 'x');
83
- const maxX = getProp(max, 'x');
84
- const minZ = getProp(min, 'z');
85
- const maxZ = getProp(max, 'z');
86
- if (Number.isFinite(minX) && Number.isFinite(maxX) && Number.isFinite(minZ) && Number.isFinite(maxZ)) {
87
- return { x: ((minX as number) + (maxX as number)) * 0.5, z: ((minZ as number) + (maxZ as number)) * 0.5 };
88
- }
89
- } catch {
90
- // Bounding info access may fail for products without geometry or with malformed data structures.
91
- // This is expected behavior - we return null coordinates as fallback.
92
- }
93
- return { x: null, z: null };
94
- };
95
-
96
- export const computePolylinesCenter = (entry: unknown): { x: number | null; z: number | null } => {
97
- try {
98
- const polylines = getProp(entry, 'polylines');
99
- if (!Array.isArray(polylines) || !polylines.length) {
100
- return { x: null, z: null };
101
- }
102
- let sumX = 0;
103
- let sumZ = 0;
104
- let count = 0;
105
- for (const polyline of polylines) {
106
- const points = getProp(polyline, 'points');
107
- if (!Array.isArray(points)) continue;
108
- for (const pt of points) {
109
- const px = getProp(pt, 'x');
110
- const pz = getProp(pt, 'z');
111
- if (Number.isFinite(px) && Number.isFinite(pz)) {
112
- sumX += px as number;
113
- sumZ += pz as number;
114
- count++;
115
- }
116
- }
117
- }
118
- if (count > 0) {
119
- return { x: sumX / count, z: sumZ / count };
120
- }
121
- } catch {
122
- // Polyline access may fail - return null
123
- }
124
- return { x: null, z: null };
125
- };
126
-
127
- /**
128
- * Normalize raw product entries into viewport items using explicit field paths.
129
- * Requires productFields (id/name/area/class) pointing to propertySets.<Set>.<Prop>.value.
130
- */
131
- export const buildViewportItems = (
132
- entries: ProductLike[],
133
- options?: BuildViewportItemsOptions
134
- ): NormalizedProductItem[] => {
135
- if (!options?.productFields) {
136
- throw new Error('buildViewportItems requires productFields (idPath/namePath/areaPath/classPath).');
137
- }
138
- const { idPath, namePath, areaPath } = options.productFields;
139
- if (!idPath || !namePath || !areaPath) {
140
- throw new Error(
141
- 'productFields must include idPath, namePath, and areaPath using propertySets.<Set>.<Prop>.value.'
142
- );
143
- }
144
- return (entries ?? []).map((entry: any) => normalizeProductEntry(entry, options.productFields!));
145
- };
146
-
147
- export function sectionStateToModelView(state: SectionRenderState, labelStyle?: LabelStyle): SvgModelView {
148
- const model = buildModelViewFromRaw(
149
- state.paths,
150
- state.labels,
151
- state.transform,
152
- state.geometryTransform,
153
- labelStyle
154
- );
155
- if (!model) {
156
- throw new Error('sectionStateToModelView requires paths, transform, and geometryTransform.');
157
- }
158
- return model;
159
- }
1
+ import type { SectionRenderState } from '@twinfinity/geometry';
2
+ import { normalizePropertySets } from './metadata';
3
+ import { getPathValue } from './renderUtils';
4
+ import { buildModelViewFromRaw } from './model';
5
+ import { ProductFieldPaths, NormalizedProductItem, SvgModelView, LabelStyle } from './types';
6
+
7
+ import { getProp } from './typeGuards';
8
+
9
+ export interface ProductLike {
10
+ product?: any;
11
+ polylines?: Array<{ points?: Array<{ x: number; z: number }> }>;
12
+ }
13
+
14
+ export interface BuildViewportItemsOptions {
15
+ productFields?: ProductFieldPaths;
16
+ }
17
+
18
+ /**
19
+ * Normalize a single product entry using explicit field paths.
20
+ */
21
+ export const normalizeProductEntry = (entry: ProductLike, productFields: ProductFieldPaths): NormalizedProductItem => {
22
+ if (!productFields?.idPath || !productFields?.namePath || !productFields?.areaPath) {
23
+ throw new Error(
24
+ 'productFields must include idPath, namePath, and areaPath using propertySets.<Set>.<Prop>.value.'
25
+ );
26
+ }
27
+ const product = getProp(entry, 'product') ?? entry;
28
+ const propertySets = normalizePropertySets(product);
29
+ const getVal = (path: string): unknown => getPathValue({ propertySets }, path);
30
+ const idVal = getVal(productFields.idPath) ?? getProp(product, 'gid');
31
+ const nameVal = getVal(productFields.namePath);
32
+ const areaVal = getVal(productFields.areaPath);
33
+ // class.type = IfcClassType ("space", "walls", etc.) — used by template filters
34
+ // class.id = PredefinedIfcClassId ("IfcSpace", "IfcWall", etc.) — used as fallback
35
+ // classPath from propertySets may contain a PredefinedIfcClassId
36
+ const classPathVal = productFields.classPath ? getVal(productFields.classPath) : null;
37
+ const productClass = getProp(product, 'class');
38
+ const classType = getProp(productClass, 'type') as string | null; // IfcClassType
39
+ const classId = getProp(productClass, 'id') as string | null; // PredefinedIfcClassId
40
+ // ifcClass = IfcClassType category; ifcClassId = PredefinedIfcClassId
41
+ const ifcClass = (classType ?? null) as string | null;
42
+ const ifcClassId = (classPathVal ?? classId ?? null) as string | null;
43
+ // Try multiple sources for coordinates:
44
+ // 1. BBox center from product
45
+ // 2. Polylines center from entry
46
+ // 3. Direct x/z on entry or product
47
+ let { x, z } = getBBoxCenter(product);
48
+ if (x === null || z === null) {
49
+ const polyCenter = computePolylinesCenter(entry);
50
+ x = polyCenter.x ?? x;
51
+ z = polyCenter.z ?? z;
52
+ }
53
+ if (x === null || z === null) {
54
+ const directX = getProp(entry, 'x') ?? getProp(product, 'x');
55
+ const directZ = getProp(entry, 'z') ?? getProp(product, 'z');
56
+ if (Number.isFinite(directX)) x = directX as number;
57
+ if (Number.isFinite(directZ)) z = directZ as number;
58
+ }
59
+ const rawPolylines = getProp(entry, 'polylines');
60
+ return {
61
+ gid: getProp(product, 'gid') as string | undefined,
62
+ ifcClass,
63
+ ifcClassId,
64
+ propertySets,
65
+ productId: idVal ? String(idVal) : '-',
66
+ productName: typeof nameVal === 'string' && nameVal.trim().length ? nameVal.trim() : '-',
67
+ area: typeof areaVal === 'number' && Number.isFinite(areaVal) ? areaVal : null,
68
+ x,
69
+ z,
70
+ text: typeof nameVal === 'string' && nameVal.trim().length ? nameVal.trim() : '-',
71
+ polylines: Array.isArray(rawPolylines) ? rawPolylines : undefined
72
+ };
73
+ };
74
+
75
+ const getBBoxCenter = (product: unknown): { x: number | null; z: number | null } => {
76
+ try {
77
+ const boundingInfo = getProp(product, 'boundingInfo');
78
+ const boundingInfoResult = typeof boundingInfo === 'function' ? boundingInfo() : null;
79
+ const bbox = getProp(boundingInfoResult, 'boundingBox');
80
+ const min = getProp(bbox, 'minimumWorld') ?? getProp(bbox, 'minimum');
81
+ const max = getProp(bbox, 'maximumWorld') ?? getProp(bbox, 'maximum');
82
+ const minX = getProp(min, 'x');
83
+ const maxX = getProp(max, 'x');
84
+ const minZ = getProp(min, 'z');
85
+ const maxZ = getProp(max, 'z');
86
+ if (Number.isFinite(minX) && Number.isFinite(maxX) && Number.isFinite(minZ) && Number.isFinite(maxZ)) {
87
+ return { x: ((minX as number) + (maxX as number)) * 0.5, z: ((minZ as number) + (maxZ as number)) * 0.5 };
88
+ }
89
+ } catch {
90
+ // Bounding info access may fail for products without geometry or with malformed data structures.
91
+ // This is expected behavior - we return null coordinates as fallback.
92
+ }
93
+ return { x: null, z: null };
94
+ };
95
+
96
+ export const computePolylinesCenter = (entry: unknown): { x: number | null; z: number | null } => {
97
+ try {
98
+ const polylines = getProp(entry, 'polylines');
99
+ if (!Array.isArray(polylines) || !polylines.length) {
100
+ return { x: null, z: null };
101
+ }
102
+ let sumX = 0;
103
+ let sumZ = 0;
104
+ let count = 0;
105
+ for (const polyline of polylines) {
106
+ const points = getProp(polyline, 'points');
107
+ if (!Array.isArray(points)) continue;
108
+ for (const pt of points) {
109
+ const px = getProp(pt, 'x');
110
+ const pz = getProp(pt, 'z');
111
+ if (Number.isFinite(px) && Number.isFinite(pz)) {
112
+ sumX += px as number;
113
+ sumZ += pz as number;
114
+ count++;
115
+ }
116
+ }
117
+ }
118
+ if (count > 0) {
119
+ return { x: sumX / count, z: sumZ / count };
120
+ }
121
+ } catch {
122
+ // Polyline access may fail - return null
123
+ }
124
+ return { x: null, z: null };
125
+ };
126
+
127
+ /**
128
+ * Normalize raw product entries into viewport items using explicit field paths.
129
+ * Requires productFields (id/name/area/class) pointing to propertySets.<Set>.<Prop>.value.
130
+ */
131
+ export const buildViewportItems = (
132
+ entries: ProductLike[],
133
+ options?: BuildViewportItemsOptions
134
+ ): NormalizedProductItem[] => {
135
+ if (!options?.productFields) {
136
+ throw new Error('buildViewportItems requires productFields (idPath/namePath/areaPath/classPath).');
137
+ }
138
+ const { idPath, namePath, areaPath } = options.productFields;
139
+ if (!idPath || !namePath || !areaPath) {
140
+ throw new Error(
141
+ 'productFields must include idPath, namePath, and areaPath using propertySets.<Set>.<Prop>.value.'
142
+ );
143
+ }
144
+ return (entries ?? []).map((entry: any) => normalizeProductEntry(entry, options.productFields!));
145
+ };
146
+
147
+ export function sectionStateToModelView(state: SectionRenderState, labelStyle?: LabelStyle): SvgModelView {
148
+ const model = buildModelViewFromRaw(
149
+ state.paths,
150
+ state.labels,
151
+ state.transform,
152
+ state.geometryTransform,
153
+ labelStyle
154
+ );
155
+ if (!model) {
156
+ throw new Error('sectionStateToModelView requires paths, transform, and geometryTransform.');
157
+ }
158
+ return model;
159
+ }