@twinfinity/printing 5.0.3-ci.28538-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +208 -0
  2. package/dist/BuildInfo.d.ts +25 -0
  3. package/dist/BuildInfo.d.ts.map +1 -0
  4. package/dist/BuildInfo.js +15 -0
  5. package/dist/BuildInfo.js.map +1 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +9 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/layout/analyzeTemplate.d.ts +29 -0
  11. package/dist/layout/analyzeTemplate.d.ts.map +1 -0
  12. package/dist/layout/analyzeTemplate.js +168 -0
  13. package/dist/layout/analyzeTemplate.js.map +1 -0
  14. package/dist/layout/clipUtils.d.ts +18 -0
  15. package/dist/layout/clipUtils.d.ts.map +1 -0
  16. package/dist/layout/clipUtils.js +165 -0
  17. package/dist/layout/clipUtils.js.map +1 -0
  18. package/dist/layout/colorConstants.d.ts +11 -0
  19. package/dist/layout/colorConstants.d.ts.map +1 -0
  20. package/dist/layout/colorConstants.js +14 -0
  21. package/dist/layout/colorConstants.js.map +1 -0
  22. package/dist/layout/elementKinds.d.ts +21 -0
  23. package/dist/layout/elementKinds.d.ts.map +1 -0
  24. package/dist/layout/elementKinds.js +17 -0
  25. package/dist/layout/elementKinds.js.map +1 -0
  26. package/dist/layout/exportDrawing.d.ts +143 -0
  27. package/dist/layout/exportDrawing.d.ts.map +1 -0
  28. package/dist/layout/exportDrawing.js +146 -0
  29. package/dist/layout/exportDrawing.js.map +1 -0
  30. package/dist/layout/filters.d.ts +33 -0
  31. package/dist/layout/filters.d.ts.map +1 -0
  32. package/dist/layout/filters.js +77 -0
  33. package/dist/layout/filters.js.map +1 -0
  34. package/dist/layout/geometryConstants.d.ts +8 -0
  35. package/dist/layout/geometryConstants.d.ts.map +1 -0
  36. package/dist/layout/geometryConstants.js +10 -0
  37. package/dist/layout/geometryConstants.js.map +1 -0
  38. package/dist/layout/index.d.ts +86 -0
  39. package/dist/layout/index.d.ts.map +1 -0
  40. package/dist/layout/index.js +363 -0
  41. package/dist/layout/index.js.map +1 -0
  42. package/dist/layout/itemBuilders.d.ts +29 -0
  43. package/dist/layout/itemBuilders.d.ts.map +1 -0
  44. package/dist/layout/itemBuilders.js +135 -0
  45. package/dist/layout/itemBuilders.js.map +1 -0
  46. package/dist/layout/labelsRender.d.ts +29 -0
  47. package/dist/layout/labelsRender.d.ts.map +1 -0
  48. package/dist/layout/labelsRender.js +109 -0
  49. package/dist/layout/labelsRender.js.map +1 -0
  50. package/dist/layout/legendConstants.d.ts +12 -0
  51. package/dist/layout/legendConstants.d.ts.map +1 -0
  52. package/dist/layout/legendConstants.js +12 -0
  53. package/dist/layout/legendConstants.js.map +1 -0
  54. package/dist/layout/legendRender.d.ts +13 -0
  55. package/dist/layout/legendRender.d.ts.map +1 -0
  56. package/dist/layout/legendRender.js +424 -0
  57. package/dist/layout/legendRender.js.map +1 -0
  58. package/dist/layout/legendRows.d.ts +26 -0
  59. package/dist/layout/legendRows.d.ts.map +1 -0
  60. package/dist/layout/legendRows.js +45 -0
  61. package/dist/layout/legendRows.js.map +1 -0
  62. package/dist/layout/legendUtils.d.ts +21 -0
  63. package/dist/layout/legendUtils.d.ts.map +1 -0
  64. package/dist/layout/legendUtils.js +110 -0
  65. package/dist/layout/legendUtils.js.map +1 -0
  66. package/dist/layout/metadata.d.ts +43 -0
  67. package/dist/layout/metadata.d.ts.map +1 -0
  68. package/dist/layout/metadata.js +191 -0
  69. package/dist/layout/metadata.js.map +1 -0
  70. package/dist/layout/model.d.ts +15 -0
  71. package/dist/layout/model.d.ts.map +1 -0
  72. package/dist/layout/model.js +310 -0
  73. package/dist/layout/model.js.map +1 -0
  74. package/dist/layout/options.d.ts +17 -0
  75. package/dist/layout/options.d.ts.map +1 -0
  76. package/dist/layout/options.js +2 -0
  77. package/dist/layout/options.js.map +1 -0
  78. package/dist/layout/presets.d.ts +23 -0
  79. package/dist/layout/presets.d.ts.map +1 -0
  80. package/dist/layout/presets.js +41 -0
  81. package/dist/layout/presets.js.map +1 -0
  82. package/dist/layout/qrRender.d.ts +13 -0
  83. package/dist/layout/qrRender.d.ts.map +1 -0
  84. package/dist/layout/qrRender.js +97 -0
  85. package/dist/layout/qrRender.js.map +1 -0
  86. package/dist/layout/renderDrawing.d.ts +62 -0
  87. package/dist/layout/renderDrawing.d.ts.map +1 -0
  88. package/dist/layout/renderDrawing.js +555 -0
  89. package/dist/layout/renderDrawing.js.map +1 -0
  90. package/dist/layout/renderUtils.d.ts +36 -0
  91. package/dist/layout/renderUtils.d.ts.map +1 -0
  92. package/dist/layout/renderUtils.js +117 -0
  93. package/dist/layout/renderUtils.js.map +1 -0
  94. package/dist/layout/sectionValidation.d.ts +3 -0
  95. package/dist/layout/sectionValidation.d.ts.map +1 -0
  96. package/dist/layout/sectionValidation.js +2 -0
  97. package/dist/layout/sectionValidation.js.map +1 -0
  98. package/dist/layout/templateVariables.d.ts +137 -0
  99. package/dist/layout/templateVariables.d.ts.map +1 -0
  100. package/dist/layout/templateVariables.js +295 -0
  101. package/dist/layout/templateVariables.js.map +1 -0
  102. package/dist/layout/typeGuards.d.ts +5 -0
  103. package/dist/layout/typeGuards.d.ts.map +1 -0
  104. package/dist/layout/typeGuards.js +9 -0
  105. package/dist/layout/typeGuards.js.map +1 -0
  106. package/dist/layout/types.d.ts +816 -0
  107. package/dist/layout/types.d.ts.map +1 -0
  108. package/dist/layout/types.js +2 -0
  109. package/dist/layout/types.js.map +1 -0
  110. package/dist/layout/utils.d.ts +9 -0
  111. package/dist/layout/utils.d.ts.map +1 -0
  112. package/dist/layout/utils.js +84 -0
  113. package/dist/layout/utils.js.map +1 -0
  114. package/dist/layout/validation.d.ts +7 -0
  115. package/dist/layout/validation.d.ts.map +1 -0
  116. package/dist/layout/validation.js +352 -0
  117. package/dist/layout/validation.js.map +1 -0
  118. package/dist/layout/validationHelpers.d.ts +15 -0
  119. package/dist/layout/validationHelpers.d.ts.map +1 -0
  120. package/dist/layout/validationHelpers.js +43 -0
  121. package/dist/layout/validationHelpers.js.map +1 -0
  122. package/dist/layout/validationUtils.d.ts +5 -0
  123. package/dist/layout/validationUtils.d.ts.map +1 -0
  124. package/dist/layout/validationUtils.js +5 -0
  125. package/dist/layout/validationUtils.js.map +1 -0
  126. package/dist/layout/viewportPrep.d.ts +47 -0
  127. package/dist/layout/viewportPrep.d.ts.map +1 -0
  128. package/dist/layout/viewportPrep.js +275 -0
  129. package/dist/layout/viewportPrep.js.map +1 -0
  130. package/dist/layout/viewportRender.d.ts +42 -0
  131. package/dist/layout/viewportRender.d.ts.map +1 -0
  132. package/dist/layout/viewportRender.js +337 -0
  133. package/dist/layout/viewportRender.js.map +1 -0
  134. package/dist/layout/viewportUtils.d.ts +9 -0
  135. package/dist/layout/viewportUtils.d.ts.map +1 -0
  136. package/dist/layout/viewportUtils.js +40 -0
  137. package/dist/layout/viewportUtils.js.map +1 -0
  138. package/dist/layout/warnings.d.ts +2 -0
  139. package/dist/layout/warnings.d.ts.map +1 -0
  140. package/dist/layout/warnings.js +4 -0
  141. package/dist/layout/warnings.js.map +1 -0
  142. package/dist/printToPdf.d.ts +38 -0
  143. package/dist/printToPdf.d.ts.map +1 -0
  144. package/dist/printToPdf.js +79 -0
  145. package/dist/printToPdf.js.map +1 -0
  146. package/dist/printToSvg.d.ts +11 -0
  147. package/dist/printToSvg.d.ts.map +1 -0
  148. package/dist/printToSvg.js +168 -0
  149. package/dist/printToSvg.js.map +1 -0
  150. package/dist/sections.d.ts +118 -0
  151. package/dist/sections.d.ts.map +1 -0
  152. package/dist/sections.js +839 -0
  153. package/dist/sections.js.map +1 -0
  154. package/dist/templates/default-template.json +137 -0
  155. package/dist/types.d.ts +54 -0
  156. package/dist/types.d.ts.map +1 -0
  157. package/dist/types.js +11 -0
  158. package/dist/types.js.map +1 -0
  159. package/package.json +47 -0
  160. package/src/BuildInfo.ts +41 -0
  161. package/src/index.ts +10 -0
  162. package/src/layout/analyzeTemplate.ts +218 -0
  163. package/src/layout/clipUtils.ts +193 -0
  164. package/src/layout/colorConstants.ts +16 -0
  165. package/src/layout/elementKinds.ts +35 -0
  166. package/src/layout/exportDrawing.ts +206 -0
  167. package/src/layout/filters.ts +80 -0
  168. package/src/layout/geometryConstants.ts +11 -0
  169. package/src/layout/index.ts +671 -0
  170. package/src/layout/itemBuilders.ts +159 -0
  171. package/src/layout/labelsRender.ts +170 -0
  172. package/src/layout/legendConstants.ts +11 -0
  173. package/src/layout/legendRender.ts +543 -0
  174. package/src/layout/legendRows.ts +62 -0
  175. package/src/layout/legendUtils.ts +144 -0
  176. package/src/layout/metadata.ts +210 -0
  177. package/src/layout/model.ts +372 -0
  178. package/src/layout/options.ts +17 -0
  179. package/src/layout/presets.ts +51 -0
  180. package/src/layout/qrRender.ts +126 -0
  181. package/src/layout/qrcode.d.ts +2 -0
  182. package/src/layout/renderDrawing.ts +710 -0
  183. package/src/layout/renderUtils.ts +138 -0
  184. package/src/layout/sectionValidation.ts +2 -0
  185. package/src/layout/templateVariables.ts +317 -0
  186. package/src/layout/typeGuards.ts +9 -0
  187. package/src/layout/types.ts +835 -0
  188. package/src/layout/utils.ts +85 -0
  189. package/src/layout/validation.ts +392 -0
  190. package/src/layout/validationHelpers.ts +59 -0
  191. package/src/layout/validationUtils.ts +6 -0
  192. package/src/layout/viewportPrep.ts +351 -0
  193. package/src/layout/viewportRender.ts +442 -0
  194. package/src/layout/viewportUtils.ts +50 -0
  195. package/src/layout/warnings.ts +3 -0
  196. package/src/printToPdf.ts +108 -0
  197. package/src/printToSvg.ts +188 -0
  198. package/src/sections.ts +1019 -0
  199. package/src/templates/default-template.json +137 -0
  200. package/src/types.ts +49 -0
  201. package/templates/default-a0-landscape.json +209 -0
  202. package/templates/default-a0-portrait.json +195 -0
  203. package/templates/default-a1-landscape.json +167 -0
  204. package/templates/default-a1-portrait.json +153 -0
  205. package/templates/default-a3-landscape.json +111 -0
  206. package/templates/default-a3-portrait.json +125 -0
  207. package/templates/default-a4-landscape.json +97 -0
  208. package/templates/default-a4-portrait.json +97 -0
  209. package/templates/template-schema.json +1395 -0
