GENERATED by tools/embed_viewer.py from views/viewer.html — do not edit. The viewer page, embedded so an installed binary needs no views/ directory on disk (--page FILE overrides). Chunked because released nurlc stack-overflows on string literals past ~48 KB.
@ viewer_html_chunks → i@ viewer_html_chunk i k → spackages/ply/src/view.nu — look at the cloud.
Serves two things on localhost: the viewer page, and the bytes of a PLY file.
GET / the viewer, one self-contained WebGL document GET /cloud the PLY, verbatim
The page is compiled into the binary (viewer_html_data.nu, generated by tools/embed_viewer.py) because nurlpkg install ships a binary and nothing else — a views/ path next to the executable can never resolve for an installed tool. A page_override file path replaces it while editing the page.
The cloud is read into memory once and handed out on every request. A large scene is ~100+ MB, which is a lot to hold but exactly what the browser is about to hold anyway, and it means the file can be replaced underneath a running viewer without the server noticing a half-written one.
( vw_serve path host port page_override quiet tls ) → i exit code
host is the BIND address: 127.0.0.1 stays private to the machine, 0.0.0.0 serves every interface, a specific address (192.168.1.30) serves exactly that adapter. tls != 0 generates a fresh self-signed P-256 certificate at startup (std/x509_gen) and serves HTTPS — the browser warns once, but the bytes cross the LAN encrypted.
: VwStateImpl: VwStateImpl {
String page
String name
( Vec u ) cloud
}
What the handlers serve, set up once. A handle over an rcbox: each route's closure holds a share, and the app's last owner releases it.
: VwState { s ctl }@ VwState_share VwState h → VwState@ VwState_drop sink VwState h → v@ vw_page s override → StringThe page as compiled in. --page FILE reads from disk instead, which is the only way to iterate on the viewer without rebuilding.
@ h_vw_index VwState h HttpRequest req Params p → HttpResponse@ h_vw_cloud VwState h HttpRequest req Params p → HttpResponse@ vw_serve s path s host i port s page_override i quiet i tls → iServe path until interrupted. Returns a process exit code.
packages/ply/src/ply.nu — a streaming PLY point-cloud writer.
The property layout is the one every viewer reads and the bundled viewer parses: x, y, z as float32 and red, green, blue as uchar — 15 bytes a vertex in binary_little_endian, one line a vertex in ascii.
The vertex count is usually not known until the last point is pushed, so the header is written with a fixed-width zero-padded placeholder and patched in place at the end, rather than holding the whole cloud in memory:
( ply_create path ascii comment ) → !PlyW String ( ply_vertex w x y z r g b ) → v buffered; flushes itself ( ply_count w ) → i vertices pushed so far ( ply_finish w ) → b flush, patch the count, close
A PlyW is a handle: every copy is the same writer, and nothing is released by hand. ply_finish closes the file either way; its F means the file could not be finished and should not be trusted. A writer whose last owner lets go of it unfinished is finished then, as ply_finish would (a buffered writer flushes when dropped).
Writes are buffered ~4 KB at a time — a vertex is 15 bytes, and a write per vertex would make the file the slowest part of a pipeline that computes millions of them.
: i PLY_COUNT_WIDTH 12The count field is fixed-width so the header keeps its byte length when the real number is patched in. 12 digits holds any cloud a filesystem holds.
: i PLY_FLUSH_AT 4096: PlyWImpl: PlyWImpl {
File f // raw 0 once finished
i n
i ascii
i cnt_off // where the count field starts, for the patch at the end
String abuf
( Vec u ) bbuf
}
% Drop PlyWImpl { @ drop PlyWImpl w → v { : b _ok ( __ply_close . w f . w ascii . w abuf . w bbuf . w cnt_off . w n ) } }Finish a writer nobody finished: flush, patch the count, close.
: PlyW { s ctl }@ PlyW_share PlyW h → PlyW@ PlyW_drop sink PlyW h → v@ ply_create s path i ascii s comment → !PlyW StringOpen path and write the header. comment goes in as one comment line ("" for none) — a place for provenance, not for structure.
@ ply_count PlyW w__h → i@ ply_flush PlyW w__h → bWrite out whatever is buffered. Idempotent; ply_vertex calls it on its own past PLY_FLUSH_AT bytes.
@ ply_vertex PlyW w__h f x f y f z i r i g i b → vPush one vertex. Colour components are clamped to 0..255.
@ ply_finish PlyW w__h → bFlush, rewind to the count field, overwrite it in place, close. The buffers go with the handle's last owner.
packages/ply/src/main.nu — the ply CLI.
ply view <cloud.ply> [--port n] [--host a] [--tls] [--page f] ply info <cloud.ply> print the header ply <cloud.ply> shorthand for view
The viewer serves one self-contained WebGL page on localhost and the cloud next to it; nothing is installed, nothing leaves the machine.
@ main → i