ExCodecs.Spatial.Codec.Binary (ex_codecs v0.2.3)

Copy Markdown View Source

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 × record

Each 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

decode(data, opts \\ [])

@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

encode(data, opts \\ [])

@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 whose points are 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} where payload is an EXCP binary().
  • {:error, %ExCodecs.Error{reason: :invalid_data, codec: :spatial_binary}} when data is 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

stream_decode(source, opts \\ [])

@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 :auto path detection): header then chunked records - Rust mmap + DirtyCpu unpack when available, otherwise IO.binread/2 per record.
  • source: :binary: chunked Rust unpack when available; otherwise materializes through decode/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 :source controls resolution.

  • opts (keyword()) - :source may be :binary (default), :file, or :auto. Prefer :file for paths and :binary for payloads; :auto is opt-in path sniffing (see docs/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 read
  • reason: :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()

stream_encode_to_file(enumerable, path, opts \\ [])

@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. :alpha implies color bytes (RGBA).

Returns

  • :ok
  • {:error, %ExCodecs.Error{reason: :invalid_options}} when :schema is 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