beckhoff-xts-viewer-3d 5.2.2 → 5.3.0

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.
@@ -1,123 +1,123 @@
1
- # Screenshots
2
-
3
- The README and docs-site image strip, captured straight out of the playground
4
- through the viewer's own `exportScreenshot()` ref method. No external CAD
5
- render and no hand-editing: every image below was produced by automating the
6
- playground over `window.__viewer` / `window.__playground`.
7
-
8
- ## How to refresh
9
-
10
- ```bash
11
- npm i -g playwright && playwright install chromium # once
12
- pnpm docs:capture-screenshots # the whole strip
13
- pnpm docs:capture-screenshots --only 07,12 # by number or filename
14
- pnpm docs:capture-screenshots --list
15
- ```
16
-
17
- [`scripts/capture-screenshots.mjs`](../../scripts/capture-screenshots.mjs)
18
- boots the playground's Vite dev server in-process, drives it in headless
19
- Chromium and writes the files here. Playwright is deliberately not a
20
- dependency of the repo — CI never runs this script and a browser download in
21
- every install would not pay for itself.
22
-
23
- What the script takes care of, and why each part is load-bearing:
24
-
25
- - **Framing** is computed, not hand-held. A first pass fits the scene AABB to
26
- the frustum analytically; a second pass renders a 640 × 360 alpha probe,
27
- measures the bounding box of the non-transparent pixels and dollies + pans
28
- until the geometry fills 94 % of the frame. An XTS layout is a thin ribbon
29
- inside a large box, so the box fit alone leaves half the frame empty.
30
- - **Readiness** is checked in pixels. `getBoundingBox()` goes non-empty as
31
- soon as the config changes, while the GLBs are still streaming and the scene
32
- subtree is a Suspense fallback — capturing then yields a blank frame with no
33
- error. The script waits until an AABB-fit probe actually covers part of its
34
- frame, and refuses to write a shot whose final frame is under 0.5 % covered.
35
- - **Fonts are proxied through Node.** Station, area, dimension and annotation
36
- labels fetch a WOFF from jsDelivr, and the drive-status icons resolve a glyph
37
- font through `unicode-font-resolver`. A failed label font suspends the whole
38
- scene; a failed glyph resolve renders a warning icon's "!" as exploded
39
- geometry. Both are silent, so every font request is served from the Node
40
- process, which can reach the network through a proxy.
41
- - **Motion is stopped** before every capture: the mover drivers are switched
42
- off and each shot sets its mover positions explicitly, so a re-run produces
43
- the same image.
44
-
45
- ## Format
46
-
47
- 1920 × 1080 WebP at quality 0.92 on a solid `#eef0f3` ground, 4× MSAA. Full HD
48
- is what the README and the docs site actually display, and the whole strip
49
- weighs ~1.4 MB — it ships inside the npm tarball (`package.json#files`).
50
-
51
- The ground is deliberately opaque rather than the transparent alpha the old
52
- PNGs used: the AT2 housings are anodised anthracite (`#34383c`, see
53
- [`scripts/materialLibrary.mjs`](../../scripts/materialLibrary.mjs)) and
54
- disappear into a dark-mode README on a transparent canvas. `--background
55
- transparent`, `--format png`, `--width`, `--height`, `--samples`, `--quality`
56
- and `--fill` override the defaults for a one-off.
57
-
58
- ## File map
59
-
60
- Every shot is taken from one of the machine layouts in
61
- [`playground/src/configs.ts`](../../playground/src/configs.ts) — closed,
62
- buildable tracks with the infeed modules, station stops and zones where a real
63
- line would have them, rather than the calibration chains that visit every
64
- module type in one run. A unit test pins that each layout closes.
65
-
66
- | File | Demo key | Camera | Shows |
67
- | --------------------------- | ----------------- | -------------- | ------------------------------------------------- |
68
- | `01-packaging-line.webp` | `line-standard` | 3⁄4 isometric | hero — 5 m racetrack, 10 movers |
69
- | `02-standard-at.webp` | `line-standard` | steep 3⁄4 | Standard AT: AT2001 / AT2000 / AT2050 |
70
- | `03-eco-at2200.webp` | `line-eco` | 3⁄4 isometric | Eco AT2200 / AT2202 |
71
- | `04-nct-tools.webp` | `line-nct` | 3⁄4 isometric | NCT modules + AT8200 tool carriers |
72
- | `05-hygienic-ath.webp` | `line-hygienic` | 3⁄4 isometric | Hygienic ATH |
73
- | `06-hepco-gfx.webp` | `line-standard` ¹ | 3⁄4 isometric | Hepco GFX2 rail and carriage |
74
- | `07-materials-closeup.webp` | `line-standard` | close, low | PBR materials, stator packs, type plate |
75
- | `08-multi-track.webp` | `line-dual` | steep 3⁄4 | two lines placed with `trackTransform` |
76
- | `09-stations-areas.webp` | `line-standard` | steep 3⁄4 | stations, stops, ghost movers, zones |
77
- | `10-dimensions.webp` | `line-standard` | steep 3⁄4 | dimensions along the track |
78
- | `11-stator-heatmap.webp` | `line-standard` | steep 3⁄4 | green → red gradient along the centerline |
79
- | `12-collision.webp` | `line-standard` | close 3⁄4 | two movers parked at a 1 mm overlap |
80
- | `13-drive-status.webp` | `drive-status` | steep 3⁄4 | warning / error icons + tinted modules and movers |
81
- | `14-feed-segments.webp` | `line-standard` | steep 3⁄4 | one strand per infeed, wrapping the loop seam |
82
- | `15-measurements.webp` | `line-standard` | steep 3⁄4 | distance with ΔX/ΔY guides + two annotations |
83
- | `16-selection.webp` | `line-standard` | steep 3⁄4 | two modules and one mover selected |
84
- | `17-plan-view.webp` | `line-standard` | top-down ortho | `exportScreenshot({ mode: 'top-down' })` |
85
- | `18-shadows.webp` | `line-standard` | low 3⁄4 | PCF-soft shadows + IBL |
86
- | `19-perf-stress.webp` | `perf-stress` | steep 3⁄4 | 3 ovals × 250 movers = 750 |
87
-
88
- ¹ with `railSystem: 'HepcoGfx'` and a GFX2 1TC carriage.
89
-
90
- The shot list itself lives at the top of the capture script; adding an image
91
- means adding one entry there, not writing browser code.
92
-
93
- ## Doing it by hand
94
-
95
- The playground still exposes the devtools handles the script drives, so a
96
- one-off shot needs no tooling:
97
-
98
- 1. `pnpm dev`, pick the demo from the sidebar, frame the camera.
99
- 2. In the console:
100
-
101
- ```js
102
- const r = await window.__viewer.exportScreenshot({
103
- mode: 'current',
104
- width: 1920,
105
- height: 1080,
106
- samples: 4,
107
- format: 'webp',
108
- quality: 0.92,
109
- backgroundColor: '#eef0f3',
110
- });
111
- await fetch('/__save-screenshot?path=docs/screenshots/08-multi-track.webp', {
112
- method: 'POST',
113
- body: r.blob,
114
- headers: { 'content-type': 'image/webp' },
115
- });
116
- ```
117
-
118
- `window.__setDemo('<DemoKey>')` switches the sample, `window.__playground`
119
- holds the sidebar toggles the script uses (shadows, stations, dimensions,
120
- selection, feed-segment colours, …) and the dev-only `POST
121
- /__save-screenshot?path=…` middleware in
122
- [`vite.config.ts`](../../vite.config.ts) writes the body bytes to a
123
- whitelisted path under `docs/screenshots/`.
1
+ # Screenshots
2
+
3
+ The README and docs-site image strip, captured straight out of the playground
4
+ through the viewer's own `exportScreenshot()` ref method. No external CAD
5
+ render and no hand-editing: every image below was produced by automating the
6
+ playground over `window.__viewer` / `window.__playground`.
7
+
8
+ ## How to refresh
9
+
10
+ ```bash
11
+ npm i -g playwright && playwright install chromium # once
12
+ pnpm docs:capture-screenshots # the whole strip
13
+ pnpm docs:capture-screenshots --only 07,12 # by number or filename
14
+ pnpm docs:capture-screenshots --list
15
+ ```
16
+
17
+ [`scripts/capture-screenshots.mjs`](../../scripts/capture-screenshots.mjs)
18
+ boots the playground's Vite dev server in-process, drives it in headless
19
+ Chromium and writes the files here. Playwright is deliberately not a
20
+ dependency of the repo — CI never runs this script and a browser download in
21
+ every install would not pay for itself.
22
+
23
+ What the script takes care of, and why each part is load-bearing:
24
+
25
+ - **Framing** is computed, not hand-held. A first pass fits the scene AABB to
26
+ the frustum analytically; a second pass renders a 640 × 360 alpha probe,
27
+ measures the bounding box of the non-transparent pixels and dollies + pans
28
+ until the geometry fills 94 % of the frame. An XTS layout is a thin ribbon
29
+ inside a large box, so the box fit alone leaves half the frame empty.
30
+ - **Readiness** is checked in pixels. `getBoundingBox()` goes non-empty as
31
+ soon as the config changes, while the GLBs are still streaming and the scene
32
+ subtree is a Suspense fallback — capturing then yields a blank frame with no
33
+ error. The script waits until an AABB-fit probe actually covers part of its
34
+ frame, and refuses to write a shot whose final frame is under 0.5 % covered.
35
+ - **Fonts are proxied through Node.** Station, area, dimension and annotation
36
+ labels fetch a WOFF from jsDelivr, and the drive-status icons resolve a glyph
37
+ font through `unicode-font-resolver`. A failed label font suspends the whole
38
+ scene; a failed glyph resolve renders a warning icon's "!" as exploded
39
+ geometry. Both are silent, so every font request is served from the Node
40
+ process, which can reach the network through a proxy.
41
+ - **Motion is stopped** before every capture: the mover drivers are switched
42
+ off and each shot sets its mover positions explicitly, so a re-run produces
43
+ the same image.
44
+
45
+ ## Format
46
+
47
+ 1920 × 1080 WebP at quality 0.92 on a solid `#eef0f3` ground, 4× MSAA. Full HD
48
+ is what the README and the docs site actually display, and the whole strip
49
+ weighs ~1.4 MB — it ships inside the npm tarball (`package.json#files`).
50
+
51
+ The ground is deliberately opaque rather than the transparent alpha the old
52
+ PNGs used: the AT2 housings are anodised anthracite (`#34383c`, see
53
+ [`scripts/materialLibrary.mjs`](../../scripts/materialLibrary.mjs)) and
54
+ disappear into a dark-mode README on a transparent canvas. `--background
55
+ transparent`, `--format png`, `--width`, `--height`, `--samples`, `--quality`
56
+ and `--fill` override the defaults for a one-off.
57
+
58
+ ## File map
59
+
60
+ Every shot is taken from one of the machine layouts in
61
+ [`playground/src/configs.ts`](../../playground/src/configs.ts) — closed,
62
+ buildable tracks with the infeed modules, station stops and zones where a real
63
+ line would have them, rather than the calibration chains that visit every
64
+ module type in one run. A unit test pins that each layout closes.
65
+
66
+ | File | Demo key | Camera | Shows |
67
+ | --------------------------- | ----------------- | -------------- | ------------------------------------------------- |
68
+ | `01-packaging-line.webp` | `line-standard` | 3⁄4 isometric | hero — 5 m racetrack, 10 movers |
69
+ | `02-standard-at.webp` | `line-standard` | steep 3⁄4 | Standard AT: AT2001 / AT2000 / AT2050 |
70
+ | `03-eco-at2200.webp` | `line-eco` | 3⁄4 isometric | Eco AT2200 / AT2202 |
71
+ | `04-nct-tools.webp` | `line-nct` | 3⁄4 isometric | NCT modules + AT8200 tool carriers |
72
+ | `05-hygienic-ath.webp` | `line-hygienic` | 3⁄4 isometric | Hygienic ATH |
73
+ | `06-hepco-gfx.webp` | `line-standard` ¹ | 3⁄4 isometric | Hepco GFX2 rail and carriage |
74
+ | `07-materials-closeup.webp` | `line-standard` | close, low | PBR materials, stator packs, type plate |
75
+ | `08-multi-track.webp` | `line-dual` | steep 3⁄4 | two lines placed with `trackTransform` |
76
+ | `09-stations-areas.webp` | `line-standard` | steep 3⁄4 | stations, stops, ghost movers, zones |
77
+ | `10-dimensions.webp` | `line-standard` | steep 3⁄4 | dimensions along the track |
78
+ | `11-stator-heatmap.webp` | `line-standard` | steep 3⁄4 | green → red gradient along the centerline |
79
+ | `12-collision.webp` | `line-standard` | close 3⁄4 | two movers parked at a 1 mm overlap |
80
+ | `13-drive-status.webp` | `drive-status` | steep 3⁄4 | warning / error icons + tinted modules and movers |
81
+ | `14-feed-segments.webp` | `line-standard` | steep 3⁄4 | one strand per infeed, wrapping the loop seam |
82
+ | `15-measurements.webp` | `line-standard` | steep 3⁄4 | distance with ΔX/ΔY guides + two annotations |
83
+ | `16-selection.webp` | `line-standard` | steep 3⁄4 | two modules and one mover selected |
84
+ | `17-plan-view.webp` | `line-standard` | top-down ortho | `exportScreenshot({ mode: 'top-down' })` |
85
+ | `18-shadows.webp` | `line-standard` | low 3⁄4 | PCF-soft shadows + IBL |
86
+ | `19-perf-stress.webp` | `perf-stress` | steep 3⁄4 | 3 ovals × 250 movers = 750 |
87
+
88
+ ¹ with `railSystem: 'HepcoGfx'` and a GFX2 1TC carriage.
89
+
90
+ The shot list itself lives at the top of the capture script; adding an image
91
+ means adding one entry there, not writing browser code.
92
+
93
+ ## Doing it by hand
94
+
95
+ The playground still exposes the devtools handles the script drives, so a
96
+ one-off shot needs no tooling:
97
+
98
+ 1. `pnpm dev`, pick the demo from the sidebar, frame the camera.
99
+ 2. In the console:
100
+
101
+ ```js
102
+ const r = await window.__viewer.exportScreenshot({
103
+ mode: 'current',
104
+ width: 1920,
105
+ height: 1080,
106
+ samples: 4,
107
+ format: 'webp',
108
+ quality: 0.92,
109
+ backgroundColor: '#eef0f3',
110
+ });
111
+ await fetch('/__save-screenshot?path=docs/screenshots/08-multi-track.webp', {
112
+ method: 'POST',
113
+ body: r.blob,
114
+ headers: { 'content-type': 'image/webp' },
115
+ });
116
+ ```
117
+
118
+ `window.__setDemo('<DemoKey>')` switches the sample, `window.__playground`
119
+ holds the sidebar toggles the script uses (shadows, stations, dimensions,
120
+ selection, feed-segment colours, …) and the dev-only `POST
121
+ /__save-screenshot?path=…` middleware in
122
+ [`vite.config.ts`](../../vite.config.ts) writes the body bytes to a
123
+ whitelisted path under `docs/screenshots/`.