Ply.Types (Ply v0.1.0)

Copy Markdown View Source

PLY scalar types: names, sizes, and decoding.

Type names

The canonical PLY specification defines eight scalar types under C names — char, uchar, short, ushort, int, uint, float, double. Real files also use explicit width aliases (int8, uint8, float32, …) which are not in the spec but are emitted by common tooling. Both spellings are accepted and normalised to a single canonical atom:

iex> Ply.Types.normalize("float")
{:ok, :float32}

iex> Ply.Types.normalize("float32")
{:ok, :float32}

Note that char is a signed 8-bit integer, not a character.

Non-finite floats

BEAM floats cannot represent IEEE NaN or infinity — a bit-syntax match such as <<f::float-32>> = <<0x7F, 0xC0, 0, 0>> raises MatchError rather than producing a value. Since real Gaussian-splat exports do contain NaN (hence splat-transform's --filter-nan flag), decoding checks for those bit patterns first and yields :nan, :infinity, or :neg_infinity instead of crashing.

iex> Ply.Types.decode_scalar(<<0x7F, 0xC0, 0x00, 0x00>>, :float32, :big)
:nan

Summary

Types

t()

A decoded scalar. Floats may decode to a non-finite atom — see the module docs.

Functions

All canonical type atoms.

Checks that a value can be stored by a type without being destroyed.

Decodes one scalar from the front of a binary.

Encodes a scalar value to its binary representation.

Normalises a PLY type name to its canonical atom.

Parses a scalar from ASCII text.

Size in bytes of a scalar type.

The canonical specification spelling for a type, used when writing headers.

Types

endianness()

@type endianness() :: :little | :big

t()

@type t() ::
  :int8 | :uint8 | :int16 | :uint16 | :int32 | :uint32 | :float32 | :float64

value()

@type value() :: integer() | float() | :nan | :infinity | :neg_infinity

A decoded scalar. Floats may decode to a non-finite atom — see the module docs.

Functions

all()

@spec all() :: [t()]

All canonical type atoms.

check(value, type)

@spec check(term(), t()) :: :ok | {:error, String.t()}

Checks that a value can be stored by a type without being destroyed.

encode_scalar/3 assumes valid input for speed; this is the gate callers use before writing. Without it an out-of-range integer wraps silently — 300 stored as uchar becomes 44 — an oversized float becomes infinity, and an undersized one becomes zero; all three corrupt a file that reports as written cleanly.

This checks representability, not precision. Storing 0.1 as float32 loses digits and is accepted, because that rounding is inherent to the format; rejecting it would make the type unusable. What is rejected is a value that cannot survive at all.

iex> Ply.Types.check(300, :uint8)
{:error, "300 is outside the range of uint8 (0..255)"}

iex> Ply.Types.check(200, :uint8)
:ok

decode_scalar(bin, atom1, atom2)

@spec decode_scalar(binary(), t(), endianness()) :: value()

Decodes one scalar from the front of a binary.

The binary must be at least size(type) bytes; callers are expected to have checked. Floats that carry a non-finite bit pattern decode to :nan / :infinity / :neg_infinity rather than raising.

encode_scalar(value, type, endian)

@spec encode_scalar(value(), t(), endianness()) :: binary()

Encodes a scalar value to its binary representation.

Accepts the non-finite atoms produced by decode_scalar/3, so a decode/encode round trip preserves them.

Assumes the value fits the type — call check/2 first for untrusted input.

normalize(name)

@spec normalize(String.t()) :: {:ok, t()} | :error

Normalises a PLY type name to its canonical atom.

Accepts both spec names and width aliases (see the module docs).

parse_ascii(text, type)

@spec parse_ascii(String.t(), t()) :: {:ok, value()} | :error

Parses a scalar from ASCII text.

Handles the C %g output real writers emit — exponent notation, -0, and the unspecified-but-common nan / inf spellings.

size(type)

@spec size(t()) :: pos_integer()

Size in bytes of a scalar type.

iex> Ply.Types.size(:float32)
4

spec_name(type)

@spec spec_name(t()) :: String.t()

The canonical specification spelling for a type, used when writing headers.

iex> Ply.Types.spec_name(:float32)
"float"