Compact little-endian binary format for point clouds.
Layout
magic: "EXCP" (4 bytes)
version: u16 LE = 1
flags: u16 LE (bit0=color, bit1=alpha, bit2=normal)
count: u64 LE
records: count × recordEach record always has x,y,z as f32. Optional rgb/rgba as u8
and normals as f32 follow according to flags.
Generic attributes are not stored in this format — use PLY for that.
Summary
Functions
Decodes an EXCP version 1 payload into a point cloud.
Encodes a point cloud in the EXCP version 1 binary format.
Returns an enumerable over points in an EXCP payload.
Functions
@spec decode( binary(), keyword() ) :: {:ok, ExCodecs.Spatial.PointCloud.t()} | {:error, ExCodecs.Error.t()}
Decodes an EXCP version 1 payload into a point cloud.
The decoder reads the global color/alpha/normal flags and the declared number
of fixed-shape records described by encode/2. Trailing bytes are currently
ignored. Decoded metadata contains %{"format" => "excp", "version" => 1};
attributes and other cloud-level fields use their constructor defaults.
Arguments
data(binary()) — a complete EXCP payload.opts(keyword()) — reserved and currently ignored.
Returns
{:ok, %PointCloud{}}{:error, %ExCodecs.Error{reason: :invalid_data, codec: :spatial_binary}}when the magic/header is invalid or too short, the version is not 1, or a declared coordinate, RGB/RGBA, or normal field is truncated.
Raises / exceptions
Corrupt/truncated payloads covered above are returned, and opts is ignored.
This function has a catch-all data clause, so non-binary input also returns
:invalid_data; it does not intentionally raise for external payloads.
Examples
iex> alias ExCodecs.Spatial.{Point, PointCloud}
iex> {:ok, bin} = ExCodecs.Spatial.Codec.Binary.encode(PointCloud.new([Point.new(1.0, 2.0, 3.0)]))
iex> {:ok, %PointCloud{points: [p]}} = ExCodecs.Spatial.Codec.Binary.decode(bin)
iex> p.x
1.0
@spec encode( ExCodecs.Spatial.PointCloud.t(), keyword() ) :: {:ok, binary()} | {:error, ExCodecs.Error.t()}
Encodes a point cloud in the EXCP version 1 binary format.
The 16-byte header is "EXCP", version u16 little-endian, global flags
u16 little-endian, and point count u64 little-endian. Each record starts
with three little-endian f32 coordinates. Global flag bits then add RGB
(3 bytes), RGBA (4 bytes; alpha takes precedence), and/or three little-endian
f32 normal components to every record.
Flags are selected from the whole cloud. Consequently, points missing an optional field are zero-filled (missing alpha is 255). Point attributes, cloud bounds, transform, and metadata are not represented.
Arguments
data(PointCloud.t()) — a cloud whosepointsare valid%Point{}structs with numeric coordinates, optional{r, g, b}or{r, g, b, a}color, and optional{nx, ny, nz}normal.opts(keyword()) — reserved and currently ignored.
Returns
{:ok, payload}wherepayloadis an EXCPbinary().{:error, %ExCodecs.Error{reason: :invalid_data, codec: :spatial_binary}}whendatais not a%PointCloud{}.
Raises / exceptions
A wrong top-level type is returned as :invalid_data. Because opts is
ignored, even a non-keyword value does not raise. Manually malformed cloud or
point structs can raise FunctionClauseError, MatchError, ArgumentError,
or a bitstring construction exception when a point, tuple shape, channel
range, or numeric field violates the struct contract.
Examples
iex> alias ExCodecs.Spatial.{Point, PointCloud}
iex> cloud = PointCloud.new([Point.new(1, 2, 3, color: {10, 20, 30})])
iex> {:ok, <<"EXCP", 1::little-16, flags::little-16, 1::little-64, _::binary>>} =
...> ExCodecs.Spatial.Codec.Binary.encode(cloud)
iex> Bitwise.band(flags, 0b001)
1
@spec stream_decode( binary(), keyword() ) :: Enumerable.t()
Returns an enumerable over points in an EXCP payload.
Arguments
data(binary()) — a complete EXCP payload.opts(keyword()) — reserved and currently ignored.
Returns
An Enumerable.t() that yields decoded %Point{} values after the complete
cloud has been materialized. If decode/2 fails, it yields exactly one
{:error, %ExCodecs.Error{reason: :invalid_data, codec: :spatial_binary}} element.
Raises / exceptions
Raises FunctionClauseError when data is not a binary because this public
function is guarded. opts is ignored. Binary validation failures are
delayed as the single error element rather than raised.
Examples
iex> alias ExCodecs.Spatial.{Point, PointCloud}
iex> {:ok, bin} = ExCodecs.Spatial.Codec.Binary.encode(PointCloud.new([Point.new(0, 0, 0)]))
iex> [%Point{x: 0.0, y: 0.0, z: 0.0}] =
...> ExCodecs.Spatial.Codec.Binary.stream_decode(bin) |> Enum.to_list()