@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.
- package/dist/BuildInfo.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/layout/analyzeTemplate.d.ts +4 -4
- package/dist/layout/analyzeTemplate.d.ts.map +1 -1
- package/dist/layout/analyzeTemplate.js +18 -16
- package/dist/layout/analyzeTemplate.js.map +1 -1
- package/dist/layout/classClosure.d.ts +41 -0
- package/dist/layout/classClosure.d.ts.map +1 -0
- package/dist/layout/classClosure.js +143 -0
- package/dist/layout/classClosure.js.map +1 -0
- package/dist/layout/clipUtils.d.ts +11 -0
- package/dist/layout/clipUtils.d.ts.map +1 -1
- package/dist/layout/clipUtils.js +67 -6
- package/dist/layout/clipUtils.js.map +1 -1
- package/dist/layout/filters.d.ts +14 -8
- package/dist/layout/filters.d.ts.map +1 -1
- package/dist/layout/filters.js +28 -3
- package/dist/layout/filters.js.map +1 -1
- package/dist/layout/index.d.ts +24 -57
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +20 -337
- package/dist/layout/index.js.map +1 -1
- package/dist/layout/itemBuilders.d.ts +11 -0
- package/dist/layout/itemBuilders.d.ts.map +1 -1
- package/dist/layout/itemBuilders.js +17 -3
- package/dist/layout/itemBuilders.js.map +1 -1
- package/dist/layout/labelsRender.d.ts.map +1 -1
- package/dist/layout/labelsRender.js +15 -27
- package/dist/layout/labelsRender.js.map +1 -1
- package/dist/layout/legendRender.d.ts +18 -6
- package/dist/layout/legendRender.d.ts.map +1 -1
- package/dist/layout/legendRender.js +49 -79
- package/dist/layout/legendRender.js.map +1 -1
- package/dist/layout/metadata.d.ts.map +1 -1
- package/dist/layout/metadata.js +26 -5
- package/dist/layout/metadata.js.map +1 -1
- package/dist/layout/model.d.ts +4 -3
- package/dist/layout/model.d.ts.map +1 -1
- package/dist/layout/model.js +17 -57
- package/dist/layout/model.js.map +1 -1
- package/dist/layout/options.d.ts +1 -2
- package/dist/layout/options.d.ts.map +1 -1
- package/dist/layout/options.js.map +1 -1
- package/dist/layout/preflight.d.ts +32 -0
- package/dist/layout/preflight.d.ts.map +1 -0
- package/dist/layout/preflight.js +69 -0
- package/dist/layout/preflight.js.map +1 -0
- package/dist/layout/qrRender.d.ts.map +1 -1
- package/dist/layout/qrRender.js +11 -10
- package/dist/layout/qrRender.js.map +1 -1
- package/dist/layout/renderDrawing.d.ts +28 -17
- package/dist/layout/renderDrawing.d.ts.map +1 -1
- package/dist/layout/renderDrawing.js +107 -88
- package/dist/layout/renderDrawing.js.map +1 -1
- package/dist/layout/renderLayout.d.ts +25 -0
- package/dist/layout/renderLayout.d.ts.map +1 -0
- package/dist/layout/renderLayout.js +315 -0
- package/dist/layout/renderLayout.js.map +1 -0
- package/dist/layout/renderTemplateSheet.d.ts +99 -0
- package/dist/layout/renderTemplateSheet.d.ts.map +1 -0
- package/dist/layout/renderTemplateSheet.js +296 -0
- package/dist/layout/renderTemplateSheet.js.map +1 -0
- package/dist/layout/renderUtils.d.ts +19 -0
- package/dist/layout/renderUtils.d.ts.map +1 -1
- package/dist/layout/renderUtils.js +47 -4
- package/dist/layout/renderUtils.js.map +1 -1
- package/dist/layout/sectionDefs.d.ts +65 -0
- package/dist/layout/sectionDefs.d.ts.map +1 -0
- package/dist/layout/sectionDefs.js +88 -0
- package/dist/layout/sectionDefs.js.map +1 -0
- package/dist/layout/slabModelView.d.ts +34 -0
- package/dist/layout/slabModelView.d.ts.map +1 -0
- package/dist/layout/slabModelView.js +175 -0
- package/dist/layout/slabModelView.js.map +1 -0
- package/dist/layout/templateVariables.d.ts +1 -1
- package/dist/layout/templateVariables.d.ts.map +1 -1
- package/dist/layout/templateVariables.js +21 -9
- package/dist/layout/templateVariables.js.map +1 -1
- package/dist/layout/types.d.ts +164 -83
- package/dist/layout/types.d.ts.map +1 -1
- package/dist/layout/types.js.map +1 -1
- package/dist/layout/validation.d.ts.map +1 -1
- package/dist/layout/validation.js +78 -33
- package/dist/layout/validation.js.map +1 -1
- package/dist/layout/viewportPrep.d.ts +8 -6
- package/dist/layout/viewportPrep.d.ts.map +1 -1
- package/dist/layout/viewportPrep.js +35 -23
- package/dist/layout/viewportPrep.js.map +1 -1
- package/dist/layout/viewportRender.d.ts +9 -7
- package/dist/layout/viewportRender.d.ts.map +1 -1
- package/dist/layout/viewportRender.js +93 -126
- package/dist/layout/viewportRender.js.map +1 -1
- package/dist/layout/viewportUtils.d.ts +20 -0
- package/dist/layout/viewportUtils.d.ts.map +1 -1
- package/dist/layout/viewportUtils.js +107 -4
- package/dist/layout/viewportUtils.js.map +1 -1
- package/dist/printToPdf.d.ts +7 -1
- package/dist/printToPdf.d.ts.map +1 -1
- package/dist/printToPdf.js +81 -14
- package/dist/printToPdf.js.map +1 -1
- package/dist/printToSvg.d.ts.map +1 -1
- package/dist/printToSvg.js +7 -3
- package/dist/printToSvg.js.map +1 -1
- package/dist/schemaUtils.d.ts +9 -0
- package/dist/schemaUtils.d.ts.map +1 -0
- package/dist/schemaUtils.js +14 -0
- package/dist/schemaUtils.js.map +1 -0
- package/dist/templates/default-template.json +30 -8
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -1
- package/dist/types.js.map +1 -1
- package/package.json +8 -3
- package/src/index.ts +4 -0
- package/src/layout/analyzeTemplate.ts +21 -19
- package/src/layout/classClosure.ts +142 -0
- package/src/layout/clipUtils.ts +65 -6
- package/src/layout/filters.ts +31 -3
- package/src/layout/index.ts +61 -571
- package/src/layout/itemBuilders.ts +20 -5
- package/src/layout/labelsRender.ts +23 -40
- package/src/layout/legendRender.ts +64 -84
- package/src/layout/metadata.ts +26 -5
- package/src/layout/model.ts +21 -66
- package/src/layout/options.ts +1 -2
- package/src/layout/preflight.ts +96 -0
- package/src/layout/qrRender.ts +11 -10
- package/src/layout/renderDrawing.ts +132 -110
- package/src/layout/renderLayout.ts +483 -0
- package/src/layout/renderTemplateSheet.ts +435 -0
- package/src/layout/renderUtils.ts +53 -4
- package/src/layout/sectionDefs.ts +131 -0
- package/src/layout/slabModelView.ts +196 -0
- package/src/layout/templateVariables.ts +25 -9
- package/src/layout/types.ts +160 -70
- package/src/layout/validation.ts +98 -32
- package/src/layout/viewportPrep.ts +47 -29
- package/src/layout/viewportRender.ts +114 -141
- package/src/layout/viewportUtils.ts +118 -5
- package/src/printToPdf.ts +84 -15
- package/src/printToSvg.ts +7 -3
- package/src/schemaUtils.ts +13 -0
- package/src/templates/default-template.json +30 -8
- package/src/types.ts +12 -2
- package/templates/default-a0-landscape.json +171 -66
- package/templates/default-a0-portrait.json +159 -60
- package/templates/default-a1-landscape.json +135 -48
- package/templates/default-a1-portrait.json +123 -42
- package/templates/default-a3-landscape.json +87 -24
- package/templates/default-a3-portrait.json +99 -30
- package/templates/default-a4-landscape.json +75 -18
- package/templates/default-a4-portrait.json +75 -18
- package/templates/llm-authoring-guide.md +247 -0
- package/templates/template-schema.json +295 -120
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { escapeAttr, getPathValue, resolvePath } from './renderUtils';
|
|
2
|
-
import { rotateBounds, pickScale } from './viewportUtils';
|
|
3
|
-
import {
|
|
1
|
+
import { escapeAttr, getPathValue, resolvePath, formatNumber } from './renderUtils';
|
|
2
|
+
import { rotateBounds, pickScale, rotatedContentExtents } from './viewportUtils';
|
|
3
|
+
import { matchesFilter } from './filters';
|
|
4
4
|
import { isRecord, getProp } from './typeGuards';
|
|
5
5
|
// Debug overlay for clip polygons. Keep false for normal builds.
|
|
6
6
|
const DEBUG_CLIP_OVERLAY = false;
|
|
@@ -28,7 +28,7 @@ const matApply = (m, p) => {
|
|
|
28
28
|
const [a, b, c, d, e, f] = m;
|
|
29
29
|
return { x: a * p.x + c * p.y + e, y: b * p.x + d * p.y + f };
|
|
30
30
|
};
|
|
31
|
-
export const renderViewportElement = (vp, baseModel, dataWithViewport, preferredScales, scales,
|
|
31
|
+
export const renderViewportElement = (vp, baseModel, dataWithViewport, preferredScales, scales, resourceMap, viewportScales, viewportClipPolygonsLocal, viewportClipBounds, viewportClipLabels, viewportClipStyles, viewportHighlight, viewportRenderInfo, warnings, legendColorMaps, precomputed, includeFilters) => {
|
|
32
32
|
const parts = [];
|
|
33
33
|
if (!baseModel) {
|
|
34
34
|
// Warning already pushed by viewportPrep; avoid duplicate.
|
|
@@ -54,7 +54,19 @@ export const renderViewportElement = (vp, baseModel, dataWithViewport, preferred
|
|
|
54
54
|
const mmPerModel = scaleInfo.mmPerModelUnit;
|
|
55
55
|
const contentWidthMm = renderWidthModel * mmPerModel;
|
|
56
56
|
const contentHeightMm = renderHeightModel * mmPerModel;
|
|
57
|
-
|
|
57
|
+
// Tight bounds of the rotated geometry (see rotatedContentExtents): sizes the
|
|
58
|
+
// content box and re-centers the rotation pivot so diagonally-oriented models
|
|
59
|
+
// straightened via rotationDeg fill the viewport instead of shrinking to the
|
|
60
|
+
// inflated AABB-of-AABB. Clip keeps the AABB path — its bounds are the clip
|
|
61
|
+
// region, not the full geometry.
|
|
62
|
+
const tight = hasClip ? null : rotatedContentExtents(model, rotationDeg);
|
|
63
|
+
if (tight && precomputed?.renderCenterModelX == null && precomputed?.renderCenterModelY == null) {
|
|
64
|
+
renderCenterModelX = tight.centerViewX;
|
|
65
|
+
renderCenterModelY = tight.centerViewY;
|
|
66
|
+
}
|
|
67
|
+
const rotated = tight
|
|
68
|
+
? { width: tight.width * mmPerModel, height: tight.height * mmPerModel }
|
|
69
|
+
: rotateBounds(contentWidthMm, contentHeightMm, rotationDeg);
|
|
58
70
|
const centerX = vp.align === 'topLeft' ? vp.xMm + rotated.width / 2 : vp.xMm + vp.widthMm / 2;
|
|
59
71
|
const centerY = vp.align === 'topLeft' ? vp.yMm + rotated.height / 2 : vp.yMm + vp.heightMm / 2;
|
|
60
72
|
const contentOffsetX = vp.align === 'topLeft' ? vp.xMm : vp.xMm + Math.max(0, (vp.widthMm - rotated.width) * 0.5);
|
|
@@ -63,33 +75,13 @@ export const renderViewportElement = (vp, baseModel, dataWithViewport, preferred
|
|
|
63
75
|
let clipId = null;
|
|
64
76
|
const clipDebugPolys = [];
|
|
65
77
|
const clipStyle = viewportClipStyles[vp.id];
|
|
66
|
-
const clipStroke = clipStyle?.
|
|
67
|
-
const clipStrokeWidth = clipStyle?.
|
|
68
|
-
const clipStrokeOpacity = clipStyle?.
|
|
78
|
+
const clipStroke = clipStyle?.color;
|
|
79
|
+
const clipStrokeWidth = clipStyle?.widthMm;
|
|
80
|
+
const clipStrokeOpacity = clipStyle?.opacity;
|
|
81
|
+
const clipDash = clipStyle?.dash;
|
|
69
82
|
const clipFill = clipStyle?.fill;
|
|
70
83
|
const clipFillOpacity = clipStyle?.fillOpacity;
|
|
71
|
-
const transformAttr = `translate(${centerX} ${centerY}) rotate(${rotationDeg}) scale(${mmPerModel}) translate(${-renderCenterModelX} ${-renderCenterModelY})`;
|
|
72
|
-
// Debug: log model bounds and path breakdown
|
|
73
|
-
// if (model.transform) {
|
|
74
|
-
// console.log(`[DEBUG MODEL vp="${vp.id}"] Model bounds: viewMinX=${model.transform.viewMinX.toFixed(2)} viewMaxX=${model.transform.viewMaxX?.toFixed(2)} viewMinZ=${model.transform.viewMinZ.toFixed(2)} viewMaxZ=${model.transform.viewMaxZ.toFixed(2)} viewWidth=${model.transform.viewWidth.toFixed(2)} viewHeight=${model.transform.viewHeight.toFixed(2)}`);
|
|
75
|
-
// if (model.rawPaths) {
|
|
76
|
-
// const fills = model.rawPaths.filter(p => p.meta?.isFill);
|
|
77
|
-
// const segments = model.rawPaths.filter(p => p.meta?.isSegment);
|
|
78
|
-
// const other = model.rawPaths.filter(p => !p.meta?.isFill && !p.meta?.isSegment);
|
|
79
|
-
// console.log(`[DEBUG MODEL vp="${vp.id}"] Path breakdown: ${model.rawPaths.length} total (${fills.length} fills, ${segments.length} segments, ${other.length} other)`);
|
|
80
|
-
// // Sample a few segment paths
|
|
81
|
-
// const sampleSegments = segments.slice(0, 3);
|
|
82
|
-
// sampleSegments.forEach((seg, idx) => {
|
|
83
|
-
// console.log(`[DEBUG MODEL vp="${vp.id}"] Segment ${idx}: class=${seg.meta?.class} fill=${seg.fill} stroke=${seg.stroke} strokeWidth=${seg.strokeWidth} d=${seg.d.substring(0, 50)}...`);
|
|
84
|
-
// });
|
|
85
|
-
// // Check if geometryTransform is present
|
|
86
|
-
// if (model.geometryTransform) {
|
|
87
|
-
// console.log(`[DEBUG MODEL vp="${vp.id}"] geometryTransform="${model.geometryTransform}"`);
|
|
88
|
-
// } else {
|
|
89
|
-
// console.log(`[DEBUG MODEL vp="${vp.id}"] WARNING: geometryTransform is MISSING!`);
|
|
90
|
-
// }
|
|
91
|
-
// }
|
|
92
|
-
// }
|
|
84
|
+
const transformAttr = `translate(${formatNumber(centerX)} ${formatNumber(centerY)}) rotate(${rotationDeg}) scale(${mmPerModel}) translate(${formatNumber(-renderCenterModelX)} ${formatNumber(-renderCenterModelY)})`;
|
|
93
85
|
// Guard: detect NaN/Infinity in computed transform values
|
|
94
86
|
if (!Number.isFinite(centerX) ||
|
|
95
87
|
!Number.isFinite(centerY) ||
|
|
@@ -99,81 +91,62 @@ export const renderViewportElement = (vp, baseModel, dataWithViewport, preferred
|
|
|
99
91
|
warnings.push(`Viewport '${vp.id}' has non-finite transform values (centerX=${centerX}, centerY=${centerY}, mmPerModel=${mmPerModel}). Skipping render.`);
|
|
100
92
|
return parts;
|
|
101
93
|
}
|
|
102
|
-
// console.log(`[DEBUG CLIP APPLY vp="${vp.id}"] clipPolysLocal exists: ${!!clipPolysLocal}, length: ${clipPolysLocal?.length ?? 0}`);
|
|
103
94
|
if (clipPolysLocal && clipPolysLocal.length) {
|
|
104
|
-
// console.log(`[DEBUG CLIP APPLY vp="${vp.id}"] *** APPLYING CLIP-PATH *** with ${clipPolysLocal.length} polygons`);
|
|
105
95
|
if (clipPolysLocal.every((poly) => !poly.length)) {
|
|
106
96
|
warnings.push(`Viewport '${vp.id}' clip has no points after prep; skipping clip.`);
|
|
107
97
|
}
|
|
108
98
|
clipId = `clip-${vp.id}-${parts.length}`;
|
|
109
|
-
const polyPartsPage = [];
|
|
110
99
|
// Clip polygon points are in model-view coordinates (not SVG-internal),
|
|
111
100
|
// so transform them with outer only (model-view → page). Do NOT apply
|
|
112
101
|
// geometryTransform — that maps SVG-internal → model-view, which is only
|
|
113
102
|
// correct for path data inside the <g transform="geometryTransform"> group.
|
|
114
103
|
const outer = matMultiply(matTranslate(centerX, centerY), matMultiply(matRotateDeg(rotationDeg), matMultiply(matScale(mmPerModel, mmPerModel), matTranslate(-renderCenterModelX, -renderCenterModelY))));
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (p.x < clipMinX)
|
|
120
|
-
clipMinX = p.x;
|
|
121
|
-
if (p.x > clipMaxX)
|
|
122
|
-
clipMaxX = p.x;
|
|
123
|
-
if (p.y < clipMinY)
|
|
124
|
-
clipMinY = p.y;
|
|
125
|
-
if (p.y > clipMaxY)
|
|
126
|
-
clipMaxY = p.y;
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
// console.log(`[DEBUG CLIP vp="${vp.id}"] Local bounds: x=${clipMinX.toFixed(2)}..${clipMaxX.toFixed(2)} y=${clipMinY.toFixed(2)}..${clipMaxY.toFixed(2)} (w=${(clipMaxX - clipMinX).toFixed(2)} h=${(clipMaxY - clipMinY).toFixed(2)})`);
|
|
104
|
+
// Rings from unioned clips can include holes — a single even-odd path
|
|
105
|
+
// (rather than one polygon per ring) makes holes subtract correctly in
|
|
106
|
+
// both the clip mask and any fill.
|
|
107
|
+
const ringDs = [];
|
|
130
108
|
clipPolysLocal.forEach((poly) => {
|
|
131
109
|
const pagePtsArr = poly.map((p) => matApply(outer, p));
|
|
132
|
-
const pagePts = pagePtsArr.map((p) => `${p.x},${p.y}`).join(' ');
|
|
133
|
-
|
|
134
|
-
if (clipStroke
|
|
135
|
-
clipDebugPolys.push(`<polygon points="${pagePts}"` +
|
|
136
|
-
|
|
110
|
+
const pagePts = pagePtsArr.map((p) => `${formatNumber(p.x)},${formatNumber(p.y)}`).join(' ');
|
|
111
|
+
ringDs.push('M ' + pagePtsArr.map((p) => `${formatNumber(p.x)} ${formatNumber(p.y)}`).join(' L ') + ' Z');
|
|
112
|
+
if (clipStroke) {
|
|
113
|
+
clipDebugPolys.push(`<polygon points="${pagePts}" fill="none"` +
|
|
114
|
+
` stroke="${escapeAttr(clipStroke)}"` +
|
|
137
115
|
(clipStrokeWidth ? ` stroke-width="${clipStrokeWidth}"` : '') +
|
|
138
116
|
(clipStrokeOpacity !== undefined ? ` stroke-opacity="${clipStrokeOpacity}"` : '') +
|
|
139
|
-
(
|
|
140
|
-
(clipFillOpacity !== undefined ? ` fill-opacity="${clipFillOpacity}"` : '') +
|
|
117
|
+
(clipDash ? ` stroke-dasharray="${escapeAttr(clipDash)}"` : '') +
|
|
141
118
|
` />`);
|
|
142
119
|
}
|
|
143
120
|
if (DEBUG_CLIP_OVERLAY) {
|
|
144
121
|
clipDebugPolys.push(`<polygon points="${pagePts}" stroke="#ff00ff" stroke-width="0.4" fill="#000000" fill-opacity="0.35" />`);
|
|
145
122
|
}
|
|
146
123
|
});
|
|
147
|
-
|
|
124
|
+
if (clipFill && clipFill !== 'none') {
|
|
125
|
+
clipDebugPolys.unshift(`<path d="${ringDs.join(' ')}" fill-rule="evenodd" fill="${escapeAttr(clipFill)}"` +
|
|
126
|
+
(clipFillOpacity !== undefined ? ` fill-opacity="${clipFillOpacity}"` : '') +
|
|
127
|
+
` stroke="none" />`);
|
|
128
|
+
}
|
|
129
|
+
parts.push(`<defs><clipPath id="${clipId}" clipPathUnits="userSpaceOnUse"><path d="${ringDs.join(' ')}" clip-rule="evenodd" /></clipPath></defs>`);
|
|
148
130
|
}
|
|
149
131
|
parts.push(`<g${clipId ? ` clip-path="url(#${clipId})"` : ''}>`);
|
|
150
132
|
parts.push(`<g transform="${transformAttr}">`);
|
|
151
133
|
// Render fill polygons below structural lines
|
|
152
134
|
const allowedGids = viewportHighlight[vp.id]?.gids ?? null;
|
|
153
|
-
const fillSvg = buildFillSvg(vp, dataWithViewport, model, allowedGids, legendColorMaps);
|
|
135
|
+
const fillSvg = buildFillSvg(vp, dataWithViewport, model, allowedGids, legendColorMaps, warnings, includeFilters);
|
|
154
136
|
if (fillSvg)
|
|
155
137
|
parts.push(fillSvg);
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// console.log(`[DEBUG PATH vp="${vp.id}"] Sample wall path starts at x=${dMatch[1]} y=${dMatch[2]} (world coords, will be transformed by geometryTransform)`);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// Compensate stroke-widths for the viewport scale transform.
|
|
173
|
-
// Paths have stroke-width in model units; the scale(mmPerModel) transform
|
|
174
|
-
// would make them thinner/thicker depending on zoom. Dividing by mmPerModel
|
|
175
|
-
// ensures strokes render at their intended mm size regardless of viewport scale.
|
|
176
|
-
const compensatedSvg = model.groupSvg.replace(/stroke-width="([0-9.eE+-]+)"/g, (_, w) => `stroke-width="${parseFloat(w) / mmPerModel}"`);
|
|
138
|
+
// Compensate stroke-widths AND dash arrays for the viewport scale
|
|
139
|
+
// transform. Path styling is authored in mm; the scale(mmPerModel)
|
|
140
|
+
// transform would multiply both, so divide them out — otherwise
|
|
141
|
+
// stroke-dasharray "4 2" means 4 model units (metres), not 4 mm, and the
|
|
142
|
+
// dash period changes with the viewport scale.
|
|
143
|
+
const compensatedSvg = model.groupSvg
|
|
144
|
+
.replace(/stroke-width="([0-9.eE+-]+)"/g, (_, w) => `stroke-width="${parseFloat(w) / mmPerModel}"`)
|
|
145
|
+
.replace(/stroke-dasharray="([0-9.eE+\-, ]+)"/g, (_, dashes) => `stroke-dasharray="${dashes
|
|
146
|
+
.split(/[\s,]+/)
|
|
147
|
+
.filter(Boolean)
|
|
148
|
+
.map((d) => parseFloat(d) / mmPerModel)
|
|
149
|
+
.join(' ')}"`);
|
|
177
150
|
parts.push(compensatedSvg);
|
|
178
151
|
parts.push('</g>');
|
|
179
152
|
parts.push('</g>');
|
|
@@ -196,25 +169,25 @@ export const renderViewportElement = (vp, baseModel, dataWithViewport, preferred
|
|
|
196
169
|
if (vp.border) {
|
|
197
170
|
const borderStroke = vp.border.stroke ?? '#000';
|
|
198
171
|
const borderWidth = vp.border.strokeWidthMm ?? 0.25;
|
|
199
|
-
parts.push(`<rect x="${vp.xMm}" y="${vp.yMm}" width="${vp.widthMm}" height="${vp.heightMm}" stroke="${escapeAttr(borderStroke)}" stroke-width="${borderWidth}" fill="none"${vp.border.
|
|
172
|
+
parts.push(`<rect x="${vp.xMm}" y="${vp.yMm}" width="${vp.widthMm}" height="${vp.heightMm}" stroke="${escapeAttr(borderStroke)}" stroke-width="${borderWidth}" fill="none"${vp.border.dash ? ` stroke-dasharray="${escapeAttr(vp.border.dash)}"` : ''} />`);
|
|
200
173
|
}
|
|
201
174
|
if (vp.northArrow) {
|
|
202
175
|
const arrow = vp.northArrow;
|
|
203
|
-
const
|
|
176
|
+
const resource = resourceMap.get(arrow.resourceId);
|
|
204
177
|
const size = arrow.sizeMm ?? 12;
|
|
205
178
|
const offsetX = arrow.offsetXMm ?? 8;
|
|
206
179
|
const offsetY = arrow.offsetYMm ?? 8;
|
|
207
180
|
const arrowX = vp.xMm + vp.widthMm - offsetX - size;
|
|
208
181
|
const arrowY = vp.yMm + offsetY;
|
|
209
182
|
const arrowRotation = arrow.rotationDeg ?? 0;
|
|
210
|
-
if (!
|
|
211
|
-
warnings.push(`Missing
|
|
183
|
+
if (!resource) {
|
|
184
|
+
warnings.push(`Missing resource '${arrow.resourceId}' for north arrow on viewport '${vp.id}'.`);
|
|
212
185
|
}
|
|
213
|
-
else if (
|
|
214
|
-
parts.push(`<image href="${escapeAttr(
|
|
186
|
+
else if (resource.src) {
|
|
187
|
+
parts.push(`<image href="${escapeAttr(resource.src)}" x="${arrowX}" y="${arrowY}" width="${size}" height="${size}" transform="rotate(${arrowRotation}, ${arrowX + size / 2}, ${arrowY + size / 2})" />`);
|
|
215
188
|
}
|
|
216
|
-
else if (
|
|
217
|
-
parts.push(`<g transform="translate(${arrowX} ${arrowY}) rotate(${arrowRotation} ${size / 2} ${size / 2})">${
|
|
189
|
+
else if (resource.content) {
|
|
190
|
+
parts.push(`<g transform="translate(${arrowX} ${arrowY}) rotate(${arrowRotation} ${size / 2} ${size / 2})">${resource.content}</g>`);
|
|
218
191
|
}
|
|
219
192
|
}
|
|
220
193
|
return parts;
|
|
@@ -227,7 +200,7 @@ export const renderViewportElement = (vp, baseModel, dataWithViewport, preferred
|
|
|
227
200
|
* Returns an SVG fragment in model-view coordinates (same space as model.groupSvg),
|
|
228
201
|
* or null if no fills should be rendered.
|
|
229
202
|
*/
|
|
230
|
-
function buildFillSvg(vp, data, model, allowedGids, legendColorMaps) {
|
|
203
|
+
function buildFillSvg(vp, data, model, allowedGids, legendColorMaps, warnings, includeFilters) {
|
|
231
204
|
const fills = vp.fills;
|
|
232
205
|
if (!fills?.length || !model.transform)
|
|
233
206
|
return null;
|
|
@@ -239,7 +212,7 @@ function buildFillSvg(vp, data, model, allowedGids, legendColorMaps) {
|
|
|
239
212
|
for (const rule of fills) {
|
|
240
213
|
const opacity = rule.fillOpacity ?? 0.35;
|
|
241
214
|
// Find items matching this fill rule
|
|
242
|
-
let matching = items.filter((item) => isRecord(item) ?
|
|
215
|
+
let matching = items.filter((item) => isRecord(item) ? matchesFilter(item, rule.class, rule.where) : false);
|
|
243
216
|
// Filter by allowed GIDs (from clip polygon center point test)
|
|
244
217
|
// Only apply if BOTH: clip filtering is active AND this fill rule opts in via matchClipFilter
|
|
245
218
|
if (allowedGids !== null && rule.matchClipFilter === true) {
|
|
@@ -247,21 +220,19 @@ function buildFillSvg(vp, data, model, allowedGids, legendColorMaps) {
|
|
|
247
220
|
const gid = getProp(item, 'gid') ?? getProp(item, 'id');
|
|
248
221
|
return gid !== null && gid !== undefined && allowedGids.has(String(gid));
|
|
249
222
|
});
|
|
250
|
-
// console.log(`[DEBUG FILL vp="${vp.id}"] Clip filter applied (matchClipFilter=true): ${beforeFilter} → ${matching.length} items (allowedGids.size=${allowedGids.size})`);
|
|
251
223
|
}
|
|
252
|
-
else if (
|
|
253
|
-
//
|
|
224
|
+
else if (includeFilters?.length) {
|
|
225
|
+
// The viewport's includeClasses gates generic fills exactly like it gates
|
|
226
|
+
// line paths — the include list is authoritative for what geometry appears.
|
|
227
|
+
// GID-gated rules (matchClipFilter with an active clip/highlight set, the
|
|
228
|
+
// branch above) are exempt, mirroring highlight/clip classes in
|
|
229
|
+
// filterModelForViewport: a clipped apartment's space fills must render
|
|
230
|
+
// even in a view whose includeClasses excludes spaces.
|
|
231
|
+
matching = matching.filter((item) => isRecord(item) &&
|
|
232
|
+
includeFilters.some((f) => typeof f === 'string'
|
|
233
|
+
? matchesFilter(item, f, undefined)
|
|
234
|
+
: matchesFilter(item, f.class, f.where)));
|
|
254
235
|
}
|
|
255
|
-
// console.log(`[DEBUG FILL vp="${vp.id}"] Fill rule (class="${rule.class}", where=${JSON.stringify(rule.where)}) matched ${matching.length} items`);
|
|
256
|
-
// if (matching.length > 0) {
|
|
257
|
-
// console.log(`[DEBUG FILL vp="${vp.id}"] First matched item:`, {
|
|
258
|
-
// gid: getProp(matching[0], 'gid'),
|
|
259
|
-
// name: getProp(matching[0], 'name'),
|
|
260
|
-
// ifcClass: getProp(matching[0], 'ifcClass'),
|
|
261
|
-
// propertySets: getProp(matching[0], 'propertySets'),
|
|
262
|
-
// polylines: Array.isArray(getProp(matching[0], 'polylines')) ? (getProp(matching[0], 'polylines') as any[]).length : 0
|
|
263
|
-
// });
|
|
264
|
-
// }
|
|
265
236
|
if (!matching.length)
|
|
266
237
|
continue;
|
|
267
238
|
for (const item of matching) {
|
|
@@ -277,37 +248,32 @@ function buildFillSvg(vp, data, model, allowedGids, legendColorMaps) {
|
|
|
277
248
|
fillColor = `rgb(${color[0]},${color[1]},${color[2]})`;
|
|
278
249
|
}
|
|
279
250
|
else if (rule.fill === 'legend') {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
continue;
|
|
251
|
+
const legendColors = legendColorMaps?.get(vp.id);
|
|
252
|
+
if (!legendColors) {
|
|
253
|
+
warnings.push(`Viewport '${vp.id}' has fill: "legend" but no legend with assignColors targets it (legends exist for: ${Array.from(legendColorMaps?.keys() ?? []).join(', ') || 'none'}).`);
|
|
254
|
+
break;
|
|
285
255
|
}
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
256
|
+
// Resolve the item's colour: by item identity first (covers
|
|
257
|
+
// ungrouped legends — every item its own colour), then by the
|
|
258
|
+
// legend's groupBy property value (shared across a group).
|
|
259
|
+
let resolved;
|
|
260
|
+
const gid = getProp(item, 'gid') ?? getProp(item, 'id');
|
|
261
|
+
if (gid !== null && gid !== undefined) {
|
|
262
|
+
resolved = legendColors.byGid.get(String(gid));
|
|
263
|
+
}
|
|
264
|
+
if (!resolved && legendColors.groupBy) {
|
|
265
|
+
const value = resolvePath(item, legendColors.groupBy);
|
|
292
266
|
if (value !== undefined && value !== null) {
|
|
293
|
-
|
|
294
|
-
break;
|
|
267
|
+
resolved = legendColors.byValue.get(String(value));
|
|
295
268
|
}
|
|
296
269
|
}
|
|
297
|
-
if (!
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
// console.log(`[FILL] No color mapping for "${legendKey}" (property: ${matchedProperty}). Available keys:`, Array.from(legendColorMap.keys()));
|
|
301
|
-
// }
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
fillColor = legendColorMap.get(legendKey);
|
|
305
|
-
// console.log(`[FILL] ✓ Matched "${legendKey}" -> ${fillColor}`);
|
|
270
|
+
if (!resolved)
|
|
271
|
+
continue; // item not represented in the legend
|
|
272
|
+
fillColor = resolved;
|
|
306
273
|
}
|
|
307
274
|
else {
|
|
308
275
|
fillColor = rule.fill;
|
|
309
276
|
}
|
|
310
|
-
// console.log(`[FILL] Processing ${polylines.length} polyline(s) for item with color ${fillColor}`);
|
|
311
277
|
for (const polyline of polylines) {
|
|
312
278
|
const points = isRecord(polyline) ? getProp(polyline, 'points') : null;
|
|
313
279
|
if (!Array.isArray(points) || points.length < 3)
|
|
@@ -322,7 +288,9 @@ function buildFillSvg(vp, data, model, allowedGids, legendColorMaps) {
|
|
|
322
288
|
continue;
|
|
323
289
|
const mx = px - viewMinX;
|
|
324
290
|
const my = viewMaxZ - pz;
|
|
325
|
-
pathParts.push(i === 0
|
|
291
|
+
pathParts.push(i === 0
|
|
292
|
+
? `M ${formatNumber(mx)} ${formatNumber(my)}`
|
|
293
|
+
: `L ${formatNumber(mx)} ${formatNumber(my)}`);
|
|
326
294
|
}
|
|
327
295
|
if (pathParts.length < 3)
|
|
328
296
|
continue;
|
|
@@ -331,7 +299,6 @@ function buildFillSvg(vp, data, model, allowedGids, legendColorMaps) {
|
|
|
331
299
|
}
|
|
332
300
|
}
|
|
333
301
|
}
|
|
334
|
-
// console.log(`[FILL] Generated ${parts.length} fill polygon(s) for viewport "${vp.id}"`);
|
|
335
302
|
return parts.length ? `<g>${parts.join('')}</g>` : null;
|
|
336
303
|
}
|
|
337
304
|
//# sourceMappingURL=viewportRender.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewportRender.js","sourceRoot":"/","sources":["layout/viewportRender.ts"],"names":[],"mappings":"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","sourcesContent":["import { escapeAttr, getPathValue, resolvePath } from './renderUtils';\nimport { rotateBounds, pickScale } from './viewportUtils';\nimport { ViewportRenderInfo } from './labelsRender';\nimport { matchesV2Filter } from './filters';\nimport { ComputedViewportScale, SvgModelView, ViewportElement, AssetSpec } from './types';\nimport { isRecord, getProp } from './typeGuards';\n\ntype PolyPoint = { x: number; y: number };\n// Debug overlay for clip polygons. Keep false for normal builds.\nconst DEBUG_CLIP_OVERLAY = false;\n\ntype Mat = [number, number, number, number, number, number]; // a b c d e f\n\nconst matTranslate = (tx: number, ty: number): Mat => [1, 0, 0, 1, tx, ty];\nconst matScale = (sx: number, sy: number): Mat => [sx, 0, 0, sy, 0, 0];\nconst matRotateDeg = (deg: number): Mat => {\n const rad = (deg * Math.PI) / 180;\n const c = Math.cos(rad);\n const s = Math.sin(rad);\n return [c, s, -s, c, 0, 0];\n};\nconst matMultiply = (m1: Mat, m2: Mat): Mat => {\n const [a1, b1, c1, d1, e1, f1] = m1;\n const [a2, b2, c2, d2, e2, f2] = m2;\n return [\n a1 * a2 + c1 * b2,\n b1 * a2 + d1 * b2,\n a1 * c2 + c1 * d2,\n b1 * c2 + d1 * d2,\n a1 * e2 + c1 * f2 + e1,\n b1 * e2 + d1 * f2 + f1\n ];\n};\nconst matApply = (m: Mat, p: PolyPoint): PolyPoint => {\n const [a, b, c, d, e, f] = m;\n return { x: a * p.x + c * p.y + e, y: b * p.x + d * p.y + f };\n};\nexport const renderViewportElement = (\n vp: ViewportElement,\n baseModel: SvgModelView | null,\n dataWithViewport: any,\n preferredScales: number[],\n scales: Record<string, { preferredScales?: number[] }>,\n assetMap: Map<string, AssetSpec>,\n viewportScales: Record<string, ComputedViewportScale>,\n viewportClipPolygonsLocal: Record<string, Array<Array<PolyPoint>>>,\n viewportClipBounds: Record<\n string,\n { minX: number; maxX: number; minY: number; maxY: number; width: number; height: number }\n >,\n viewportClipLabels: Record<string, boolean>,\n viewportClipStyles: Record<\n string,\n { stroke?: string; strokeWidthMm?: number; strokeOpacity?: number; fill?: string; fillOpacity?: number }\n >,\n viewportHighlight: Record<string, { gids: Set<string> | null; styles: any }>,\n viewportRenderInfo: Record<string, ViewportRenderInfo>,\n warnings: string[],\n legendColorMaps?: Map<string, Map<string, string>>,\n precomputed?: {\n effectiveWidthModel?: number;\n effectiveHeightModel?: number;\n clipPolygonsMm?: Array<Array<PolyPoint>>;\n clipPolygonsLocal?: Array<Array<PolyPoint>>;\n clipBounds?: { minX: number; maxX: number; minY: number; maxY: number; width: number; height: number };\n renderCenterModelX?: number;\n renderCenterModelY?: number;\n }\n): string[] => {\n const parts: string[] = [];\n if (!baseModel) {\n // Warning already pushed by viewportPrep; avoid duplicate.\n return parts;\n }\n\n const model = baseModel;\n let renderWidthModel = precomputed?.effectiveWidthModel ?? model.widthModel;\n let renderHeightModel = precomputed?.effectiveHeightModel ?? model.heightModel;\n let renderCenterModelX = precomputed?.renderCenterModelX ?? renderWidthModel * 0.5;\n let renderCenterModelY = precomputed?.renderCenterModelY ?? renderHeightModel * 0.5;\n\n const clipBounds = viewportClipBounds[vp.id];\n const clipLocal = viewportClipPolygonsLocal[vp.id];\n const hasClip = !!clipBounds && Array.isArray(clipLocal) && clipLocal.length > 0;\n if (hasClip) {\n renderWidthModel = clipBounds!.width;\n renderHeightModel = clipBounds!.height;\n renderCenterModelX = (clipBounds!.minX + clipBounds!.maxX) * 0.5;\n renderCenterModelY = (clipBounds!.minY + clipBounds!.maxY) * 0.5;\n }\n\n const scaleInfo =\n viewportScales[vp.id] ??\n pickScale(\n vp,\n { ...model, widthModel: renderWidthModel, heightModel: renderHeightModel },\n preferredScales,\n scales\n );\n const rotationDeg = vp.rotationDeg ?? 0;\n const mmPerModel = scaleInfo.mmPerModelUnit;\n const contentWidthMm = renderWidthModel * mmPerModel;\n const contentHeightMm = renderHeightModel * mmPerModel;\n const rotated = rotateBounds(contentWidthMm, contentHeightMm, rotationDeg);\n\n const centerX = vp.align === 'topLeft' ? vp.xMm + rotated.width / 2 : vp.xMm + vp.widthMm / 2;\n const centerY = vp.align === 'topLeft' ? vp.yMm + rotated.height / 2 : vp.yMm + vp.heightMm / 2;\n\n const contentOffsetX = vp.align === 'topLeft' ? vp.xMm : vp.xMm + Math.max(0, (vp.widthMm - rotated.width) * 0.5);\n const contentOffsetY = vp.align === 'topLeft' ? vp.yMm : vp.yMm + Math.max(0, (vp.heightMm - rotated.height) * 0.5);\n\n const clipPolysLocal = clipLocal;\n let clipId: string | null = null;\n const clipDebugPolys: string[] = [];\n const clipStyle = viewportClipStyles[vp.id];\n const clipStroke = clipStyle?.stroke;\n const clipStrokeWidth = clipStyle?.strokeWidthMm;\n const clipStrokeOpacity = clipStyle?.strokeOpacity;\n const clipFill = clipStyle?.fill;\n const clipFillOpacity = clipStyle?.fillOpacity;\n const transformAttr = `translate(${centerX} ${centerY}) rotate(${rotationDeg}) scale(${mmPerModel}) translate(${-renderCenterModelX} ${-renderCenterModelY})`;\n\n // Debug: log model bounds and path breakdown\n // if (model.transform) {\n // console.log(`[DEBUG MODEL vp=\"${vp.id}\"] Model bounds: viewMinX=${model.transform.viewMinX.toFixed(2)} viewMaxX=${model.transform.viewMaxX?.toFixed(2)} viewMinZ=${model.transform.viewMinZ.toFixed(2)} viewMaxZ=${model.transform.viewMaxZ.toFixed(2)} viewWidth=${model.transform.viewWidth.toFixed(2)} viewHeight=${model.transform.viewHeight.toFixed(2)}`);\n\n // if (model.rawPaths) {\n // const fills = model.rawPaths.filter(p => p.meta?.isFill);\n // const segments = model.rawPaths.filter(p => p.meta?.isSegment);\n // const other = model.rawPaths.filter(p => !p.meta?.isFill && !p.meta?.isSegment);\n // console.log(`[DEBUG MODEL vp=\"${vp.id}\"] Path breakdown: ${model.rawPaths.length} total (${fills.length} fills, ${segments.length} segments, ${other.length} other)`);\n\n // // Sample a few segment paths\n // const sampleSegments = segments.slice(0, 3);\n // sampleSegments.forEach((seg, idx) => {\n // console.log(`[DEBUG MODEL vp=\"${vp.id}\"] Segment ${idx}: class=${seg.meta?.class} fill=${seg.fill} stroke=${seg.stroke} strokeWidth=${seg.strokeWidth} d=${seg.d.substring(0, 50)}...`);\n // });\n\n // // Check if geometryTransform is present\n // if (model.geometryTransform) {\n // console.log(`[DEBUG MODEL vp=\"${vp.id}\"] geometryTransform=\"${model.geometryTransform}\"`);\n // } else {\n // console.log(`[DEBUG MODEL vp=\"${vp.id}\"] WARNING: geometryTransform is MISSING!`);\n // }\n // }\n // }\n\n // Guard: detect NaN/Infinity in computed transform values\n if (\n !Number.isFinite(centerX) ||\n !Number.isFinite(centerY) ||\n !Number.isFinite(mmPerModel) ||\n !Number.isFinite(renderCenterModelX) ||\n !Number.isFinite(renderCenterModelY)\n ) {\n warnings.push(\n `Viewport '${vp.id}' has non-finite transform values (centerX=${centerX}, centerY=${centerY}, mmPerModel=${mmPerModel}). Skipping render.`\n );\n return parts;\n }\n\n // console.log(`[DEBUG CLIP APPLY vp=\"${vp.id}\"] clipPolysLocal exists: ${!!clipPolysLocal}, length: ${clipPolysLocal?.length ?? 0}`);\n if (clipPolysLocal && clipPolysLocal.length) {\n // console.log(`[DEBUG CLIP APPLY vp=\"${vp.id}\"] *** APPLYING CLIP-PATH *** with ${clipPolysLocal.length} polygons`);\n if (clipPolysLocal.every((poly) => !poly.length)) {\n warnings.push(`Viewport '${vp.id}' clip has no points after prep; skipping clip.`);\n }\n clipId = `clip-${vp.id}-${parts.length}`;\n const polyPartsPage: string[] = [];\n // Clip polygon points are in model-view coordinates (not SVG-internal),\n // so transform them with outer only (model-view → page). Do NOT apply\n // geometryTransform — that maps SVG-internal → model-view, which is only\n // correct for path data inside the <g transform=\"geometryTransform\"> group.\n const outer = matMultiply(\n matTranslate(centerX, centerY),\n matMultiply(\n matRotateDeg(rotationDeg),\n matMultiply(matScale(mmPerModel, mmPerModel), matTranslate(-renderCenterModelX, -renderCenterModelY))\n )\n );\n\n // Debug: log clip polygon bounds\n let clipMinX = Infinity,\n clipMaxX = -Infinity,\n clipMinY = Infinity,\n clipMaxY = -Infinity;\n clipPolysLocal.forEach((poly) => {\n poly.forEach((p) => {\n if (p.x < clipMinX) clipMinX = p.x;\n if (p.x > clipMaxX) clipMaxX = p.x;\n if (p.y < clipMinY) clipMinY = p.y;\n if (p.y > clipMaxY) clipMaxY = p.y;\n });\n });\n // console.log(`[DEBUG CLIP vp=\"${vp.id}\"] Local bounds: x=${clipMinX.toFixed(2)}..${clipMaxX.toFixed(2)} y=${clipMinY.toFixed(2)}..${clipMaxY.toFixed(2)} (w=${(clipMaxX - clipMinX).toFixed(2)} h=${(clipMaxY - clipMinY).toFixed(2)})`);\n\n clipPolysLocal.forEach((poly) => {\n const pagePtsArr = poly.map((p) => matApply(outer, p));\n const pagePts = pagePtsArr.map((p) => `${p.x},${p.y}`).join(' ');\n polyPartsPage.push(`<polygon points=\"${pagePts}\" />`);\n if (clipStroke || clipFill) {\n clipDebugPolys.push(\n `<polygon points=\"${pagePts}\"` +\n (clipStroke ? ` stroke=\"${escapeAttr(clipStroke)}\"` : '') +\n (clipStrokeWidth ? ` stroke-width=\"${clipStrokeWidth}\"` : '') +\n (clipStrokeOpacity !== undefined ? ` stroke-opacity=\"${clipStrokeOpacity}\"` : '') +\n (clipFill ? ` fill=\"${escapeAttr(clipFill)}\"` : ' fill=\"none\"') +\n (clipFillOpacity !== undefined ? ` fill-opacity=\"${clipFillOpacity}\"` : '') +\n ` />`\n );\n }\n if (DEBUG_CLIP_OVERLAY) {\n clipDebugPolys.push(\n `<polygon points=\"${pagePts}\" stroke=\"#ff00ff\" stroke-width=\"0.4\" fill=\"#000000\" fill-opacity=\"0.35\" />`\n );\n }\n });\n parts.push(\n `<defs><clipPath id=\"${clipId}\" clipPathUnits=\"userSpaceOnUse\">${polyPartsPage.join('')}</clipPath></defs>`\n );\n }\n\n parts.push(`<g${clipId ? ` clip-path=\"url(#${clipId})\"` : ''}>`);\n parts.push(`<g transform=\"${transformAttr}\">`);\n\n // Render fill polygons below structural lines\n const allowedGids = viewportHighlight[vp.id]?.gids ?? null;\n const fillSvg = buildFillSvg(vp, dataWithViewport, model, allowedGids, legendColorMaps);\n if (fillSvg) parts.push(fillSvg);\n\n // Debug: log sample path coordinates from model.groupSvg\n if (clipId && model.rawPaths && model.rawPaths.length > 0) {\n const asMeta = (m: unknown): { isSegment?: boolean; isFill?: boolean; class?: string } | undefined =>\n m != null && typeof m === 'object'\n ? (m as { isSegment?: boolean; isFill?: boolean; class?: string })\n : undefined;\n const samplePath = model.rawPaths.find((p) => {\n const pm = asMeta(p.meta);\n return pm?.isSegment || (!pm?.isFill && !pm?.class);\n });\n if (samplePath) {\n const dMatch = samplePath.d.match(/M([\\d.-]+)\\s+([\\d.-]+)/);\n if (dMatch) {\n // console.log(`[DEBUG PATH vp=\"${vp.id}\"] Sample wall path starts at x=${dMatch[1]} y=${dMatch[2]} (world coords, will be transformed by geometryTransform)`);\n }\n }\n }\n\n // Compensate stroke-widths for the viewport scale transform.\n // Paths have stroke-width in model units; the scale(mmPerModel) transform\n // would make them thinner/thicker depending on zoom. Dividing by mmPerModel\n // ensures strokes render at their intended mm size regardless of viewport scale.\n const compensatedSvg = model.groupSvg.replace(\n /stroke-width=\"([0-9.eE+-]+)\"/g,\n (_, w) => `stroke-width=\"${parseFloat(w) / mmPerModel}\"`\n );\n parts.push(compensatedSvg);\n parts.push('</g>');\n parts.push('</g>');\n\n if (clipDebugPolys.length) {\n parts.push(`<g pointer-events=\"none\">${clipDebugPolys.join('')}</g>`);\n }\n\n viewportRenderInfo[vp.id] = {\n mmPerModel,\n contentWidthMm: rotated.width,\n contentHeightMm: rotated.height,\n offsetX: contentOffsetX,\n offsetY: contentOffsetY,\n rotationDeg,\n transform: model.transform!,\n centerX,\n centerY,\n centerModelX: renderCenterModelX,\n centerModelY: renderCenterModelY\n };\n\n if (vp.border) {\n const borderStroke = vp.border.stroke ?? '#000';\n const borderWidth = vp.border.strokeWidthMm ?? 0.25;\n parts.push(\n `<rect x=\"${vp.xMm}\" y=\"${vp.yMm}\" width=\"${vp.widthMm}\" height=\"${vp.heightMm}\" stroke=\"${escapeAttr(\n borderStroke\n )}\" stroke-width=\"${borderWidth}\" fill=\"none\"${vp.border.dashed ? ' stroke-dasharray=\"4 2\"' : ''} />`\n );\n }\n\n if (vp.northArrow) {\n const arrow = vp.northArrow;\n const asset = assetMap.get(arrow.assetId);\n const size = arrow.sizeMm ?? 12;\n const offsetX = arrow.offsetXMm ?? 8;\n const offsetY = arrow.offsetYMm ?? 8;\n const arrowX = vp.xMm + vp.widthMm - offsetX - size;\n const arrowY = vp.yMm + offsetY;\n const arrowRotation = arrow.rotationDeg ?? 0;\n if (!asset) {\n warnings.push(`Missing asset '${arrow.assetId}' for north arrow on viewport '${vp.id}'.`);\n } else if (asset.src) {\n parts.push(\n `<image href=\"${escapeAttr(asset.src)}\" x=\"${arrowX}\" y=\"${arrowY}\" width=\"${size}\" height=\"${size}\" transform=\"rotate(${arrowRotation}, ${arrowX + size / 2}, ${\n arrowY + size / 2\n })\" />`\n );\n } else if (asset.content) {\n parts.push(\n `<g transform=\"translate(${arrowX} ${arrowY}) rotate(${arrowRotation} ${size / 2} ${size / 2})\">${asset.content}</g>`\n );\n }\n }\n\n return parts;\n};\n\n// ---------------------------------------------------------------------------\n// Fill polygon rendering\n// ---------------------------------------------------------------------------\n\n/**\n * Build SVG fill polygons from viewport fill rules and item data.\n * Returns an SVG fragment in model-view coordinates (same space as model.groupSvg),\n * or null if no fills should be rendered.\n */\nfunction buildFillSvg(\n vp: ViewportElement,\n data: any,\n model: SvgModelView,\n allowedGids: Set<string> | null,\n legendColorMaps?: Map<string, Map<string, string>>\n): string | null {\n const fills = vp.fills;\n if (!fills?.length || !model.transform) return null;\n\n const items = getPathValue(data, `viewports.${vp.id}.items`);\n if (!Array.isArray(items) || !items.length) return null;\n\n const { viewMinX, viewMaxZ } = model.transform;\n const parts: string[] = [];\n\n for (const rule of fills) {\n const opacity = rule.fillOpacity ?? 0.35;\n // Find items matching this fill rule\n let matching = items.filter((item: unknown) =>\n isRecord(item) ? matchesV2Filter(item, rule.class, rule.where) : false\n );\n\n // Filter by allowed GIDs (from clip polygon center point test)\n // Only apply if BOTH: clip filtering is active AND this fill rule opts in via matchClipFilter\n if (allowedGids !== null && rule.matchClipFilter === true) {\n matching = matching.filter((item: unknown) => {\n const gid = getProp(item, 'gid') ?? getProp(item, 'id');\n return gid !== null && gid !== undefined && allowedGids.has(String(gid));\n });\n // console.log(`[DEBUG FILL vp=\"${vp.id}\"] Clip filter applied (matchClipFilter=true): ${beforeFilter} → ${matching.length} items (allowedGids.size=${allowedGids.size})`);\n } else if (rule.matchClipFilter === true) {\n // console.log(`[DEBUG FILL vp=\"${vp.id}\"] Fill rule has matchClipFilter=true but no clip polygon active`);\n }\n // console.log(`[DEBUG FILL vp=\"${vp.id}\"] Fill rule (class=\"${rule.class}\", where=${JSON.stringify(rule.where)}) matched ${matching.length} items`);\n // if (matching.length > 0) {\n // console.log(`[DEBUG FILL vp=\"${vp.id}\"] First matched item:`, {\n // gid: getProp(matching[0], 'gid'),\n // name: getProp(matching[0], 'name'),\n // ifcClass: getProp(matching[0], 'ifcClass'),\n // propertySets: getProp(matching[0], 'propertySets'),\n // polylines: Array.isArray(getProp(matching[0], 'polylines')) ? (getProp(matching[0], 'polylines') as any[]).length : 0\n // });\n // }\n if (!matching.length) continue;\n\n for (const item of matching) {\n const polylines = getProp(item, 'polylines');\n if (!Array.isArray(polylines)) continue;\n\n // Resolve fill color for this item\n const color = getProp(item, 'color') as [number, number, number, number] | undefined;\n let fillColor: string;\n if (rule.fill === 'sample') {\n if (!color) continue; // no sampled color available\n fillColor = `rgb(${color[0]},${color[1]},${color[2]})`;\n } else if (rule.fill === 'legend') {\n // Use color from legend color map\n const legendColorMap = legendColorMaps?.get(vp.id);\n if (!legendColorMap) {\n console.warn(\n `[FILL] fill=\"legend\" specified but no legend color map found for viewport \"${vp.id}\". Available viewports:`,\n Array.from(legendColorMaps?.keys() ?? [])\n );\n continue;\n }\n // Find matching legend entry - need to determine which property to use for lookup\n // For now, try common properties (can be made configurable later)\n let legendKey: string | undefined;\n const possibleKeys = ['spacename', 'BIP.spacename', 'name'];\n for (const key of possibleKeys) {\n const value = resolvePath(item, key);\n if (value !== undefined && value !== null) {\n legendKey = String(value);\n break;\n }\n }\n if (!legendKey || !legendColorMap.has(legendKey)) {\n // No matching color in legend, skip this item\n // if (legendKey) {\n // console.log(`[FILL] No color mapping for \"${legendKey}\" (property: ${matchedProperty}). Available keys:`, Array.from(legendColorMap.keys()));\n // }\n continue;\n }\n fillColor = legendColorMap.get(legendKey)!;\n // console.log(`[FILL] ✓ Matched \"${legendKey}\" -> ${fillColor}`);\n } else {\n fillColor = rule.fill;\n }\n\n // console.log(`[FILL] Processing ${polylines.length} polyline(s) for item with color ${fillColor}`);\n for (const polyline of polylines) {\n const points = isRecord(polyline) ? getProp(polyline, 'points') : null;\n if (!Array.isArray(points) || points.length < 3) continue;\n\n // Build path in model-view coordinates\n const pathParts: string[] = [];\n for (let i = 0; i < points.length; i++) {\n const pt = points[i];\n const px = isRecord(pt) ? getProp(pt, 'x') : null;\n const pz = isRecord(pt) ? getProp(pt, 'z') : null;\n if (!Number.isFinite(px) || !Number.isFinite(pz)) continue;\n const mx = (px as number) - viewMinX;\n const my = viewMaxZ - (pz as number);\n pathParts.push(i === 0 ? `M ${mx} ${my}` : `L ${mx} ${my}`);\n }\n if (pathParts.length < 3) continue;\n pathParts.push('Z');\n parts.push(\n `<path d=\"${pathParts.join(' ')}\" fill=\"${escapeAttr(fillColor)}\" fill-opacity=\"${opacity}\" stroke=\"none\"/>`\n );\n }\n }\n }\n\n // console.log(`[FILL] Generated ${parts.length} fill polygon(s) for viewport \"${vp.id}\"`);\n return parts.length ? `<g>${parts.join('')}</g>` : null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"viewportRender.js","sourceRoot":"/","sources":["layout/viewportRender.ts"],"names":[],"mappings":"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","sourcesContent":["import { escapeAttr, getPathValue, resolvePath, formatNumber } from './renderUtils';\nimport { rotateBounds, pickScale, rotatedContentExtents } from './viewportUtils';\nimport { ViewportRenderInfo } from './labelsRender';\nimport { matchesFilter } from './filters';\nimport { ComputedViewportScale, SvgModelView, ViewportElement, ResourceSpec, ResolvedIncludeFilter } from './types';\nimport { isRecord, getProp } from './typeGuards';\nimport type { LegendColorAssignment } from './legendRender';\n\ntype PolyPoint = { x: number; y: number };\n// Debug overlay for clip polygons. Keep false for normal builds.\nconst DEBUG_CLIP_OVERLAY = false;\n\ntype Mat = [number, number, number, number, number, number]; // a b c d e f\n\nconst matTranslate = (tx: number, ty: number): Mat => [1, 0, 0, 1, tx, ty];\nconst matScale = (sx: number, sy: number): Mat => [sx, 0, 0, sy, 0, 0];\nconst matRotateDeg = (deg: number): Mat => {\n const rad = (deg * Math.PI) / 180;\n const c = Math.cos(rad);\n const s = Math.sin(rad);\n return [c, s, -s, c, 0, 0];\n};\nconst matMultiply = (m1: Mat, m2: Mat): Mat => {\n const [a1, b1, c1, d1, e1, f1] = m1;\n const [a2, b2, c2, d2, e2, f2] = m2;\n return [\n a1 * a2 + c1 * b2,\n b1 * a2 + d1 * b2,\n a1 * c2 + c1 * d2,\n b1 * c2 + d1 * d2,\n a1 * e2 + c1 * f2 + e1,\n b1 * e2 + d1 * f2 + f1\n ];\n};\nconst matApply = (m: Mat, p: PolyPoint): PolyPoint => {\n const [a, b, c, d, e, f] = m;\n return { x: a * p.x + c * p.y + e, y: b * p.x + d * p.y + f };\n};\nexport const renderViewportElement = (\n vp: ViewportElement,\n baseModel: SvgModelView | null,\n dataWithViewport: any,\n preferredScales: number[],\n scales: Record<string, { preferredScales?: number[] }>,\n resourceMap: Map<string, ResourceSpec>,\n viewportScales: Record<string, ComputedViewportScale>,\n viewportClipPolygonsLocal: Record<string, Array<Array<PolyPoint>>>,\n viewportClipBounds: Record<\n string,\n { minX: number; maxX: number; minY: number; maxY: number; width: number; height: number }\n >,\n viewportClipLabels: Record<string, boolean>,\n viewportClipStyles: Record<\n string,\n { color?: string; widthMm?: number; opacity?: number; dash?: string; fill?: string; fillOpacity?: number }\n >,\n viewportHighlight: Record<string, { gids: Set<string> | null; styles: any }>,\n viewportRenderInfo: Record<string, ViewportRenderInfo>,\n warnings: string[],\n legendColorMaps?: Map<string, LegendColorAssignment>,\n precomputed?: {\n effectiveWidthModel?: number;\n effectiveHeightModel?: number;\n clipPolygonsMm?: Array<Array<PolyPoint>>;\n clipPolygonsLocal?: Array<Array<PolyPoint>>;\n clipBounds?: { minX: number; maxX: number; minY: number; maxY: number; width: number; height: number };\n renderCenterModelX?: number;\n renderCenterModelY?: number;\n },\n includeFilters?: ResolvedIncludeFilter[] | null\n): string[] => {\n const parts: string[] = [];\n if (!baseModel) {\n // Warning already pushed by viewportPrep; avoid duplicate.\n return parts;\n }\n\n const model = baseModel;\n let renderWidthModel = precomputed?.effectiveWidthModel ?? model.widthModel;\n let renderHeightModel = precomputed?.effectiveHeightModel ?? model.heightModel;\n let renderCenterModelX = precomputed?.renderCenterModelX ?? renderWidthModel * 0.5;\n let renderCenterModelY = precomputed?.renderCenterModelY ?? renderHeightModel * 0.5;\n\n const clipBounds = viewportClipBounds[vp.id];\n const clipLocal = viewportClipPolygonsLocal[vp.id];\n const hasClip = !!clipBounds && Array.isArray(clipLocal) && clipLocal.length > 0;\n if (hasClip) {\n renderWidthModel = clipBounds!.width;\n renderHeightModel = clipBounds!.height;\n renderCenterModelX = (clipBounds!.minX + clipBounds!.maxX) * 0.5;\n renderCenterModelY = (clipBounds!.minY + clipBounds!.maxY) * 0.5;\n }\n\n const scaleInfo =\n viewportScales[vp.id] ??\n pickScale(\n vp,\n { ...model, widthModel: renderWidthModel, heightModel: renderHeightModel },\n preferredScales,\n scales\n );\n const rotationDeg = vp.rotationDeg ?? 0;\n const mmPerModel = scaleInfo.mmPerModelUnit;\n const contentWidthMm = renderWidthModel * mmPerModel;\n const contentHeightMm = renderHeightModel * mmPerModel;\n // Tight bounds of the rotated geometry (see rotatedContentExtents): sizes the\n // content box and re-centers the rotation pivot so diagonally-oriented models\n // straightened via rotationDeg fill the viewport instead of shrinking to the\n // inflated AABB-of-AABB. Clip keeps the AABB path — its bounds are the clip\n // region, not the full geometry.\n const tight = hasClip ? null : rotatedContentExtents(model, rotationDeg);\n if (tight && precomputed?.renderCenterModelX == null && precomputed?.renderCenterModelY == null) {\n renderCenterModelX = tight.centerViewX;\n renderCenterModelY = tight.centerViewY;\n }\n const rotated = tight\n ? { width: tight.width * mmPerModel, height: tight.height * mmPerModel }\n : rotateBounds(contentWidthMm, contentHeightMm, rotationDeg);\n\n const centerX = vp.align === 'topLeft' ? vp.xMm + rotated.width / 2 : vp.xMm + vp.widthMm / 2;\n const centerY = vp.align === 'topLeft' ? vp.yMm + rotated.height / 2 : vp.yMm + vp.heightMm / 2;\n\n const contentOffsetX = vp.align === 'topLeft' ? vp.xMm : vp.xMm + Math.max(0, (vp.widthMm - rotated.width) * 0.5);\n const contentOffsetY = vp.align === 'topLeft' ? vp.yMm : vp.yMm + Math.max(0, (vp.heightMm - rotated.height) * 0.5);\n\n const clipPolysLocal = clipLocal;\n let clipId: string | null = null;\n const clipDebugPolys: string[] = [];\n const clipStyle = viewportClipStyles[vp.id];\n const clipStroke = clipStyle?.color;\n const clipStrokeWidth = clipStyle?.widthMm;\n const clipStrokeOpacity = clipStyle?.opacity;\n const clipDash = clipStyle?.dash;\n const clipFill = clipStyle?.fill;\n const clipFillOpacity = clipStyle?.fillOpacity;\n const transformAttr = `translate(${formatNumber(centerX)} ${formatNumber(centerY)}) rotate(${rotationDeg}) scale(${mmPerModel}) translate(${formatNumber(-renderCenterModelX)} ${formatNumber(-renderCenterModelY)})`;\n\n // Guard: detect NaN/Infinity in computed transform values\n if (\n !Number.isFinite(centerX) ||\n !Number.isFinite(centerY) ||\n !Number.isFinite(mmPerModel) ||\n !Number.isFinite(renderCenterModelX) ||\n !Number.isFinite(renderCenterModelY)\n ) {\n warnings.push(\n `Viewport '${vp.id}' has non-finite transform values (centerX=${centerX}, centerY=${centerY}, mmPerModel=${mmPerModel}). Skipping render.`\n );\n return parts;\n }\n\n if (clipPolysLocal && clipPolysLocal.length) {\n if (clipPolysLocal.every((poly) => !poly.length)) {\n warnings.push(`Viewport '${vp.id}' clip has no points after prep; skipping clip.`);\n }\n clipId = `clip-${vp.id}-${parts.length}`;\n // Clip polygon points are in model-view coordinates (not SVG-internal),\n // so transform them with outer only (model-view → page). Do NOT apply\n // geometryTransform — that maps SVG-internal → model-view, which is only\n // correct for path data inside the <g transform=\"geometryTransform\"> group.\n const outer = matMultiply(\n matTranslate(centerX, centerY),\n matMultiply(\n matRotateDeg(rotationDeg),\n matMultiply(matScale(mmPerModel, mmPerModel), matTranslate(-renderCenterModelX, -renderCenterModelY))\n )\n );\n\n // Rings from unioned clips can include holes — a single even-odd path\n // (rather than one polygon per ring) makes holes subtract correctly in\n // both the clip mask and any fill.\n const ringDs: string[] = [];\n clipPolysLocal.forEach((poly) => {\n const pagePtsArr = poly.map((p) => matApply(outer, p));\n const pagePts = pagePtsArr.map((p) => `${formatNumber(p.x)},${formatNumber(p.y)}`).join(' ');\n ringDs.push('M ' + pagePtsArr.map((p) => `${formatNumber(p.x)} ${formatNumber(p.y)}`).join(' L ') + ' Z');\n if (clipStroke) {\n clipDebugPolys.push(\n `<polygon points=\"${pagePts}\" fill=\"none\"` +\n ` stroke=\"${escapeAttr(clipStroke)}\"` +\n (clipStrokeWidth ? ` stroke-width=\"${clipStrokeWidth}\"` : '') +\n (clipStrokeOpacity !== undefined ? ` stroke-opacity=\"${clipStrokeOpacity}\"` : '') +\n (clipDash ? ` stroke-dasharray=\"${escapeAttr(clipDash)}\"` : '') +\n ` />`\n );\n }\n if (DEBUG_CLIP_OVERLAY) {\n clipDebugPolys.push(\n `<polygon points=\"${pagePts}\" stroke=\"#ff00ff\" stroke-width=\"0.4\" fill=\"#000000\" fill-opacity=\"0.35\" />`\n );\n }\n });\n if (clipFill && clipFill !== 'none') {\n clipDebugPolys.unshift(\n `<path d=\"${ringDs.join(' ')}\" fill-rule=\"evenodd\" fill=\"${escapeAttr(clipFill)}\"` +\n (clipFillOpacity !== undefined ? ` fill-opacity=\"${clipFillOpacity}\"` : '') +\n ` stroke=\"none\" />`\n );\n }\n parts.push(\n `<defs><clipPath id=\"${clipId}\" clipPathUnits=\"userSpaceOnUse\"><path d=\"${ringDs.join(' ')}\" clip-rule=\"evenodd\" /></clipPath></defs>`\n );\n }\n\n parts.push(`<g${clipId ? ` clip-path=\"url(#${clipId})\"` : ''}>`);\n parts.push(`<g transform=\"${transformAttr}\">`);\n\n // Render fill polygons below structural lines\n const allowedGids = viewportHighlight[vp.id]?.gids ?? null;\n const fillSvg = buildFillSvg(vp, dataWithViewport, model, allowedGids, legendColorMaps, warnings, includeFilters);\n if (fillSvg) parts.push(fillSvg);\n\n // Compensate stroke-widths AND dash arrays for the viewport scale\n // transform. Path styling is authored in mm; the scale(mmPerModel)\n // transform would multiply both, so divide them out — otherwise\n // stroke-dasharray \"4 2\" means 4 model units (metres), not 4 mm, and the\n // dash period changes with the viewport scale.\n const compensatedSvg = model.groupSvg\n .replace(/stroke-width=\"([0-9.eE+-]+)\"/g, (_, w) => `stroke-width=\"${parseFloat(w) / mmPerModel}\"`)\n .replace(\n /stroke-dasharray=\"([0-9.eE+\\-, ]+)\"/g,\n (_, dashes: string) =>\n `stroke-dasharray=\"${dashes\n .split(/[\\s,]+/)\n .filter(Boolean)\n .map((d) => parseFloat(d) / mmPerModel)\n .join(' ')}\"`\n );\n parts.push(compensatedSvg);\n parts.push('</g>');\n parts.push('</g>');\n\n if (clipDebugPolys.length) {\n parts.push(`<g pointer-events=\"none\">${clipDebugPolys.join('')}</g>`);\n }\n\n viewportRenderInfo[vp.id] = {\n mmPerModel,\n contentWidthMm: rotated.width,\n contentHeightMm: rotated.height,\n offsetX: contentOffsetX,\n offsetY: contentOffsetY,\n rotationDeg,\n transform: model.transform!,\n centerX,\n centerY,\n centerModelX: renderCenterModelX,\n centerModelY: renderCenterModelY\n };\n\n if (vp.border) {\n const borderStroke = vp.border.stroke ?? '#000';\n const borderWidth = vp.border.strokeWidthMm ?? 0.25;\n parts.push(\n `<rect x=\"${vp.xMm}\" y=\"${vp.yMm}\" width=\"${vp.widthMm}\" height=\"${vp.heightMm}\" stroke=\"${escapeAttr(\n borderStroke\n )}\" stroke-width=\"${borderWidth}\" fill=\"none\"${vp.border.dash ? ` stroke-dasharray=\"${escapeAttr(vp.border.dash)}\"` : ''} />`\n );\n }\n\n if (vp.northArrow) {\n const arrow = vp.northArrow;\n const resource = resourceMap.get(arrow.resourceId);\n const size = arrow.sizeMm ?? 12;\n const offsetX = arrow.offsetXMm ?? 8;\n const offsetY = arrow.offsetYMm ?? 8;\n const arrowX = vp.xMm + vp.widthMm - offsetX - size;\n const arrowY = vp.yMm + offsetY;\n const arrowRotation = arrow.rotationDeg ?? 0;\n if (!resource) {\n warnings.push(`Missing resource '${arrow.resourceId}' for north arrow on viewport '${vp.id}'.`);\n } else if (resource.src) {\n parts.push(\n `<image href=\"${escapeAttr(resource.src)}\" x=\"${arrowX}\" y=\"${arrowY}\" width=\"${size}\" height=\"${size}\" transform=\"rotate(${arrowRotation}, ${arrowX + size / 2}, ${\n arrowY + size / 2\n })\" />`\n );\n } else if (resource.content) {\n parts.push(\n `<g transform=\"translate(${arrowX} ${arrowY}) rotate(${arrowRotation} ${size / 2} ${size / 2})\">${resource.content}</g>`\n );\n }\n }\n\n return parts;\n};\n\n// ---------------------------------------------------------------------------\n// Fill polygon rendering\n// ---------------------------------------------------------------------------\n\n/**\n * Build SVG fill polygons from viewport fill rules and item data.\n * Returns an SVG fragment in model-view coordinates (same space as model.groupSvg),\n * or null if no fills should be rendered.\n */\nfunction buildFillSvg(\n vp: ViewportElement,\n data: any,\n model: SvgModelView,\n allowedGids: Set<string> | null,\n legendColorMaps: Map<string, LegendColorAssignment> | undefined,\n warnings: string[],\n includeFilters?: ResolvedIncludeFilter[] | null\n): string | null {\n const fills = vp.fills;\n if (!fills?.length || !model.transform) return null;\n\n const items = getPathValue(data, `viewports.${vp.id}.items`);\n if (!Array.isArray(items) || !items.length) return null;\n\n const { viewMinX, viewMaxZ } = model.transform;\n const parts: string[] = [];\n\n for (const rule of fills) {\n const opacity = rule.fillOpacity ?? 0.35;\n // Find items matching this fill rule\n let matching = items.filter((item: unknown) =>\n isRecord(item) ? matchesFilter(item, rule.class, rule.where) : false\n );\n\n // Filter by allowed GIDs (from clip polygon center point test)\n // Only apply if BOTH: clip filtering is active AND this fill rule opts in via matchClipFilter\n if (allowedGids !== null && rule.matchClipFilter === true) {\n matching = matching.filter((item: unknown) => {\n const gid = getProp(item, 'gid') ?? getProp(item, 'id');\n return gid !== null && gid !== undefined && allowedGids.has(String(gid));\n });\n } else if (includeFilters?.length) {\n // The viewport's includeClasses gates generic fills exactly like it gates\n // line paths — the include list is authoritative for what geometry appears.\n // GID-gated rules (matchClipFilter with an active clip/highlight set, the\n // branch above) are exempt, mirroring highlight/clip classes in\n // filterModelForViewport: a clipped apartment's space fills must render\n // even in a view whose includeClasses excludes spaces.\n matching = matching.filter(\n (item: unknown) =>\n isRecord(item) &&\n includeFilters.some((f) =>\n typeof f === 'string'\n ? matchesFilter(item, f, undefined)\n : matchesFilter(item, f.class, f.where)\n )\n );\n }\n if (!matching.length) continue;\n\n for (const item of matching) {\n const polylines = getProp(item, 'polylines');\n if (!Array.isArray(polylines)) continue;\n\n // Resolve fill color for this item\n const color = getProp(item, 'color') as [number, number, number, number] | undefined;\n let fillColor: string;\n if (rule.fill === 'sample') {\n if (!color) continue; // no sampled color available\n fillColor = `rgb(${color[0]},${color[1]},${color[2]})`;\n } else if (rule.fill === 'legend') {\n const legendColors = legendColorMaps?.get(vp.id);\n if (!legendColors) {\n warnings.push(\n `Viewport '${vp.id}' has fill: \"legend\" but no legend with assignColors targets it (legends exist for: ${Array.from(legendColorMaps?.keys() ?? []).join(', ') || 'none'}).`\n );\n break;\n }\n // Resolve the item's colour: by item identity first (covers\n // ungrouped legends — every item its own colour), then by the\n // legend's groupBy property value (shared across a group).\n let resolved: string | undefined;\n const gid = getProp(item, 'gid') ?? getProp(item, 'id');\n if (gid !== null && gid !== undefined) {\n resolved = legendColors.byGid.get(String(gid));\n }\n if (!resolved && legendColors.groupBy) {\n const value = resolvePath(item, legendColors.groupBy);\n if (value !== undefined && value !== null) {\n resolved = legendColors.byValue.get(String(value));\n }\n }\n if (!resolved) continue; // item not represented in the legend\n fillColor = resolved;\n } else {\n fillColor = rule.fill;\n }\n\n for (const polyline of polylines) {\n const points = isRecord(polyline) ? getProp(polyline, 'points') : null;\n if (!Array.isArray(points) || points.length < 3) continue;\n\n // Build path in model-view coordinates\n const pathParts: string[] = [];\n for (let i = 0; i < points.length; i++) {\n const pt = points[i];\n const px = isRecord(pt) ? getProp(pt, 'x') : null;\n const pz = isRecord(pt) ? getProp(pt, 'z') : null;\n if (!Number.isFinite(px) || !Number.isFinite(pz)) continue;\n const mx = (px as number) - viewMinX;\n const my = viewMaxZ - (pz as number);\n pathParts.push(\n i === 0\n ? `M ${formatNumber(mx)} ${formatNumber(my)}`\n : `L ${formatNumber(mx)} ${formatNumber(my)}`\n );\n }\n if (pathParts.length < 3) continue;\n pathParts.push('Z');\n parts.push(\n `<path d=\"${pathParts.join(' ')}\" fill=\"${escapeAttr(fillColor)}\" fill-opacity=\"${opacity}\" stroke=\"none\"/>`\n );\n }\n }\n }\n\n return parts.length ? `<g>${parts.join('')}</g>` : null;\n}\n"]}
|
|
@@ -3,6 +3,26 @@ export declare const rotateBounds: (width: number, height: number, rotationDeg:
|
|
|
3
3
|
width: number;
|
|
4
4
|
height: number;
|
|
5
5
|
};
|
|
6
|
+
export interface RotatedContentExtents {
|
|
7
|
+
/** Tight extents of the rotated content, model units (rotated/sheet axes). */
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
/** Tight-bounds center mapped back to view space — the rotation pivot that centers the content. */
|
|
11
|
+
centerViewX: number;
|
|
12
|
+
centerViewY: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Tight bounds of the model's actual geometry under viewport rotation.
|
|
16
|
+
*
|
|
17
|
+
* Rotating the model's AABB *rectangle* only ever inflates it — for content that
|
|
18
|
+
* sits diagonally in model space (the main reason to set rotationDeg: straighten
|
|
19
|
+
* a building on the sheet), the true rotated bounds SHRINK instead, and fitting
|
|
20
|
+
* against the inflated rectangle picks a scale several preset steps too small.
|
|
21
|
+
* Returns null when tight bounds can't be derived (no rotation, no rawPaths /
|
|
22
|
+
* transform, or path data that isn't plain absolute polylines) — callers then
|
|
23
|
+
* keep the AABB-based rotateBounds behavior.
|
|
24
|
+
*/
|
|
25
|
+
export declare const rotatedContentExtents: (model: SvgModelView, rotationDeg: number) => RotatedContentExtents | null;
|
|
6
26
|
export declare const pickScale: (viewport: ViewportElement, model: SvgModelView, preferred: number[], scalePresets: Record<string, {
|
|
7
27
|
preferredScales?: number[];
|
|
8
28
|
}>) => ComputedViewportScale;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewportUtils.d.ts","sourceRoot":"/","sources":["layout/viewportUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/E,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,EAAE,aAAa,MAAM,KAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAQhH,CAAC;AAEF,eAAO,MAAM,SAAS,GAClB,UAAU,eAAe,EACzB,OAAO,YAAY,EACnB,WAAW,MAAM,EAAE,EACnB,cAAc,MAAM,CAAC,MAAM,EAAE;IAAE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,KAC7D,
|
|
1
|
+
{"version":3,"file":"viewportUtils.d.ts","sourceRoot":"/","sources":["layout/viewportUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/E,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,EAAE,aAAa,MAAM,KAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAQhH,CAAC;AAEF,MAAM,WAAW,qBAAqB;IAClC,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,mGAAmG;IACnG,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACvB;AASD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,YAAY,EAAE,aAAa,MAAM,KAAG,qBAAqB,GAAG,IA8DxG,CAAC;AAEF,eAAO,MAAM,SAAS,GAClB,UAAU,eAAe,EACzB,OAAO,YAAY,EACnB,WAAW,MAAM,EAAE,EACnB,cAAc,MAAM,CAAC,MAAM,EAAE;IAAE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,KAC7D,qBAqDF,CAAC"}
|