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 or file.
Streams %Point{} values to an EXCP file using an explicit schema.
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( Path.t() | binary(), keyword() ) :: Enumerable.t()
Returns an enumerable over points in an EXCP payload or file.
Streaming behavior
source: :file(or:autopath detection): header then chunked records - Rust mmap + DirtyCpu unpack when available, otherwiseIO.binread/2per record.source: :binary: chunked Rust unpack when available; otherwise materializes throughdecode/2.
Prefer source: :file for multi-MB / multi-GB .excp paths.
Pass accel: false to force the pure-Elixir path.
Arguments
source(Path.t() | binary()) - filesystem path or complete EXCP payload. Both are binaries, so:sourcecontrols resolution.opts(keyword()) -:sourcemay be:binary(default),:file, or:auto. Prefer:filefor paths and:binaryfor payloads;:autois opt-in path sniffing (seedocs/spatial_formats.md).:binary.
Returns
An Enumerable.t() yielding %Point{} values. Failures are delayed until
enumeration as exactly one {:error, %ExCodecs.Error{}}:
reason: :io_error- the file cannot be opened or readreason: :invalid_data- bad magic/version, truncated header, or a truncated record mid-stream (codec: :spatial_binary)
Raises / exceptions
Raises FunctionClauseError when source is not a binary. Unsupported
:source values raise CaseClauseError.
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()
@spec stream_encode_to_file(Enumerable.t(), Path.t(), keyword()) :: :ok | {:error, ExCodecs.Error.t()}
Streams %Point{} values to an EXCP file using an explicit schema.
Writes a placeholder header, encodes each point as it arrives, then seeks back to patch the final count. Peak memory is O(one point), not the cloud.
Arguments
enumerable(Enumerable.t()) -%Point{}elements.path(Path.t()) - destination file path.opts(keyword()) - requires:schema, a list such as[],[:color],[:color, :alpha],[:normal], or[:color, :normal]. Map form%{color: true, alpha: false, normal: true}is also accepted.:alphaimplies color bytes (RGBA).
Returns
:ok{:error, %ExCodecs.Error{reason: :invalid_options}}when:schemais missing or invalid{:error, %ExCodecs.Error{reason: :invalid_data}}when an element is not a%Point{}{:error, %ExCodecs.Error{reason: :io_error}}on file failures
Raises / exceptions
Schema and non-%Point{} element failures are returned. Invalid path terms or
non-keyword opts can raise FunctionClauseError or ArgumentError in the
path/file/keyword APIs. Malformed point field shapes can raise during
bitstring construction (same class of exceptions as encode/2).
Examples
iex> alias ExCodecs.Spatial.{Point, Codec.Binary}
iex> path = Path.join(System.tmp_dir!(), "excp_enc_9.excp")
iex> :ok = Binary.stream_encode_to_file([Point.new(1, 2, 3, color: {1, 2, 3})], path, schema: [:color])
iex> {:ok, <<"EXCP", _::binary>>} = File.read(path)
iex> File.rm!(path)
:ok