NURLNURL registrynurl-lang.org →

← ply

ply 0.3.5 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 Arc: each route's closure holds a share, and the app's last owner releases it.

: VwState { ( Arc VwStateImpl ) a }

@ VwState_share VwState h → VwState

@ 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.

@ 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 → i

Serve path until interrupted. Returns a process exit code.


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  // closed once finished (file_is_open says so)
    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 { ( Locked PlyWImpl ) st }

@ PlyW_share PlyW h → PlyW

@ PlyW_drop sink PlyW h → v

@ ply_create s path i ascii s comment → !PlyW String

Open 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 → b

Write 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 → v

Push one vertex. Colour components are clamped to 0..255.

@ ply_finish PlyW w__h → b

Flush, rewind to the count field, overwrite it in place, close. The buffers go with the handle's last owner.


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