Derives a camera that frames a splat.
Every viewer and thumbnail renderer needs a starting viewpoint, and no
converter supplies one — splat-transform's default camera is a fixed
2,1,-2 looking at the origin regardless of what the scene actually
contains, which points at nothing for most real captures.
Percentiles, not extremes
Bounds come from percentiles rather than min/max. A real capture has stray splats — reconstruction noise flung far from the scene — and a single one is enough to inflate the bounding box by an order of magnitude and push the camera so far back the room becomes a dot. Trimming the outer 1% at each end costs nothing and is the difference between a usable framing and a wasted render.
Note that the trim is a whole number of splats: at the 1% default it removes nothing at all until there are at least 100 of them. That is the right behaviour — on tiny inputs every point is signal — but it does mean small fixtures exercise the min/max path, not the percentile one.
Rows, not columns
Non-finite values are filtered by row: if any of a splat's x, y or z is
NaN, the whole splat is dropped. Filtering each axis independently would
disagree with --filter-nan, which drops whole splats — so a row of
{NaN, 1.0e9, 0.0} would contribute its enormous y to the bounds while
being absent from the converted file, and the camera would frame geometry
that is not there.
Summary
Types
Axis-aligned bounds, as {min, max} per axis.
A camera pose, in the scene's own coordinate space.
Functions
Computes percentile bounds from packed little-endian float32 columns.
Derives a camera that frames the given bounds.
Derives a camera directly from packed columns.
Formats a camera as splat-transform's x,y,z argument form.
Types
Functions
@spec bounds( %{required(String.t()) => binary()}, keyword() ) :: {:ok, bounds()} | {:error, SplatTools.Error.t()}
Computes percentile bounds from packed little-endian float32 columns.
Takes the output shape of Ply.columns!/3 — one binary per property — so no
intermediate list of boxed floats is built for the whole file.
Options
:percentile— fraction trimmed from each end, default0.01.0.0gives true min/max. Must be in[0.0, 0.5): at0.5every value is trimmed, and above it the trim crosses over and returns a low above the high.
@spec from_bounds( bounds(), keyword() ) :: {:ok, t()} | {:error, SplatTools.Error.t()}
Derives a camera that frames the given bounds.
Places the camera on a diagonal from the scene centre, far enough back that the whole bounding sphere fits the vertical field of view, with a margin.
Options
:fov— vertical field of view in degrees, default50.0. Must be in(0, 180).:margin— extra distance as a fraction, default0.25:elevation— how far above centre to sit, as a fraction of scene height, default0.35
@spec from_columns( %{required(String.t()) => binary()}, keyword() ) :: {:ok, t()} | {:error, SplatTools.Error.t()}
Derives a camera directly from packed columns.
The common path: Ply.columns!/3 then this.
Formats a camera as splat-transform's x,y,z argument form.
iex> SplatTools.Camera.to_arg({1.0, 2.0, 3.5})
"1.0,2.0,3.5"