@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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"itemBuilders.js","sourceRoot":"/","sources":["layout/itemBuilders.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SectionRenderState } from '@twinfinity/geometry';\nimport { normalizePropertySets } from './metadata';\nimport { getPathValue } from './renderUtils';\nimport { buildModelViewFromRaw } from './model';\nimport { ProductFieldPaths, NormalizedProductItem, SvgModelView, LabelStyle } from './types';\n\nimport { getProp } from './typeGuards';\n\nexport interface ProductLike {\n product?: any;\n polylines?: Array<{ points?: Array<{ x: number; z: number }> }>;\n}\n\nexport interface BuildViewportItemsOptions {\n productFields?: ProductFieldPaths;\n}\n\n/**\n * Normalize a single product entry using explicit field paths.\n */\nexport const normalizeProductEntry = (entry: ProductLike, productFields: ProductFieldPaths): NormalizedProductItem => {\n if (!productFields?.idPath || !productFields?.namePath || !productFields?.areaPath) {\n throw new Error(\n 'productFields must include idPath, namePath, and areaPath using propertySets.<Set>.<Prop>.value.'\n );\n }\n const product = getProp(entry, 'product') ?? entry;\n const propertySets = normalizePropertySets(product);\n const getVal = (path: string): unknown => getPathValue({ propertySets }, path);\n const idVal = getVal(productFields.idPath) ?? getProp(product, 'gid');\n const nameVal = getVal(productFields.namePath);\n const areaVal = getVal(productFields.areaPath);\n // class.type = IfcClassType (\"space\", \"walls\", etc.) — used by template filters\n // class.id = PredefinedIfcClassId (\"IfcSpace\", \"IfcWall\", etc.) — used as fallback\n // classPath from propertySets may contain a PredefinedIfcClassId\n const classPathVal = productFields.classPath ? getVal(productFields.classPath) : null;\n const productClass = getProp(product, 'class');\n const classType = getProp(productClass, 'type') as string | null; // IfcClassType\n const classId = getProp(productClass, 'id') as string | null; // PredefinedIfcClassId\n // ifcClass = IfcClassType category; ifcClassId = PredefinedIfcClassId\n const ifcClass = (classType ?? null) as string | null;\n const ifcClassId = (classPathVal ?? classId ?? null) as string | null;\n // Try multiple sources for coordinates:\n // 1. BBox center from product\n // 2. Polylines center from entry\n // 3. Direct x/z on entry or product\n let { x, z } = getBBoxCenter(product);\n if (x === null || z === null) {\n const polyCenter = computePolylinesCenter(entry);\n x = polyCenter.x ?? x;\n z = polyCenter.z ?? z;\n }\n if (x === null || z === null) {\n const directX = getProp(entry, 'x') ?? getProp(product, 'x');\n const directZ = getProp(entry, 'z') ?? getProp(product, 'z');\n if (Number.isFinite(directX)) x = directX as number;\n if (Number.isFinite(directZ)) z = directZ as number;\n }\n const rawPolylines = getProp(entry, 'polylines');\n return {\n gid: getProp(product, 'gid') as string | undefined,\n ifcClass,\n ifcClassId,\n propertySets,\n productId: idVal ? String(idVal) : '-',\n productName: typeof nameVal === 'string' && nameVal.trim().length ? nameVal.trim() : '-',\n area: typeof areaVal === 'number' && Number.isFinite(areaVal) ? areaVal : null,\n x,\n z,\n text: typeof nameVal === 'string' && nameVal.trim().length ? nameVal.trim() : '-',\n polylines: Array.isArray(rawPolylines) ? rawPolylines : undefined\n };\n};\n\nconst getBBoxCenter = (product: unknown): { x: number | null; z: number | null } => {\n try {\n const boundingInfo = getProp(product, 'boundingInfo');\n const boundingInfoResult = typeof boundingInfo === 'function' ? boundingInfo() : null;\n const bbox = getProp(boundingInfoResult, 'boundingBox');\n const min = getProp(bbox, 'minimumWorld') ?? getProp(bbox, 'minimum');\n const max = getProp(bbox, 'maximumWorld') ?? getProp(bbox, 'maximum');\n const minX = getProp(min, 'x');\n const maxX = getProp(max, 'x');\n const minZ = getProp(min, 'z');\n const maxZ = getProp(max, 'z');\n if (Number.isFinite(minX) && Number.isFinite(maxX) && Number.isFinite(minZ) && Number.isFinite(maxZ)) {\n return { x: ((minX as number) + (maxX as number)) * 0.5, z: ((minZ as number) + (maxZ as number)) * 0.5 };\n }\n } catch {\n // Bounding info access may fail for products without geometry or with malformed data structures.\n // This is expected behavior - we return null coordinates as fallback.\n }\n return { x: null, z: null };\n};\n\nexport const computePolylinesCenter = (entry: unknown): { x: number | null; z: number | null } => {\n try {\n const polylines = getProp(entry, 'polylines');\n if (!Array.isArray(polylines) || !polylines.length) {\n return { x: null, z: null };\n }\n let sumX = 0;\n let sumZ = 0;\n let count = 0;\n for (const polyline of polylines) {\n const points = getProp(polyline, 'points');\n if (!Array.isArray(points)) continue;\n for (const pt of points) {\n const px = getProp(pt, 'x');\n const pz = getProp(pt, 'z');\n if (Number.isFinite(px) && Number.isFinite(pz)) {\n sumX += px as number;\n sumZ += pz as number;\n count++;\n }\n }\n }\n if (count > 0) {\n return { x: sumX / count, z: sumZ / count };\n }\n } catch {\n // Polyline access may fail - return null\n }\n return { x: null, z: null };\n};\n\n/**\n * Normalize raw product entries into viewport items using explicit field paths.\n * Requires productFields (id/name/area/class) pointing to propertySets.<Set>.<Prop>.value.\n */\nexport const buildViewportItems = (\n entries: ProductLike[],\n options?: BuildViewportItemsOptions\n): NormalizedProductItem[] => {\n if (!options?.productFields) {\n throw new Error('buildViewportItems requires productFields (idPath/namePath/areaPath/classPath).');\n }\n const { idPath, namePath, areaPath } = options.productFields;\n if (!idPath || !namePath || !areaPath) {\n throw new Error(\n 'productFields must include idPath, namePath, and areaPath using propertySets.<Set>.<Prop>.value.'\n );\n }\n return (entries ?? []).map((entry: any) => normalizeProductEntry(entry, options.productFields!));\n};\n\nexport function sectionStateToModelView(state: SectionRenderState, labelStyle?: LabelStyle): SvgModelView {\n const model = buildModelViewFromRaw(\n state.paths,\n state.labels,\n state.transform,\n state.geometryTransform,\n labelStyle\n );\n if (!model) {\n throw new Error('sectionStateToModelView requires paths, transform, and geometryTransform.');\n }\n return model;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"itemBuilders.js","sourceRoot":"/","sources":["layout/itemBuilders.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGhD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAWvC;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAkB,EAAE,aAAgC,EAAyB,EAAE;IACjH,IAAI,CAAC,aAAa,EAAE,MAAM,IAAI,CAAC,aAAa,EAAE,QAAQ,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC;QACjF,MAAM,IAAI,KAAK,CACX,kGAAkG,CACrG,CAAC;IACN,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,KAAK,CAAC;IACnD,MAAM,YAAY,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,YAAY,CAAC,EAAE,YAAY,EAAE,EAAE,IAAI,CAAC,CAAC;IAC/E,MAAM,KAAK,GAAG,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC/C,gFAAgF;IAChF,mFAAmF;IACnF,iEAAiE;IACjE,MAAM,YAAY,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACtF,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,EAAE,MAAM,CAAkB,CAAC,CAAC,eAAe;IACjF,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,IAAI,CAAkB,CAAC,CAAC,uBAAuB;IACrF,sEAAsE;IACtE,MAAM,QAAQ,GAAG,CAAC,SAAS,IAAI,IAAI,CAAkB,CAAC;IACtD,MAAM,UAAU,GAAG,CAAC,YAAY,IAAI,OAAO,IAAI,IAAI,CAAkB,CAAC;IACtE,wCAAwC;IACxC,8BAA8B;IAC9B,iCAAiC;IACjC,oCAAoC;IACpC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAC3B,MAAM,UAAU,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC7D,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,CAAC,GAAG,OAAiB,CAAC;QACpD,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,CAAC,GAAG,OAAiB,CAAC;IACxD,CAAC;IACD,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACjD,OAAO;QACH,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,CAAuB;QAClD,QAAQ;QACR,UAAU;QACV,YAAY;QACZ,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG;QACtC,WAAW,EAAE,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG;QACxF,IAAI,EAAE,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;QAC9E,CAAC;QACD,CAAC;QACD,IAAI,EAAE,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG;QACjF,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;KACpE,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,OAAgB,EAA0C,EAAE;IAC/E,IAAI,CAAC;QACD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QACtD,MAAM,kBAAkB,GAAG,OAAO,YAAY,KAAK,UAAU,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACtF,MAAM,IAAI,GAAG,OAAO,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;QACxD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACtE,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC/B,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACnG,OAAO,EAAE,CAAC,EAAE,CAAE,IAAe,GAAI,IAAe,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,CAAE,IAAe,GAAI,IAAe,CAAC,GAAG,GAAG,EAAE,CAAC;QAC9G,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACL,iGAAiG;QACjG,sEAAsE;IAC1E,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;AAChC,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EAAW,EAA0C,EAAE;IAC1E,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1D,OAAO;QACH,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAe,CAAC,CAAC,CAAC,IAAI;QAClD,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAe,CAAC,CAAC,CAAC,IAAI;KACrD,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,KAAc,EAA0C,EAAE;IAC7F,IAAI,CAAC;QACD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAC9C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YACjD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;QAChC,CAAC;QACD,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,SAAS;YACrC,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;gBACtB,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;gBACpC,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;oBAC7B,IAAI,IAAI,EAAE,CAAC;oBACX,IAAI,IAAI,EAAE,CAAC;oBACX,KAAK,EAAE,CAAC;gBACZ,CAAC;YACL,CAAC;QACL,CAAC;QACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC,EAAE,IAAI,GAAG,KAAK,EAAE,CAAC,EAAE,IAAI,GAAG,KAAK,EAAE,CAAC;QAChD,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACL,yCAAyC;IAC7C,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;AAChC,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAC9B,OAAsB,EACtB,OAAmC,EACZ,EAAE;IACzB,IAAI,CAAC,OAAO,EAAE,aAAa,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,iFAAiF,CAAC,CAAC;IACvG,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC;IAC7D,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACX,kGAAkG,CACrG,CAAC;IACN,CAAC;IACD,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC,qBAAqB,CAAC,KAAK,EAAE,OAAO,CAAC,aAAc,CAAC,CAAC,CAAC;AACrG,CAAC,CAAC;AAEF,MAAM,UAAU,uBAAuB,CAAC,KAAyB,EAAE,UAAuB;IACtF,MAAM,KAAK,GAAG,qBAAqB,CAC/B,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,iBAAiB,EACvB,UAAU,CACb,CAAC;IACF,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IACjG,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC","sourcesContent":["import type { SectionRenderState } from '@twinfinity/geometry';\nimport { normalizePropertySets } from './metadata';\nimport { getPathValue } from './renderUtils';\nimport { buildModelViewFromRaw } from './model';\nimport { ProductFieldPaths, NormalizedProductItem, SvgModelView, LabelStyle } from './types';\n\nimport { getProp } from './typeGuards';\n\nexport interface ProductLike {\n product?: any;\n polylines?: Array<{ points?: Array<{ x: number; z: number }> }>;\n}\n\nexport interface BuildViewportItemsOptions {\n productFields?: ProductFieldPaths;\n}\n\n/**\n * Normalize a single product entry using explicit field paths.\n */\nexport const normalizeProductEntry = (entry: ProductLike, productFields: ProductFieldPaths): NormalizedProductItem => {\n if (!productFields?.idPath || !productFields?.namePath || !productFields?.areaPath) {\n throw new Error(\n 'productFields must include idPath, namePath, and areaPath using propertySets.<Set>.<Prop>.value.'\n );\n }\n const product = getProp(entry, 'product') ?? entry;\n const propertySets = normalizePropertySets(product);\n const getVal = (path: string): unknown => getPathValue({ propertySets }, path);\n const idVal = getVal(productFields.idPath) ?? getProp(product, 'gid');\n const nameVal = getVal(productFields.namePath);\n const areaVal = getVal(productFields.areaPath);\n // class.type = IfcClassType (\"space\", \"walls\", etc.) — used by template filters\n // class.id = PredefinedIfcClassId (\"IfcSpace\", \"IfcWall\", etc.) — used as fallback\n // classPath from propertySets may contain a PredefinedIfcClassId\n const classPathVal = productFields.classPath ? getVal(productFields.classPath) : null;\n const productClass = getProp(product, 'class');\n const classType = getProp(productClass, 'type') as string | null; // IfcClassType\n const classId = getProp(productClass, 'id') as string | null; // PredefinedIfcClassId\n // ifcClass = IfcClassType category; ifcClassId = PredefinedIfcClassId\n const ifcClass = (classType ?? null) as string | null;\n const ifcClassId = (classPathVal ?? classId ?? null) as string | null;\n // Try multiple sources for coordinates:\n // 1. BBox center from product\n // 2. Polylines center from entry\n // 3. Direct x/z on entry or product\n let { x, z } = getBBoxCenter(product);\n if (x === null || z === null) {\n const polyCenter = computePolylinesCenter(entry);\n x = polyCenter.x ?? x;\n z = polyCenter.z ?? z;\n }\n if (x === null || z === null) {\n const directX = getProp(entry, 'x') ?? getProp(product, 'x');\n const directZ = getProp(entry, 'z') ?? getProp(product, 'z');\n if (Number.isFinite(directX)) x = directX as number;\n if (Number.isFinite(directZ)) z = directZ as number;\n }\n const rawPolylines = getProp(entry, 'polylines');\n return {\n gid: getProp(product, 'gid') as string | undefined,\n ifcClass,\n ifcClassId,\n propertySets,\n productId: idVal ? String(idVal) : '-',\n productName: typeof nameVal === 'string' && nameVal.trim().length ? nameVal.trim() : '-',\n area: typeof areaVal === 'number' && Number.isFinite(areaVal) ? areaVal : null,\n x,\n z,\n text: typeof nameVal === 'string' && nameVal.trim().length ? nameVal.trim() : '-',\n polylines: Array.isArray(rawPolylines) ? rawPolylines : undefined\n };\n};\n\nconst getBBoxCenter = (product: unknown): { x: number | null; z: number | null } => {\n try {\n const boundingInfo = getProp(product, 'boundingInfo');\n const boundingInfoResult = typeof boundingInfo === 'function' ? boundingInfo() : null;\n const bbox = getProp(boundingInfoResult, 'boundingBox');\n const min = getProp(bbox, 'minimumWorld') ?? getProp(bbox, 'minimum');\n const max = getProp(bbox, 'maximumWorld') ?? getProp(bbox, 'maximum');\n const minX = getProp(min, 'x');\n const maxX = getProp(max, 'x');\n const minZ = getProp(min, 'z');\n const maxZ = getProp(max, 'z');\n if (Number.isFinite(minX) && Number.isFinite(maxX) && Number.isFinite(minZ) && Number.isFinite(maxZ)) {\n return { x: ((minX as number) + (maxX as number)) * 0.5, z: ((minZ as number) + (maxZ as number)) * 0.5 };\n }\n } catch {\n // Bounding info access may fail for products without geometry or with malformed data structures.\n // This is expected behavior - we return null coordinates as fallback.\n }\n return { x: null, z: null };\n};\n\n/**\n * Read the plan-projection X/Z of a polyline point, tolerating both shapes the\n * section pipeline emits: `Vec3` arrays `[x, y, z]` (from\n * `computePlaneSectionsForProducts`) and `{ x, z }` objects. Coords are `null`\n * when the point is neither / non-finite. Shared by every consumer so the dual\n * contract is handled in exactly one place.\n */\nexport const readXZ = (pt: unknown): { x: number | null; z: number | null } => {\n const rawX = Array.isArray(pt) ? pt[0] : getProp(pt, 'x');\n const rawZ = Array.isArray(pt) ? pt[2] : getProp(pt, 'z');\n return {\n x: Number.isFinite(rawX) ? (rawX as number) : null,\n z: Number.isFinite(rawZ) ? (rawZ as number) : null\n };\n};\n\nexport const computePolylinesCenter = (entry: unknown): { x: number | null; z: number | null } => {\n try {\n const polylines = getProp(entry, 'polylines');\n if (!Array.isArray(polylines) || !polylines.length) {\n return { x: null, z: null };\n }\n let sumX = 0;\n let sumZ = 0;\n let count = 0;\n for (const polyline of polylines) {\n const points = getProp(polyline, 'points');\n if (!Array.isArray(points)) continue;\n for (const pt of points) {\n const { x: px, z: pz } = readXZ(pt);\n if (px !== null && pz !== null) {\n sumX += px;\n sumZ += pz;\n count++;\n }\n }\n }\n if (count > 0) {\n return { x: sumX / count, z: sumZ / count };\n }\n } catch {\n // Polyline access may fail - return null\n }\n return { x: null, z: null };\n};\n\n/**\n * Normalize raw product entries into viewport items using explicit field paths.\n * Requires productFields (id/name/area/class) pointing to propertySets.<Set>.<Prop>.value.\n */\nexport const buildViewportItems = (\n entries: ProductLike[],\n options?: BuildViewportItemsOptions\n): NormalizedProductItem[] => {\n if (!options?.productFields) {\n throw new Error('buildViewportItems requires productFields (idPath/namePath/areaPath/classPath).');\n }\n const { idPath, namePath, areaPath } = options.productFields;\n if (!idPath || !namePath || !areaPath) {\n throw new Error(\n 'productFields must include idPath, namePath, and areaPath using propertySets.<Set>.<Prop>.value.'\n );\n }\n return (entries ?? []).map((entry: any) => normalizeProductEntry(entry, options.productFields!));\n};\n\nexport function sectionStateToModelView(state: SectionRenderState, labelStyle?: LabelStyle): SvgModelView {\n const model = buildModelViewFromRaw(\n state.paths,\n state.labels,\n state.transform,\n state.geometryTransform,\n labelStyle\n );\n if (!model) {\n throw new Error('sectionStateToModelView requires paths, transform, and geometryTransform.');\n }\n return model;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labelsRender.d.ts","sourceRoot":"/","sources":["layout/labelsRender.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"labelsRender.d.ts","sourceRoot":"/","sources":["layout/labelsRender.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAepD,MAAM,WAAW,kBAAkB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,YAAY,GACrB,eAAe,aAAa,EAAE,EAC9B,MAAM,UAAU,EAChB,oBAAoB,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,EACtD,oBAAoB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3C,2BAA2B,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAC,EACjF,2BAA2B,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAC,EACjF,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,kBAAkB,OAAO,EACzB,UAAU,MAAM,EAAE,KACnB,MAAM,EA0GR,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { matchesFilter } from './filters';
|
|
2
2
|
import { pointInPolygon } from './clipUtils';
|
|
3
|
-
import { getPathValue, resolvePath, escapeAttr, escapeText } from './renderUtils';
|
|
3
|
+
import { getPathValue, resolvePath, escapeAttr, escapeText, formatNumber, splitPlaceholder, applyPlaceholderFilter } from './renderUtils';
|
|
4
4
|
import { isRecord, getProp } from './typeGuards';
|
|
5
5
|
export const renderLabels = (labelElements, data, viewportRenderInfo, viewportClipLabels, viewportClipPolygonsLocal, viewportClipPolygonsWorld, styles, dataWithViewport, warnings) => {
|
|
6
6
|
const parts = [];
|
|
@@ -24,32 +24,21 @@ export const renderLabels = (labelElements, data, viewportRenderInfo, viewportCl
|
|
|
24
24
|
continue;
|
|
25
25
|
}
|
|
26
26
|
// Filter by class + where
|
|
27
|
-
let filteredItems = dl.class || dl.where ? items.filter((item) =>
|
|
28
|
-
console.log(`[Labels ${dl.id}] Total items:`, items.length, 'After class+where filter:', filteredItems.length);
|
|
27
|
+
let filteredItems = dl.class || dl.where ? items.filter((item) => matchesFilter(item, dl.class, dl.where)) : items;
|
|
29
28
|
// If matchClipFilter is true, filter by spatial position inside clip polygon
|
|
30
29
|
if (dl.matchClipFilter && viewportClipPolygonsWorld[dl.viewport]?.length) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const beforeClipFilter = filteredItems.length;
|
|
30
|
+
// pointInPolygon expects y — world polys are {x, z}; map ONCE, not per item.
|
|
31
|
+
const polys2D = viewportClipPolygonsWorld[dl.viewport].map((poly) => poly.map((p) => ({ x: p.x, y: p.z })));
|
|
34
32
|
filteredItems = filteredItems.filter((item) => {
|
|
35
33
|
const ix = getProp(item, 'x');
|
|
36
34
|
const iz = getProp(item, 'z');
|
|
37
35
|
if (!Number.isFinite(ix) || !Number.isFinite(iz))
|
|
38
36
|
return false;
|
|
39
|
-
return
|
|
40
|
-
// pointInPolygon expects y coordinate, but we use z for world space
|
|
41
|
-
const polyWith2D = poly.map((p) => ({ x: p.x, y: p.z }));
|
|
42
|
-
return pointInPolygon(polyWith2D, ix, iz);
|
|
43
|
-
});
|
|
37
|
+
return polys2D.some((poly) => pointInPolygon(poly, ix, iz));
|
|
44
38
|
});
|
|
45
|
-
console.log(`[Labels ${dl.id}] After matchClipFilter (spatial):`, filteredItems.length, '(was:', beforeClipFilter, ')');
|
|
46
|
-
// Debug: log first few items to see their properties
|
|
47
|
-
if (filteredItems.length === 0 && beforeClipFilter > 0) {
|
|
48
|
-
console.log('[Labels] Sample item that was filtered out:', items[0]);
|
|
49
|
-
}
|
|
50
39
|
}
|
|
51
40
|
if (!filteredItems.length) {
|
|
52
|
-
|
|
41
|
+
warnings.push(`Labels '${dl.id}' matched 0 of ${items.length} items after filtering.`);
|
|
53
42
|
continue;
|
|
54
43
|
}
|
|
55
44
|
const styleName = dl.textStyle;
|
|
@@ -58,10 +47,9 @@ export const renderLabels = (labelElements, data, viewportRenderInfo, viewportCl
|
|
|
58
47
|
warnings.push(`Labels '${dl.id}' missing textStyle '${styleName ?? '(none)'}'.`);
|
|
59
48
|
continue;
|
|
60
49
|
}
|
|
61
|
-
|
|
50
|
+
// Schema default is center — labels sit on item centroids.
|
|
51
|
+
const anchor = dl.align === 'left' ? 'start' : dl.align === 'right' ? 'end' : 'middle';
|
|
62
52
|
const t = vpInfo.transform;
|
|
63
|
-
let renderedCount = 0;
|
|
64
|
-
let spatiallyFilteredCount = 0;
|
|
65
53
|
filteredItems.forEach((item) => {
|
|
66
54
|
// Hardcoded x/z for BIM floor plane convention
|
|
67
55
|
const xVal = getProp(item, 'x');
|
|
@@ -78,15 +66,16 @@ export const renderLabels = (labelElements, data, viewportRenderInfo, viewportCl
|
|
|
78
66
|
if (viewportClipLabels[dl.viewport] && viewportClipPolygonsLocal[dl.viewport]?.length) {
|
|
79
67
|
const inside = viewportClipPolygonsLocal[dl.viewport].some((poly) => pointInPolygon(poly, viewX, viewY));
|
|
80
68
|
if (!inside) {
|
|
81
|
-
spatiallyFilteredCount++;
|
|
82
69
|
return;
|
|
83
70
|
}
|
|
84
71
|
}
|
|
85
|
-
renderedCount++;
|
|
86
72
|
const text = escapeText(dl.text && typeof dl.text === 'string'
|
|
87
|
-
? dl.text.replace(/\{\{\s*([^}]+?)\s*\}\}/g, (_m,
|
|
73
|
+
? dl.text.replace(/\{\{\s*([^}]+?)\s*\}\}/g, (_m, expr) => {
|
|
74
|
+
const { path, filter } = splitPlaceholder(expr);
|
|
88
75
|
const val = resolvePath(item, path) ?? getPathValue(dataWithViewport, path);
|
|
89
|
-
return val === undefined || val === null || typeof val === 'object'
|
|
76
|
+
return val === undefined || val === null || typeof val === 'object'
|
|
77
|
+
? ''
|
|
78
|
+
: String(applyPlaceholderFilter(val, filter));
|
|
90
79
|
})
|
|
91
80
|
: '');
|
|
92
81
|
const fontFamily = getProp(style, 'fontFamily');
|
|
@@ -95,14 +84,13 @@ export const renderLabels = (labelElements, data, viewportRenderInfo, viewportCl
|
|
|
95
84
|
const color = getProp(style, 'color');
|
|
96
85
|
const letterSpacingMm = getProp(style, 'letterSpacingMm');
|
|
97
86
|
const lineHeightMm = getProp(style, 'lineHeightMm');
|
|
98
|
-
parts.push(`<text x="${xMm}" y="${yMm}" font-family="${escapeAttr(String(fontFamily))}" font-size="${fontSizeMm}"` +
|
|
87
|
+
parts.push(`<text x="${formatNumber(xMm)}" y="${formatNumber(yMm)}" font-family="${escapeAttr(String(fontFamily))}" font-size="${fontSizeMm}"` +
|
|
99
88
|
(fontWeight ? ` font-weight="${escapeAttr(String(fontWeight))}"` : '') +
|
|
100
89
|
(color ? ` fill="${escapeAttr(String(color))}"` : '') +
|
|
101
90
|
(letterSpacingMm !== undefined ? ` letter-spacing="${letterSpacingMm}"` : '') +
|
|
102
91
|
(lineHeightMm !== undefined ? ` line-height="${lineHeightMm}"` : '') +
|
|
103
92
|
` text-anchor="${anchor}">${text}</text>`);
|
|
104
93
|
});
|
|
105
|
-
console.log(`[Labels ${dl.id}] Rendered ${renderedCount} labels. Spatially filtered out: ${spatiallyFilteredCount}`);
|
|
106
94
|
}
|
|
107
95
|
return parts;
|
|
108
96
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labelsRender.js","sourceRoot":"/","sources":["layout/labelsRender.ts"],"names":[],"mappings":"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","sourcesContent":["import { LabelsElement, LayoutData } from './types';\nimport { matchesV2Filter } from './filters';\nimport { pointInPolygon } from './clipUtils';\nimport { getPathValue, resolvePath, escapeAttr, escapeText } from './renderUtils';\n\nimport { isRecord, getProp } from './typeGuards';\n\nexport interface ViewportRenderInfo {\n mmPerModel: number;\n contentWidthMm: number;\n contentHeightMm: number;\n offsetX: number;\n offsetY: number;\n rotationDeg: number;\n transform: {\n viewMinX: number;\n viewMaxX: number;\n viewMinZ: number;\n viewMaxZ: number;\n viewWidth: number;\n viewHeight: number;\n };\n centerX: number;\n centerY: number;\n centerModelX: number;\n centerModelY: number;\n}\n\nexport const renderLabels = (\n labelElements: LabelsElement[],\n data: LayoutData,\n viewportRenderInfo: Record<string, ViewportRenderInfo>,\n viewportClipLabels: Record<string, boolean>,\n viewportClipPolygonsLocal: Record<string, Array<Array<{ x: number; y: number }>>>,\n viewportClipPolygonsWorld: Record<string, Array<Array<{ x: number; z: number }>>>,\n styles: Record<string, unknown>,\n dataWithViewport: unknown,\n warnings: string[]\n): string[] => {\n const parts: string[] = [];\n\n for (const dl of labelElements) {\n const vpInfo = viewportRenderInfo[dl.viewport];\n if (!vpInfo) {\n warnings.push(`Labels '${dl.id}' missing viewport '${dl.viewport}'.`);\n continue;\n }\n if (vpInfo.rotationDeg !== 0) {\n warnings.push(`Labels '${dl.id}' skipped because rotation is not supported.`);\n continue;\n }\n if (!vpInfo.transform) {\n warnings.push(`Labels '${dl.id}' skipped because model transform is missing.`);\n continue;\n }\n // Items come from the bound viewport's data\n const items = getPathValue(data, `viewports.${dl.viewport}.items`);\n if (!Array.isArray(items) || !items.length) {\n continue;\n }\n // Filter by class + where\n let filteredItems =\n dl.class || dl.where ? items.filter((item) => matchesV2Filter(item, dl.class, dl.where)) : items;\n\n console.log(`[Labels ${dl.id}] Total items:`, items.length, 'After class+where filter:', filteredItems.length);\n\n // If matchClipFilter is true, filter by spatial position inside clip polygon\n if (dl.matchClipFilter && viewportClipPolygonsWorld[dl.viewport]?.length) {\n const clipPolygons = viewportClipPolygonsWorld[dl.viewport];\n console.log(`[Labels ${dl.id}] Applying matchClipFilter (spatial) with ${clipPolygons.length} polygons`);\n const beforeClipFilter = filteredItems.length;\n filteredItems = filteredItems.filter((item) => {\n const ix = getProp(item, 'x');\n const iz = getProp(item, 'z');\n if (!Number.isFinite(ix) || !Number.isFinite(iz)) return false;\n return clipPolygons.some((poly) => {\n // pointInPolygon expects y coordinate, but we use z for world space\n const polyWith2D = poly.map((p) => ({ x: p.x, y: p.z }));\n return pointInPolygon(polyWith2D, ix as number, iz as number);\n });\n });\n console.log(\n `[Labels ${dl.id}] After matchClipFilter (spatial):`,\n filteredItems.length,\n '(was:',\n beforeClipFilter,\n ')'\n );\n\n // Debug: log first few items to see their properties\n if (filteredItems.length === 0 && beforeClipFilter > 0) {\n console.log('[Labels] Sample item that was filtered out:', items[0]);\n }\n }\n\n if (!filteredItems.length) {\n console.log(`[Labels ${dl.id}] No items left after filtering, skipping.`);\n continue;\n }\n const styleName = dl.textStyle;\n const style = styleName && isRecord(styles) ? getProp(styles, styleName) : undefined;\n if (!isRecord(style)) {\n warnings.push(`Labels '${dl.id}' missing textStyle '${styleName ?? '(none)'}'.`);\n continue;\n }\n const anchor = dl.align === 'center' ? 'middle' : dl.align === 'right' ? 'end' : 'start';\n const t = vpInfo.transform;\n\n let renderedCount = 0;\n let spatiallyFilteredCount = 0;\n\n filteredItems.forEach((item) => {\n // Hardcoded x/z for BIM floor plane convention\n const xVal = getProp(item, 'x');\n const yVal = getProp(item, 'z');\n if (!Number.isFinite(xVal as number) || !Number.isFinite(yVal as number)) {\n return;\n }\n const viewX = (xVal as number) - t.viewMinX;\n const viewY = t.viewMaxZ - (yVal as number);\n const centerModelX = vpInfo.centerModelX;\n const centerModelY = vpInfo.centerModelY;\n const xMm = vpInfo.centerX + (viewX - centerModelX) * vpInfo.mmPerModel;\n const yMm = vpInfo.centerY + (viewY - centerModelY) * vpInfo.mmPerModel;\n\n if (viewportClipLabels[dl.viewport] && viewportClipPolygonsLocal[dl.viewport]?.length) {\n const inside = viewportClipPolygonsLocal[dl.viewport].some((poly) =>\n pointInPolygon(poly, viewX, viewY)\n );\n if (!inside) {\n spatiallyFilteredCount++;\n return;\n }\n }\n\n renderedCount++;\n\n const text = escapeText(\n dl.text && typeof dl.text === 'string'\n ? dl.text.replace(/\\{\\{\\s*([^}]+?)\\s*\\}\\}/g, (_m, path) => {\n const val = resolvePath(item, path) ?? getPathValue(dataWithViewport, path);\n return val === undefined || val === null || typeof val === 'object' ? '' : String(val);\n })\n : ''\n );\n\n const fontFamily = getProp(style, 'fontFamily');\n const fontSizeMm = getProp(style, 'fontSizeMm');\n const fontWeight = getProp(style, 'fontWeight');\n const color = getProp(style, 'color');\n const letterSpacingMm = getProp(style, 'letterSpacingMm');\n const lineHeightMm = getProp(style, 'lineHeightMm');\n\n parts.push(\n `<text x=\"${xMm}\" y=\"${yMm}\" font-family=\"${escapeAttr(String(fontFamily))}\" font-size=\"${fontSizeMm}\"` +\n (fontWeight ? ` font-weight=\"${escapeAttr(String(fontWeight))}\"` : '') +\n (color ? ` fill=\"${escapeAttr(String(color))}\"` : '') +\n (letterSpacingMm !== undefined ? ` letter-spacing=\"${letterSpacingMm}\"` : '') +\n (lineHeightMm !== undefined ? ` line-height=\"${lineHeightMm}\"` : '') +\n ` text-anchor=\"${anchor}\">${text}</text>`\n );\n });\n\n console.log(\n `[Labels ${dl.id}] Rendered ${renderedCount} labels. Spatially filtered out: ${spatiallyFilteredCount}`\n );\n }\n\n return parts;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"labelsRender.js","sourceRoot":"/","sources":["layout/labelsRender.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACH,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,sBAAsB,EACzB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAuBjD,MAAM,CAAC,MAAM,YAAY,GAAG,CACxB,aAA8B,EAC9B,IAAgB,EAChB,kBAAsD,EACtD,kBAA2C,EAC3C,yBAAiF,EACjF,yBAAiF,EACjF,MAA+B,EAC/B,gBAAyB,EACzB,QAAkB,EACV,EAAE;IACV,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,kBAAkB,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,uBAAuB,EAAE,CAAC,QAAQ,IAAI,CAAC,CAAC;YACtE,SAAS;QACb,CAAC;QACD,IAAI,MAAM,CAAC,WAAW,KAAK,CAAC,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,8CAA8C,CAAC,CAAC;YAC9E,SAAS;QACb,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YACpB,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,+CAA+C,CAAC,CAAC;YAC/E,SAAS;QACb,CAAC;QACD,4CAA4C;QAC5C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,QAAQ,QAAQ,CAAC,CAAC;QACnE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACzC,SAAS;QACb,CAAC;QACD,0BAA0B;QAC1B,IAAI,aAAa,GACb,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAEnG,6EAA6E;QAC7E,IAAI,EAAE,CAAC,eAAe,IAAI,yBAAyB,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;YACvE,6EAA6E;YAC7E,MAAM,OAAO,GAAG,yBAAyB,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5G,aAAa,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;gBAC1C,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBAC9B,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAAE,OAAO,KAAK,CAAC;gBAC/D,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,EAAY,EAAE,EAAY,CAAC,CAAC,CAAC;YACpF,CAAC,CAAC,CAAC;QACP,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;YACxB,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,kBAAkB,KAAK,CAAC,MAAM,yBAAyB,CAAC,CAAC;YACvF,SAAS;QACb,CAAC;QACD,MAAM,SAAS,GAAG,EAAE,CAAC,SAAS,CAAC;QAC/B,MAAM,KAAK,GAAG,SAAS,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,wBAAwB,SAAS,IAAI,QAAQ,IAAI,CAAC,CAAC;YACjF,SAAS;QACb,CAAC;QACD,2DAA2D;QAC3D,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;QACvF,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC;QAE3B,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YAC3B,+CAA+C;YAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAChC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAc,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAc,CAAC,EAAE,CAAC;gBACvE,OAAO;YACX,CAAC;YACD,MAAM,KAAK,GAAI,IAAe,GAAG,CAAC,CAAC,QAAQ,CAAC;YAC5C,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,GAAI,IAAe,CAAC;YAC5C,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;YACzC,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;YACzC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,GAAG,YAAY,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;YACxE,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,GAAG,YAAY,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC;YAExE,IAAI,kBAAkB,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,yBAAyB,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;gBACpF,MAAM,MAAM,GAAG,yBAAyB,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAChE,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CACrC,CAAC;gBACF,IAAI,CAAC,MAAM,EAAE,CAAC;oBACV,OAAO;gBACX,CAAC;YACL,CAAC;YAED,MAAM,IAAI,GAAG,UAAU,CACnB,EAAE,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC,IAAI,KAAK,QAAQ;gBAClC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE;oBACpD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;oBAChD,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,YAAY,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;oBAC5E,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;wBAC/D,CAAC,CAAC,EAAE;wBACJ,CAAC,CAAC,MAAM,CAAC,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;gBACtD,CAAC,CAAC;gBACJ,CAAC,CAAC,EAAE,CACX,CAAC;YAEF,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;YAChD,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;YAChD,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;YAChD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACtC,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;YAC1D,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;YAEpD,KAAK,CAAC,IAAI,CACN,YAAY,YAAY,CAAC,GAAG,CAAC,QAAQ,YAAY,CAAC,GAAG,CAAC,kBAAkB,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,gBAAgB,UAAU,GAAG;gBAC/H,CAAC,UAAU,CAAC,CAAC,CAAC,iBAAiB,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtE,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrD,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,oBAAoB,eAAe,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7E,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,YAAY,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpE,iBAAiB,MAAM,KAAK,IAAI,SAAS,CAChD,CAAC;QACN,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC,CAAC","sourcesContent":["import { LabelsElement, LayoutData } from './types';\nimport { matchesFilter } from './filters';\nimport { pointInPolygon } from './clipUtils';\nimport {\n getPathValue,\n resolvePath,\n escapeAttr,\n escapeText,\n formatNumber,\n splitPlaceholder,\n applyPlaceholderFilter\n} from './renderUtils';\n\nimport { isRecord, getProp } from './typeGuards';\n\nexport interface ViewportRenderInfo {\n mmPerModel: number;\n contentWidthMm: number;\n contentHeightMm: number;\n offsetX: number;\n offsetY: number;\n rotationDeg: number;\n transform: {\n viewMinX: number;\n viewMaxX: number;\n viewMinZ: number;\n viewMaxZ: number;\n viewWidth: number;\n viewHeight: number;\n };\n centerX: number;\n centerY: number;\n centerModelX: number;\n centerModelY: number;\n}\n\nexport const renderLabels = (\n labelElements: LabelsElement[],\n data: LayoutData,\n viewportRenderInfo: Record<string, ViewportRenderInfo>,\n viewportClipLabels: Record<string, boolean>,\n viewportClipPolygonsLocal: Record<string, Array<Array<{ x: number; y: number }>>>,\n viewportClipPolygonsWorld: Record<string, Array<Array<{ x: number; z: number }>>>,\n styles: Record<string, unknown>,\n dataWithViewport: unknown,\n warnings: string[]\n): string[] => {\n const parts: string[] = [];\n\n for (const dl of labelElements) {\n const vpInfo = viewportRenderInfo[dl.viewport];\n if (!vpInfo) {\n warnings.push(`Labels '${dl.id}' missing viewport '${dl.viewport}'.`);\n continue;\n }\n if (vpInfo.rotationDeg !== 0) {\n warnings.push(`Labels '${dl.id}' skipped because rotation is not supported.`);\n continue;\n }\n if (!vpInfo.transform) {\n warnings.push(`Labels '${dl.id}' skipped because model transform is missing.`);\n continue;\n }\n // Items come from the bound viewport's data\n const items = getPathValue(data, `viewports.${dl.viewport}.items`);\n if (!Array.isArray(items) || !items.length) {\n continue;\n }\n // Filter by class + where\n let filteredItems =\n dl.class || dl.where ? items.filter((item) => matchesFilter(item, dl.class, dl.where)) : items;\n\n // If matchClipFilter is true, filter by spatial position inside clip polygon\n if (dl.matchClipFilter && viewportClipPolygonsWorld[dl.viewport]?.length) {\n // pointInPolygon expects y — world polys are {x, z}; map ONCE, not per item.\n const polys2D = viewportClipPolygonsWorld[dl.viewport].map((poly) => poly.map((p) => ({ x: p.x, y: p.z })));\n filteredItems = filteredItems.filter((item) => {\n const ix = getProp(item, 'x');\n const iz = getProp(item, 'z');\n if (!Number.isFinite(ix) || !Number.isFinite(iz)) return false;\n return polys2D.some((poly) => pointInPolygon(poly, ix as number, iz as number));\n });\n }\n\n if (!filteredItems.length) {\n warnings.push(`Labels '${dl.id}' matched 0 of ${items.length} items after filtering.`);\n continue;\n }\n const styleName = dl.textStyle;\n const style = styleName && isRecord(styles) ? getProp(styles, styleName) : undefined;\n if (!isRecord(style)) {\n warnings.push(`Labels '${dl.id}' missing textStyle '${styleName ?? '(none)'}'.`);\n continue;\n }\n // Schema default is center — labels sit on item centroids.\n const anchor = dl.align === 'left' ? 'start' : dl.align === 'right' ? 'end' : 'middle';\n const t = vpInfo.transform;\n\n filteredItems.forEach((item) => {\n // Hardcoded x/z for BIM floor plane convention\n const xVal = getProp(item, 'x');\n const yVal = getProp(item, 'z');\n if (!Number.isFinite(xVal as number) || !Number.isFinite(yVal as number)) {\n return;\n }\n const viewX = (xVal as number) - t.viewMinX;\n const viewY = t.viewMaxZ - (yVal as number);\n const centerModelX = vpInfo.centerModelX;\n const centerModelY = vpInfo.centerModelY;\n const xMm = vpInfo.centerX + (viewX - centerModelX) * vpInfo.mmPerModel;\n const yMm = vpInfo.centerY + (viewY - centerModelY) * vpInfo.mmPerModel;\n\n if (viewportClipLabels[dl.viewport] && viewportClipPolygonsLocal[dl.viewport]?.length) {\n const inside = viewportClipPolygonsLocal[dl.viewport].some((poly) =>\n pointInPolygon(poly, viewX, viewY)\n );\n if (!inside) {\n return;\n }\n }\n\n const text = escapeText(\n dl.text && typeof dl.text === 'string'\n ? dl.text.replace(/\\{\\{\\s*([^}]+?)\\s*\\}\\}/g, (_m, expr) => {\n const { path, filter } = splitPlaceholder(expr);\n const val = resolvePath(item, path) ?? getPathValue(dataWithViewport, path);\n return val === undefined || val === null || typeof val === 'object'\n ? ''\n : String(applyPlaceholderFilter(val, filter));\n })\n : ''\n );\n\n const fontFamily = getProp(style, 'fontFamily');\n const fontSizeMm = getProp(style, 'fontSizeMm');\n const fontWeight = getProp(style, 'fontWeight');\n const color = getProp(style, 'color');\n const letterSpacingMm = getProp(style, 'letterSpacingMm');\n const lineHeightMm = getProp(style, 'lineHeightMm');\n\n parts.push(\n `<text x=\"${formatNumber(xMm)}\" y=\"${formatNumber(yMm)}\" font-family=\"${escapeAttr(String(fontFamily))}\" font-size=\"${fontSizeMm}\"` +\n (fontWeight ? ` font-weight=\"${escapeAttr(String(fontWeight))}\"` : '') +\n (color ? ` fill=\"${escapeAttr(String(color))}\"` : '') +\n (letterSpacingMm !== undefined ? ` letter-spacing=\"${letterSpacingMm}\"` : '') +\n (lineHeightMm !== undefined ? ` line-height=\"${lineHeightMm}\"` : '') +\n ` text-anchor=\"${anchor}\">${text}</text>`\n );\n });\n }\n\n return parts;\n};\n"]}
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { LegendElement, PageSpec, StyleMap } from './types';
|
|
2
|
-
export declare const applyLegendFilter: (legendData: unknown[], legend: LegendElement,
|
|
2
|
+
export declare const applyLegendFilter: (legendData: unknown[], legend: LegendElement, viewportClipPolygonsWorld: Record<string, Array<Array<{
|
|
3
3
|
x: number;
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
z: number;
|
|
5
|
+
}>>>) => unknown[];
|
|
6
|
+
/**
|
|
7
|
+
* Colors a legend assigned to its rows (assignColors), for consumption by
|
|
8
|
+
* viewport fill rules with `fill: "legend"`. Lookup order for an item:
|
|
9
|
+
* its gid (per-row identity — works without groupBy), then the value of the
|
|
10
|
+
* legend's groupBy property (shared across a group).
|
|
11
|
+
*/
|
|
12
|
+
export interface LegendColorAssignment {
|
|
13
|
+
/** The legend's groupBy property path, when set. */
|
|
14
|
+
groupBy?: string;
|
|
15
|
+
/** Row color by item gid/id. */
|
|
16
|
+
byGid: Map<string, string>;
|
|
17
|
+
/** Row color by groupBy property value (only populated with groupBy). */
|
|
18
|
+
byValue: Map<string, string>;
|
|
19
|
+
}
|
|
8
20
|
export interface LegendResult {
|
|
9
21
|
svg: string;
|
|
10
|
-
|
|
22
|
+
colors?: LegendColorAssignment;
|
|
11
23
|
}
|
|
12
24
|
export declare const buildLegendGroup: (legend: LegendElement, styles: StyleMap | undefined, page: PageSpec, marginMm: number | undefined, legendData: unknown[], fillOpacity?: number) => LegendResult | null;
|
|
13
25
|
//# sourceMappingURL=legendRender.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legendRender.d.ts","sourceRoot":"/","sources":["layout/legendRender.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AA+E5D,eAAO,MAAM,iBAAiB,GAC1B,YAAY,OAAO,EAAE,EACrB,QAAQ,aAAa,EACrB,
|
|
1
|
+
{"version":3,"file":"legendRender.d.ts","sourceRoot":"/","sources":["layout/legendRender.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AA+E5D,eAAO,MAAM,iBAAiB,GAC1B,YAAY,OAAO,EAAE,EACrB,QAAQ,aAAa,EACrB,2BAA2B,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAC,KAClF,OAAO,EAyBT,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IAClC,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,yEAAyE;IACzE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAClC;AAED,eAAO,MAAM,gBAAgB,GACzB,QAAQ,aAAa,EACrB,QAAQ,QAAQ,GAAG,SAAS,EAC5B,MAAM,QAAQ,EACd,UAAU,MAAM,GAAG,SAAS,EAC5B,YAAY,OAAO,EAAE,EACrB,cAAc,MAAM,KACrB,YAAY,GAAG,IAiYjB,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_LEGEND_SWATCH_WIDTH_MM, DEFAULT_LEGEND_CELL_PADDING_MM, DEFAULT_LEGEND_BORDER_WIDTH_MM, DEFAULT_LEGEND_GRID_WIDTH_MM, DEFAULT_LEGEND_HEADER_WIDTH_MM, DEFAULT_LEGEND_OFFSET_MM, MAX_LEGEND_ROWS } from './legendConstants';
|
|
2
2
|
import { computeLegendColumnWidths, computeLegendTextYOffset, parseLegendColor, formatLegendValue } from './legendUtils';
|
|
3
|
-
import { resolvePath } from './renderUtils';
|
|
4
|
-
import {
|
|
3
|
+
import { resolvePath, escapeText, escapeAttr } from './renderUtils';
|
|
4
|
+
import { matchesFilter } from './filters';
|
|
5
5
|
import { pointInPolygon } from './clipUtils';
|
|
6
6
|
/**
|
|
7
7
|
* Convert HSL to RGB for PDF compatibility.
|
|
@@ -58,32 +58,28 @@ function generateLegendColors(count, palette) {
|
|
|
58
58
|
return colors;
|
|
59
59
|
}
|
|
60
60
|
import { isRecord, getProp } from './typeGuards';
|
|
61
|
-
export const applyLegendFilter = (legendData, legend,
|
|
61
|
+
export const applyLegendFilter = (legendData, legend, viewportClipPolygonsWorld) => {
|
|
62
62
|
// Filter by class + where
|
|
63
63
|
let rows = Array.isArray(legendData)
|
|
64
|
-
? legendData.filter((row) =>
|
|
64
|
+
? legendData.filter((row) => matchesFilter(row, legend.class, legend.where))
|
|
65
65
|
: [];
|
|
66
66
|
if (!rows.length)
|
|
67
67
|
return rows;
|
|
68
|
+
// clipToViewport filters in WORLD space (same convention as labels and
|
|
69
|
+
// clipAllowedGids) so the result is available before viewports render —
|
|
70
|
+
// legend color maps must exist by then for fill: "legend".
|
|
68
71
|
if (legend.clipToViewport) {
|
|
69
72
|
const targetViewportId = legend.viewport;
|
|
70
|
-
const polys = targetViewportId ?
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!Number.isFinite(rowX) || !Number.isFinite(rowZ))
|
|
81
|
-
return false;
|
|
82
|
-
const viewX = Number(rowX) - viewMinX;
|
|
83
|
-
const viewY = viewMaxZ - Number(rowZ);
|
|
84
|
-
return polys.some((poly) => pointInPolygon(poly, viewX, viewY));
|
|
85
|
-
});
|
|
86
|
-
}
|
|
73
|
+
const polys = targetViewportId ? viewportClipPolygonsWorld[targetViewportId] : null;
|
|
74
|
+
if (polys && polys.length) {
|
|
75
|
+
const polys2D = polys.map((poly) => poly.map((p) => ({ x: p.x, y: p.z })));
|
|
76
|
+
rows = rows.filter((row) => {
|
|
77
|
+
const rowX = getProp(row, 'x');
|
|
78
|
+
const rowZ = getProp(row, 'z');
|
|
79
|
+
if (!Number.isFinite(rowX) || !Number.isFinite(rowZ))
|
|
80
|
+
return false;
|
|
81
|
+
return polys2D.some((poly) => pointInPolygon(poly, Number(rowX), Number(rowZ)));
|
|
82
|
+
});
|
|
87
83
|
}
|
|
88
84
|
}
|
|
89
85
|
return rows;
|
|
@@ -91,34 +87,24 @@ export const applyLegendFilter = (legendData, legend, viewports, viewportClipPol
|
|
|
91
87
|
export const buildLegendGroup = (legend, styles, page, marginMm, legendData, fillOpacity) => {
|
|
92
88
|
if (!legend || !legendData || !legendData.length)
|
|
93
89
|
return null;
|
|
94
|
-
// Debug: Log raw input data
|
|
95
|
-
// console.log(`[LEGEND INPUT] Received ${legendData.length} items for legend "${legend.id}"`);
|
|
96
|
-
// if (legend.groupBy) {
|
|
97
|
-
// // Count duplicates by groupBy property
|
|
98
|
-
// const counts = new Map<string, number>();
|
|
99
|
-
// for (const item of legendData) {
|
|
100
|
-
// const key = String(resolvePath(item, legend.groupBy) ?? '');
|
|
101
|
-
// counts.set(key, (counts.get(key) || 0) + 1);
|
|
102
|
-
// }
|
|
103
|
-
// console.log('[LEGEND INPUT] Items per group (before grouping):', Array.from(counts.entries()).map(([k, v]) => `${k}: ${v}`).join(', '));
|
|
104
|
-
// }
|
|
105
90
|
const pageMargin = typeof marginMm === 'number' ? marginMm : 0;
|
|
106
91
|
const fontStyle = (legend.textStyle && styles?.[legend.textStyle]) || styles?.['body'];
|
|
107
92
|
const fontSizeMm = typeof fontStyle?.fontSizeMm === 'number' ? fontStyle.fontSizeMm : 5;
|
|
108
|
-
|
|
109
|
-
|
|
93
|
+
// Escaped once here — every emitted <text>/<rect> below interpolates these
|
|
94
|
+
// (and cell text via escapeText) into XML.
|
|
95
|
+
const fontFamily = escapeAttr(String(fontStyle?.fontFamily ?? 'Arial, sans-serif'));
|
|
96
|
+
const fontColor = escapeAttr(String(isRecord(fontStyle) ? (getProp(fontStyle, 'color') ?? '#000') : '#000'));
|
|
110
97
|
// Show color column if enabled (but swatches will be empty if no fills configured)
|
|
111
98
|
const showColorColumn = legend.showColorColumn !== false;
|
|
112
99
|
const colorColumnWidthMm = showColorColumn ? (legend.colorColumnWidthMm ?? DEFAULT_LEGEND_SWATCH_WIDTH_MM) : 0;
|
|
113
100
|
const hasFills = fillOpacity !== undefined;
|
|
114
|
-
// console.log(`[LEGEND RENDER] fillOpacity=${fillOpacity}, hasFills=${hasFills}, showColorColumn=${showColorColumn}`);
|
|
115
101
|
const cellPaddingMm = legend.cellPaddingMm ?? DEFAULT_LEGEND_CELL_PADDING_MM;
|
|
116
|
-
const borderStrokeWidthMm = legend.
|
|
117
|
-
const borderStrokeColor = legend.
|
|
118
|
-
const gridStrokeWidthMm = legend.
|
|
119
|
-
const gridStrokeColor = legend.
|
|
120
|
-
const headerStrokeWidthMm = legend.
|
|
121
|
-
const headerStrokeColor = legend.
|
|
102
|
+
const borderStrokeWidthMm = legend.border?.strokeWidthMm ?? DEFAULT_LEGEND_BORDER_WIDTH_MM;
|
|
103
|
+
const borderStrokeColor = escapeAttr(legend.border?.stroke ?? '#000000');
|
|
104
|
+
const gridStrokeWidthMm = legend.grid?.strokeWidthMm ?? DEFAULT_LEGEND_GRID_WIDTH_MM;
|
|
105
|
+
const gridStrokeColor = escapeAttr(legend.grid?.stroke ?? '#000000');
|
|
106
|
+
const headerStrokeWidthMm = legend.header?.strokeWidthMm ?? DEFAULT_LEGEND_HEADER_WIDTH_MM;
|
|
107
|
+
const headerStrokeColor = escapeAttr(legend.header?.stroke ?? '#000000');
|
|
122
108
|
const anchor = legend.anchor ?? 'top-right';
|
|
123
109
|
const legendMarginMm = legend.marginMm ?? DEFAULT_LEGEND_OFFSET_MM;
|
|
124
110
|
// Group rows if groupBy is specified
|
|
@@ -133,12 +119,6 @@ export const buildLegendGroup = (legend, styles, page, marginMm, legendData, fil
|
|
|
133
119
|
}
|
|
134
120
|
groups.get(groupKey).push(row);
|
|
135
121
|
}
|
|
136
|
-
// console.log(`[LEGEND GROUPING] Total input rows: ${legendData.length}, Groups created: ${groups.size}`);
|
|
137
|
-
// for (const [key, rows] of groups.entries()) {
|
|
138
|
-
// if (rows.length > 1) {
|
|
139
|
-
// console.log(`[LEGEND GROUPING] Group "${key}": ${rows.length} rows`);
|
|
140
|
-
// }
|
|
141
|
-
// }
|
|
142
122
|
// Aggregate each group
|
|
143
123
|
processedData = Array.from(groups.values()).map((groupRows) => {
|
|
144
124
|
if (groupRows.length === 0)
|
|
@@ -170,15 +150,6 @@ export const buildLegendGroup = (legend, styles, page, marginMm, legendData, fil
|
|
|
170
150
|
const num = typeof v === 'number' ? v : parseFloat(String(v));
|
|
171
151
|
return acc + num;
|
|
172
152
|
}, 0);
|
|
173
|
-
// Debug logging for aggregation
|
|
174
|
-
// if (groupRows.length > 1 && legend.groupBy) {
|
|
175
|
-
// const groupKey = resolvePath(groupRows[0], legend.groupBy);
|
|
176
|
-
// console.log(`[LEGEND AGGREGATION] Group "${groupKey}", Column "${col.property}":`, {
|
|
177
|
-
// itemCount: values.length,
|
|
178
|
-
// values: values,
|
|
179
|
-
// sum: sum
|
|
180
|
-
// });
|
|
181
|
-
// }
|
|
182
153
|
// Set the aggregated value in the nested property structure
|
|
183
154
|
const pathParts = col.property.split('.');
|
|
184
155
|
let target = aggregated;
|
|
@@ -195,28 +166,27 @@ export const buildLegendGroup = (legend, styles, page, marginMm, legendData, fil
|
|
|
195
166
|
return aggregated;
|
|
196
167
|
});
|
|
197
168
|
}
|
|
198
|
-
// Generate colors if assignColors is enabled
|
|
169
|
+
// Generate colors if assignColors is enabled — consumed by viewport fill
|
|
170
|
+
// rules with fill: "legend" (byGid works without groupBy: every row gets
|
|
171
|
+
// its own color).
|
|
199
172
|
let generatedColors = null;
|
|
200
|
-
let
|
|
173
|
+
let colors;
|
|
201
174
|
if (legend.assignColors?.enabled) {
|
|
202
175
|
const palette = legend.assignColors.palette ?? 'auto';
|
|
203
176
|
generatedColors = generateLegendColors(processedData.length, palette);
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
for (let i = 0; i < processedData.length; i++) {
|
|
209
|
-
const row = processedData[i];
|
|
177
|
+
colors = { groupBy: legend.groupBy || undefined, byGid: new Map(), byValue: new Map() };
|
|
178
|
+
for (let i = 0; i < processedData.length; i++) {
|
|
179
|
+
const row = processedData[i];
|
|
180
|
+
if (legend.groupBy) {
|
|
210
181
|
const groupValue = String(resolvePath(row, legend.groupBy) ?? '');
|
|
211
|
-
if (!
|
|
212
|
-
|
|
213
|
-
// console.log(`[LEGEND COLORS] Mapped "${groupValue}" -> ${generatedColors[i]}`);
|
|
182
|
+
if (!colors.byValue.has(groupValue)) {
|
|
183
|
+
colors.byValue.set(groupValue, generatedColors[i]);
|
|
214
184
|
}
|
|
215
185
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
186
|
+
const gid = getProp(row, 'gid') ?? getProp(row, 'id');
|
|
187
|
+
if (gid !== null && gid !== undefined) {
|
|
188
|
+
colors.byGid.set(String(gid), generatedColors[i]);
|
|
189
|
+
}
|
|
220
190
|
}
|
|
221
191
|
}
|
|
222
192
|
const rowsMapped = processedData.map((row, index) => {
|
|
@@ -316,14 +286,14 @@ export const buildLegendGroup = (legend, styles, page, marginMm, legendData, fil
|
|
|
316
286
|
lines.push(`<line x1="${originXmm}" y1="${originYmm + headerHeightMm}" x2="${originXmm + tableWidthMm}" y2="${originYmm + headerHeightMm}" stroke="${headerStrokeColor}" stroke-width="${headerStrokeWidthMm}" />`);
|
|
317
287
|
for (let i = 2; i < columnCount; i++) {
|
|
318
288
|
const x = originXmm + colXmm[i];
|
|
319
|
-
lines.push(`<line x1="${x}" y1="${originYmm}" x2="${x}" y2="${originYmm + tableHeightMm}" stroke="${gridStrokeColor}" stroke-width="${
|
|
289
|
+
lines.push(`<line x1="${x}" y1="${originYmm}" x2="${x}" y2="${originYmm + tableHeightMm}" stroke="${gridStrokeColor}" stroke-width="${gridStrokeWidthMm}" />`);
|
|
320
290
|
}
|
|
321
291
|
for (let r = 0; r <= rowsMapped.length; r++) {
|
|
322
292
|
// Skip last grid line if summary enabled (we draw a heavier separator line instead)
|
|
323
293
|
if (r === rowsMapped.length && summaryEnabled)
|
|
324
294
|
continue;
|
|
325
295
|
const y = originYmm + headerHeightMm + r * rowHeightMm;
|
|
326
|
-
lines.push(`<line x1="${originXmm}" y1="${y}" x2="${originXmm + tableWidthMm}" y2="${y}" stroke="${gridStrokeColor}" stroke-width="${
|
|
296
|
+
lines.push(`<line x1="${originXmm}" y1="${y}" x2="${originXmm + tableWidthMm}" y2="${y}" stroke="${gridStrokeColor}" stroke-width="${gridStrokeWidthMm}" />`);
|
|
327
297
|
}
|
|
328
298
|
const textYOffsetMm = computeLegendTextYOffset(fontSizeMm);
|
|
329
299
|
legend.columns.slice(0, columnCount - dataColStartIdx).forEach((col, idx) => {
|
|
@@ -337,7 +307,7 @@ export const buildLegendGroup = (legend, styles, page, marginMm, legendData, fil
|
|
|
337
307
|
: colStart + innerPad;
|
|
338
308
|
const y = originYmm + headerHeightMm / 2 + textYOffsetMm;
|
|
339
309
|
const anchorText = col.align === 'center' ? 'middle' : col.align === 'right' ? 'end' : 'start';
|
|
340
|
-
texts.push(`<text x="${x}" y="${y}" font-family="${fontFamily}" font-size="${fontSizeMm}" fill="${fontColor}" dominant-baseline="middle" text-anchor="${anchorText}">${col.label}</text>`);
|
|
310
|
+
texts.push(`<text x="${x}" y="${y}" font-family="${fontFamily}" font-size="${fontSizeMm}" fill="${fontColor}" dominant-baseline="middle" text-anchor="${anchorText}">${escapeText(col.label)}</text>`);
|
|
341
311
|
});
|
|
342
312
|
const swatchOpacity = typeof fillOpacity === 'number' ? Math.min(Math.max(fillOpacity, 0), 1) : 1;
|
|
343
313
|
rowsMapped.slice(0, MAX_LEGEND_ROWS).forEach((row, rIndex) => {
|
|
@@ -348,7 +318,7 @@ export const buildLegendGroup = (legend, styles, page, marginMm, legendData, fil
|
|
|
348
318
|
// row.color can be either a CSS color string (from generated colors) or RGB string
|
|
349
319
|
const swatchFill = row.color; // Already a CSS color string
|
|
350
320
|
const opacityAttr = swatchOpacity < 1 ? ` fill-opacity="${swatchOpacity.toFixed(3)}"` : '';
|
|
351
|
-
rects.push(`<rect x="${originXmm + colXmm[0]}" y="${yTop}" width="${colWidthsMm[0]}" height="${rowHeightMm}" fill="${swatchFill}"${opacityAttr} stroke="none" />`);
|
|
321
|
+
rects.push(`<rect x="${originXmm + colXmm[0]}" y="${yTop}" width="${colWidthsMm[0]}" height="${rowHeightMm}" fill="${escapeAttr(String(swatchFill))}"${opacityAttr} stroke="none" />`);
|
|
352
322
|
}
|
|
353
323
|
else {
|
|
354
324
|
// Empty swatch when no fills configured - just the border cell
|
|
@@ -369,7 +339,7 @@ export const buildLegendGroup = (legend, styles, page, marginMm, legendData, fil
|
|
|
369
339
|
: colStart + innerPad;
|
|
370
340
|
const y = yTop + rowHeightMm / 2 + textYOffsetMm;
|
|
371
341
|
const anchorText = col.align === 'center' ? 'middle' : col.align === 'right' ? 'end' : 'start';
|
|
372
|
-
texts.push(`<text x="${x}" y="${y}" font-family="${fontFamily}" font-size="${fontSizeMm}" fill="${fontColor}" dominant-baseline="middle" text-anchor="${anchorText}">${text}</text>`);
|
|
342
|
+
texts.push(`<text x="${x}" y="${y}" font-family="${fontFamily}" font-size="${fontSizeMm}" fill="${fontColor}" dominant-baseline="middle" text-anchor="${anchorText}">${escapeText(text)}</text>`);
|
|
373
343
|
});
|
|
374
344
|
});
|
|
375
345
|
// Render summary row (if enabled)
|
|
@@ -400,7 +370,7 @@ export const buildLegendGroup = (legend, styles, page, marginMm, legendData, fil
|
|
|
400
370
|
const innerPad = cellPaddingMm + gridStrokeWidthMm * 0.5;
|
|
401
371
|
const labelX = contentOriginXmm + innerPad;
|
|
402
372
|
const labelY = yTop + rowHeightMm / 2 + textYOffsetMm;
|
|
403
|
-
texts.push(`<text x="${labelX}" y="${labelY}" font-family="${fontFamily}" font-size="${fontSizeMm}" fill="${fontColor}" font-weight="bold" dominant-baseline="middle" text-anchor="start">${summaryLabel}</text>`);
|
|
373
|
+
texts.push(`<text x="${labelX}" y="${labelY}" font-family="${fontFamily}" font-size="${fontSizeMm}" fill="${fontColor}" font-weight="bold" dominant-baseline="middle" text-anchor="start">${escapeText(summaryLabel)}</text>`);
|
|
404
374
|
// Render summary values for other columns
|
|
405
375
|
legend.columns.slice(1, columnCount - dataColStartIdx).forEach((col, cIdx) => {
|
|
406
376
|
const summaryText = summaryValues[cIdx + 1] ?? '';
|
|
@@ -415,10 +385,10 @@ export const buildLegendGroup = (legend, styles, page, marginMm, legendData, fil
|
|
|
415
385
|
: colStart + innerPad;
|
|
416
386
|
const y = yTop + rowHeightMm / 2 + textYOffsetMm;
|
|
417
387
|
const anchorText = col.align === 'center' ? 'middle' : col.align === 'right' ? 'end' : 'start';
|
|
418
|
-
texts.push(`<text x="${x}" y="${y}" font-family="${fontFamily}" font-size="${fontSizeMm}" fill="${fontColor}" font-weight="bold" dominant-baseline="middle" text-anchor="${anchorText}">${summaryText}</text>`);
|
|
388
|
+
texts.push(`<text x="${x}" y="${y}" font-family="${fontFamily}" font-size="${fontSizeMm}" fill="${fontColor}" font-weight="bold" dominant-baseline="middle" text-anchor="${anchorText}">${escapeText(summaryText)}</text>`);
|
|
419
389
|
});
|
|
420
390
|
}
|
|
421
391
|
const svg = [`<g id="legend">`, ...rects, ...lines, ...texts, `</g>`].join('');
|
|
422
|
-
return { svg,
|
|
392
|
+
return { svg, colors };
|
|
423
393
|
};
|
|
424
394
|
//# sourceMappingURL=legendRender.js.map
|