@twinfinity/printing 6.1.0-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.
Files changed (157) hide show
  1. package/dist/BuildInfo.js +3 -3
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/layout/analyzeTemplate.d.ts +4 -4
  7. package/dist/layout/analyzeTemplate.d.ts.map +1 -1
  8. package/dist/layout/analyzeTemplate.js +18 -16
  9. package/dist/layout/analyzeTemplate.js.map +1 -1
  10. package/dist/layout/classClosure.d.ts +41 -0
  11. package/dist/layout/classClosure.d.ts.map +1 -0
  12. package/dist/layout/classClosure.js +143 -0
  13. package/dist/layout/classClosure.js.map +1 -0
  14. package/dist/layout/clipUtils.d.ts +11 -0
  15. package/dist/layout/clipUtils.d.ts.map +1 -1
  16. package/dist/layout/clipUtils.js +67 -6
  17. package/dist/layout/clipUtils.js.map +1 -1
  18. package/dist/layout/filters.d.ts +14 -8
  19. package/dist/layout/filters.d.ts.map +1 -1
  20. package/dist/layout/filters.js +28 -3
  21. package/dist/layout/filters.js.map +1 -1
  22. package/dist/layout/index.d.ts +24 -57
  23. package/dist/layout/index.d.ts.map +1 -1
  24. package/dist/layout/index.js +20 -337
  25. package/dist/layout/index.js.map +1 -1
  26. package/dist/layout/itemBuilders.d.ts +11 -0
  27. package/dist/layout/itemBuilders.d.ts.map +1 -1
  28. package/dist/layout/itemBuilders.js +17 -3
  29. package/dist/layout/itemBuilders.js.map +1 -1
  30. package/dist/layout/labelsRender.d.ts.map +1 -1
  31. package/dist/layout/labelsRender.js +15 -27
  32. package/dist/layout/labelsRender.js.map +1 -1
  33. package/dist/layout/legendRender.d.ts +18 -6
  34. package/dist/layout/legendRender.d.ts.map +1 -1
  35. package/dist/layout/legendRender.js +49 -79
  36. package/dist/layout/legendRender.js.map +1 -1
  37. package/dist/layout/metadata.d.ts.map +1 -1
  38. package/dist/layout/metadata.js +26 -5
  39. package/dist/layout/metadata.js.map +1 -1
  40. package/dist/layout/model.d.ts +4 -3
  41. package/dist/layout/model.d.ts.map +1 -1
  42. package/dist/layout/model.js +17 -57
  43. package/dist/layout/model.js.map +1 -1
  44. package/dist/layout/options.d.ts +1 -2
  45. package/dist/layout/options.d.ts.map +1 -1
  46. package/dist/layout/options.js.map +1 -1
  47. package/dist/layout/preflight.d.ts +32 -0
  48. package/dist/layout/preflight.d.ts.map +1 -0
  49. package/dist/layout/preflight.js +69 -0
  50. package/dist/layout/preflight.js.map +1 -0
  51. package/dist/layout/qrRender.d.ts.map +1 -1
  52. package/dist/layout/qrRender.js +11 -10
  53. package/dist/layout/qrRender.js.map +1 -1
  54. package/dist/layout/renderDrawing.d.ts +28 -17
  55. package/dist/layout/renderDrawing.d.ts.map +1 -1
  56. package/dist/layout/renderDrawing.js +107 -88
  57. package/dist/layout/renderDrawing.js.map +1 -1
  58. package/dist/layout/renderLayout.d.ts +25 -0
  59. package/dist/layout/renderLayout.d.ts.map +1 -0
  60. package/dist/layout/renderLayout.js +315 -0
  61. package/dist/layout/renderLayout.js.map +1 -0
  62. package/dist/layout/renderTemplateSheet.d.ts +99 -0
  63. package/dist/layout/renderTemplateSheet.d.ts.map +1 -0
  64. package/dist/layout/renderTemplateSheet.js +296 -0
  65. package/dist/layout/renderTemplateSheet.js.map +1 -0
  66. package/dist/layout/renderUtils.d.ts +19 -0
  67. package/dist/layout/renderUtils.d.ts.map +1 -1
  68. package/dist/layout/renderUtils.js +47 -4
  69. package/dist/layout/renderUtils.js.map +1 -1
  70. package/dist/layout/sectionDefs.d.ts +65 -0
  71. package/dist/layout/sectionDefs.d.ts.map +1 -0
  72. package/dist/layout/sectionDefs.js +88 -0
  73. package/dist/layout/sectionDefs.js.map +1 -0
  74. package/dist/layout/slabModelView.d.ts +34 -0
  75. package/dist/layout/slabModelView.d.ts.map +1 -0
  76. package/dist/layout/slabModelView.js +175 -0
  77. package/dist/layout/slabModelView.js.map +1 -0
  78. package/dist/layout/templateVariables.d.ts +1 -1
  79. package/dist/layout/templateVariables.d.ts.map +1 -1
  80. package/dist/layout/templateVariables.js +21 -9
  81. package/dist/layout/templateVariables.js.map +1 -1
  82. package/dist/layout/types.d.ts +164 -83
  83. package/dist/layout/types.d.ts.map +1 -1
  84. package/dist/layout/types.js.map +1 -1
  85. package/dist/layout/validation.d.ts.map +1 -1
  86. package/dist/layout/validation.js +78 -33
  87. package/dist/layout/validation.js.map +1 -1
  88. package/dist/layout/viewportPrep.d.ts +8 -6
  89. package/dist/layout/viewportPrep.d.ts.map +1 -1
  90. package/dist/layout/viewportPrep.js +35 -23
  91. package/dist/layout/viewportPrep.js.map +1 -1
  92. package/dist/layout/viewportRender.d.ts +9 -7
  93. package/dist/layout/viewportRender.d.ts.map +1 -1
  94. package/dist/layout/viewportRender.js +93 -126
  95. package/dist/layout/viewportRender.js.map +1 -1
  96. package/dist/layout/viewportUtils.d.ts +20 -0
  97. package/dist/layout/viewportUtils.d.ts.map +1 -1
  98. package/dist/layout/viewportUtils.js +107 -4
  99. package/dist/layout/viewportUtils.js.map +1 -1
  100. package/dist/printToPdf.d.ts +7 -1
  101. package/dist/printToPdf.d.ts.map +1 -1
  102. package/dist/printToPdf.js +81 -14
  103. package/dist/printToPdf.js.map +1 -1
  104. package/dist/printToSvg.d.ts.map +1 -1
  105. package/dist/printToSvg.js +7 -3
  106. package/dist/printToSvg.js.map +1 -1
  107. package/dist/schemaUtils.d.ts +9 -0
  108. package/dist/schemaUtils.d.ts.map +1 -0
  109. package/dist/schemaUtils.js +14 -0
  110. package/dist/schemaUtils.js.map +1 -0
  111. package/dist/templates/default-template.json +30 -8
  112. package/dist/types.d.ts +2 -1
  113. package/dist/types.d.ts.map +1 -1
  114. package/dist/types.js +10 -1
  115. package/dist/types.js.map +1 -1
  116. package/package.json +8 -3
  117. package/src/index.ts +4 -0
  118. package/src/layout/analyzeTemplate.ts +21 -19
  119. package/src/layout/classClosure.ts +142 -0
  120. package/src/layout/clipUtils.ts +65 -6
  121. package/src/layout/filters.ts +31 -3
  122. package/src/layout/index.ts +61 -571
  123. package/src/layout/itemBuilders.ts +20 -5
  124. package/src/layout/labelsRender.ts +23 -40
  125. package/src/layout/legendRender.ts +64 -84
  126. package/src/layout/metadata.ts +26 -5
  127. package/src/layout/model.ts +21 -66
  128. package/src/layout/options.ts +1 -2
  129. package/src/layout/preflight.ts +96 -0
  130. package/src/layout/qrRender.ts +11 -10
  131. package/src/layout/renderDrawing.ts +132 -110
  132. package/src/layout/renderLayout.ts +483 -0
  133. package/src/layout/renderTemplateSheet.ts +435 -0
  134. package/src/layout/renderUtils.ts +53 -4
  135. package/src/layout/sectionDefs.ts +131 -0
  136. package/src/layout/slabModelView.ts +196 -0
  137. package/src/layout/templateVariables.ts +25 -9
  138. package/src/layout/types.ts +160 -70
  139. package/src/layout/validation.ts +98 -32
  140. package/src/layout/viewportPrep.ts +47 -29
  141. package/src/layout/viewportRender.ts +114 -141
  142. package/src/layout/viewportUtils.ts +118 -5
  143. package/src/printToPdf.ts +84 -15
  144. package/src/printToSvg.ts +7 -3
  145. package/src/schemaUtils.ts +13 -0
  146. package/src/templates/default-template.json +30 -8
  147. package/src/types.ts +12 -2
  148. package/templates/default-a0-landscape.json +171 -66
  149. package/templates/default-a0-portrait.json +159 -60
  150. package/templates/default-a1-landscape.json +135 -48
  151. package/templates/default-a1-portrait.json +123 -42
  152. package/templates/default-a3-landscape.json +87 -24
  153. package/templates/default-a3-portrait.json +99 -30
  154. package/templates/default-a4-landscape.json +75 -18
  155. package/templates/default-a4-portrait.json +75 -18
  156. package/templates/llm-authoring-guide.md +247 -0
  157. package/templates/template-schema.json +295 -120