@@ -0,0 +1,165 @@
1
+ import { EPSILON_DIVISION_SAFETY, CENTER_FRACTION } from './geometryConstants';
2
+ import { isRecord, getProp } from './typeGuards';
3
+ export const pointInPolygon = (polygon, x, y) => {
4
+ let inside = false;
5
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
6
+ const xi = polygon[i].x, yi = polygon[i].y;
7
+ const xj = polygon[j].x, yj = polygon[j].y;
8
+ const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
9
+ if (intersect)
10
+ inside = !inside;
11
+ }
12
+ return inside;
13
+ };
14
+ export const filterItemsInPolygons = (items, polygons, xField = 'x', yField = 'z', getPathValue) => {
15
+ if (!Array.isArray(items) || !Array.isArray(polygons) || polygons.length === 0)
16
+ return items ?? [];
17
+ return items.filter((item) => {
18
+ const xVal = getPathValue(item, xField);
19
+ const yVal = getPathValue(item, yField);
20
+ if (!Number.isFinite(xVal) || !Number.isFinite(yVal))
21
+ return false;
22
+ const x = Number(xVal);
23
+ const y = Number(yVal);
24
+ return polygons.some((poly) => pointInPolygon(poly, x, y));
25
+ });
26
+ };
27
+ export const filterEntriesByClip = (entries, polygons) => {
28
+ if (!Array.isArray(entries) || !Array.isArray(polygons) || polygons.length === 0)
29
+ return entries ?? [];
30
+ const getCenter = (entry) => {
31
+ const product = getProp(entry, 'product') ?? entry;
32
+ try {
33
+ const boundingInfoFn = getProp(product, 'boundingInfo');
34
+ const boundingInfo = typeof boundingInfoFn === 'function' ? boundingInfoFn() : null;
35
+ const bbox = isRecord(boundingInfo) ? getProp(boundingInfo, 'boundingBox') : null;
36
+ const min = isRecord(bbox) ? (getProp(bbox, 'minimumWorld') ?? getProp(bbox, 'minimum')) : null;
37
+ const max = isRecord(bbox) ? (getProp(bbox, 'maximumWorld') ?? getProp(bbox, 'maximum')) : null;
38
+ const minX = isRecord(min) ? getProp(min, 'x') : null;
39
+ const maxX = isRecord(max) ? getProp(max, 'x') : null;
40
+ const minZ = isRecord(min) ? getProp(min, 'z') : null;
41
+ const maxZ = isRecord(max) ? getProp(max, 'z') : null;
42
+ if (Number.isFinite(minX) && Number.isFinite(maxX) && Number.isFinite(minZ) && Number.isFinite(maxZ)) {
43
+ return {
44
+ x: (Number(minX) + Number(maxX)) * CENTER_FRACTION,
45
+ z: (Number(minZ) + Number(maxZ)) * CENTER_FRACTION
46
+ };
47
+ }
48
+ }
49
+ catch {
50
+ // Bounding info access may fail for products without geometry or with malformed data structures.
51
+ // This is expected behavior - we fall back to entry.x/z properties below.
52
+ }
53
+ const x = getProp(entry, 'x');
54
+ const z = getProp(entry, 'z');
55
+ return { x: typeof x === 'number' ? x : null, z: typeof z === 'number' ? z : null };
56
+ };
57
+ return entries.filter((entry) => {
58
+ const center = getCenter(entry);
59
+ if (!Number.isFinite(center.x) || !Number.isFinite(center.z))
60
+ return false;
61
+ const x = Number(center.x);
62
+ const z = Number(center.z);
63
+ return polygons.some((poly) => pointInPolygon(poly.map((p) => ({ x: p.x, y: p.z })), x, z));
64
+ });
65
+ };
66
+ export const offsetPolygons = (polys, delta) => {
67
+ if (!Array.isArray(polys) || !polys.length || !Number.isFinite(delta) || delta === 0)
68
+ return polys ?? [];
69
+ const intersectLines = (p, r, q, s) => {
70
+ const rxs = r.x * s.y - r.y * s.x;
71
+ if (Math.abs(rxs) < EPSILON_DIVISION_SAFETY)
72
+ return null; // parallel
73
+ const t = ((q.x - p.x) * s.y - (q.y - p.y) * s.x) / rxs;
74
+ return { x: p.x + t * r.x, y: p.y + t * r.y };
75
+ };
76
+ const offsetPoly = (poly) => {
77
+ const n = poly.length;
78
+ if (n < 3)
79
+ return poly;
80
+ const area = poly.reduce((sum, p, i) => {
81
+ const q = poly[(i + 1) % n];
82
+ return sum + p.x * q.y - q.x * p.y;
83
+ }, 0);
84
+ const orient = area >= 0 ? 1 : -1; // ccw => outward is right normal
85
+ const out = [];
86
+ for (let i = 0; i < n; i++) {
87
+ const p0 = poly[(i + n - 1) % n];
88
+ const p1 = poly[i];
89
+ const p2 = poly[(i + 1) % n];
90
+ const e1x = p1.x - p0.x;
91
+ const e1y = p1.y - p0.y;
92
+ const e2x = p2.x - p1.x;
93
+ const e2y = p2.y - p1.y;
94
+ const len1 = Math.hypot(e1x, e1y) || EPSILON_DIVISION_SAFETY;
95
+ const len2 = Math.hypot(e2x, e2y) || EPSILON_DIVISION_SAFETY;
96
+ const n1x = (orient * e1y) / len1;
97
+ const n1y = (orient * -e1x) / len1;
98
+ const n2x = (orient * e2y) / len2;
99
+ const n2y = (orient * -e2x) / len2;
100
+ const line1Point = { x: p1.x + n1x * delta, y: p1.y + n1y * delta };
101
+ const line2Point = { x: p1.x + n2x * delta, y: p1.y + n2y * delta };
102
+ const inter = intersectLines(line1Point, { x: e1x, y: e1y }, line2Point, { x: e2x, y: e2y });
103
+ out.push(inter ?? line2Point);
104
+ }
105
+ return out;
106
+ };
107
+ return polys.map(offsetPoly);
108
+ };
109
+ export const projectPolygonsToLocal = (polygonsSource, xField, yField, model, rotationDeg, contentOffsetX, contentOffsetY, mmPerModel) => {
110
+ const cosA = Math.cos((rotationDeg * Math.PI) / 180);
111
+ const sinA = Math.sin((rotationDeg * Math.PI) / 180);
112
+ const toLocal = (px, py) => {
113
+ const viewX = px - model.transform.viewMinX;
114
+ const viewY = model.transform.viewMaxZ - py;
115
+ const x0 = viewX - model.widthModel / 2;
116
+ const y0 = viewY - model.heightModel / 2;
117
+ const x1 = x0 * cosA - y0 * sinA;
118
+ const y1 = x0 * sinA + y0 * cosA;
119
+ return { x: x1, y: y1 };
120
+ };
121
+ const toPage = (px, py) => {
122
+ const viewX = px - model.transform.viewMinX;
123
+ const viewY = model.transform.viewMaxZ - py;
124
+ const x0 = viewX - model.widthModel / 2;
125
+ const y0 = viewY - model.heightModel / 2;
126
+ const x1 = x0 * cosA - y0 * sinA;
127
+ const y1 = x0 * sinA + y0 * cosA;
128
+ return { x: contentOffsetX + x1 * mmPerModel, y: contentOffsetY + y1 * mmPerModel };
129
+ };
130
+ const projectPolyLocal = (poly) => poly
131
+ .map((pt) => {
132
+ const px = Number(getProp(pt, xField));
133
+ const py = Number(getProp(pt, yField));
134
+ if (!Number.isFinite(px) || !Number.isFinite(py))
135
+ return null;
136
+ return toLocal(px, py);
137
+ })
138
+ .filter(Boolean);
139
+ const projectPoly = (poly) => poly
140
+ .map((pt) => {
141
+ const px = Number(getProp(pt, xField));
142
+ const py = Number(getProp(pt, yField));
143
+ if (!Number.isFinite(px) || !Number.isFinite(py))
144
+ return null;
145
+ return toPage(px, py);
146
+ })
147
+ .filter(Boolean);
148
+ const local = [];
149
+ const page = [];
150
+ if (Array.isArray(polygonsSource)) {
151
+ polygonsSource.forEach((poly) => {
152
+ const points = getProp(poly, 'points');
153
+ const pl = Array.isArray(points) ? points : Array.isArray(poly) ? poly : null;
154
+ if (!pl || pl.length < 3)
155
+ return;
156
+ const projectedLocal = projectPolyLocal(pl);
157
+ if (projectedLocal.length >= 3) {
158
+ local.push(projectedLocal);
159
+ page.push(projectPoly(pl));
160
+ }
161
+ });
162
+ }
163
+ return { local, page };
164
+ };
165
+ //# sourceMappingURL=clipUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipUtils.js","sourceRoot":"/","sources":["layout/clipUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SvgModelView } from './types';\r\nimport { EPSILON_DIVISION_SAFETY, CENTER_FRACTION } from './geometryConstants';\r\n\r\nexport type Point = { x: number; y: number };\r\nexport type Polygon = Point[];\r\n\r\nimport { isRecord, getProp } from './typeGuards';\r\n\r\nexport const pointInPolygon = (polygon: Polygon, x: number, y: number): boolean => {\r\n let inside = false;\r\n for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\r\n const xi = polygon[i].x,\r\n yi = polygon[i].y;\r\n const xj = polygon[j].x,\r\n yj = polygon[j].y;\r\n const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;\r\n if (intersect) inside = !inside;\r\n }\r\n return inside;\r\n};\r\n\r\nexport const filterItemsInPolygons = (\r\n items: unknown[],\r\n polygons: Polygon[],\r\n xField = 'x',\r\n yField = 'z',\r\n getPathValue: (obj: unknown, path: string) => unknown\r\n): unknown[] => {\r\n if (!Array.isArray(items) || !Array.isArray(polygons) || polygons.length === 0) return items ?? [];\r\n return items.filter((item) => {\r\n const xVal = getPathValue(item, xField);\r\n const yVal = getPathValue(item, yField);\r\n if (!Number.isFinite(xVal) || !Number.isFinite(yVal)) return false;\r\n const x = Number(xVal);\r\n const y = Number(yVal);\r\n return polygons.some((poly) => pointInPolygon(poly, x, y));\r\n });\r\n};\r\n\r\nexport const filterEntriesByClip = (\r\n entries: unknown[],\r\n polygons: Array<Array<{ x: number; z: number }>>\r\n): unknown[] => {\r\n if (!Array.isArray(entries) || !Array.isArray(polygons) || polygons.length === 0) return entries ?? [];\r\n\r\n const getCenter = (entry: unknown): { x: number | null; z: number | null } => {\r\n const product = getProp(entry, 'product') ?? entry;\r\n try {\r\n const boundingInfoFn = getProp(product, 'boundingInfo');\r\n const boundingInfo = typeof boundingInfoFn === 'function' ? boundingInfoFn() : null;\r\n const bbox = isRecord(boundingInfo) ? getProp(boundingInfo, 'boundingBox') : null;\r\n const min = isRecord(bbox) ? (getProp(bbox, 'minimumWorld') ?? getProp(bbox, 'minimum')) : null;\r\n const max = isRecord(bbox) ? (getProp(bbox, 'maximumWorld') ?? getProp(bbox, 'maximum')) : null;\r\n const minX = isRecord(min) ? getProp(min, 'x') : null;\r\n const maxX = isRecord(max) ? getProp(max, 'x') : null;\r\n const minZ = isRecord(min) ? getProp(min, 'z') : null;\r\n const maxZ = isRecord(max) ? getProp(max, 'z') : null;\r\n if (Number.isFinite(minX) && Number.isFinite(maxX) && Number.isFinite(minZ) && Number.isFinite(maxZ)) {\r\n return {\r\n x: (Number(minX) + Number(maxX)) * CENTER_FRACTION,\r\n z: (Number(minZ) + Number(maxZ)) * CENTER_FRACTION\r\n };\r\n }\r\n } catch {\r\n // Bounding info access may fail for products without geometry or with malformed data structures.\r\n // This is expected behavior - we fall back to entry.x/z properties below.\r\n }\r\n const x = getProp(entry, 'x');\r\n const z = getProp(entry, 'z');\r\n return { x: typeof x === 'number' ? x : null, z: typeof z === 'number' ? z : null };\r\n };\r\n\r\n return entries.filter((entry) => {\r\n const center = getCenter(entry);\r\n if (!Number.isFinite(center.x) || !Number.isFinite(center.z)) return false;\r\n const x = Number(center.x);\r\n const z = Number(center.z);\r\n return polygons.some((poly) =>\r\n pointInPolygon(\r\n poly.map((p) => ({ x: p.x, y: p.z })),\r\n x,\r\n z\r\n )\r\n );\r\n });\r\n};\r\n\r\nexport const offsetPolygons = (polys: Polygon[], delta: number): Polygon[] => {\r\n if (!Array.isArray(polys) || !polys.length || !Number.isFinite(delta) || delta === 0) return polys ?? [];\r\n const intersectLines = (p: Point, r: Point, q: Point, s: Point): Point | null => {\r\n const rxs = r.x * s.y - r.y * s.x;\r\n if (Math.abs(rxs) < EPSILON_DIVISION_SAFETY) return null; // parallel\r\n const t = ((q.x - p.x) * s.y - (q.y - p.y) * s.x) / rxs;\r\n return { x: p.x + t * r.x, y: p.y + t * r.y };\r\n };\r\n const offsetPoly = (poly: Polygon): Polygon => {\r\n const n = poly.length;\r\n if (n < 3) return poly;\r\n const area = poly.reduce((sum, p, i) => {\r\n const q = poly[(i + 1) % n];\r\n return sum + p.x * q.y - q.x * p.y;\r\n }, 0);\r\n const orient = area >= 0 ? 1 : -1; // ccw => outward is right normal\r\n const out: Polygon = [];\r\n for (let i = 0; i < n; i++) {\r\n const p0 = poly[(i + n - 1) % n];\r\n const p1 = poly[i];\r\n const p2 = poly[(i + 1) % n];\r\n const e1x = p1.x - p0.x;\r\n const e1y = p1.y - p0.y;\r\n const e2x = p2.x - p1.x;\r\n const e2y = p2.y - p1.y;\r\n const len1 = Math.hypot(e1x, e1y) || EPSILON_DIVISION_SAFETY;\r\n const len2 = Math.hypot(e2x, e2y) || EPSILON_DIVISION_SAFETY;\r\n const n1x = (orient * e1y) / len1;\r\n const n1y = (orient * -e1x) / len1;\r\n const n2x = (orient * e2y) / len2;\r\n const n2y = (orient * -e2x) / len2;\r\n const line1Point = { x: p1.x + n1x * delta, y: p1.y + n1y * delta };\r\n const line2Point = { x: p1.x + n2x * delta, y: p1.y + n2y * delta };\r\n const inter = intersectLines(line1Point, { x: e1x, y: e1y }, line2Point, { x: e2x, y: e2y });\r\n out.push(inter ?? line2Point);\r\n }\r\n return out;\r\n };\r\n return polys.map(offsetPoly);\r\n};\r\n\r\nexport const projectPolygonsToLocal = (\r\n polygonsSource: unknown,\r\n xField: string,\r\n yField: string,\r\n model: SvgModelView,\r\n rotationDeg: number,\r\n contentOffsetX: number,\r\n contentOffsetY: number,\r\n mmPerModel: number\r\n): { local: Polygon[]; page: Polygon[] } => {\r\n const cosA = Math.cos((rotationDeg * Math.PI) / 180);\r\n const sinA = Math.sin((rotationDeg * Math.PI) / 180);\r\n const toLocal = (px: number, py: number): Point => {\r\n const viewX = px - model.transform!.viewMinX;\r\n const viewY = model.transform!.viewMaxZ - py;\r\n const x0 = viewX - model.widthModel / 2;\r\n const y0 = viewY - model.heightModel / 2;\r\n const x1 = x0 * cosA - y0 * sinA;\r\n const y1 = x0 * sinA + y0 * cosA;\r\n return { x: x1, y: y1 };\r\n };\r\n const toPage = (px: number, py: number): Point => {\r\n const viewX = px - model.transform!.viewMinX;\r\n const viewY = model.transform!.viewMaxZ - py;\r\n const x0 = viewX - model.widthModel / 2;\r\n const y0 = viewY - model.heightModel / 2;\r\n const x1 = x0 * cosA - y0 * sinA;\r\n const y1 = x0 * sinA + y0 * cosA;\r\n return { x: contentOffsetX + x1 * mmPerModel, y: contentOffsetY + y1 * mmPerModel };\r\n };\r\n const projectPolyLocal = (poly: unknown[]): Polygon =>\r\n poly\r\n .map((pt: unknown) => {\r\n const px = Number(getProp(pt, xField));\r\n const py = Number(getProp(pt, yField));\r\n if (!Number.isFinite(px) || !Number.isFinite(py)) return null;\r\n return toLocal(px, py);\r\n })\r\n .filter(Boolean) as Polygon;\r\n const projectPoly = (poly: unknown[]): Polygon =>\r\n poly\r\n .map((pt: unknown) => {\r\n const px = Number(getProp(pt, xField));\r\n const py = Number(getProp(pt, yField));\r\n if (!Number.isFinite(px) || !Number.isFinite(py)) return null;\r\n return toPage(px, py);\r\n })\r\n .filter(Boolean) as Polygon;\r\n\r\n const local: Polygon[] = [];\r\n const page: Polygon[] = [];\r\n if (Array.isArray(polygonsSource)) {\r\n polygonsSource.forEach((poly: unknown) => {\r\n const points = getProp(poly, 'points');\r\n const pl = Array.isArray(points) ? points : Array.isArray(poly) ? poly : null;\r\n if (!pl || pl.length < 3) return;\r\n const projectedLocal = projectPolyLocal(pl);\r\n if (projectedLocal.length >= 3) {\r\n local.push(projectedLocal);\r\n page.push(projectPoly(pl));\r\n }\r\n });\r\n }\r\n return { local, page };\r\n};\r\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Color computation constants
3
+ */
4
+ export declare const RGB_MAX = 255;
5
+ export declare const RGB_MIN = 0;
6
+ export declare const RED_SHIFT = 16;
7
+ export declare const GREEN_SHIFT = 8;
8
+ export declare const HASH_COLOR_MODULO = 255;
9
+ export declare const HASH_GREEN_SHIFT = 4;
10
+ export declare const HASH_BLUE_SHIFT = 8;
11
+ //# sourceMappingURL=colorConstants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colorConstants.d.ts","sourceRoot":"/","sources":["layout/colorConstants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,eAAO,MAAM,OAAO,MAAM,CAAC;AAC3B,eAAO,MAAM,OAAO,IAAI,CAAC;AAGzB,eAAO,MAAM,SAAS,KAAK,CAAC;AAC5B,eAAO,MAAM,WAAW,IAAI,CAAC;AAG7B,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,eAAe,IAAI,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Color computation constants
3
+ */
4
+ // RGB color value limits
5
+ export const RGB_MAX = 255; // Maximum value for RGB color components (0-255)
6
+ export const RGB_MIN = 0; // Minimum value for RGB color components
7
+ // Bit shift offsets for color packing/unpacking
8
+ export const RED_SHIFT = 16; // Shift for red component in 24-bit color
9
+ export const GREEN_SHIFT = 8; // Shift for green component in 24-bit color
10
+ // Color modulo for hash-based color generation
11
+ export const HASH_COLOR_MODULO = 255; // Modulo for generating colors from hash values
12
+ export const HASH_GREEN_SHIFT = 4; // Bit shift for green in hash-based colors
13
+ export const HASH_BLUE_SHIFT = 8; // Bit shift for blue in hash-based colors
14
+ //# sourceMappingURL=colorConstants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colorConstants.js","sourceRoot":"/","sources":["layout/colorConstants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,yBAAyB;AACzB,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,iDAAiD;AAC7E,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,yCAAyC;AAEnE,gDAAgD;AAChD,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC,CAAC,0CAA0C;AACvE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,CAAC,4CAA4C;AAE1E,+CAA+C;AAC/C,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC,CAAC,gDAAgD;AACtF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,CAAC,2CAA2C;AAC9E,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,CAAC,0CAA0C","sourcesContent":["/**\r\n * Color computation constants\r\n */\r\n\r\n// RGB color value limits\r\nexport const RGB_MAX = 255; // Maximum value for RGB color components (0-255)\r\nexport const RGB_MIN = 0; // Minimum value for RGB color components\r\n\r\n// Bit shift offsets for color packing/unpacking\r\nexport const RED_SHIFT = 16; // Shift for red component in 24-bit color\r\nexport const GREEN_SHIFT = 8; // Shift for green component in 24-bit color\r\n\r\n// Color modulo for hash-based color generation\r\nexport const HASH_COLOR_MODULO = 255; // Modulo for generating colors from hash values\r\nexport const HASH_GREEN_SHIFT = 4; // Bit shift for green in hash-based colors\r\nexport const HASH_BLUE_SHIFT = 8; // Bit shift for blue in hash-based colors\r\n"]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Constants for layout element kinds
3
+ */
4
+ export declare const ELEMENT_KIND_VIEWPORT = "viewport";
5
+ export declare const ELEMENT_KIND_TEXT = "text";
6
+ export declare const ELEMENT_KIND_IMAGE = "image";
7
+ export declare const ELEMENT_KIND_LINE = "line";
8
+ export declare const ELEMENT_KIND_RECT = "rect";
9
+ export declare const ELEMENT_KIND_CIRCLE = "circle";
10
+ export declare const ELEMENT_KIND_POLYLINE = "polyline";
11
+ export declare const ELEMENT_KIND_POLYGON = "polygon";
12
+ export declare const ELEMENT_KIND_SCALE_BAR = "scaleBar";
13
+ export declare const ELEMENT_KIND_QR = "qr";
14
+ export declare const ELEMENT_KIND_LABELS = "labels";
15
+ export declare const ELEMENT_KIND_LEGEND = "legend";
16
+ export declare const ELEMENT_KIND_GROUP = "group";
17
+ /**
18
+ * Union type of all valid element kinds
19
+ */
20
+ export type ElementKind = typeof ELEMENT_KIND_VIEWPORT | typeof ELEMENT_KIND_TEXT | typeof ELEMENT_KIND_IMAGE | typeof ELEMENT_KIND_LINE | typeof ELEMENT_KIND_RECT | typeof ELEMENT_KIND_CIRCLE | typeof ELEMENT_KIND_POLYLINE | typeof ELEMENT_KIND_POLYGON | typeof ELEMENT_KIND_SCALE_BAR | typeof ELEMENT_KIND_QR | typeof ELEMENT_KIND_LABELS | typeof ELEMENT_KIND_LEGEND | typeof ELEMENT_KIND_GROUP;
21
+ //# sourceMappingURL=elementKinds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elementKinds.d.ts","sourceRoot":"/","sources":["layout/elementKinds.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,qBAAqB,aAAa,CAAC;AAChD,eAAO,MAAM,iBAAiB,SAAS,CAAC;AACxC,eAAO,MAAM,kBAAkB,UAAU,CAAC;AAC1C,eAAO,MAAM,iBAAiB,SAAS,CAAC;AACxC,eAAO,MAAM,iBAAiB,SAAS,CAAC;AACxC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAC5C,eAAO,MAAM,qBAAqB,aAAa,CAAC;AAChD,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAC9C,eAAO,MAAM,sBAAsB,aAAa,CAAC;AACjD,eAAO,MAAM,eAAe,OAAO,CAAC;AACpC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAC5C,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAC5C,eAAO,MAAM,kBAAkB,UAAU,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,WAAW,GACjB,OAAO,qBAAqB,GAC5B,OAAO,iBAAiB,GACxB,OAAO,kBAAkB,GACzB,OAAO,iBAAiB,GACxB,OAAO,iBAAiB,GACxB,OAAO,mBAAmB,GAC1B,OAAO,qBAAqB,GAC5B,OAAO,oBAAoB,GAC3B,OAAO,sBAAsB,GAC7B,OAAO,eAAe,GACtB,OAAO,mBAAmB,GAC1B,OAAO,mBAAmB,GAC1B,OAAO,kBAAkB,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Constants for layout element kinds
3
+ */
4
+ export const ELEMENT_KIND_VIEWPORT = 'viewport';
5
+ export const ELEMENT_KIND_TEXT = 'text';
6
+ export const ELEMENT_KIND_IMAGE = 'image';
7
+ export const ELEMENT_KIND_LINE = 'line';
8
+ export const ELEMENT_KIND_RECT = 'rect';
9
+ export const ELEMENT_KIND_CIRCLE = 'circle';
10
+ export const ELEMENT_KIND_POLYLINE = 'polyline';
11
+ export const ELEMENT_KIND_POLYGON = 'polygon';
12
+ export const ELEMENT_KIND_SCALE_BAR = 'scaleBar';
13
+ export const ELEMENT_KIND_QR = 'qr';
14
+ export const ELEMENT_KIND_LABELS = 'labels';
15
+ export const ELEMENT_KIND_LEGEND = 'legend';
16
+ export const ELEMENT_KIND_GROUP = 'group';
17
+ //# sourceMappingURL=elementKinds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elementKinds.js","sourceRoot":"/","sources":["layout/elementKinds.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,UAAU,CAAC;AAChD,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACxC,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAC1C,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACxC,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACxC,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC;AAC5C,MAAM,CAAC,MAAM,qBAAqB,GAAG,UAAU,CAAC;AAChD,MAAM,CAAC,MAAM,oBAAoB,GAAG,SAAS,CAAC;AAC9C,MAAM,CAAC,MAAM,sBAAsB,GAAG,UAAU,CAAC;AACjD,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC;AAC5C,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC;AAC5C,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC","sourcesContent":["/**\r\n * Constants for layout element kinds\r\n */\r\n\r\nexport const ELEMENT_KIND_VIEWPORT = 'viewport';\r\nexport const ELEMENT_KIND_TEXT = 'text';\r\nexport const ELEMENT_KIND_IMAGE = 'image';\r\nexport const ELEMENT_KIND_LINE = 'line';\r\nexport const ELEMENT_KIND_RECT = 'rect';\r\nexport const ELEMENT_KIND_CIRCLE = 'circle';\r\nexport const ELEMENT_KIND_POLYLINE = 'polyline';\r\nexport const ELEMENT_KIND_POLYGON = 'polygon';\r\nexport const ELEMENT_KIND_SCALE_BAR = 'scaleBar';\r\nexport const ELEMENT_KIND_QR = 'qr';\r\nexport const ELEMENT_KIND_LABELS = 'labels';\r\nexport const ELEMENT_KIND_LEGEND = 'legend';\r\nexport const ELEMENT_KIND_GROUP = 'group';\r\n\r\n/**\r\n * Union type of all valid element kinds\r\n */\r\nexport type ElementKind =\r\n | typeof ELEMENT_KIND_VIEWPORT\r\n | typeof ELEMENT_KIND_TEXT\r\n | typeof ELEMENT_KIND_IMAGE\r\n | typeof ELEMENT_KIND_LINE\r\n | typeof ELEMENT_KIND_RECT\r\n | typeof ELEMENT_KIND_CIRCLE\r\n | typeof ELEMENT_KIND_POLYLINE\r\n | typeof ELEMENT_KIND_POLYGON\r\n | typeof ELEMENT_KIND_SCALE_BAR\r\n | typeof ELEMENT_KIND_QR\r\n | typeof ELEMENT_KIND_LABELS\r\n | typeof ELEMENT_KIND_LEGEND\r\n | typeof ELEMENT_KIND_GROUP;\r\n"]}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Drawing export utilities for converting SVG markup to various formats.
3
+ *
4
+ * This module provides functions for exporting drawings to PDF and SVG files.
5
+ * These are pure functions that take SVG markup and export options, with no
6
+ * dependencies on BIM viewer APIs or application state.
7
+ *
8
+ * @module exportDrawing
9
+ */
10
+ /**
11
+ * Options for exporting a drawing to PDF format.
12
+ */
13
+ export interface ExportPdfOptions {
14
+ /**
15
+ * Output filename (with or without .pdf extension).
16
+ * Default: "drawing.pdf"
17
+ */
18
+ filename?: string;
19
+ /**
20
+ * Optional page dimensions in millimeters.
21
+ * If not provided, automatically extracted from SVG width/height attributes.
22
+ * Only needed if you want to override the SVG's embedded page size.
23
+ */
24
+ pageSize?: {
25
+ widthMm: number;
26
+ heightMm: number;
27
+ };
28
+ /**
29
+ * If true, opens PDF in a new browser tab.
30
+ * If false, downloads the PDF file.
31
+ * Default: false (download)
32
+ */
33
+ openInNewTab?: boolean;
34
+ }
35
+ /**
36
+ * Options for exporting a drawing to SVG format.
37
+ */
38
+ export interface ExportSvgOptions {
39
+ /**
40
+ * Output filename (with or without .svg extension).
41
+ * Default: "drawing.svg"
42
+ */
43
+ filename?: string;
44
+ /**
45
+ * If true, opens SVG in a new browser tab.
46
+ * If false, downloads the SVG file.
47
+ * Default: false (download)
48
+ */
49
+ openInNewTab?: boolean;
50
+ }
51
+ /**
52
+ * Exports an SVG drawing to PDF format.
53
+ *
54
+ * Converts the SVG markup to a vector PDF using jsPDF and svg2pdf.js.
55
+ * The PDF maintains full vector quality with no rasterization.
56
+ *
57
+ * **Page size is automatically extracted from the SVG** (width/height attributes),
58
+ * so you don't need to specify it manually. The template's page dimensions are
59
+ * embedded in the SVG markup by renderDrawing() or renderLayout().
60
+ *
61
+ * **Features:**
62
+ * - ✅ Vector PDF output (scalable, searchable text)
63
+ * - ✅ Preserves colors, strokes, fills, fonts
64
+ * - ✅ No quality loss (unlike raster export)
65
+ * - ✅ Small file size (vector graphics)
66
+ * - ✅ Automatic page size detection from SVG
67
+ *
68
+ * **Use cases:**
69
+ * - Print-ready construction documents
70
+ * - Archival drawings
71
+ * - Sharing with clients/contractors
72
+ * - CAD import (vector format)
73
+ *
74
+ * @param svg - SVG markup string (from renderDrawing() or renderLayout())
75
+ * @param options - Export options (filename, optional page size override, open vs download)
76
+ * @returns Promise that resolves when export completes
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * // Basic usage - page size is automatically extracted from SVG
81
+ * const { svg } = await renderDrawing({ template, models });
82
+ * await exportToPdf(svg, {
83
+ * filename: "floor-plan.pdf",
84
+ * openInNewTab: true
85
+ * });
86
+ *
87
+ * // With custom filename
88
+ * await exportToPdf(svg, {
89
+ * filename: `${template.id}-${new Date().toISOString().slice(0,10)}.pdf`
90
+ * });
91
+ *
92
+ * // Override page size if needed (rare)
93
+ * await exportToPdf(svg, {
94
+ * filename: "custom.pdf",
95
+ * pageSize: { widthMm: 420, heightMm: 297 }, // A3 landscape override
96
+ * openInNewTab: false
97
+ * });
98
+ * ```
99
+ *
100
+ * @throws {Error} If PDF generation fails or SVG page size cannot be extracted
101
+ */
102
+ export declare function exportToPdf(svg: string, options?: ExportPdfOptions): Promise<void>;
103
+ /**
104
+ * Exports an SVG drawing to SVG file format.
105
+ *
106
+ * Saves or displays the SVG markup as a standalone .svg file.
107
+ * The SVG can be opened in browsers, vector editors (Inkscape, Illustrator),
108
+ * or imported into CAD software.
109
+ *
110
+ * **Features:**
111
+ * - ✅ Native vector format (editable in SVG editors)
112
+ * - ✅ Smallest file size (XML text)
113
+ * - ✅ Web-friendly (can embed in HTML)
114
+ * - ✅ Full styling preserved
115
+ *
116
+ * **Use cases:**
117
+ * - Web display (embed in HTML)
118
+ * - Vector editing (Inkscape, Illustrator)
119
+ * - Further processing (scripting, automation)
120
+ * - Archival (open standard format)
121
+ *
122
+ * @param svg - SVG markup string (from renderDrawing())
123
+ * @param options - Export options (filename, open vs download)
124
+ * @returns Promise that resolves when export completes
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * // Basic usage
129
+ * const { svg } = await renderDrawing({ template, models });
130
+ * await exportToSvg(svg, {
131
+ * filename: "floor-plan.svg",
132
+ * openInNewTab: true
133
+ * });
134
+ *
135
+ * // Download with custom name
136
+ * await exportToSvg(svg, {
137
+ * filename: `${template.id}-${Date.now()}.svg`,
138
+ * openInNewTab: false
139
+ * });
140
+ * ```
141
+ */
142
+ export declare function exportToSvg(svg: string, options?: ExportSvgOptions): void;
143
+ //# sourceMappingURL=exportDrawing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exportDrawing.d.ts","sourceRoot":"/","sources":["layout/exportDrawing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqCH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,QAAQ,CAAC,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAa5F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,IAAI,CAS7E"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Drawing export utilities for converting SVG markup to various formats.
3
+ *
4
+ * This module provides functions for exporting drawings to PDF and SVG files.
5
+ * These are pure functions that take SVG markup and export options, with no
6
+ * dependencies on BIM viewer APIs or application state.
7
+ *
8
+ * @module exportDrawing
9
+ */
10
+ import { exportSectionToPdf, exportSectionToSvg } from '../printToPdf';
11
+ /**
12
+ * Extracts page dimensions from SVG markup.
13
+ * Parses the width/height attributes from the root <svg> element.
14
+ *
15
+ * @param svg - SVG markup string
16
+ * @returns Page dimensions in millimeters
17
+ * @throws {Error} If SVG dimensions cannot be parsed
18
+ */
19
+ function extractPageSizeFromSvg(svg) {
20
+ // Match width and height attributes (supports both "420mm" and "420" formats)
21
+ const widthMatch = svg.match(/width="([0-9.]+)(?:mm)?"/);
22
+ const heightMatch = svg.match(/height="([0-9.]+)(?:mm)?"/);
23
+ if (!widthMatch || !heightMatch) {
24
+ throw new Error('Cannot extract page size from SVG. SVG must have width and height attributes. ' +
25
+ 'Provide pageSize option explicitly or ensure SVG contains width/height attributes.');
26
+ }
27
+ const widthMm = parseFloat(widthMatch[1]);
28
+ const heightMm = parseFloat(heightMatch[1]);
29
+ if (!Number.isFinite(widthMm) || !Number.isFinite(heightMm) || widthMm <= 0 || heightMm <= 0) {
30
+ throw new Error(`Invalid page dimensions extracted from SVG: ${widthMm}mm × ${heightMm}mm. ` +
31
+ 'Provide pageSize option explicitly.');
32
+ }
33
+ return { widthMm, heightMm };
34
+ }
35
+ /**
36
+ * Exports an SVG drawing to PDF format.
37
+ *
38
+ * Converts the SVG markup to a vector PDF using jsPDF and svg2pdf.js.
39
+ * The PDF maintains full vector quality with no rasterization.
40
+ *
41
+ * **Page size is automatically extracted from the SVG** (width/height attributes),
42
+ * so you don't need to specify it manually. The template's page dimensions are
43
+ * embedded in the SVG markup by renderDrawing() or renderLayout().
44
+ *
45
+ * **Features:**
46
+ * - ✅ Vector PDF output (scalable, searchable text)
47
+ * - ✅ Preserves colors, strokes, fills, fonts
48
+ * - ✅ No quality loss (unlike raster export)
49
+ * - ✅ Small file size (vector graphics)
50
+ * - ✅ Automatic page size detection from SVG
51
+ *
52
+ * **Use cases:**
53
+ * - Print-ready construction documents
54
+ * - Archival drawings
55
+ * - Sharing with clients/contractors
56
+ * - CAD import (vector format)
57
+ *
58
+ * @param svg - SVG markup string (from renderDrawing() or renderLayout())
59
+ * @param options - Export options (filename, optional page size override, open vs download)
60
+ * @returns Promise that resolves when export completes
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // Basic usage - page size is automatically extracted from SVG
65
+ * const { svg } = await renderDrawing({ template, models });
66
+ * await exportToPdf(svg, {
67
+ * filename: "floor-plan.pdf",
68
+ * openInNewTab: true
69
+ * });
70
+ *
71
+ * // With custom filename
72
+ * await exportToPdf(svg, {
73
+ * filename: `${template.id}-${new Date().toISOString().slice(0,10)}.pdf`
74
+ * });
75
+ *
76
+ * // Override page size if needed (rare)
77
+ * await exportToPdf(svg, {
78
+ * filename: "custom.pdf",
79
+ * pageSize: { widthMm: 420, heightMm: 297 }, // A3 landscape override
80
+ * openInNewTab: false
81
+ * });
82
+ * ```
83
+ *
84
+ * @throws {Error} If PDF generation fails or SVG page size cannot be extracted
85
+ */
86
+ export async function exportToPdf(svg, options = {}) {
87
+ const filename = options.filename ?? 'drawing.pdf';
88
+ const filenameWithExt = filename.endsWith('.pdf') ? filename : `${filename}.pdf`;
89
+ // Extract page size from SVG if not provided
90
+ const pageSize = options.pageSize ?? extractPageSizeFromSvg(svg);
91
+ await exportSectionToPdf({
92
+ svgMarkup: svg,
93
+ fileName: filenameWithExt,
94
+ pageSizeMm: pageSize,
95
+ openInNewTab: options.openInNewTab ?? false
96
+ });
97
+ }
98
+ /**
99
+ * Exports an SVG drawing to SVG file format.
100
+ *
101
+ * Saves or displays the SVG markup as a standalone .svg file.
102
+ * The SVG can be opened in browsers, vector editors (Inkscape, Illustrator),
103
+ * or imported into CAD software.
104
+ *
105
+ * **Features:**
106
+ * - ✅ Native vector format (editable in SVG editors)
107
+ * - ✅ Smallest file size (XML text)
108
+ * - ✅ Web-friendly (can embed in HTML)
109
+ * - ✅ Full styling preserved
110
+ *
111
+ * **Use cases:**
112
+ * - Web display (embed in HTML)
113
+ * - Vector editing (Inkscape, Illustrator)
114
+ * - Further processing (scripting, automation)
115
+ * - Archival (open standard format)
116
+ *
117
+ * @param svg - SVG markup string (from renderDrawing())
118
+ * @param options - Export options (filename, open vs download)
119
+ * @returns Promise that resolves when export completes
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * // Basic usage
124
+ * const { svg } = await renderDrawing({ template, models });
125
+ * await exportToSvg(svg, {
126
+ * filename: "floor-plan.svg",
127
+ * openInNewTab: true
128
+ * });
129
+ *
130
+ * // Download with custom name
131
+ * await exportToSvg(svg, {
132
+ * filename: `${template.id}-${Date.now()}.svg`,
133
+ * openInNewTab: false
134
+ * });
135
+ * ```
136
+ */
137
+ export function exportToSvg(svg, options = {}) {
138
+ const filename = options.filename ?? 'drawing.svg';
139
+ const filenameWithExt = filename.endsWith('.svg') ? filename : `${filename}.svg`;
140
+ exportSectionToSvg({
141
+ svgMarkup: svg,
142
+ fileName: filenameWithExt,
143
+ openInNewTab: options.openInNewTab ?? false
144
+ });
145
+ }
146
+ //# sourceMappingURL=exportDrawing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exportDrawing.js","sourceRoot":"/","sources":["layout/exportDrawing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEvE;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAAC,GAAW;IACvC,8EAA8E;IAC9E,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAE3D,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACX,gFAAgF;YAC5E,oFAAoF,CAC3F,CAAC;IACN,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAE5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAC3F,MAAM,IAAI,KAAK,CACX,+CAA+C,OAAO,QAAQ,QAAQ,MAAM;YACxE,qCAAqC,CAC5C,CAAC;IACN,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC;AAgDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW,EAAE,UAA4B,EAAE;IACzE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,aAAa,CAAC;IACnD,MAAM,eAAe,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,MAAM,CAAC;IAEjF,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAEjE,MAAM,kBAAkB,CAAC;QACrB,SAAS,EAAE,GAAG;QACd,QAAQ,EAAE,eAAe;QACzB,UAAU,EAAE,QAAe;QAC3B,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,KAAK;KAC9C,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,UAA4B,EAAE;IACnE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,aAAa,CAAC;IACnD,MAAM,eAAe,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,MAAM,CAAC;IAEjF,kBAAkB,CAAC;QACf,SAAS,EAAE,GAAG;QACd,QAAQ,EAAE,eAAe;QACzB,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,KAAK;KAC9C,CAAC,CAAC;AACP,CAAC","sourcesContent":["/**\r\n * Drawing export utilities for converting SVG markup to various formats.\r\n *\r\n * This module provides functions for exporting drawings to PDF and SVG files.\r\n * These are pure functions that take SVG markup and export options, with no\r\n * dependencies on BIM viewer APIs or application state.\r\n *\r\n * @module exportDrawing\r\n */\r\n\r\nimport { exportSectionToPdf, exportSectionToSvg } from '../printToPdf';\r\n\r\n/**\r\n * Extracts page dimensions from SVG markup.\r\n * Parses the width/height attributes from the root <svg> element.\r\n *\r\n * @param svg - SVG markup string\r\n * @returns Page dimensions in millimeters\r\n * @throws {Error} If SVG dimensions cannot be parsed\r\n */\r\nfunction extractPageSizeFromSvg(svg: string): { widthMm: number; heightMm: number } {\r\n // Match width and height attributes (supports both \"420mm\" and \"420\" formats)\r\n const widthMatch = svg.match(/width=\"([0-9.]+)(?:mm)?\"/);\r\n const heightMatch = svg.match(/height=\"([0-9.]+)(?:mm)?\"/);\r\n\r\n if (!widthMatch || !heightMatch) {\r\n throw new Error(\r\n 'Cannot extract page size from SVG. SVG must have width and height attributes. ' +\r\n 'Provide pageSize option explicitly or ensure SVG contains width/height attributes.'\r\n );\r\n }\r\n\r\n const widthMm = parseFloat(widthMatch[1]);\r\n const heightMm = parseFloat(heightMatch[1]);\r\n\r\n if (!Number.isFinite(widthMm) || !Number.isFinite(heightMm) || widthMm <= 0 || heightMm <= 0) {\r\n throw new Error(\r\n `Invalid page dimensions extracted from SVG: ${widthMm}mm × ${heightMm}mm. ` +\r\n 'Provide pageSize option explicitly.'\r\n );\r\n }\r\n\r\n return { widthMm, heightMm };\r\n}\r\n\r\n/**\r\n * Options for exporting a drawing to PDF format.\r\n */\r\nexport interface ExportPdfOptions {\r\n /**\r\n * Output filename (with or without .pdf extension).\r\n * Default: \"drawing.pdf\"\r\n */\r\n filename?: string;\r\n\r\n /**\r\n * Optional page dimensions in millimeters.\r\n * If not provided, automatically extracted from SVG width/height attributes.\r\n * Only needed if you want to override the SVG's embedded page size.\r\n */\r\n pageSize?: {\r\n widthMm: number;\r\n heightMm: number;\r\n };\r\n\r\n /**\r\n * If true, opens PDF in a new browser tab.\r\n * If false, downloads the PDF file.\r\n * Default: false (download)\r\n */\r\n openInNewTab?: boolean;\r\n}\r\n\r\n/**\r\n * Options for exporting a drawing to SVG format.\r\n */\r\nexport interface ExportSvgOptions {\r\n /**\r\n * Output filename (with or without .svg extension).\r\n * Default: \"drawing.svg\"\r\n */\r\n filename?: string;\r\n\r\n /**\r\n * If true, opens SVG in a new browser tab.\r\n * If false, downloads the SVG file.\r\n * Default: false (download)\r\n */\r\n openInNewTab?: boolean;\r\n}\r\n\r\n/**\r\n * Exports an SVG drawing to PDF format.\r\n *\r\n * Converts the SVG markup to a vector PDF using jsPDF and svg2pdf.js.\r\n * The PDF maintains full vector quality with no rasterization.\r\n *\r\n * **Page size is automatically extracted from the SVG** (width/height attributes),\r\n * so you don't need to specify it manually. The template's page dimensions are\r\n * embedded in the SVG markup by renderDrawing() or renderLayout().\r\n *\r\n * **Features:**\r\n * - ✅ Vector PDF output (scalable, searchable text)\r\n * - ✅ Preserves colors, strokes, fills, fonts\r\n * - ✅ No quality loss (unlike raster export)\r\n * - ✅ Small file size (vector graphics)\r\n * - ✅ Automatic page size detection from SVG\r\n *\r\n * **Use cases:**\r\n * - Print-ready construction documents\r\n * - Archival drawings\r\n * - Sharing with clients/contractors\r\n * - CAD import (vector format)\r\n *\r\n * @param svg - SVG markup string (from renderDrawing() or renderLayout())\r\n * @param options - Export options (filename, optional page size override, open vs download)\r\n * @returns Promise that resolves when export completes\r\n *\r\n * @example\r\n * ```typescript\r\n * // Basic usage - page size is automatically extracted from SVG\r\n * const { svg } = await renderDrawing({ template, models });\r\n * await exportToPdf(svg, {\r\n * filename: \"floor-plan.pdf\",\r\n * openInNewTab: true\r\n * });\r\n *\r\n * // With custom filename\r\n * await exportToPdf(svg, {\r\n * filename: `${template.id}-${new Date().toISOString().slice(0,10)}.pdf`\r\n * });\r\n *\r\n * // Override page size if needed (rare)\r\n * await exportToPdf(svg, {\r\n * filename: \"custom.pdf\",\r\n * pageSize: { widthMm: 420, heightMm: 297 }, // A3 landscape override\r\n * openInNewTab: false\r\n * });\r\n * ```\r\n *\r\n * @throws {Error} If PDF generation fails or SVG page size cannot be extracted\r\n */\r\nexport async function exportToPdf(svg: string, options: ExportPdfOptions = {}): Promise<void> {\r\n const filename = options.filename ?? 'drawing.pdf';\r\n const filenameWithExt = filename.endsWith('.pdf') ? filename : `${filename}.pdf`;\r\n\r\n // Extract page size from SVG if not provided\r\n const pageSize = options.pageSize ?? extractPageSizeFromSvg(svg);\r\n\r\n await exportSectionToPdf({\r\n svgMarkup: svg,\r\n fileName: filenameWithExt,\r\n pageSizeMm: pageSize as any,\r\n openInNewTab: options.openInNewTab ?? false\r\n });\r\n}\r\n\r\n/**\r\n * Exports an SVG drawing to SVG file format.\r\n *\r\n * Saves or displays the SVG markup as a standalone .svg file.\r\n * The SVG can be opened in browsers, vector editors (Inkscape, Illustrator),\r\n * or imported into CAD software.\r\n *\r\n * **Features:**\r\n * - ✅ Native vector format (editable in SVG editors)\r\n * - ✅ Smallest file size (XML text)\r\n * - ✅ Web-friendly (can embed in HTML)\r\n * - ✅ Full styling preserved\r\n *\r\n * **Use cases:**\r\n * - Web display (embed in HTML)\r\n * - Vector editing (Inkscape, Illustrator)\r\n * - Further processing (scripting, automation)\r\n * - Archival (open standard format)\r\n *\r\n * @param svg - SVG markup string (from renderDrawing())\r\n * @param options - Export options (filename, open vs download)\r\n * @returns Promise that resolves when export completes\r\n *\r\n * @example\r\n * ```typescript\r\n * // Basic usage\r\n * const { svg } = await renderDrawing({ template, models });\r\n * await exportToSvg(svg, {\r\n * filename: \"floor-plan.svg\",\r\n * openInNewTab: true\r\n * });\r\n *\r\n * // Download with custom name\r\n * await exportToSvg(svg, {\r\n * filename: `${template.id}-${Date.now()}.svg`,\r\n * openInNewTab: false\r\n * });\r\n * ```\r\n */\r\nexport function exportToSvg(svg: string, options: ExportSvgOptions = {}): void {\r\n const filename = options.filename ?? 'drawing.svg';\r\n const filenameWithExt = filename.endsWith('.svg') ? filename : `${filename}.svg`;\r\n\r\n exportSectionToSvg({\r\n svgMarkup: svg,\r\n fileName: filenameWithExt,\r\n openInNewTab: options.openInNewTab ?? false\r\n });\r\n}\r\n"]}
@@ -0,0 +1,33 @@
1
+ import type { WhereCondition } from './types';
2
+ declare const normalizeClass: (cls: unknown) => string | null;
3
+ /** Compare two class strings, treating the 'Ifc' prefix as optional.
4
+ * e.g. "IfcSpace" matches "Space", "space", and "IfcSpace". */
5
+ declare const classEquals: (a: string | null, b: string | null) => boolean;
6
+ declare const normalizeValue: (v: unknown) => unknown;
7
+ /**
8
+ * Check whether an item's ifcClass matches the given class filter.
9
+ *
10
+ * Uses normalizeClass() for string comparison — works for extracted items
11
+ * where we have a normalized ifcClass string (not a live product).
12
+ *
13
+ * For live products, the caller should use product.class.is() directly.
14
+ */
15
+ declare const matchesClass: (itemClass: unknown, classFilter: string | string[] | undefined) => boolean;
16
+ /**
17
+ * Check whether an item matches a `where` condition.
18
+ *
19
+ * Each key in `where` is a short property path (e.g. "BIP.spacetype").
20
+ * Each value is either a scalar (equality check) or an array (any-of check).
21
+ * Multiple keys = AND. String comparison is case-insensitive.
22
+ */
23
+ declare const matchesWhere: (item: unknown, where: WhereCondition | undefined) => boolean;
24
+ /**
25
+ * V2 filter: match items by class + where conditions.
26
+ *
27
+ * @param item - An object with ifcClass and propertySets
28
+ * @param classFilter - IFC class name(s) to match, or undefined for any
29
+ * @param where - Property conditions (short paths), or undefined for no conditions
30
+ */
31
+ export declare const matchesV2Filter: (item: unknown, classFilter: string | string[] | undefined, where: WhereCondition | undefined) => boolean;
32
+ export { normalizeClass, classEquals, normalizeValue, matchesClass, matchesWhere };
33
+ //# sourceMappingURL=filters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filters.d.ts","sourceRoot":"/","sources":["layout/filters.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,QAAA,MAAM,cAAc,GAAI,KAAK,OAAO,KAAG,MAAM,GAAG,IAAmE,CAAC;AAEpH;gEACgE;AAChE,QAAA,MAAM,WAAW,GAAI,GAAG,MAAM,GAAG,IAAI,EAAE,GAAG,MAAM,GAAG,IAAI,KAAG,OAMzD,CAAC;AACF,QAAA,MAAM,cAAc,GAAI,GAAG,OAAO,KAAG,OAA+D,CAAC;AAErG;;;;;;;GAOG;AACH,QAAA,MAAM,YAAY,GAAI,WAAW,OAAO,EAAE,aAAa,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KAAG,OAMtF,CAAC;AAEF;;;;;;GAMG;AACH,QAAA,MAAM,YAAY,GAAI,MAAM,OAAO,EAAE,OAAO,cAAc,GAAG,SAAS,KAAG,OAYxE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GACxB,MAAM,OAAO,EACb,aAAa,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EAC1C,OAAO,cAAc,GAAG,SAAS,KAClC,OAUF,CAAC;AAEF,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC"}