from polyxios import transforms
from polyxios._io import Source, open_target
from polyxios._registry import Codec, build_default_registry, resolve
from polyxios._scene import (
SceneData,
SceneImage,
SceneMaterial,
SceneNode,
SceneTexture,
)
from polyxios._types import PolyData, make_polydata
from polyxios.exceptions import UnsupportedFormatError
from polyxios.fetcher import fetch
from polyxios.helper import read_polydata, visualize_mesh
from polyxios.validate import validate
from polyxios.version import version as __version__
_REGISTRY: dict[str, Codec] = build_default_registry()
def supported_extensions(*, registry: dict | None = None) -> list[str]:
"""List the file extensions the registry resolves.
Almost every one of them both reads and writes. Three kinds do not, and
are listed all the same because resolving to a message that names the
problem beats resolving to nothing:
- an extension several formats share, such as ``.dat``, reads by content
and needs ``fmt=`` to write;
- ``.plt`` resolves only to the error saying binary Tecplot is not read;
- the ParaView meta-file extensions read but raise on write.
Parameters
----------
registry
Custom codec registry. Uses the built-in registry if None.
Returns
-------
list of str
Sorted extensions, each with its leading dot (e.g. '.obj').
"""
return sorted(registry or _REGISTRY)
def read(
path: Source,
*,
fmt: str | None = None,
lazy: bool = False,
registry: dict | None = None,
**opts: object,
) -> PolyData:
"""Read a mesh file and return a PolyData.
Parameters
----------
path
File path to read, or an open binary file object - anything with a
``read``, such as ``io.BytesIO`` or a socket's ``makefile('rb')``.
A handle is read from wherever it stands and is never closed here.
fmt
Format override (e.g. '.vtk'). The leading dot and the case are
both optional, and a trailing '.gz' names the compression rather
than the format. Inferred from the file extension if None, which a
file object only has when it carries a usable ``name``; pass ``fmt``
for any other buffer.
lazy
If True and the format supports it, use mmap for the binary data
section so pages are loaded on demand. A file object has to be
backed by a real file for that; an in-memory buffer raises
LazyReadError.
registry
Custom codec registry. Uses the built-in registry if None.
**opts
Format-specific options passed to the codec's read function.
Returns
-------
PolyData
Parsed mesh data.
Raises
------
UnsupportedFormatError
If no codec matches, or if a nameless file object was passed
without ``fmt``.
CodecError
If the file object is open in text mode where the format is read as
bytes, or if it is a stream that cannot seek where the format needs
to look twice - at the file's size, or at its opening to tell two
formats sharing an extension apart.
LazyReadError
If ``lazy`` is set and the source cannot be mapped.
TypeError
If an option is passed that the chosen codec's reader does not take.
"""
codec = resolve(path, fmt, registry or _REGISTRY)
return codec.read(path=path, lazy=lazy, **opts)
def write(
poly: PolyData,
path: Source,
*,
fmt: str | None = None,
registry: dict | None = None,
**opts: object,
) -> None:
"""Write a PolyData to a mesh file.
Parameters
----------
poly
PolyData to write.
path
Output file path, or an open binary file object - anything with a
``write``, such as ``io.BytesIO``. A handle is written at wherever
it stands and is never closed here, so a caller can keep appending
to it afterwards.
fmt
Format override (e.g. '.vtk'). The leading dot and the case are
both optional, and a trailing '.gz' compresses the output - which is
the only way to ask a nameless buffer for gzip. Inferred from the
file extension if None, which a file object only has when it carries
a usable ``name``; pass ``fmt`` for any other buffer.
registry
Custom codec registry. Uses the built-in registry if None.
**opts
Format-specific options passed to the codec's write function.
Raises
------
UnsupportedFormatError
If no codec matches, or if a nameless file object was passed
without ``fmt``.
CodecError
If the file object is open in text mode where the format is written
as bytes, or if the format refuses the destination it was given.
"""
codec = resolve(path, fmt, registry or _REGISTRY)
with open_target(path, fmt=fmt) as target:
codec.write(poly=poly, path=target, **opts)
[docs]
def read_scene(
path: Source,
*,
fmt: str | None = None,
registry: dict | None = None,
**opts: object,
) -> SceneData:
"""Read a scene-graph file and return a SceneData.
Parameters
----------
path
Path to the scene file (e.g. ``.gltf`` or ``.glb``).
fmt
Format override (e.g. ``'.glb'``). Inferred from the file
extension when ``None``.
registry
Custom codec registry. Uses the built-in registry if ``None``.
**opts
Format-specific options passed to the codec's ``read_scene`` function.
Returns
-------
SceneData
Full scene graph with meshes, nodes, materials, textures, and images.
Raises
------
UnsupportedFormatError
If the format does not carry scene data. Use :func:`read` for
flat single-mesh formats.
"""
codec = resolve(path, fmt, registry or _REGISTRY)
if codec.read_scene is None:
raise UnsupportedFormatError(
"Format does not carry scene data; use polyxios.read()."
)
return codec.read_scene(path=path, **opts)
[docs]
def write_scene(
scene: SceneData,
path: Source,
*,
fmt: str | None = None,
registry: dict | None = None,
**opts: object,
) -> None:
"""Write a SceneData to a scene-graph file.
Parameters
----------
scene
SceneData to serialize.
path
Output file path.
fmt
Format override (e.g. ``'.glb'``). Inferred from the file
extension when ``None``.
registry
Custom codec registry. Uses the built-in registry if ``None``.
**opts
Format-specific options passed to the codec's ``write_scene``
function (e.g. ``binary=False`` for ``.gltf`` + ``.bin``).
Raises
------
UnsupportedFormatError
If the format does not support scene writing. Use :func:`write`
to write a flat PolyData instead.
"""
codec = resolve(path, fmt, registry or _REGISTRY)
if codec.write_scene is None:
raise UnsupportedFormatError(
"Format does not support scene writing; use polyxios.write()."
)
with open_target(path, fmt=fmt) as target:
codec.write_scene(scene=scene, path=target, **opts)
__all__ = [
"Codec",
"PolyData",
"SceneData",
"SceneImage",
"SceneMaterial",
"SceneNode",
"SceneTexture",
"Source",
"__version__",
"fetch",
"make_polydata",
"read",
"read_polydata",
"read_scene",
"supported_extensions",
"transforms",
"validate",
"visualize_mesh",
"write",
"write_scene",
]