Files
cadSet/SimpleCADAPI/tools/scene_contract_fixtures/scene_cases.py

219 lines
6.7 KiB
Python

"""Minimal, nested, and repeated-instance Scene fixture builders."""
from __future__ import annotations
from copy import deepcopy
from simplecadapi.scene import with_scene_revision
from .common import (
IDENTITY,
JsonObject,
ScenePackage,
replace_entity_sidecar,
set_product_material_appearance,
transform,
)
def _product_definition(
definition_id: str,
kind: str,
semantic_id: str,
renderable: JsonObject | None = None,
) -> JsonObject:
definition: JsonObject = {
"definition_id": definition_id,
"kind": kind,
"name": semantic_id.replace("_", " ").title(),
"sdk_metadata": {},
"source": {
"kind": "product_manual",
"root_id": "root",
"semantic_id": semantic_id,
"semantic_type": "Part" if kind == "part" else "Assembly",
},
}
if renderable is not None:
definition.update(
{
field: renderable[field]
for field in (
"appearance_id",
"edge_asset_id",
"entity_asset_id",
"geometry_asset_id",
)
}
)
return definition
def _product_node(
component_path: list[str],
definition_id: str,
order: int,
local_transform: JsonObject | None = None,
) -> JsonObject:
node_id = "instance/root" + "".join(f"/{part}" for part in component_path)
parent_id = (
None
if not component_path
else "instance/root" + "".join(f"/{part}" for part in component_path[:-1])
)
return {
"appearance_override_id": None,
"definition_id": definition_id,
"name": component_path[-1] if component_path else "root",
"node_id": node_id,
"order": order,
"parent_node_id": parent_id,
"sdk_metadata": {},
"selectable": True,
"source": {
"component_path": component_path,
"kind": "product_occurrence",
"root_id": "root",
},
"transform": deepcopy(local_transform or IDENTITY),
"visible": True,
}
def _manual_connector(
owner_definition_id: str,
owner_kind: str,
owner_semantic_id: str,
connector_id: str,
anchor_kind: str,
local_transform: JsonObject,
*,
forwarded_from: JsonObject | None = None,
) -> JsonObject:
connector: JsonObject = {
"anchor_kind": anchor_kind,
"connector_id": connector_id,
"connector_snapshot_id": (
f"connector/root/{owner_kind}/{owner_semantic_id}/{connector_id}"
),
"local_transform": local_transform,
"name": connector_id.replace("_", " ").title(),
"owner_definition_id": owner_definition_id,
"sdk_metadata": {},
"source": {"kind": "manual", "source_id": "fixture"},
}
if forwarded_from is not None:
connector["forwarded_from"] = forwarded_from
return connector
def build_product_scene_package(
base_scene: JsonObject,
base_entity: JsonObject,
base_blobs: dict[str, bytes],
*,
nested: bool,
) -> ScenePackage:
scene = deepcopy(base_scene)
entity = deepcopy(base_entity)
root_definition_id = "definition/root/assembly/root_assembly"
part_definition_id = "definition/root/part/shared_part"
renderable = deepcopy(scene["definitions"][0])
definitions = [
_product_definition(root_definition_id, "assembly", "root_assembly"),
_product_definition(part_definition_id, "part", "shared_part", renderable),
]
nodes = [_product_node([], root_definition_id, 0)]
connectors: list[JsonObject] = []
if nested:
nested_definition_id = "definition/root/assembly/nested_assembly"
definitions.append(
_product_definition(
nested_definition_id, "assembly", "nested_assembly"
)
)
nodes.extend(
[
_product_node(["subassembly"], nested_definition_id, 0),
_product_node(["subassembly", "part"], part_definition_id, 0),
]
)
part_connector = _manual_connector(
part_definition_id,
"part",
"shared_part",
"mount",
"placement",
transform(),
)
nested_connector = _manual_connector(
nested_definition_id,
"assembly",
"nested_assembly",
"nested_mount",
"forwarded",
transform(),
forwarded_from={
"offset": None,
"source_component_id": "part",
"source_connector_id": "mount",
"source_connector_snapshot_id": part_connector[
"connector_snapshot_id"
],
"source_definition_id": part_definition_id,
},
)
root_offset = transform([0, 0, 5])
root_connector = _manual_connector(
root_definition_id,
"assembly",
"root_assembly",
"root_mount",
"forwarded",
deepcopy(root_offset),
forwarded_from={
"offset": root_offset,
"source_component_id": "subassembly",
"source_connector_id": "nested_mount",
"source_connector_snapshot_id": nested_connector[
"connector_snapshot_id"
],
"source_definition_id": nested_definition_id,
},
)
connectors = [part_connector, nested_connector, root_connector]
scene["scene_id"] = "nested_fixture"
else:
nodes.extend(
[
_product_node(
["left"], part_definition_id, 0, transform([-1000, 0, 0])
),
_product_node(
["right"], part_definition_id, 1, transform([1000, 0, 0])
),
]
)
scene["scene_id"] = "repeated_fixture"
scene["definitions"] = sorted(
definitions, key=lambda value: value["definition_id"].encode("utf-8")
)
scene["nodes"] = sorted(
nodes, key=lambda value: value["node_id"].encode("utf-8")
)
scene["connectors"] = sorted(
connectors,
key=lambda value: value["connector_snapshot_id"].encode("utf-8"),
)
entity["definition_id"] = part_definition_id
for record in entity["entities"]:
if record["kind"] != "solid":
record["connector_binding_status"] = "source_not_model"
scene, entity, blobs = replace_entity_sidecar(
scene, entity, dict(base_blobs)
)
set_product_material_appearance(scene)
return with_scene_revision(scene), entity, blobs