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

Copy Markdown View Source

ASCII and binary PLY for point clouds and Gaussian splats.

Options

  • :format / :ply_format - wire encoding: :ascii (default), :binary / :binary_le, :binary_be (not Spatial's format: :ply)
  • :comments - header comments
  • :as - decode as :auto | :point_cloud | :gaussian_cloud

  • :source - for stream_decode/2: :auto | :file | :binary

Public functions: encode/2, decode/2, stream_decode/2.

Summary

Types

Wire encoding used for a PLY payload.

Functions

Decodes a complete PLY 1.0 payload into a point or Gaussian cloud.

Encodes a point cloud or Gaussian cloud as a PLY 1.0 payload.

Returns an enumerable over vertices from a PLY path or binary.

Types

ply_format()

@type ply_format() :: :ascii | :binary | :binary_le | :binary_be

Wire encoding used for a PLY payload.

:ascii writes textual scalar values. :binary_le and :binary_be write packed scalar values in little- and big-endian order, respectively. :binary is an encoding-option shorthand for :binary_le; decoded headers report the explicit endian atom.

For example, encode(cloud, ply_format: :binary_be) emits a format binary_big_endian 1.0 header.

Functions

decode(data, opts \\ [])

Decodes a complete PLY 1.0 payload into a point or Gaussian cloud.

ASCII and binary little-/big-endian scalar vertex properties are supported: char/int8, uchar/uint8, short/int16, ushort/uint16, int/int32, uint/uint32, float/float32, and double/float64. List properties and non-vertex payloads are unsupported.

Point decoding recognizes x/y/z, RGB or RGBA, and nx/ny/nz; all other scalar properties become string-keyed point attributes. Gaussian decoding interprets f_dc_*, opacity, scale, rotation, and ordered f_rest_* properties. Header comments and the detected PLY format are retained in cloud metadata.

Arguments

  • data (binary()) - the complete PLY header and vertex body.
  • opts (keyword()) - :as may be :auto (default), :point_cloud, or :gaussian_cloud. :auto chooses Gaussian output when the property set contains f_dc_0, scale_0, or rot_0; otherwise it chooses points. The wire format is always read from the header.

Returns

  • {:ok, %PointCloud{}} or {:ok, %GaussianCloud{}}, including metadata.
  • {:error, %ExCodecs.Error{reason: :invalid_data, codec: :ply}} for non-binary input, missing/invalid magic or format/header lines, missing vertex elements, invalid counts/properties, unsupported list/property types, invalid ASCII scalar tokens, token/property count mismatches, missing x/y/z, too few ASCII rows, or a short binary body.

Raises / exceptions

The listed structural failures are returned. Keyword.get/3 raises FunctionClauseError when opts is not a proper keyword list. An unsupported :as value raises CaseClauseError. Malformed binary row contents may also raise (KeyError, ArgumentError, FunctionClauseError, or a bitstring match exception) when scalar unpacking is not fully wrapped.

Examples

iex> alias ExCodecs.Spatial.{Point, PointCloud}
iex> cloud = PointCloud.new([Point.new(1.0, 0.0, 0.0, normal: {0.0, 0.0, 1.0})])
iex> {:ok, ply} = ExCodecs.Spatial.Codec.PLY.encode(cloud, ply_format: :binary_le)
iex> {:ok, %PointCloud{points: [point]}} = ExCodecs.Spatial.Codec.PLY.decode(ply)
iex> point.normal
{0.0, 0.0, 1.0}

encode(data, opts \\ [])

Encodes a point cloud or Gaussian cloud as a PLY 1.0 payload.

Point vertices always contain x, y, and z as float. If any point has color or a normal, the corresponding uchar RGB/RGBA or float normal properties are emitted for every point; missing values are zero-filled (alpha defaults to 255). The union of point attribute keys is emitted as sorted float properties, with absent attributes set to 0.0.

Gaussian vertices contain position, f_dc_0..2, opacity, scale, and quaternion rot_0..3 (w, x, y, z) as float. If spherical-harmonic coefficients are present, flattened f_rest_N properties are appended and shorter rows are zero-filled.

Arguments

  • data (PointCloud.t() | GaussianCloud.t()) - a cloud containing valid %Point{} or %Gaussian{} structs.

  • opts (keyword()) - supported options:
    • :ply_format - preferred wire format: :ascii (default), :binary (little-endian), :binary_le, or :binary_be.
    • :format - legacy alias for :ply_format when calling this codec directly. :ply_format takes precedence.
    • :comments - enumerable of strings for PLY comment header lines; defaults to cloud.metadata.comments.

:little and :big are also normalized to little-/big-endian output. Unrecognized wire-format values return :invalid_options.

Returns

  • {:ok, payload} where payload is an ASCII or binary PLY binary().
  • {:error, %ExCodecs.Error{reason: :invalid_options, codec: :ply}} when :format / :ply_format is not a supported wire encoding.
  • {:error, %ExCodecs.Error{reason: :invalid_data, codec: :ply}} when data is not a supported cloud or any exception occurs while deriving properties, building the header, or encoding rows.

Raises / exceptions

The cloud clauses rescue encoding exceptions and return :invalid_data, including invalid keyword options, malformed nested structs, non-string comments, invalid scalar ranges/types, and bad attribute shapes. Unsupported top-level data is also returned as :invalid_data; this function does not intentionally raise.

Examples

iex> alias ExCodecs.Spatial.{Point, PointCloud}
iex> cloud = PointCloud.new([Point.new(0, 1, 2, color: {255, 64, 0})])
iex> {:ok, ply} = ExCodecs.Spatial.Codec.PLY.encode(cloud, ply_format: :ascii)
iex> String.contains?(ply, "property uchar red")
true

stream_decode(source, opts \\ [])

@spec stream_decode(
  binary() | Path.t(),
  keyword()
) :: Enumerable.t()

Returns an enumerable over vertices from a PLY path or binary.

Streaming behavior

  • source: :file (or :auto path detection): scans the header until end_header, then yields one vertex at a time - binary formats via fixed-stride IO.binread/2, ASCII via line reads. Peak memory is O(header + one vertex / read chunk), not the whole cloud.
  • source: :binary: still materializes through decode/2, then yields.

Arguments

  • source (Path.t() | binary()) - a path or complete PLY payload.

  • opts (keyword()) - :source may be :binary (default), :file, or :auto. Prefer :file for paths and :binary for payloads; :auto is opt-in path sniffing. PLY also accepts :as. :binary. Auto mode treats a short path-like binary as a file only when it names a regular file; otherwise it is payload data. :as has the same meaning as in decode/2.

Returns

An Enumerable.t() yielding %Point{} or %Gaussian{}. Decode failures, including reason: :invalid_data, are represented by exactly one {:error, %ExCodecs.Error{}} element. A selected path that cannot be read yields one error with reason: :io_error and the file reason in details.

Raises / exceptions

File and normal decode failures become stream elements. A non-binary source raises FunctionClauseError. Keyword access raises FunctionClauseError for invalid opts; unsupported :source values raise CaseClauseError. Exceptions described by decode/2 may occur while the stream is initialized or enumerated.

Examples

iex> alias ExCodecs.Spatial.{Point, PointCloud}
iex> {:ok, ply} = ExCodecs.Spatial.Codec.PLY.encode(PointCloud.new([Point.new(0, 0, 0)]))
iex> [%Point{x: 0.0}] = ExCodecs.Spatial.Codec.PLY.stream_decode(ply) |> Enum.to_list()