package/src/printToPdf.ts CHANGED
@@ -7,7 +7,8 @@
7
7
  export interface PrintToPdfOptions {
8
8
  svgMarkup: string;
9
9
  fileName?: string;
10
- pageFormat?: 'a0' | 'a1' | 'a2' | 'a3' | 'a4';
10
+ /** ISO name or an arbitrary [width, height] in mm. */
11
+ pageFormat?: 'a0' | 'a1' | 'a2' | 'a3' | 'a4' | [number, number];
11
12
  orientation?: 'portrait' | 'landscape';
12
13
  marginMm?: number;
13
14
  }
@@ -17,16 +18,16 @@ export interface PrintToPdfOptions {
17
18
  * Requires jspdf and svg2pdf.js as peer dependencies.
18
19
  */
19
20
  export async function printToPdf(options: PrintToPdfOptions): Promise<Blob> {
20
- // Dynamic imports for optional peer dependencies (installed by consumer, not this package)
21
+ // Dynamic imports for optional peer dependencies (installed by consumer,
22
+ // not this package). Typechecked against the workspace-hoisted installs
23
+ // (template-workbench depends on both), resolved at runtime for consumers.
21
24
  let jspdfModule: any;
22
25
  try {
23
- // @ts-expect-error — optional peer dependency resolved at runtime
24
26
  jspdfModule = await import('jspdf');
25
27
  } catch {
26
28
  throw new Error('jspdf is required for PDF export. Install it: bun add jspdf');
27
29
  }
28
30
  try {
29
- // @ts-expect-error — optional peer dependency resolved at runtime
30
31
  await import('svg2pdf.js');
31
32
  } catch {
32
33
  throw new Error('svg2pdf.js is required for PDF export. Install it: bun add svg2pdf.js');
@@ -37,7 +38,10 @@ export async function printToPdf(options: PrintToPdfOptions): Promise<Blob> {
37
38
  const orientation = options.orientation ?? 'landscape';
38
39
  const margin = options.marginMm ?? 10;
39
40
 
40
- const doc = new jsPDF({ format, orientation, unit: 'mm' });
41
+ // compress: jsPDF leaves content streams uncompressed by default — a full
42
+ // sheet of vector linework is megabytes of plain text. Flate matches what
43
+ // browser print-to-PDF produces (~5-10x smaller for typical sheets).
44
+ const doc = new jsPDF({ format, orientation, unit: 'mm', compress: true });
41
45
  const pageWidth = doc.internal.pageSize.getWidth();
42
46
  const pageHeight = doc.internal.pageSize.getHeight();
43
47
 
@@ -45,12 +49,29 @@ export async function printToPdf(options: PrintToPdfOptions): Promise<Blob> {
45
49
  const svgDoc = parser.parseFromString(options.svgMarkup, 'image/svg+xml');
46
50
  const svgElement = svgDoc.documentElement;
47
51
 
48
- await doc.svg(svgElement, {
49
- x: margin,
50
- y: margin,
51
- width: pageWidth - margin * 2,
52
- height: pageHeight - margin * 2
53
- });
52
+ try {
53
+ await doc.svg(svgElement, {
54
+ x: margin,
55
+ y: margin,
56
+ width: pageWidth - margin * 2,
57
+ height: pageHeight - margin * 2
58
+ });
59
+ } catch (err) {
60
+ // svg2pdf loads every <image> href over XHR and rejects with the raw
61
+ // ProgressEvent on network/CORS failure — which stringifies to the
62
+ // useless "[object ProgressEvent]". Translate, and name the linked
63
+ // images so the failing one is findable. Remote images render fine in
64
+ // the browser preview but fail export unless the host sends CORS
65
+ // headers; data: URIs and inline resource `content` always work.
66
+ if (err instanceof Error) throw err;
67
+ const hrefs = collectImageHrefs(options.svgMarkup);
68
+ throw new Error(
69
+ 'PDF export failed while loading a linked <image> resource (network or CORS). ' +
70
+ 'Template images must be data: URIs, inline SVG resource `content`, or served ' +
71
+ 'with CORS headers — the on-screen preview does not enforce this, PDF export does.' +
72
+ (hrefs.length ? ` Linked images: ${hrefs.join(', ')}` : '')
73
+ );
74
+ }
54
75
 
55
76
  const blob = doc.output('blob');
56
77
 
@@ -61,6 +82,24 @@ export async function printToPdf(options: PrintToPdfOptions): Promise<Blob> {
61
82
  return blob;
62
83
  }
63
84
 
85
+ /**
86
+ * External image references in an SVG, for error reporting. data: URIs are
87
+ * summarized rather than dumped (they can be hundreds of KB).
88
+ */
89
+ export function collectImageHrefs(svgMarkup: string): string[] {
90
+ const hrefs: string[] = [];
91
+ // Both quote styles: SVG allows either, and this feeds an error message — matching only double
92
+ // quotes meant the "which image broke the export?" hint could come back empty on valid markup,
93
+ // which is worse than not having it.
94
+ for (const m of svgMarkup.matchAll(/<image\b[^>]*?(?:xlink:)?href\s*=\s*("([^"]*)"|'([^']*)')/g)) {
95
+ const href = m[2] ?? m[3] ?? '';
96
+ hrefs.push(
97
+ href.startsWith('data:') ? `${href.slice(0, href.indexOf(',') + 1 || 32)}… (${href.length} chars)` : href
98
+ );
99
+ }
100
+ return hrefs;
101
+ }
102
+
64
103
  /**
65
104
  * Export an SVG string directly as a downloadable SVG file.
66
105
  */
@@ -83,10 +122,40 @@ export interface ExportSectionToPdfOptions {
83
122
  }
84
123
 
85
124
  export async function exportSectionToPdf(options: ExportSectionToPdfOptions): Promise<void> {
86
- await printToPdf({
87
- svgMarkup: options.svgMarkup,
88
- fileName: options.fileName
89
- });
125
+ const size = options.pageSizeMm;
126
+ // Claim the tab synchronously, while still inside the click that started this. Opening it after
127
+ // the await puts the call outside the user gesture, which is precisely what popup blockers stop
128
+ // — and rendering a sheet takes long enough that they always would.
129
+ const tab = options.openInNewTab ? window.open('', '_blank') : null;
130
+ let blob: Blob;
131
+ try {
132
+ blob = await printToPdf({
133
+ svgMarkup: options.svgMarkup,
134
+ // When opening in a tab, skip jsPDF's own save() download.
135
+ fileName: options.openInNewTab ? undefined : options.fileName,
136
+ pageFormat: size ? [size.width, size.height] : undefined,
137
+ orientation: size ? (size.width >= size.height ? 'landscape' : 'portrait') : undefined,
138
+ marginMm: size ? 0 : undefined
139
+ });
140
+ } catch (err) {
141
+ // Claiming the tab up front means a failed export would otherwise leave an empty one sitting
142
+ // there — and a broken linked <image> makes this throw, which is not a rare path.
143
+ tab?.close();
144
+ throw err;
145
+ }
146
+ if (options.openInNewTab) {
147
+ const url = URL.createObjectURL(blob);
148
+ // A blocked window.open returns null; fall back to the download so the export still lands
149
+ // somewhere the user can find rather than appearing to do nothing.
150
+ if (tab) tab.location.href = url;
151
+ else {
152
+ const link = document.createElement('a');
153
+ link.href = url;
154
+ link.download = options.fileName;
155
+ link.click();
156
+ }
157
+ setTimeout(() => URL.revokeObjectURL(url), 60_000);
158
+ }
90
159
  }
91
160
 
92
161
  export interface ExportSectionToSvgOptions {
package/src/printToSvg.ts CHANGED
@@ -40,9 +40,10 @@ export function printToSvg(geometry: SlabSectionGeometry, options: PrintToSvgOpt
40
40
  const proj = {
41
41
  hiddenLines: projectPolylines(geometry.hiddenLines, options.planeBasis),
42
42
  outlineHidden: projectPolylines(geometry.outlineHidden, options.planeBasis),
43
+ silhouettesHidden: projectPolylines(geometry.silhouettesHidden, options.planeBasis),
43
44
  visibleLines: projectPolylines(geometry.visibleLines, options.planeBasis),
44
45
  outlineVisible: projectPolylines(geometry.outlineVisible, options.planeBasis),
45
- silhouettes: projectPolylines(geometry.silhouettes, options.planeBasis),
46
+ silhouettesVisible: projectPolylines(geometry.silhouettesVisible, options.planeBasis),
46
47
  featureEdges: projectPolylines(geometry.featureEdges, options.planeBasis),
47
48
  cutLines: projectPolylines(geometry.cutLines, options.planeBasis),
48
49
  stairArrows: projectPolylines(geometry.stairArrows, options.planeBasis)
@@ -70,12 +71,14 @@ export function printToSvg(geometry: SlabSectionGeometry, options: PrintToSvgOpt
70
71
 
71
72
  parts.push('<g fill="none" stroke-linecap="round" stroke-linejoin="round">');
72
73
 
74
+ // Hidden categories paint first so visible linework always draws over them.
73
75
  const categories: Array<[keyof typeof proj, keyof Required<SvgRenderStyles>]> = [
74
76
  ['hiddenLines', 'hiddenLines'],
75
77
  ['outlineHidden', 'outlineHidden'],
78
+ ['silhouettesHidden', 'silhouettesHidden'],
76
79
  ['visibleLines', 'visibleLines'],
77
80
  ['outlineVisible', 'outlineVisible'],
78
- ['silhouettes', 'silhouettes'],
81
+ ['silhouettesVisible', 'silhouettesVisible'],
79
82
  ['featureEdges', 'featureEdges'],
80
83
  ['cutLines', 'cutLines'],
81
84
  ['stairArrows', 'stairArrows']
@@ -179,7 +182,8 @@ function mergeStyles(userStyles?: SvgRenderStyles): Required<SvgRenderStyles> {
179
182
  cutLines: { ...DEFAULT_SVG_STYLES.cutLines, ...userStyles?.cutLines },
180
183
  visibleLines: { ...DEFAULT_SVG_STYLES.visibleLines, ...userStyles?.visibleLines },
181
184
  hiddenLines: { ...DEFAULT_SVG_STYLES.hiddenLines, ...userStyles?.hiddenLines },
182
- silhouettes: { ...DEFAULT_SVG_STYLES.silhouettes, ...userStyles?.silhouettes },
185
+ silhouettesVisible: { ...DEFAULT_SVG_STYLES.silhouettesVisible, ...userStyles?.silhouettesVisible },
186
+ silhouettesHidden: { ...DEFAULT_SVG_STYLES.silhouettesHidden, ...userStyles?.silhouettesHidden },
183
187
  featureEdges: { ...DEFAULT_SVG_STYLES.featureEdges, ...userStyles?.featureEdges },
184
188
  stairArrows: { ...DEFAULT_SVG_STYLES.stairArrows, ...userStyles?.stairArrows },
185
189
  outlineVisible: { ...DEFAULT_SVG_STYLES.outlineVisible, ...userStyles?.outlineVisible },
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The shipped `templates/template-schema.json` contains VS Code snippet
3
+ * placeholders (`${1:value}`) inside `defaultSnippets` bodies — valid for
4
+ * Monaco/VS Code, but not strict JSON. Consumers that need to parse the
5
+ * schema (ajv validation, editors without snippet support) must strip them
6
+ * first; this is the one shared implementation.
7
+ */
8
+ export function sanitizeTemplateSchemaSnippets(schemaJsonText: string): string {
9
+ return schemaJsonText
10
+ .replace(/\$\{\d+:(-?[\d.]+)\}/g, '$1') // ${1:420} -> 420
11
+ .replace(/\$\{\d+:([^}"]*)\}/g, '$1') // ${2:main} -> main
12
+ .replace(/\$\{\d+\}|\$\d+/g, ''); // ${3} / $0 -> (removed)
13
+ }
@@ -1,11 +1,23 @@
1
1
  {
2
2
  "id": "a3-landscape-with-logo",
3
- "page": { "widthMm": 420, "heightMm": 297, "marginMm": 10, "backgroundColor": "#ffffff" },
3
+ "page": {
4
+ "widthMm": 420,
5
+ "heightMm": 297,
6
+ "marginMm": 10,
7
+ "backgroundColor": "#ffffff"
8
+ },
4
9
  "styles": {
5
- "body": { "fontFamily": "Arial", "fontSizeMm": 3.5 }
10
+ "body": {
11
+ "fontFamily": "Arial",
12
+ "fontSizeMm": 3.5
13
+ }
6
14
  },
7
- "assets": [
8
- { "id": "logo-main", "src": "https://twinfinity.com/theme-assets/logo.svg" }
15
+ "allowRemoteResources": true,
16
+ "resources": [
17
+ {
18
+ "id": "logo-main",
19
+ "src": "https://twinfinity.com/theme-assets/logo.svg"
20
+ }
9
21
  ],
10
22
  "elements": [
11
23
  {
@@ -15,7 +27,7 @@
15
27
  "yMm": 10,
16
28
  "widthMm": 80,
17
29
  "heightMm": 20,
18
- "assetId": "logo-main",
30
+ "resourceId": "logo-main",
19
31
  "fit": "contain"
20
32
  },
21
33
  {
@@ -47,9 +59,19 @@
47
59
  "widthMm": 410,
48
60
  "heightMm": 230,
49
61
  "scaleMode": "fitAndRound",
50
- "preferredScales": [2, 10, 25, 50, 100, 150, 250, 500, 1000, 2500],
51
- "align": "center",
52
- "border": { "stroke": "none", "strokeWidthMm": 0.25 }
62
+ "preferredScales": [
63
+ 2,
64
+ 10,
65
+ 25,
66
+ 50,
67
+ 100,
68
+ 150,
69
+ 250,
70
+ 500,
71
+ 1000,
72
+ 2500
73
+ ],
74
+ "align": "center"
53
75
  },
54
76
  {
55
77
  "kind": "scaleBar",
package/src/types.ts CHANGED
@@ -12,7 +12,8 @@ export interface SvgRenderStyles {
12
12
  cutLines?: SvgLineStyle;
13
13
  visibleLines?: SvgLineStyle;
14
14
  hiddenLines?: SvgLineStyle;
15
- silhouettes?: SvgLineStyle;
15
+ silhouettesVisible?: SvgLineStyle;
16
+ silhouettesHidden?: SvgLineStyle;
16
17
  featureEdges?: SvgLineStyle;
17
18
  stairArrows?: SvgLineStyle;
18
19
  outlineVisible?: SvgLineStyle;
@@ -41,7 +42,16 @@ export const DEFAULT_SVG_STYLES: Required<SvgRenderStyles> = {
41
42
  cutLines: { enabled: true, stroke: '#dc2626', strokeWidth: 0.01, opacity: 1 },
42
43
  visibleLines: { enabled: true, stroke: '#111827', strokeWidth: 0.005, opacity: 1 },
43
44
  hiddenLines: { enabled: true, stroke: '#6b7280', strokeWidth: 0.004, strokeDasharray: '0.02,0.01', opacity: 1 },
44
- silhouettes: { enabled: true, stroke: '#000000', strokeWidth: 0.006, opacity: 1 },
45
+ silhouettesVisible: { enabled: true, stroke: '#000000', strokeWidth: 0.006, opacity: 1 },
46
+ // Hidden contours follow the hiddenLines drafting convention (dashed grey), not the
47
+ // visible outline's heavy solid stroke.
48
+ silhouettesHidden: {
49
+ enabled: true,
50
+ stroke: '#6b7280',
51
+ strokeWidth: 0.004,
52
+ strokeDasharray: '0.02,0.01',
53
+ opacity: 1
54
+ },
45
55
  featureEdges: { enabled: true, stroke: '#374151', strokeWidth: 0.005, opacity: 1 },
46
56
  stairArrows: { enabled: true, stroke: '#111827', strokeWidth: 0.005, opacity: 1 },
47
57
  outlineVisible: { enabled: true, stroke: '#111827', strokeWidth: 0.004, opacity: 1 },
@@ -3,20 +3,58 @@
3
3
  "id": "default-a0-landscape",
4
4
  "presets": {
5
5
  "includeClasses": {
6
- "defaultBuilding": ["space", "walls", "windows", "doors", "beam", "columns", "stairs", "floor"]
6
+ "defaultBuilding": [
7
+ "space",
8
+ "walls",
9
+ "windows",
10
+ "doors",
11
+ "beam",
12
+ "columns",
13
+ "stairs",
14
+ "floor"
15
+ ]
7
16
  },
8
17
  "scales": {
9
- "default": { "preferredScales": [2, 10, 25, 50, 100, 150, 250, 500, 1000, 2500] }
18
+ "default": {
19
+ "preferredScales": [
20
+ 2,
21
+ 10,
22
+ 25,
23
+ 50,
24
+ 100,
25
+ 150,
26
+ 250,
27
+ 500,
28
+ 1000,
29
+ 2500
30
+ ]
31
+ }
10
32
  }
11
33
  },
12
- "page": { "widthMm": 1189, "heightMm": 841, "marginMm": 5, "backgroundColor": "#ffffff" },
34
+ "page": {
35
+ "widthMm": 1189,
36
+ "heightMm": 841,
37
+ "marginMm": 5,
38
+ "backgroundColor": "#ffffff"
39
+ },
13
40
  "styles": {
14
- "body": { "fontFamily": "Arial", "fontSizeMm": 3.5, "color": "#000000" },
15
- "spaceLabels": { "fontFamily": "Arial", "fontSizeMm": 5, "color": "#000000" }
41
+ "body": {
42
+ "fontFamily": "Arial",
43
+ "fontSizeMm": 3.5,
44
+ "color": "#000000"
45
+ },
46
+ "spaceLabels": {
47
+ "fontFamily": "Arial",
48
+ "fontSizeMm": 5,
49
+ "color": "#000000"
50
+ }
16
51
  },
17
- "allowRemoteAssets": true,
18
- "assets": [
19
- { "id": "logo-main", "src": "https://twinfinity.com/theme-assets/logo.svg" }
52
+ "allowRemoteResources": true,
53
+ "resources": [
54
+ {
55
+ "id": "logo-main",
56
+ "src": "https://twinfinity.com/theme-assets/logo.svg"
57
+ }
20
58
  ],
21
59
  "elements": [
22
60
  {
@@ -26,7 +64,7 @@
26
64
  "yMm": 7,
27
65
  "widthMm": 40,
28
66
  "heightMm": 10,
29
- "assetId": "logo-main",
67
+ "resourceId": "logo-main",
30
68
  "fit": "contain"
31
69
  },
32
70
  {
@@ -47,19 +85,38 @@
47
85
  "yMm": 20,
48
86
  "widthMm": 1179,
49
87
  "heightMm": 794,
50
- "includeClasses": { "preset": "defaultBuilding" },
88
+ "includeClasses": {
89
+ "preset": "defaultBuilding"
90
+ },
51
91
  "scaleMode": "fitAndRound",
52
92
  "scalePreset": "default",
53
93
  "align": "center",
54
- "rotationDeg": 0,
55
- "fills": [{ "class": "space", "fill": "sample", "fillOpacity": 0.35 }],
94
+ "fills": [
95
+ {
96
+ "class": "space",
97
+ "fill": "sample",
98
+ "fillOpacity": 0.35
99
+ }
100
+ ],
56
101
  "lineStyles": {
57
- "default": { "widthMm": 0.01, "color": "#1f2937" },
58
- "visibleLines": { "widthMm": 0.01, "color": "#1f2937" },
59
- "hiddenLines": { "widthMm": 0.005, "color": "#9ca3af", "dash": "4 2" },
60
- "stairArrows": { "widthMm": 0.015, "color": "#1f2937" }
61
- },
62
- "border": { "stroke": "none", "strokeWidthMm": 0.25 }
102
+ "default": {
103
+ "widthMm": 0.25,
104
+ "color": "#1f2937"
105
+ },
106
+ "visibleLines": {
107
+ "widthMm": 0.25,
108
+ "color": "#1f2937"
109
+ },
110
+ "hiddenLines": {
111
+ "widthMm": 0.13,
112
+ "color": "#9ca3af",
113
+ "dash": "4 2"
114
+ },
115
+ "stairArrows": {
116
+ "widthMm": 0.35,
117
+ "color": "#1f2937"
118
+ }
119
+ }
63
120
  },
64
121
  {
65
122
  "kind": "text",
@@ -96,114 +153,162 @@
96
153
  {
97
154
  "kind": "line",
98
155
  "id": "fold-v-29-top",
99
- "x1Mm": 29, "y1Mm": 2,
100
- "x2Mm": 29, "y2Mm": 8,
101
- "stroke": "#000", "strokeWidthMm": 0.2
156
+ "x1Mm": 29,
157
+ "y1Mm": 2,
158
+ "x2Mm": 29,
159
+ "y2Mm": 8,
160
+ "stroke": "#000",
161
+ "strokeWidthMm": 0.2
102
162
  },
103
163
  {
104
164
  "kind": "line",
105
165
  "id": "fold-v-29-bottom",
106
- "x1Mm": 29, "y1Mm": 828,
107
- "x2Mm": 29, "y2Mm": 834,
108
- "stroke": "#000", "strokeWidthMm": 0.2
166
+ "x1Mm": 29,
167
+ "y1Mm": 828,
168
+ "x2Mm": 29,
169
+ "y2Mm": 834,
170
+ "stroke": "#000",
171
+ "strokeWidthMm": 0.2
109
172
  },
110
173
  {
111
174
  "kind": "line",
112
175
  "id": "fold-v-219-top",
113
- "x1Mm": 219, "y1Mm": 2,
114
- "x2Mm": 219, "y2Mm": 8,
115
- "stroke": "#000", "strokeWidthMm": 0.2
176
+ "x1Mm": 219,
177
+ "y1Mm": 2,
178
+ "x2Mm": 219,
179
+ "y2Mm": 8,
180
+ "stroke": "#000",
181
+ "strokeWidthMm": 0.2
116
182
  },
117
183
  {
118
184
  "kind": "line",
119
185
  "id": "fold-v-219-bottom",
120
- "x1Mm": 219, "y1Mm": 828,
121
- "x2Mm": 219, "y2Mm": 834,
122
- "stroke": "#000", "strokeWidthMm": 0.2
186
+ "x1Mm": 219,
187
+ "y1Mm": 828,
188
+ "x2Mm": 219,
189
+ "y2Mm": 834,
190
+ "stroke": "#000",
191
+ "strokeWidthMm": 0.2
123
192
  },
124
193
  {
125
194
  "kind": "line",
126
195
  "id": "fold-v-409-top",
127
- "x1Mm": 409, "y1Mm": 2,
128
- "x2Mm": 409, "y2Mm": 8,
129
- "stroke": "#000", "strokeWidthMm": 0.2
196
+ "x1Mm": 409,
197
+ "y1Mm": 2,
198
+ "x2Mm": 409,
199
+ "y2Mm": 8,
200
+ "stroke": "#000",
201
+ "strokeWidthMm": 0.2
130
202
  },
131
203
  {
132
204
  "kind": "line",
133
205
  "id": "fold-v-409-bottom",
134
- "x1Mm": 409, "y1Mm": 828,
135
- "x2Mm": 409, "y2Mm": 834,
136
- "stroke": "#000", "strokeWidthMm": 0.2
206
+ "x1Mm": 409,
207
+ "y1Mm": 828,
208
+ "x2Mm": 409,
209
+ "y2Mm": 834,
210
+ "stroke": "#000",
211
+ "strokeWidthMm": 0.2
137
212
  },
138
213
  {
139
214
  "kind": "line",
140
215
  "id": "fold-v-599-top",
141
- "x1Mm": 599, "y1Mm": 2,
142
- "x2Mm": 599, "y2Mm": 8,
143
- "stroke": "#000", "strokeWidthMm": 0.2
216
+ "x1Mm": 599,
217
+ "y1Mm": 2,
218
+ "x2Mm": 599,
219
+ "y2Mm": 8,
220
+ "stroke": "#000",
221
+ "strokeWidthMm": 0.2
144
222
  },
145
223
  {
146
224
  "kind": "line",
147
225
  "id": "fold-v-599-bottom",
148
- "x1Mm": 599, "y1Mm": 828,
149
- "x2Mm": 599, "y2Mm": 834,
150
- "stroke": "#000", "strokeWidthMm": 0.2
226
+ "x1Mm": 599,
227
+ "y1Mm": 828,
228
+ "x2Mm": 599,
229
+ "y2Mm": 834,
230
+ "stroke": "#000",
231
+ "strokeWidthMm": 0.2
151
232
  },
152
233
  {
153
234
  "kind": "line",
154
235
  "id": "fold-v-789-top",
155
- "x1Mm": 789, "y1Mm": 2,
156
- "x2Mm": 789, "y2Mm": 8,
157
- "stroke": "#000", "strokeWidthMm": 0.2
236
+ "x1Mm": 789,
237
+ "y1Mm": 2,
238
+ "x2Mm": 789,
239
+ "y2Mm": 8,
240
+ "stroke": "#000",
241
+ "strokeWidthMm": 0.2
158
242
  },
159
243
  {
160
244
  "kind": "line",
161
245
  "id": "fold-v-789-bottom",
162
- "x1Mm": 789, "y1Mm": 828,
163
- "x2Mm": 789, "y2Mm": 834,
164
- "stroke": "#000", "strokeWidthMm": 0.2
246
+ "x1Mm": 789,
247
+ "y1Mm": 828,
248
+ "x2Mm": 789,
249
+ "y2Mm": 834,
250
+ "stroke": "#000",
251
+ "strokeWidthMm": 0.2
165
252
  },
166
253
  {
167
254
  "kind": "line",
168
255
  "id": "fold-v-979-top",
169
- "x1Mm": 979, "y1Mm": 2,
170
- "x2Mm": 979, "y2Mm": 8,
171
- "stroke": "#000", "strokeWidthMm": 0.2
256
+ "x1Mm": 979,
257
+ "y1Mm": 2,
258
+ "x2Mm": 979,
259
+ "y2Mm": 8,
260
+ "stroke": "#000",
261
+ "strokeWidthMm": 0.2
172
262
  },
173
263
  {
174
264
  "kind": "line",
175
265
  "id": "fold-v-979-bottom",
176
- "x1Mm": 979, "y1Mm": 828,
177
- "x2Mm": 979, "y2Mm": 834,
178
- "stroke": "#000", "strokeWidthMm": 0.2
266
+ "x1Mm": 979,
267
+ "y1Mm": 828,
268
+ "x2Mm": 979,
269
+ "y2Mm": 834,
270
+ "stroke": "#000",
271
+ "strokeWidthMm": 0.2
179
272
  },
180
273
  {
181
274
  "kind": "line",
182
275
  "id": "fold-h-247-left",
183
- "x1Mm": 2, "y1Mm": 247,
184
- "x2Mm": 8, "y2Mm": 247,
185
- "stroke": "#000", "strokeWidthMm": 0.2
276
+ "x1Mm": 2,
277
+ "y1Mm": 247,
278
+ "x2Mm": 8,
279
+ "y2Mm": 247,
280
+ "stroke": "#000",
281
+ "strokeWidthMm": 0.2
186
282
  },
187
283
  {
188
284
  "kind": "line",
189
285
  "id": "fold-h-247-right",
190
- "x1Mm": 1176, "y1Mm": 247,
191
- "x2Mm": 1182, "y2Mm": 247,
192
- "stroke": "#000", "strokeWidthMm": 0.2
286
+ "x1Mm": 1176,
287
+ "y1Mm": 247,
288
+ "x2Mm": 1182,
289
+ "y2Mm": 247,
290
+ "stroke": "#000",
291
+ "strokeWidthMm": 0.2
193
292
  },
194
293
  {
195
294
  "kind": "line",
196
295
  "id": "fold-h-544-left",
197
- "x1Mm": 2, "y1Mm": 544,
198
- "x2Mm": 8, "y2Mm": 544,
199
- "stroke": "#000", "strokeWidthMm": 0.2
296
+ "x1Mm": 2,
297
+ "y1Mm": 544,
298
+ "x2Mm": 8,
299
+ "y2Mm": 544,
300
+ "stroke": "#000",
301
+ "strokeWidthMm": 0.2
200
302
  },
201
303
  {
202
304
  "kind": "line",
203
305
  "id": "fold-h-544-right",
204
- "x1Mm": 1176, "y1Mm": 544,
205
- "x2Mm": 1182, "y2Mm": 544,
206
- "stroke": "#000", "strokeWidthMm": 0.2
306
+ "x1Mm": 1176,
307
+ "y1Mm": 544,
308
+ "x2Mm": 1182,
309
+ "y2Mm": 544,
310
+ "stroke": "#000",
311
+ "strokeWidthMm": 0.2
207
312
  }
208
313
  ]
209
314
  }