@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.
- package/README.md +3 -3
- package/dist/BuildInfo.js +3 -3
- package/dist/BuildInfo.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layout/analyzeTemplate.js.map +1 -1
- package/dist/layout/clipUtils.js.map +1 -1
- package/dist/layout/colorConstants.js.map +1 -1
- package/dist/layout/elementKinds.js.map +1 -1
- package/dist/layout/exportDrawing.js.map +1 -1
- package/dist/layout/filters.js.map +1 -1
- package/dist/layout/geometryConstants.js.map +1 -1
- package/dist/layout/index.js.map +1 -1
- package/dist/layout/itemBuilders.js.map +1 -1
- package/dist/layout/labelsRender.js.map +1 -1
- package/dist/layout/legendConstants.js.map +1 -1
- package/dist/layout/legendRender.js.map +1 -1
- package/dist/layout/legendRows.js.map +1 -1
- package/dist/layout/legendUtils.js.map +1 -1
- package/dist/layout/metadata.js.map +1 -1
- package/dist/layout/model.js.map +1 -1
- package/dist/layout/options.js.map +1 -1
- package/dist/layout/presets.js.map +1 -1
- package/dist/layout/qrRender.js.map +1 -1
- package/dist/layout/renderDrawing.js.map +1 -1
- package/dist/layout/renderUtils.js.map +1 -1
- package/dist/layout/sectionValidation.js.map +1 -1
- package/dist/layout/templateVariables.js.map +1 -1
- package/dist/layout/typeGuards.js.map +1 -1
- package/dist/layout/types.js.map +1 -1
- package/dist/layout/utils.js.map +1 -1
- package/dist/layout/validation.js.map +1 -1
- package/dist/layout/validationHelpers.js.map +1 -1
- package/dist/layout/validationUtils.js.map +1 -1
- package/dist/layout/viewportPrep.js.map +1 -1
- package/dist/layout/viewportRender.js.map +1 -1
- package/dist/layout/viewportUtils.js.map +1 -1
- package/dist/layout/warnings.js.map +1 -1
- package/dist/printToPdf.js.map +1 -1
- package/dist/printToSvg.js.map +1 -1
- package/dist/sections.js.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/src/BuildInfo.ts +41 -41
- package/src/index.ts +10 -10
- package/src/layout/analyzeTemplate.ts +218 -218
- package/src/layout/clipUtils.ts +193 -193
- package/src/layout/colorConstants.ts +16 -16
- package/src/layout/elementKinds.ts +35 -35
- package/src/layout/exportDrawing.ts +206 -206
- package/src/layout/filters.ts +80 -80
- package/src/layout/geometryConstants.ts +11 -11
- package/src/layout/index.ts +671 -671
- package/src/layout/itemBuilders.ts +159 -159
- package/src/layout/labelsRender.ts +170 -170
- package/src/layout/legendConstants.ts +11 -11
- package/src/layout/legendRender.ts +543 -543
- package/src/layout/legendRows.ts +62 -62
- package/src/layout/legendUtils.ts +144 -144
- package/src/layout/metadata.ts +210 -210
- package/src/layout/model.ts +372 -372
- package/src/layout/options.ts +17 -17
- package/src/layout/presets.ts +51 -51
- package/src/layout/qrRender.ts +126 -126
- package/src/layout/qrcode.d.ts +2 -2
- package/src/layout/renderDrawing.ts +710 -710
- package/src/layout/renderUtils.ts +138 -138
- package/src/layout/sectionValidation.ts +2 -2
- package/src/layout/templateVariables.ts +317 -317
- package/src/layout/typeGuards.ts +9 -9
- package/src/layout/types.ts +835 -835
- package/src/layout/utils.ts +85 -85
- package/src/layout/validation.ts +392 -392
- package/src/layout/validationHelpers.ts +59 -59
- package/src/layout/validationUtils.ts +6 -6
- package/src/layout/viewportPrep.ts +351 -351
- package/src/layout/viewportRender.ts +442 -442
- package/src/layout/viewportUtils.ts +50 -50
- package/src/layout/warnings.ts +3 -3
- package/src/printToPdf.ts +108 -108
- package/src/printToSvg.ts +188 -188
- package/src/sections.ts +1019 -1019
- 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
|
+
}
|