Skip to content

viewer.export — device scene export

tempura/viewer/export.py serializes a finalized device into the bundle consumed by the standalone TypeScript 3D viewer. export_device_view(...) writes one scene.json metadata file plus binary array assets holding the layer height fields and gate stencils.

What it writes

For each layer the exporter records the realized bottom/top height fields, and for gates the stencil, as little-endian binary arrays referenced from scene.json (see viewer._bundle_io). Per-layer colors can be overridden. The returned DeviceViewExportManifest lists the scene file and every asset written, so callers can track or relocate the bundle.

The device must be finalized — the export reads the realized height arrays that finalize() produces.

API

export

Export finalized device geometry for the standalone TypeScript viewer.

DeviceViewExportManifest dataclass

Paths written by :func:export_device_view.

Attributes:

Name Type Description
scene_name str

Human-readable scene name stored in scene.json.

scene_json_path Path

Path to the scene metadata file.

asset_paths tuple[Path, ...]

Paths to binary array assets referenced by scene.json.

Source code in tempura/viewer/export.py
26
27
28
29
30
31
32
33
34
35
36
37
38
@dataclass(frozen=True)
class DeviceViewExportManifest:
    """Paths written by :func:`export_device_view`.

    Attributes:
        scene_name: Human-readable scene name stored in ``scene.json``.
        scene_json_path: Path to the scene metadata file.
        asset_paths: Paths to binary array assets referenced by ``scene.json``.
    """

    scene_name: str
    scene_json_path: Path
    asset_paths: tuple[Path, ...]

export_device_view(device, out_dir, *, scene_name='device', colors=None, overwrite=True, verbose=False)

Export a device into files consumed by the TypeScript 3D viewer.

The exporter writes one scene.json metadata file and one or more binary array files storing layer height fields (plus gate stencils).

Parameters:

Name Type Description Default
device Device

Device to export.

required
out_dir str | Path

Output directory for the exported scene bundle.

required
scene_name str

Label stored in the metadata file.

'device'
colors dict[str, str] | None

Optional per-layer color overrides keyed by layer name.

None
overwrite bool

If False, refuse to write into a non-empty directory.

True
verbose bool

If True, print progress messages.

False

Returns:

Type Description
DeviceViewExportManifest

Manifest describing the files written.

Raises:

Type Description
ValueError

If the device is empty or a color override is invalid.

FileExistsError

If overwrite is disabled and the destination exists.

RuntimeError

If the device is not finalized or finalized height arrays are missing.

Source code in tempura/viewer/export.py
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
def export_device_view(
    device: Device,
    out_dir: str | Path,
    *,
    scene_name: str = "device",
    colors: dict[str, str] | None = None,
    overwrite: bool = True,
    verbose: bool = False,
) -> DeviceViewExportManifest:
    """Export a device into files consumed by the TypeScript 3D viewer.

    The exporter writes one ``scene.json`` metadata file and one or more binary
    array files storing layer height fields (plus gate stencils).

    Args:
        device: Device to export.
        out_dir: Output directory for the exported scene bundle.
        scene_name: Label stored in the metadata file.
        colors: Optional per-layer color overrides keyed by layer name.
        overwrite: If ``False``, refuse to write into a non-empty directory.
        verbose: If ``True``, print progress messages.

    Returns:
        Manifest describing the files written.

    Raises:
        ValueError: If the device is empty or a color override is invalid.
        FileExistsError: If ``overwrite`` is disabled and the destination exists.
        RuntimeError: If the device is not finalized or finalized height arrays are missing.
    """

    def _log(message: str) -> None:
        """Print a progress message when verbose logging is enabled."""
        if verbose:
            print(message)

    if not device.layers:
        raise ValueError("Device must contain at least one layer before export.")

    if not device.finalized:
        raise RuntimeError("Device must be finalized before export_device_view().")

    out_path = Path(out_dir)
    if out_path.exists():
        if not out_path.is_dir():
            raise FileExistsError(f"Output path is not a directory: {out_path}")
        if not overwrite and any(out_path.iterdir()):
            raise FileExistsError(
                f"Output directory already exists and is not empty: {out_path}"
            )
    else:
        out_path.mkdir(parents=True, exist_ok=True)

    arrays_dir = out_path / "arrays"
    arrays_dir.mkdir(parents=True, exist_ok=True)

    color_map = _default_layer_colors(device)
    if colors is not None:
        unknown_layers = sorted(set(colors) - set(device.layers))
        if unknown_layers:
            raise ValueError(f"Unknown layer names in colors override: {unknown_layers}")
        color_map.update(colors)

    asset_paths: list[Path] = []
    layers_payload = []
    for order, layer in enumerate(device.layers.values()):
        _log(f"[export_device_view] Exporting layer {layer.name!r}...")
        layers_payload.append(
            _export_layer(
                layer=layer,
                order=order,
                arrays_dir=arrays_dir,
                color=color_map[layer.name],
                asset_paths=asset_paths,
            )
        )

    metadata = _serialize_scene_metadata(
        device=device,
        scene_name=scene_name,
        layers_payload=layers_payload,
    )
    scene_json_path = out_path / "scene.json"
    scene_json_path.write_text(json.dumps(metadata, indent=2) + "\n")
    _log(f"[export_device_view] Wrote {scene_json_path}")

    return DeviceViewExportManifest(
        scene_name=scene_name,
        scene_json_path=scene_json_path,
        asset_paths=tuple(asset_paths),
    )