quiltwright 0.1.0__tar.gz
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.
- quiltwright-0.1.0/LICENSE +29 -0
- quiltwright-0.1.0/PKG-INFO +249 -0
- quiltwright-0.1.0/README.md +219 -0
- quiltwright-0.1.0/pyproject.toml +112 -0
- quiltwright-0.1.0/src/quiltwright/__init__.py +95 -0
- quiltwright-0.1.0/src/quiltwright/hld.py +364 -0
- quiltwright-0.1.0/src/quiltwright/lfd.py +780 -0
- quiltwright-0.1.0/src/quiltwright/povray.py +394 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Eric G. Suchanek, PhD
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: quiltwright
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Holographic output for Looking Glass displays: off-axis multi-view quilts from PyVista or POV-Ray scenes
|
|
5
|
+
License-Expression: BSD-3-Clause
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: looking-glass,light-field,holographic-display,quilt,povray,pyvista,stereoscopy
|
|
8
|
+
Author: Eric G. Suchanek, PhD
|
|
9
|
+
Author-email: suchanek@flux-frontiers.com
|
|
10
|
+
Requires-Python: >=3.12,<3.13
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Provides-Extra: video
|
|
19
|
+
Provides-Extra: viz
|
|
20
|
+
Requires-Dist: imageio-ffmpeg (>=0.4) ; extra == "video"
|
|
21
|
+
Requires-Dist: numpy (>=1.26)
|
|
22
|
+
Requires-Dist: pillow (>=10.0)
|
|
23
|
+
Requires-Dist: pyvista (>=0.44) ; extra == "viz"
|
|
24
|
+
Project-URL: Documentation, https://github.com/suchanek/quiltwright/tree/main/docs
|
|
25
|
+
Project-URL: Homepage, https://github.com/suchanek/quiltwright
|
|
26
|
+
Project-URL: Issues, https://github.com/suchanek/quiltwright/issues
|
|
27
|
+
Project-URL: Repository, https://github.com/suchanek/quiltwright
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# Quiltwright
|
|
31
|
+
|
|
32
|
+
[](https://www.python.org/)
|
|
33
|
+
[](LICENSE)
|
|
34
|
+
[](https://pypi.org/project/quiltwright/)
|
|
35
|
+
[](https://github.com/suchanek/quiltwright/releases)
|
|
36
|
+
[](https://github.com/suchanek/quiltwright/actions/workflows/tests.yml)
|
|
37
|
+
[](https://zenodo.org/badge/latestdoi/1323414722)
|
|
38
|
+
|
|
39
|
+
**Holographic output for Looking Glass displays.**
|
|
40
|
+
|
|
41
|
+
*Eric G. Suchanek, PhD — Flux-Frontiers*
|
|
42
|
+
|
|
43
|
+
Quiltwright turns a rendered scene into a **quilt** — the tiled multi-view
|
|
44
|
+
image that [Looking Glass](https://lookingglassfactory.com) lenticular
|
|
45
|
+
light-field displays fuse into real, glasses-free depth.
|
|
46
|
+
It renders from [PyVista](https://pyvista.org)/VTK scenes or from
|
|
47
|
+
[POV-Ray](https://www.povray.org) ray-traced scenes, manages the depth budget
|
|
48
|
+
that decides whether a hologram fuses or ghosts, and drives Looking Glass
|
|
49
|
+
Bridge directly.
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
*Centre view of a 48-view quilt, ray-traced from a POV-Ray scene first
|
|
54
|
+
composed in 1994.*
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Why this exists
|
|
59
|
+
|
|
60
|
+
The hard part of light-field rendering is not tiling images into a grid. It is
|
|
61
|
+
that **each view must use an off-axis (asymmetric-frustum) projection** — the
|
|
62
|
+
camera slides sideways while continuing to face the same direction, and the
|
|
63
|
+
image plane is sheared back onto the original view axis.
|
|
64
|
+
|
|
65
|
+
The intuitive alternative is to swivel each camera to keep the subject centred.
|
|
66
|
+
That is "toe-in", and it introduces vertical parallax and keystone distortion,
|
|
67
|
+
so the display cannot fuse the views. You get ghosting instead of depth. This
|
|
68
|
+
is the single most common way light-field renders go wrong, and it produces
|
|
69
|
+
output that looks plausible in any individual frame.
|
|
70
|
+
|
|
71
|
+
Quiltwright does the off-axis projection correctly in both backends, and gives
|
|
72
|
+
you the arithmetic to know in advance whether a scene will fuse.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Install
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install quiltwright # core: quilt geometry + Bridge control
|
|
80
|
+
pip install "quiltwright[viz]" # + PyVista/VTK rendering backend
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The POV-Ray backend needs a `povray` binary on `PATH` rather than a Python
|
|
84
|
+
package:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
brew install povray # macOS
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
For the complete stack — renderers, ffmpeg, Looking Glass Bridge, pdb2pov —
|
|
91
|
+
see the [installation guide](docs/install.md).
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Quick start
|
|
96
|
+
|
|
97
|
+
### From a PyVista scene
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import pyvista as pv
|
|
101
|
+
from quiltwright import QUILT_PRESETS, render_quilt, save_quilt
|
|
102
|
+
|
|
103
|
+
p = pv.Plotter(off_screen=True)
|
|
104
|
+
p.add_mesh(pv.ParametricTorus())
|
|
105
|
+
|
|
106
|
+
spec = QUILT_PRESETS["portrait"]
|
|
107
|
+
save_quilt(render_quilt(p, spec), "torus", spec) # -> torus_qs8x6a0.75.png
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### From a POV-Ray scene
|
|
111
|
+
|
|
112
|
+
The scene file is never modified — each view wraps it with `#include` and
|
|
113
|
+
appends one camera.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from quiltwright import QUILT_PRESETS, PovCamera, render_pov_quilt, save_quilt
|
|
117
|
+
|
|
118
|
+
camera = PovCamera(location=(15, 20, 6), look_at=(44, 19.2, 45.1), fov=53.13)
|
|
119
|
+
spec = QUILT_PRESETS["16-landscape"]
|
|
120
|
+
quilt = render_pov_quilt("pov-scenes/museum/museum.pov", spec, camera,
|
|
121
|
+
include_paths=["pov-scenes/myinclude", "pov-scenes"])
|
|
122
|
+
save_quilt(quilt, "museum", spec)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The museum scene above ships in [pov-scenes/](pov-scenes/), and
|
|
126
|
+
[scripts/render_museum_hologram.py](scripts/render_museum_hologram.py) renders
|
|
127
|
+
it end-to-end with a measured depth budget — it is the worked case study in
|
|
128
|
+
[docs/povray.md](docs/povray.md).
|
|
129
|
+
|
|
130
|
+
### Send it to the display
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from quiltwright import cast_quilt, pause_quilt, resume_quilt, stop_quilt
|
|
134
|
+
|
|
135
|
+
cast_quilt("museum_qs8x6a1.77778.png", spec) # needs Looking Glass Bridge >= 2.2
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Saved filenames carry the `_qs<cols>x<rows>a<aspect>` suffix that Looking Glass
|
|
139
|
+
Studio and Bridge parse, so playback settings are detected automatically.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## The depth budget
|
|
144
|
+
|
|
145
|
+
Whether a hologram fuses comes down to **adjacent-view disparity**: how far a
|
|
146
|
+
feature moves between neighbouring views. Roughly 4–5 px is the practical
|
|
147
|
+
ceiling; past ~8 px, hard edges ghost.
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from quiltwright import QUILT_PRESETS, focal_distance_for_range, view_disparity
|
|
151
|
+
|
|
152
|
+
# Put the focal plane where near and far content are equally penalised.
|
|
153
|
+
focal = focal_distance_for_range(near=32, far=100) # harmonic mean, not midpoint
|
|
154
|
+
view_disparity(QUILT_PRESETS["16-landscape"], fov=53.13,
|
|
155
|
+
focal_distance=focal, depth=32) # -> px between adjacent views
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Three results worth knowing before you frame a shot:
|
|
159
|
+
|
|
160
|
+
- Content **at** the focal plane has zero disparity — it is welded to the glass.
|
|
161
|
+
- The focal plane belongs at the **harmonic mean** of the depth range, not the
|
|
162
|
+
midpoint. Disparity is asymmetric in depth, and near content is the expensive
|
|
163
|
+
side.
|
|
164
|
+
- A **narrower field of view increases** disparity. Zooming in magnifies the
|
|
165
|
+
scene and the parallax with it. The widely repeated "use ~14° FOV" advice is
|
|
166
|
+
specific to object-centric scenes; applied to an interior it makes ghosting
|
|
167
|
+
worse.
|
|
168
|
+
|
|
169
|
+
For interiors there is a fourth trap that no arithmetic will warn you about:
|
|
170
|
+
the camera sweep physically travels `focal_distance × tan(cone/2)` sideways,
|
|
171
|
+
and in a room that path can run through a wall. See
|
|
172
|
+
[docs/povray.md](docs/povray.md#3-sweep-clearance--the-constraint-peculiar-to-interiors).
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Supported devices
|
|
177
|
+
|
|
178
|
+
`QUILT_PRESETS` carries the official quilt settings for Portrait, Go, and the
|
|
179
|
+
16″/27″/32″/65″ panels in both orientations. The 16″ Gen3 Landscape entry is
|
|
180
|
+
verified against what Bridge reports for real hardware.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from quiltwright import QUILT_PRESETS
|
|
184
|
+
QUILT_PRESETS["16-landscape"] # 8x6 views, 7680x4320, aspect 1.7778
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Documentation
|
|
190
|
+
|
|
191
|
+
| Document | Contents |
|
|
192
|
+
|----------|----------|
|
|
193
|
+
| [docs/install.md](docs/install.md) | Installing the full stack: package extras, POV-Ray, ffmpeg, Bridge, pdb2pov |
|
|
194
|
+
| [docs/lfd.md](docs/lfd.md) | Light-field output, Bridge/Studio setup, device presets, the PyVista path |
|
|
195
|
+
| [docs/povray.md](docs/povray.md) | The POV-Ray backend: off-axis camera derivation, depth budget, sweep clearance, a worked case study |
|
|
196
|
+
| [docs/pdb2pov.md](docs/pdb2pov.md) | Rendering molecular structures from PDB files as holograms |
|
|
197
|
+
| [docs/hld.md](docs/hld.md) | Hololuminescent Displays, which play ordinary 2-D video rather than quilts |
|
|
198
|
+
|
|
199
|
+
> **Two different technologies.** Looking Glass sells a light-field line
|
|
200
|
+
> (Portrait, Go, 16″/27″/32″/65″ LFD) that consumes quilts, and a
|
|
201
|
+
> Hololuminescent line (16″/27″/86″ HLD) that plays ordinary video behind a
|
|
202
|
+
> fixed holographic optic. `quiltwright.lfd` targets the first;
|
|
203
|
+
> `quiltwright.hld` targets the second.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Testing
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
pip install -e ".[viz]" && pip install pytest
|
|
211
|
+
pytest
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Rendering tests skip cleanly on machines with no OpenGL stack, and the POV-Ray
|
|
215
|
+
tests skip when no `povray` binary is present. Under a headless CI runner, use
|
|
216
|
+
`xvfb-run -a pytest` to exercise them.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Related
|
|
221
|
+
|
|
222
|
+
- [WaveRider](https://github.com/Flux-Frontiers/waverider) — manifold-aware
|
|
223
|
+
geometric ML; its voxel visualiser renders through Quiltwright.
|
|
224
|
+
- [proteusPy](https://github.com/suchanek/proteusPy) — protein disulfide bond
|
|
225
|
+
analysis and rendering.
|
|
226
|
+
- [pdb2pov](https://github.com/suchanek/pdb2pov) — PDB to POV-Ray converter,
|
|
227
|
+
1993, still feeds this pipeline.
|
|
228
|
+
|
|
229
|
+
## Citation
|
|
230
|
+
|
|
231
|
+
If you use Quiltwright in your work, please cite it. Citation metadata is in
|
|
232
|
+
[CITATION.cff](CITATION.cff); GitHub's "Cite this repository" button generates
|
|
233
|
+
BibTeX/APA from it, and the DOI badge above resolves to the archived release
|
|
234
|
+
on Zenodo.
|
|
235
|
+
|
|
236
|
+
```bibtex
|
|
237
|
+
@software{suchanek_quiltwright,
|
|
238
|
+
author = {Suchanek, Eric G.},
|
|
239
|
+
title = {Quiltwright: Holographic Output for Looking Glass Displays},
|
|
240
|
+
url = {https://github.com/suchanek/quiltwright},
|
|
241
|
+
version = {0.1.0},
|
|
242
|
+
year = {2026}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
BSD 3-Clause. See [LICENSE](LICENSE).
|
|
249
|
+
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Quiltwright
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://pypi.org/project/quiltwright/)
|
|
6
|
+
[](https://github.com/suchanek/quiltwright/releases)
|
|
7
|
+
[](https://github.com/suchanek/quiltwright/actions/workflows/tests.yml)
|
|
8
|
+
[](https://zenodo.org/badge/latestdoi/1323414722)
|
|
9
|
+
|
|
10
|
+
**Holographic output for Looking Glass displays.**
|
|
11
|
+
|
|
12
|
+
*Eric G. Suchanek, PhD — Flux-Frontiers*
|
|
13
|
+
|
|
14
|
+
Quiltwright turns a rendered scene into a **quilt** — the tiled multi-view
|
|
15
|
+
image that [Looking Glass](https://lookingglassfactory.com) lenticular
|
|
16
|
+
light-field displays fuse into real, glasses-free depth.
|
|
17
|
+
It renders from [PyVista](https://pyvista.org)/VTK scenes or from
|
|
18
|
+
[POV-Ray](https://www.povray.org) ray-traced scenes, manages the depth budget
|
|
19
|
+
that decides whether a hologram fuses or ghosts, and drives Looking Glass
|
|
20
|
+
Bridge directly.
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
*Centre view of a 48-view quilt, ray-traced from a POV-Ray scene first
|
|
25
|
+
composed in 1994.*
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Why this exists
|
|
30
|
+
|
|
31
|
+
The hard part of light-field rendering is not tiling images into a grid. It is
|
|
32
|
+
that **each view must use an off-axis (asymmetric-frustum) projection** — the
|
|
33
|
+
camera slides sideways while continuing to face the same direction, and the
|
|
34
|
+
image plane is sheared back onto the original view axis.
|
|
35
|
+
|
|
36
|
+
The intuitive alternative is to swivel each camera to keep the subject centred.
|
|
37
|
+
That is "toe-in", and it introduces vertical parallax and keystone distortion,
|
|
38
|
+
so the display cannot fuse the views. You get ghosting instead of depth. This
|
|
39
|
+
is the single most common way light-field renders go wrong, and it produces
|
|
40
|
+
output that looks plausible in any individual frame.
|
|
41
|
+
|
|
42
|
+
Quiltwright does the off-axis projection correctly in both backends, and gives
|
|
43
|
+
you the arithmetic to know in advance whether a scene will fuse.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install quiltwright # core: quilt geometry + Bridge control
|
|
51
|
+
pip install "quiltwright[viz]" # + PyVista/VTK rendering backend
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The POV-Ray backend needs a `povray` binary on `PATH` rather than a Python
|
|
55
|
+
package:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
brew install povray # macOS
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
For the complete stack — renderers, ffmpeg, Looking Glass Bridge, pdb2pov —
|
|
62
|
+
see the [installation guide](docs/install.md).
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Quick start
|
|
67
|
+
|
|
68
|
+
### From a PyVista scene
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import pyvista as pv
|
|
72
|
+
from quiltwright import QUILT_PRESETS, render_quilt, save_quilt
|
|
73
|
+
|
|
74
|
+
p = pv.Plotter(off_screen=True)
|
|
75
|
+
p.add_mesh(pv.ParametricTorus())
|
|
76
|
+
|
|
77
|
+
spec = QUILT_PRESETS["portrait"]
|
|
78
|
+
save_quilt(render_quilt(p, spec), "torus", spec) # -> torus_qs8x6a0.75.png
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### From a POV-Ray scene
|
|
82
|
+
|
|
83
|
+
The scene file is never modified — each view wraps it with `#include` and
|
|
84
|
+
appends one camera.
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from quiltwright import QUILT_PRESETS, PovCamera, render_pov_quilt, save_quilt
|
|
88
|
+
|
|
89
|
+
camera = PovCamera(location=(15, 20, 6), look_at=(44, 19.2, 45.1), fov=53.13)
|
|
90
|
+
spec = QUILT_PRESETS["16-landscape"]
|
|
91
|
+
quilt = render_pov_quilt("pov-scenes/museum/museum.pov", spec, camera,
|
|
92
|
+
include_paths=["pov-scenes/myinclude", "pov-scenes"])
|
|
93
|
+
save_quilt(quilt, "museum", spec)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The museum scene above ships in [pov-scenes/](pov-scenes/), and
|
|
97
|
+
[scripts/render_museum_hologram.py](scripts/render_museum_hologram.py) renders
|
|
98
|
+
it end-to-end with a measured depth budget — it is the worked case study in
|
|
99
|
+
[docs/povray.md](docs/povray.md).
|
|
100
|
+
|
|
101
|
+
### Send it to the display
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from quiltwright import cast_quilt, pause_quilt, resume_quilt, stop_quilt
|
|
105
|
+
|
|
106
|
+
cast_quilt("museum_qs8x6a1.77778.png", spec) # needs Looking Glass Bridge >= 2.2
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Saved filenames carry the `_qs<cols>x<rows>a<aspect>` suffix that Looking Glass
|
|
110
|
+
Studio and Bridge parse, so playback settings are detected automatically.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## The depth budget
|
|
115
|
+
|
|
116
|
+
Whether a hologram fuses comes down to **adjacent-view disparity**: how far a
|
|
117
|
+
feature moves between neighbouring views. Roughly 4–5 px is the practical
|
|
118
|
+
ceiling; past ~8 px, hard edges ghost.
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from quiltwright import QUILT_PRESETS, focal_distance_for_range, view_disparity
|
|
122
|
+
|
|
123
|
+
# Put the focal plane where near and far content are equally penalised.
|
|
124
|
+
focal = focal_distance_for_range(near=32, far=100) # harmonic mean, not midpoint
|
|
125
|
+
view_disparity(QUILT_PRESETS["16-landscape"], fov=53.13,
|
|
126
|
+
focal_distance=focal, depth=32) # -> px between adjacent views
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Three results worth knowing before you frame a shot:
|
|
130
|
+
|
|
131
|
+
- Content **at** the focal plane has zero disparity — it is welded to the glass.
|
|
132
|
+
- The focal plane belongs at the **harmonic mean** of the depth range, not the
|
|
133
|
+
midpoint. Disparity is asymmetric in depth, and near content is the expensive
|
|
134
|
+
side.
|
|
135
|
+
- A **narrower field of view increases** disparity. Zooming in magnifies the
|
|
136
|
+
scene and the parallax with it. The widely repeated "use ~14° FOV" advice is
|
|
137
|
+
specific to object-centric scenes; applied to an interior it makes ghosting
|
|
138
|
+
worse.
|
|
139
|
+
|
|
140
|
+
For interiors there is a fourth trap that no arithmetic will warn you about:
|
|
141
|
+
the camera sweep physically travels `focal_distance × tan(cone/2)` sideways,
|
|
142
|
+
and in a room that path can run through a wall. See
|
|
143
|
+
[docs/povray.md](docs/povray.md#3-sweep-clearance--the-constraint-peculiar-to-interiors).
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Supported devices
|
|
148
|
+
|
|
149
|
+
`QUILT_PRESETS` carries the official quilt settings for Portrait, Go, and the
|
|
150
|
+
16″/27″/32″/65″ panels in both orientations. The 16″ Gen3 Landscape entry is
|
|
151
|
+
verified against what Bridge reports for real hardware.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from quiltwright import QUILT_PRESETS
|
|
155
|
+
QUILT_PRESETS["16-landscape"] # 8x6 views, 7680x4320, aspect 1.7778
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Documentation
|
|
161
|
+
|
|
162
|
+
| Document | Contents |
|
|
163
|
+
|----------|----------|
|
|
164
|
+
| [docs/install.md](docs/install.md) | Installing the full stack: package extras, POV-Ray, ffmpeg, Bridge, pdb2pov |
|
|
165
|
+
| [docs/lfd.md](docs/lfd.md) | Light-field output, Bridge/Studio setup, device presets, the PyVista path |
|
|
166
|
+
| [docs/povray.md](docs/povray.md) | The POV-Ray backend: off-axis camera derivation, depth budget, sweep clearance, a worked case study |
|
|
167
|
+
| [docs/pdb2pov.md](docs/pdb2pov.md) | Rendering molecular structures from PDB files as holograms |
|
|
168
|
+
| [docs/hld.md](docs/hld.md) | Hololuminescent Displays, which play ordinary 2-D video rather than quilts |
|
|
169
|
+
|
|
170
|
+
> **Two different technologies.** Looking Glass sells a light-field line
|
|
171
|
+
> (Portrait, Go, 16″/27″/32″/65″ LFD) that consumes quilts, and a
|
|
172
|
+
> Hololuminescent line (16″/27″/86″ HLD) that plays ordinary video behind a
|
|
173
|
+
> fixed holographic optic. `quiltwright.lfd` targets the first;
|
|
174
|
+
> `quiltwright.hld` targets the second.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Testing
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
pip install -e ".[viz]" && pip install pytest
|
|
182
|
+
pytest
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Rendering tests skip cleanly on machines with no OpenGL stack, and the POV-Ray
|
|
186
|
+
tests skip when no `povray` binary is present. Under a headless CI runner, use
|
|
187
|
+
`xvfb-run -a pytest` to exercise them.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Related
|
|
192
|
+
|
|
193
|
+
- [WaveRider](https://github.com/Flux-Frontiers/waverider) — manifold-aware
|
|
194
|
+
geometric ML; its voxel visualiser renders through Quiltwright.
|
|
195
|
+
- [proteusPy](https://github.com/suchanek/proteusPy) — protein disulfide bond
|
|
196
|
+
analysis and rendering.
|
|
197
|
+
- [pdb2pov](https://github.com/suchanek/pdb2pov) — PDB to POV-Ray converter,
|
|
198
|
+
1993, still feeds this pipeline.
|
|
199
|
+
|
|
200
|
+
## Citation
|
|
201
|
+
|
|
202
|
+
If you use Quiltwright in your work, please cite it. Citation metadata is in
|
|
203
|
+
[CITATION.cff](CITATION.cff); GitHub's "Cite this repository" button generates
|
|
204
|
+
BibTeX/APA from it, and the DOI badge above resolves to the archived release
|
|
205
|
+
on Zenodo.
|
|
206
|
+
|
|
207
|
+
```bibtex
|
|
208
|
+
@software{suchanek_quiltwright,
|
|
209
|
+
author = {Suchanek, Eric G.},
|
|
210
|
+
title = {Quiltwright: Holographic Output for Looking Glass Displays},
|
|
211
|
+
url = {https://github.com/suchanek/quiltwright},
|
|
212
|
+
version = {0.1.0},
|
|
213
|
+
year = {2026}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
BSD 3-Clause. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# pyproject.toml — quiltwright package configuration
|
|
2
|
+
#
|
|
3
|
+
# Author: Eric G. Suchanek, PhD
|
|
4
|
+
#
|
|
5
|
+
# Quick install (poetry)
|
|
6
|
+
# ----------------------
|
|
7
|
+
# poetry install core (quilt geometry + Bridge control)
|
|
8
|
+
# poetry install --with viz + pyvista/VTK rendering backend
|
|
9
|
+
# poetry install --with dev + test and lint tooling
|
|
10
|
+
#
|
|
11
|
+
# Quick install (pip)
|
|
12
|
+
# -------------------
|
|
13
|
+
# pip install -e . core
|
|
14
|
+
# pip install -e ".[viz]" + pyvista rendering backend
|
|
15
|
+
#
|
|
16
|
+
# Run tests
|
|
17
|
+
# ---------
|
|
18
|
+
# pytest uses [tool.pytest.ini_options]
|
|
19
|
+
#
|
|
20
|
+
# The POV-Ray backend needs a `povray` binary on PATH (brew install povray),
|
|
21
|
+
# not a Python dependency.
|
|
22
|
+
|
|
23
|
+
[build-system]
|
|
24
|
+
requires = ["poetry-core>=2.0.0"]
|
|
25
|
+
build-backend = "poetry.core.masonry.api"
|
|
26
|
+
|
|
27
|
+
[tool.poetry]
|
|
28
|
+
packages = [{ include = "quiltwright", from = "src" }]
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# PEP 621 project metadata
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
[project]
|
|
34
|
+
name = "quiltwright"
|
|
35
|
+
version = "0.1.0"
|
|
36
|
+
description = "Holographic output for Looking Glass displays: off-axis multi-view quilts from PyVista or POV-Ray scenes"
|
|
37
|
+
readme = "README.md"
|
|
38
|
+
license = "BSD-3-Clause"
|
|
39
|
+
license-files = ["LICENSE"]
|
|
40
|
+
authors = [{ name = "Eric G. Suchanek, PhD", email = "suchanek@flux-frontiers.com" }]
|
|
41
|
+
keywords = [
|
|
42
|
+
"looking-glass",
|
|
43
|
+
"light-field",
|
|
44
|
+
"holographic-display",
|
|
45
|
+
"quilt",
|
|
46
|
+
"povray",
|
|
47
|
+
"pyvista",
|
|
48
|
+
"stereoscopy",
|
|
49
|
+
]
|
|
50
|
+
classifiers = [
|
|
51
|
+
"Development Status :: 4 - Beta",
|
|
52
|
+
"Intended Audience :: Science/Research",
|
|
53
|
+
"Intended Audience :: Developers",
|
|
54
|
+
"Topic :: Multimedia :: Graphics :: 3D Rendering",
|
|
55
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
56
|
+
"Programming Language :: Python :: 3",
|
|
57
|
+
"Programming Language :: Python :: 3.12",
|
|
58
|
+
]
|
|
59
|
+
requires-python = ">=3.12,<3.13"
|
|
60
|
+
|
|
61
|
+
# Core stays deliberately small: quilt geometry, tiling, filenames and the
|
|
62
|
+
# Bridge HTTP client need nothing but numpy and pillow. The renderers are
|
|
63
|
+
# extras so a machine that only casts pre-rendered quilts stays lean.
|
|
64
|
+
dependencies = ["numpy>=1.26", "pillow>=10.0"]
|
|
65
|
+
|
|
66
|
+
[project.optional-dependencies]
|
|
67
|
+
viz = ["pyvista>=0.44"]
|
|
68
|
+
video = ["imageio-ffmpeg>=0.4"]
|
|
69
|
+
|
|
70
|
+
[project.urls]
|
|
71
|
+
Homepage = "https://github.com/suchanek/quiltwright"
|
|
72
|
+
Repository = "https://github.com/suchanek/quiltwright"
|
|
73
|
+
Documentation = "https://github.com/suchanek/quiltwright/tree/main/docs"
|
|
74
|
+
Issues = "https://github.com/suchanek/quiltwright/issues"
|
|
75
|
+
|
|
76
|
+
# ---------------------------------------------------------------------------
|
|
77
|
+
# Dependency groups (poetry)
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
[tool.poetry.group.viz]
|
|
80
|
+
optional = true
|
|
81
|
+
|
|
82
|
+
[tool.poetry.group.viz.dependencies]
|
|
83
|
+
pyvista = ">=0.44"
|
|
84
|
+
imageio-ffmpeg = "*"
|
|
85
|
+
|
|
86
|
+
[tool.poetry.group.dev]
|
|
87
|
+
optional = true
|
|
88
|
+
|
|
89
|
+
[tool.poetry.group.dev.dependencies]
|
|
90
|
+
pytest = ">=8.0"
|
|
91
|
+
pytest-cov = ">=5.0"
|
|
92
|
+
ruff = ">=0.6"
|
|
93
|
+
|
|
94
|
+
# ---------------------------------------------------------------------------
|
|
95
|
+
# Tooling
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
[tool.pytest.ini_options]
|
|
98
|
+
testpaths = ["tests"]
|
|
99
|
+
pythonpath = ["tests"]
|
|
100
|
+
addopts = "-q"
|
|
101
|
+
markers = ["slow: marks tests as slow (deselect with '-m \"not slow\"')"]
|
|
102
|
+
|
|
103
|
+
[tool.ruff]
|
|
104
|
+
line-length = 100
|
|
105
|
+
target-version = "py312"
|
|
106
|
+
|
|
107
|
+
[tool.ruff.lint]
|
|
108
|
+
select = ["E", "F", "I", "W", "UP", "B"]
|
|
109
|
+
ignore = ["E501"]
|
|
110
|
+
|
|
111
|
+
[tool.ruff.lint.per-file-ignores]
|
|
112
|
+
"tests/*" = ["B011"]
|