@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.
- 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
|
@@ -3,20 +3,58 @@
|
|
|
3
3
|
"id": "default-a4-portrait",
|
|
4
4
|
"presets": {
|
|
5
5
|
"includeClasses": {
|
|
6
|
-
"defaultBuilding": [
|
|
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": {
|
|
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": {
|
|
34
|
+
"page": {
|
|
35
|
+
"widthMm": 210,
|
|
36
|
+
"heightMm": 297,
|
|
37
|
+
"marginMm": 5,
|
|
38
|
+
"backgroundColor": "#ffffff"
|
|
39
|
+
},
|
|
13
40
|
"styles": {
|
|
14
|
-
"body": {
|
|
15
|
-
|
|
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
|
-
"
|
|
18
|
-
"
|
|
19
|
-
{
|
|
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
|
-
"
|
|
67
|
+
"resourceId": "logo-main",
|
|
30
68
|
"fit": "contain"
|
|
31
69
|
},
|
|
32
70
|
{
|
|
@@ -47,19 +85,38 @@
|
|
|
47
85
|
"yMm": 20,
|
|
48
86
|
"widthMm": 200,
|
|
49
87
|
"heightMm": 250,
|
|
50
|
-
"includeClasses": {
|
|
88
|
+
"includeClasses": {
|
|
89
|
+
"preset": "defaultBuilding"
|
|
90
|
+
},
|
|
51
91
|
"scaleMode": "fitAndRound",
|
|
52
92
|
"scalePreset": "default",
|
|
53
93
|
"align": "center",
|
|
54
|
-
"
|
|
55
|
-
|
|
94
|
+
"fills": [
|
|
95
|
+
{
|
|
96
|
+
"class": "space",
|
|
97
|
+
"fill": "sample",
|
|
98
|
+
"fillOpacity": 0.35
|
|
99
|
+
}
|
|
100
|
+
],
|
|
56
101
|
"lineStyles": {
|
|
57
|
-
"default":
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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",
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Layout-template authoring guide for AI assistants
|
|
2
|
+
|
|
3
|
+
You are helping a user author a **layout template** for `@twinfinity/printing`
|
|
4
|
+
— a JSON document that renders BIM building data (floor plans, sections) onto
|
|
5
|
+
a print sheet as SVG/PDF. This file is a complete, self-contained
|
|
6
|
+
specification: follow it and the output will validate and render. The
|
|
7
|
+
machine-checkable truth is the sibling `template-schema.json`; when in doubt,
|
|
8
|
+
that schema wins.
|
|
9
|
+
|
|
10
|
+
## Your output contract
|
|
11
|
+
|
|
12
|
+
- Produce **one valid JSON object** in a single code block. No comments, no
|
|
13
|
+
trailing commas, no placeholders — every value concrete and renderable.
|
|
14
|
+
- Ask the user (or assume and say so): page size, what the sheet should show
|
|
15
|
+
(which floor / which spaces), and which property set holds their data
|
|
16
|
+
(`BIP` is the common Swedish default).
|
|
17
|
+
- The user iterates in the **template-workbench** app, which shows render
|
|
18
|
+
warnings — mention that warnings (not silence) are how missing data shows
|
|
19
|
+
up.
|
|
20
|
+
|
|
21
|
+
## Units — the #1 mistake
|
|
22
|
+
|
|
23
|
+
- **Page space is millimetres**: every `xMm`/`yMm`/`widthMm`/`heightMm`/
|
|
24
|
+
`sizeMm` field, line widths (`widthMm`, `strokeWidthMm`) and `dash`
|
|
25
|
+
patterns (e.g. `"4 2"` = 4 mm dash, 2 mm gap).
|
|
26
|
+
- **Model space is metres**: `cutHeight`, `planePosition`, `depth.above`/
|
|
27
|
+
`depth.below`, `expandBy`.
|
|
28
|
+
- `cutHeight` must be a real storey cut height (floor elevation + ~1.2 m),
|
|
29
|
+
not 0 and not a guess — ask the user or tell them to adjust it in the
|
|
30
|
+
workbench (wrong height = empty or wrong-floor drawing with no error).
|
|
31
|
+
|
|
32
|
+
## Root structure
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"id": "my-template",
|
|
37
|
+
"page": { "widthMm": 420, "heightMm": 297, "backgroundColor": "#ffffff" },
|
|
38
|
+
"styles": { "body": { "fontFamily": "Arial", "fontSizeMm": 3.5, "color": "#000000" } },
|
|
39
|
+
"variables": {},
|
|
40
|
+
"presets": {},
|
|
41
|
+
"resources": [],
|
|
42
|
+
"elements": []
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- `page` is required (`marginMm` only affects legend auto-placement).
|
|
47
|
+
- `styles` is a name → text-style map: `{ fontFamily, fontSizeMm, fontWeight?,
|
|
48
|
+
color?, letterSpacingMm?, lineHeightMm? }`. **Every `textStyle` you
|
|
49
|
+
reference on an element MUST exist here** — a missing style silently skips
|
|
50
|
+
the element (with a warning).
|
|
51
|
+
- `resources`: `[{ id, src }]` or `[{ id, content }]` (inline SVG). Remote
|
|
52
|
+
`https://` sources also need root `"allowRemoteResources": true`.
|
|
53
|
+
- `presets`: reusable lists — `"includeClasses": { "name": ["walls", …] }`
|
|
54
|
+
and `"scales": { "name": { "preferredScales": [50, 100, …] } }`.
|
|
55
|
+
- `variables`: declared parameters, each **must have a `default`**:
|
|
56
|
+
`{ "spacenumber": { "type": "string", "default": "6043", "label": "Space number" } }`.
|
|
57
|
+
Names must be **simple identifiers** (`[A-Za-z_][A-Za-z0-9_]*` — no dashes
|
|
58
|
+
or dots; they'd collide with the `{{vars.<name>}}` syntax). Reference
|
|
59
|
+
anywhere in element strings — including `where` values — as
|
|
60
|
+
`{{vars.spacenumber}}`. Types: `string | number | boolean | array`;
|
|
61
|
+
optional `format` (`color|email|url|date`), `enum`, `min`/`max`,
|
|
62
|
+
`pattern` drive the editing UI.
|
|
63
|
+
|
|
64
|
+
## Elements (all have required `kind` + unique `id`; optional `zIndex`)
|
|
65
|
+
|
|
66
|
+
### viewport — a cut of the model on the page
|
|
67
|
+
Required: `xMm, yMm, widthMm, heightMm`, and in practice **`preferredScales`**
|
|
68
|
+
(e.g. `[50, 100, 200, 400]`) or a `scalePreset` — without candidates the
|
|
69
|
+
scale is NaN and the viewport is skipped.
|
|
70
|
+
|
|
71
|
+
- Section definition (self-describing cut): plans use `"cutHeight": 3.0` +
|
|
72
|
+
`"depth": { "above": 0.5, "below": 1.5 }`; vertical sections use
|
|
73
|
+
`"planeNormal": [0,0,1]` (or `[1,0,0]`, negated variants) +
|
|
74
|
+
`"planePosition": 31` (metres along the normal). Never both.
|
|
75
|
+
- **Prefer binding the cut to a variable** so the user can adjust it without
|
|
76
|
+
editing JSON: `"cutHeight": "{{vars.cutHeight}}"` + a number variable
|
|
77
|
+
`"cutHeight": { "type": "number", "default": 3, "step": 0.1, "label": "Cut height (m)" }`.
|
|
78
|
+
(Whole-value bindings keep the number type; only these two section fields
|
|
79
|
+
support it.)
|
|
80
|
+
- `scaleMode`: `"fitAndRound"` (default — picks the nearest preferred scale
|
|
81
|
+
that fits) | `"fit"` (exact fill, odd denominators) | `"fixed"` (+
|
|
82
|
+
`fixedScaleDenom`).
|
|
83
|
+
- `includeClasses`: what to draw — `["walls", "windows", "doors", "columns",
|
|
84
|
+
"stairs", "floor"]`, entries may also be `{ "class": "space", "where": {…} }`,
|
|
85
|
+
or `{ "preset": "name" }`. Omitted = everything (slow). Class names:
|
|
86
|
+
category form (`walls`, `space`) or IFC form (`IfcWall`, `IfcSpace`) —
|
|
87
|
+
interchangeable everywhere.
|
|
88
|
+
- `lineStyles`: per-category styling `{ color?, widthMm?, dash?, enabled? }`
|
|
89
|
+
for `default, defaultVisibleLines, defaultHiddenLines, cutLines,
|
|
90
|
+
visibleLines, hiddenLines, silhouettes, featureEdges, stairArrows,
|
|
91
|
+
outlineVisible, outlineHidden`. `defaultVisibleLines` styles every
|
|
92
|
+
visible-class category (cutLines, visibleLines, silhouettes, featureEdges,
|
|
93
|
+
outlineVisible) and `defaultHiddenLines` every hidden-class one
|
|
94
|
+
(hiddenLines, outlineHidden) unless the specific category key overrides it;
|
|
95
|
+
`stairArrows` follows only `default`. Sensible print widths:
|
|
96
|
+
default/visible **0.25**, hidden **0.13** + `"dash": "4 2"`, heavy
|
|
97
|
+
**0.35** (mm on paper).
|
|
98
|
+
- `stairArrows` (viewport-level, plans only): geometry of the generated stair
|
|
99
|
+
arrows — `{ "startMarker": "filled" | "open" | "none", "arrowhead":
|
|
100
|
+
"filled" | "outline" | "open", "lengthScale"?, "minLength"?, "maxLength"?,
|
|
101
|
+
"landingExtension"? }`. Defaults: filled circle at the bottom, filled
|
|
102
|
+
triangle at the top. Styling/hiding stays in `lineStyles.stairArrows`.
|
|
103
|
+
- `fills`: `[{ "class": "space", "where": {…}, "fill": "#80c8b4" | "sample" |
|
|
104
|
+
"legend", "fillOpacity": 0.35, "matchClipFilter": true }]` — `"legend"`
|
|
105
|
+
takes colors from a legend with `assignColors` on the SAME viewport;
|
|
106
|
+
`matchClipFilter` restricts fills to items inside the clip.
|
|
107
|
+
- `clip`: crop to matching items — `{ "class": "space", "where": {…}
|
|
108
|
+
(required), "expandBy": 1.5, "clipLabels": true, "color": "#000",
|
|
109
|
+
"widthMm": 0.7, "dash": "6 2", "opacity": 1 }`. The boundary is the matched
|
|
110
|
+
items' polygons expanded `expandBy` metres.
|
|
111
|
+
- `highlight`: restyle matching items — `{ "class": …, "where": {…}
|
|
112
|
+
(required), "color": "#f00", "widthMm": 0.5, "fill": "#ff0",
|
|
113
|
+
"fillOpacity": 1, "intersectsViewport": "otherViewportId"? }`.
|
|
114
|
+
- Highlight/clip classes are **exempt** from `includeClasses` — a highlighted
|
|
115
|
+
space still shows in a walls-only view.
|
|
116
|
+
- `includeClasses` gates `fills` too: a fill rule for an excluded class draws
|
|
117
|
+
nothing. Exception: a rule with `"matchClipFilter": true` under an active
|
|
118
|
+
clip follows the clip's item set instead (the clipped apartment's space
|
|
119
|
+
fills render even when spaces are excluded from drawing).
|
|
120
|
+
- `includeClasses` also scopes COMPUTATION: only the viewport's include
|
|
121
|
+
classes (plus its highlight/clip/fill/label/legend classes) take part in
|
|
122
|
+
section generation — excluded geometry neither draws nor occludes, and
|
|
123
|
+
costs nothing. Always list `includeClasses`; a viewport without them
|
|
124
|
+
computes against the entire model and is dramatically slower on big twins.
|
|
125
|
+
- Include entries can carry per-class behavior flags (both default `true`):
|
|
126
|
+
`{ "class": "flow", "occludes": false, "classify": false }` —
|
|
127
|
+
`occludes: false` = the class hides nothing (a pipe run must not erase the
|
|
128
|
+
wall behind it); `classify: false` = its own lines skip hidden-line testing
|
|
129
|
+
and draw as-is. Use both for schematic MEP linework — it is also the single
|
|
130
|
+
biggest performance lever when including dense curved classes like pipes.
|
|
131
|
+
- `border`: `{ "stroke": "#000", "strokeWidthMm": 0.25, "dash"? }`.
|
|
132
|
+
- `northArrow`: `{ "resourceId": …, "sizeMm": 12 }` — anchored top-right.
|
|
133
|
+
- Maximum **8 viewports** per template.
|
|
134
|
+
|
|
135
|
+
### text
|
|
136
|
+
`{ "kind": "text", "id", "xMm", "yMm", "text", "textStyle", "align"? }`.
|
|
137
|
+
`xMm/yMm` anchor the **baseline**. Interpolations: `{{vars.*}}` and
|
|
138
|
+
`{{viewport.<id>.scaleText}}` (renders e.g. `1:100`).
|
|
139
|
+
|
|
140
|
+
### labels — per-item text at item centroids (PLAN viewports only)
|
|
141
|
+
`{ "kind": "labels", "id", "viewport" (required), "text" (required),
|
|
142
|
+
"textStyle", "class"?, "where"?, "align"?, "matchClipFilter"? }`.
|
|
143
|
+
`text` interpolates item properties: `"{{BIP.spacename}} {{BIP.area | round:1}} m²"`.
|
|
144
|
+
Do not attach labels to elevation viewports — they would be misplaced.
|
|
145
|
+
|
|
146
|
+
### legend — table of matching items
|
|
147
|
+
`{ "kind": "legend", "id", "viewport" (required), "columns" (required),
|
|
148
|
+
"class"?, "where"?, "clipToViewport"?, "textStyle", "anchor":
|
|
149
|
+
"top-right|top-left|bottom-right|bottom-left", "anchorPositionMm"?:
|
|
150
|
+
{xMm,yMm}, "groupBy"?: "BIP.spacename", "assignColors"?: { "enabled": true,
|
|
151
|
+
"palette": "auto" }, "showColorColumn"?, "summary"?: { "enabled": true,
|
|
152
|
+
"label": "Total" }, "border"/"grid"/"header"?: { stroke, strokeWidthMm } }`.
|
|
153
|
+
Columns: `[{ "id", "label", "property": "BIP.area", "format": { "digits": 1,
|
|
154
|
+
"suffix": " m²" }, "align"?, "summary": true? }]` — `summary: true` columns
|
|
155
|
+
are summed in the totals row. `groupBy` merges equal-valued rows (numeric
|
|
156
|
+
columns summed). `assignColors` + a viewport fill `"fill": "legend"` =
|
|
157
|
+
color-coded plan matching the legend swatches.
|
|
158
|
+
|
|
159
|
+
### shapes — line / rect / circle / polyline / polygon (page mm)
|
|
160
|
+
- `line`: `{ x1Mm, y1Mm, x2Mm, y2Mm, stroke?, strokeWidthMm?, dash? }`
|
|
161
|
+
- `rect`: `{ xMm, yMm, widthMm, heightMm, stroke?, strokeWidthMm?, fill?, dash? }`
|
|
162
|
+
- `circle`: `{ cxMm, cyMm, rMm (required), stroke?, strokeWidthMm?, fill?, dash? }`
|
|
163
|
+
- `polyline`: `{ points: [{xMm,yMm}…] (≥2), stroke (required), strokeWidthMm (required), dash? }`
|
|
164
|
+
- `polygon`: `{ points (≥3), stroke?, strokeWidthMm?, fill?, dash? }`
|
|
165
|
+
|
|
166
|
+
### scaleBar
|
|
167
|
+
`{ "kind": "scaleBar", "id", "xMm", "yMm", "maxLengthMm" (required),
|
|
168
|
+
"segments": 10, "labelEvery": 2, "unit": "m", "stroke", "strokeWidthMm",
|
|
169
|
+
"textStyle" (needed for the numbers), "viewport"? }`.
|
|
170
|
+
|
|
171
|
+
### qr
|
|
172
|
+
`{ "kind": "qr", "id", "xMm", "yMm", "sizeMm" (required), "role":
|
|
173
|
+
"main"|"anchor"|"custom", "viewport"?, "anchorCorner"?, "dataTemplate"? }`.
|
|
174
|
+
`main` (default) encodes a viewer link at the drawing location; `anchor` +
|
|
175
|
+
`anchorCorner` are AR positioning markers; `custom` + `dataTemplate` is an
|
|
176
|
+
arbitrary interpolated payload.
|
|
177
|
+
|
|
178
|
+
### image
|
|
179
|
+
`{ "kind": "image", "id", "xMm", "yMm", "widthMm", "heightMm", "resourceId"
|
|
180
|
+
(required), "fit": "contain"|"cover"|"stretch" }`.
|
|
181
|
+
|
|
182
|
+
### group
|
|
183
|
+
`{ "kind": "group", "id", "children": […], "transform"?:
|
|
184
|
+
{ translateXMm, translateYMm, rotateDeg, scaleX, scaleY }, "opacity"?,
|
|
185
|
+
"clip"?: { "type": "rect", xMm, yMm, widthMm, heightMm } }`.
|
|
186
|
+
Note: `labels`/`qr` children render as page-level overlays — the group's
|
|
187
|
+
transform does not move them.
|
|
188
|
+
|
|
189
|
+
## Where-conditions (used by clip/highlight/fills/labels/legend/includeClasses)
|
|
190
|
+
|
|
191
|
+
`{ "BIP.spacetype": "ROOM" }` — key is `<PropertySet>.<property>`, value is
|
|
192
|
+
a scalar (case-insensitive equality) or an array (any-of). Multiple keys AND
|
|
193
|
+
together. Values may reference variables: `{ "BIP.spacenumber":
|
|
194
|
+
"{{vars.spacenumber}}" }`.
|
|
195
|
+
|
|
196
|
+
## Placeholder format filters
|
|
197
|
+
|
|
198
|
+
Any `{{…}}` placeholder accepts `| round:N` (round, trim trailing zeros) or
|
|
199
|
+
`| fixed:N` (exactly N decimals): `{{BIP.area | round:1}}` → `344.6`.
|
|
200
|
+
Raw numeric properties are unrounded doubles — **always filter area-like
|
|
201
|
+
values**.
|
|
202
|
+
|
|
203
|
+
## Minimal valid skeleton
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"id": "basic-plan",
|
|
208
|
+
"page": { "widthMm": 420, "heightMm": 297 },
|
|
209
|
+
"styles": { "body": { "fontFamily": "Arial", "fontSizeMm": 3.5, "color": "#000000" } },
|
|
210
|
+
"elements": [
|
|
211
|
+
{
|
|
212
|
+
"kind": "viewport",
|
|
213
|
+
"id": "main",
|
|
214
|
+
"xMm": 5, "yMm": 15, "widthMm": 410, "heightMm": 260,
|
|
215
|
+
"cutHeight": 3, "depth": { "above": 0.5, "below": 1.5 },
|
|
216
|
+
"scaleMode": "fitAndRound", "preferredScales": [50, 100, 200, 400],
|
|
217
|
+
"includeClasses": ["walls", "windows", "doors", "columns", "stairs", "floor"],
|
|
218
|
+
"lineStyles": {
|
|
219
|
+
"default": { "widthMm": 0.25, "color": "#1f2937" },
|
|
220
|
+
"hiddenLines": { "widthMm": 0.13, "color": "#9ca3af", "dash": "4 2" }
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
{ "kind": "text", "id": "scale", "xMm": 210, "yMm": 290, "textStyle": "body",
|
|
224
|
+
"align": "center", "text": "Scale: {{viewport.main.scaleText}}" }
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
For a full-featured example (variables, clip + expand, highlights, legend
|
|
230
|
+
with colors + summary, labels, plan + section viewports) see the
|
|
231
|
+
`hyresgastbilaga` template in the template-workbench gallery.
|
|
232
|
+
|
|
233
|
+
## Checklist before you emit the JSON
|
|
234
|
+
|
|
235
|
+
1. Valid JSON, unique element `id`s, ≤ 8 viewports.
|
|
236
|
+
2. Every viewport has `preferredScales` (or `scalePreset`) and exactly one
|
|
237
|
+
section definition (`cutHeight` XOR `planeNormal`+`planePosition`).
|
|
238
|
+
3. Every `textStyle` reference exists in `styles`; scaleBar has a
|
|
239
|
+
`textStyle` if it should show numbers.
|
|
240
|
+
4. Every variable has a `default`; every `{{vars.*}}` is declared.
|
|
241
|
+
5. `labels` only on plan viewports; numeric placeholders filtered
|
|
242
|
+
(`| round:1`).
|
|
243
|
+
6. `fill: "legend"` only if a legend with `assignColors` targets the same
|
|
244
|
+
viewport.
|
|
245
|
+
7. Every `resourceId` exists in `resources`; remote URLs have
|
|
246
|
+
`allowRemoteResources: true`.
|
|
247
|
+
8. Units sanity: mm on the page, metres in the model.
|