The point-cloud file scalar; a loaded PointCloud is the file path.
It carries a cloud.point_count() reader that parses self. A point
cloud is a non-core extension scalar (dotted reverse-DNS name), so its
schema self-describes with kind + extensions and a reader without a PLY
decoder can still store or skip the bytes. The format is PLY (Polygon File
Format): each vertex carries x/y/z in metres and may carry
red/green/blue colour and a confidence quality channel — a loaded
value is the standard .ply path, ready for Open3D, trimesh, or MeshLab.
PointCloud
Bases: FileScalar
A 3D point cloud as a PLY file; a loaded value is its file Path.
Source code in capturegraph-lib/capturegraph/types/scalars/geometry/point_cloud.py
| class PointCloud(FileScalar, name="edu.cornell.point_cloud", extensions=("ply",)):
"""A 3D point cloud as a PLY file; a loaded value is its file ``Path``."""
__slots__ = ()
@classmethod
def of_points(cls, positions: "np.ndarray", colors: "np.ndarray") -> Self:
"""A fresh cloud of ``positions`` (N×3 metres) and ``colors`` (N×3 ``uint8``).
The file lives in the recipe workspace, as ``new`` allocates it.
Raises:
ScratchNotConfigured: If no scratch is configured.
"""
from capturegraph.types.scalars.geometry.support.ply import write_points
cloud = cls.new()
write_points(cloud, positions, colors)
return cloud
def points(self) -> "tuple[np.ndarray, np.ndarray]":
"""The vertices as ``(positions, colors)``: N×3 ``float32`` metres and N×3 ``uint8``.
A cloud without colour reads as mid-grey.
Raises:
ValueError: If ``self`` is not a binary little-endian PLY with positions.
"""
import numpy as np
from capturegraph.types.scalars.geometry.support.ply import vertex_columns
columns = vertex_columns(self)
positions = np.stack([columns["x"], columns["y"], columns["z"]], axis=1).astype(np.float32)
if "red" in columns:
colors = np.stack([columns["red"], columns["green"], columns["blue"]], axis=1)
else:
colors = np.full_like(positions, 127.0)
return positions, colors.astype(np.uint8)
def point_count(self) -> int:
"""The number of vertices, read from the PLY header (no decode of the body).
Raises:
ValueError: If the header declares no vertex element.
"""
with open(self, "rb") as handle:
for raw in handle:
line = raw.decode("ascii", "replace").strip()
if line.startswith("element vertex"):
return int(line.split()[-1])
if line == "end_header":
break
raise ValueError(f"{self}: PLY header declares no 'element vertex'")
|
of_points(positions, colors)
classmethod
A fresh cloud of positions (N×3 metres) and colors (N×3 uint8).
The file lives in the recipe workspace, as new allocates it.
Raises:
| Type |
Description |
ScratchNotConfigured
|
If no scratch is configured.
|
Source code in capturegraph-lib/capturegraph/types/scalars/geometry/point_cloud.py
| @classmethod
def of_points(cls, positions: "np.ndarray", colors: "np.ndarray") -> Self:
"""A fresh cloud of ``positions`` (N×3 metres) and ``colors`` (N×3 ``uint8``).
The file lives in the recipe workspace, as ``new`` allocates it.
Raises:
ScratchNotConfigured: If no scratch is configured.
"""
from capturegraph.types.scalars.geometry.support.ply import write_points
cloud = cls.new()
write_points(cloud, positions, colors)
return cloud
|
point_count()
The number of vertices, read from the PLY header (no decode of the body).
Raises:
| Type |
Description |
ValueError
|
If the header declares no vertex element.
|
Source code in capturegraph-lib/capturegraph/types/scalars/geometry/point_cloud.py
| def point_count(self) -> int:
"""The number of vertices, read from the PLY header (no decode of the body).
Raises:
ValueError: If the header declares no vertex element.
"""
with open(self, "rb") as handle:
for raw in handle:
line = raw.decode("ascii", "replace").strip()
if line.startswith("element vertex"):
return int(line.split()[-1])
if line == "end_header":
break
raise ValueError(f"{self}: PLY header declares no 'element vertex'")
|
points()
The vertices as (positions, colors): N×3 float32 metres and N×3 uint8.
A cloud without colour reads as mid-grey.
Raises:
| Type |
Description |
ValueError
|
If self is not a binary little-endian PLY with positions.
|
Source code in capturegraph-lib/capturegraph/types/scalars/geometry/point_cloud.py
| def points(self) -> "tuple[np.ndarray, np.ndarray]":
"""The vertices as ``(positions, colors)``: N×3 ``float32`` metres and N×3 ``uint8``.
A cloud without colour reads as mid-grey.
Raises:
ValueError: If ``self`` is not a binary little-endian PLY with positions.
"""
import numpy as np
from capturegraph.types.scalars.geometry.support.ply import vertex_columns
columns = vertex_columns(self)
positions = np.stack([columns["x"], columns["y"], columns["z"]], axis=1).astype(np.float32)
if "red" in columns:
colors = np.stack([columns["red"], columns["green"], columns["blue"]], axis=1)
else:
colors = np.full_like(positions, 127.0)
return positions, colors.astype(np.uint8)
|