NURLNURL registrynurl-lang.org →

← ply

ply 0.3.2 API

viewer_html_data.nu

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.

API

@ viewer_html_chunks → i

@ viewer_html_chunk i k → s


view.nu

packages/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.

API

: 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_drop sink VwState h → v

@ vw_page s override → String

The page as compiled in. --page FILE reads from disk instead, which is the only way to iterate on the viewer without rebuilding.


ply.nu

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.

API

: i PLY_COUNT_WIDTH 12

The 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_drop sink PlyW h → v


main.nu

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.

API

@ main → i