mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-08-08 08:25:46 +00:00
224 lines
9.2 KiB
Python
224 lines
9.2 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
from . import syntax
|
|
|
|
|
|
def _table_rows(manifest: dict[str, Any], table_name: str, columns_name: str) -> list[dict[str, Any]]:
|
|
columns = manifest.get("tables", {}).get(columns_name)
|
|
rows = manifest.get(table_name)
|
|
if not isinstance(columns, list) or not isinstance(rows, list):
|
|
return []
|
|
materialized: list[dict[str, Any]] = []
|
|
for row in rows:
|
|
if not isinstance(row, list):
|
|
continue
|
|
materialized.append({str(columns[index]): row[index] for index in range(min(len(columns), len(row)))})
|
|
return materialized
|
|
|
|
|
|
def _relations(manifest: dict[str, Any]) -> dict[str, list[int]]:
|
|
relations = manifest.get("relations")
|
|
if not isinstance(relations, dict):
|
|
return {"faceEdgeRows": [], "edgeFaceRows": [], "edgeVertexRows": [], "vertexEdgeRows": []}
|
|
face_edge = relations.get("faceEdgeRows")
|
|
edge_face = relations.get("edgeFaceRows")
|
|
edge_vertex = relations.get("edgeVertexRows")
|
|
vertex_edge = relations.get("vertexEdgeRows")
|
|
return {
|
|
"faceEdgeRows": list(face_edge) if isinstance(face_edge, list) else [],
|
|
"edgeFaceRows": list(edge_face) if isinstance(edge_face, list) else [],
|
|
"edgeVertexRows": list(edge_vertex) if isinstance(edge_vertex, list) else [],
|
|
"vertexEdgeRows": list(vertex_edge) if isinstance(vertex_edge, list) else [],
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SelectorIndex:
|
|
manifest: dict[str, Any]
|
|
occurrences: list[dict[str, Any]]
|
|
shapes: list[dict[str, Any]]
|
|
faces: list[dict[str, Any]]
|
|
edges: list[dict[str, Any]]
|
|
vertices: list[dict[str, Any]]
|
|
relations: dict[str, list[int]]
|
|
occurrence_by_id: dict[str, dict[str, Any]]
|
|
shape_by_id: dict[str, dict[str, Any]]
|
|
face_by_id: dict[str, dict[str, Any]]
|
|
edge_by_id: dict[str, dict[str, Any]]
|
|
vertex_by_id: dict[str, dict[str, Any]]
|
|
single_occurrence_id: str
|
|
leaf_occurrence_ids: tuple[str, ...]
|
|
|
|
|
|
def build_selector_index(manifest: dict[str, Any]) -> SelectorIndex:
|
|
occurrences = _table_rows(manifest, "occurrences", "occurrenceColumns")
|
|
shapes = _table_rows(manifest, "shapes", "shapeColumns")
|
|
faces = _table_rows(manifest, "faces", "faceColumns")
|
|
edges = _table_rows(manifest, "edges", "edgeColumns")
|
|
vertices = _table_rows(manifest, "vertices", "vertexColumns")
|
|
relations = _relations(manifest)
|
|
|
|
occurrence_by_id = {str(row.get("id")): row for row in occurrences if row.get("id")}
|
|
shape_by_id = {str(row.get("id")): row for row in shapes if row.get("id")}
|
|
face_by_id = {str(row.get("id")): row for row in faces if row.get("id")}
|
|
edge_by_id = {str(row.get("id")): row for row in edges if row.get("id")}
|
|
vertex_by_id = {str(row.get("id")): row for row in vertices if row.get("id")}
|
|
|
|
leaf_occurrence_ids = tuple(
|
|
sorted(
|
|
{
|
|
str(row.get("occurrenceId"))
|
|
for row in shapes
|
|
if isinstance(row.get("occurrenceId"), str) and row.get("occurrenceId")
|
|
}
|
|
)
|
|
)
|
|
single_occurrence_id = leaf_occurrence_ids[0] if len(leaf_occurrence_ids) == 1 else ""
|
|
|
|
return SelectorIndex(
|
|
manifest=manifest,
|
|
occurrences=occurrences,
|
|
shapes=shapes,
|
|
faces=faces,
|
|
edges=edges,
|
|
vertices=vertices,
|
|
relations=relations,
|
|
occurrence_by_id=occurrence_by_id,
|
|
shape_by_id=shape_by_id,
|
|
face_by_id=face_by_id,
|
|
edge_by_id=edge_by_id,
|
|
vertex_by_id=vertex_by_id,
|
|
single_occurrence_id=single_occurrence_id,
|
|
leaf_occurrence_ids=leaf_occurrence_ids,
|
|
)
|
|
|
|
|
|
def entry_summary(index: SelectorIndex) -> dict[str, Any]:
|
|
stats = index.manifest.get("stats") if isinstance(index.manifest.get("stats"), dict) else {}
|
|
bbox = index.manifest.get("bbox") if isinstance(index.manifest.get("bbox"), dict) else None
|
|
return {
|
|
"kind": "part",
|
|
"occurrenceCount": int(stats.get("occurrenceCount") or len(index.occurrences)),
|
|
"leafOccurrenceCount": int(stats.get("leafOccurrenceCount") or len(index.leaf_occurrence_ids)),
|
|
"shapeCount": int(stats.get("shapeCount") or len(index.shapes)),
|
|
"faceCount": int(stats.get("faceCount") or len(index.faces)),
|
|
"edgeCount": int(stats.get("edgeCount") or len(index.edges)),
|
|
"vertexCount": int(stats.get("vertexCount") or len(index.vertices)),
|
|
"bounds": bbox,
|
|
}
|
|
|
|
|
|
def canonicalize_selector(raw_selector: str, index: SelectorIndex) -> str | None:
|
|
parsed = syntax.parse_selector(raw_selector)
|
|
if parsed is None:
|
|
return None
|
|
if parsed.selector_type == "opaque":
|
|
return parsed.canonical
|
|
if parsed.occurrence_id:
|
|
return parsed.canonical
|
|
if not index.single_occurrence_id:
|
|
return None
|
|
if parsed.selector_type == "shape":
|
|
return f"{index.single_occurrence_id}.s{parsed.ordinal}"
|
|
if parsed.selector_type == "face":
|
|
return f"{index.single_occurrence_id}.f{parsed.ordinal}"
|
|
if parsed.selector_type == "edge":
|
|
return f"{index.single_occurrence_id}.e{parsed.ordinal}"
|
|
if parsed.selector_type == "vertex":
|
|
return f"{index.single_occurrence_id}.v{parsed.ordinal}"
|
|
return parsed.canonical
|
|
|
|
|
|
def display_selector(selector: str, index: SelectorIndex) -> str:
|
|
if not selector or not index.single_occurrence_id:
|
|
return selector
|
|
prefix = f"{index.single_occurrence_id}."
|
|
if selector.startswith(prefix):
|
|
tail = selector[len(prefix) :]
|
|
if tail.startswith(("s", "f", "e", "v")):
|
|
return tail
|
|
return selector
|
|
|
|
|
|
def lookup_selector(selector: str, index: SelectorIndex) -> tuple[str, dict[str, Any]] | None:
|
|
normalized = canonicalize_selector(selector, index)
|
|
if not normalized:
|
|
return None
|
|
if normalized in index.occurrence_by_id:
|
|
return "occurrence", index.occurrence_by_id[normalized]
|
|
if normalized in index.shape_by_id:
|
|
return "shape", index.shape_by_id[normalized]
|
|
if normalized in index.face_by_id:
|
|
return "face", index.face_by_id[normalized]
|
|
if normalized in index.edge_by_id:
|
|
return "edge", index.edge_by_id[normalized]
|
|
if normalized in index.vertex_by_id:
|
|
return "vertex", index.vertex_by_id[normalized]
|
|
return None
|
|
|
|
|
|
def face_adjacent_edge_selectors(face_row: dict[str, Any], index: SelectorIndex) -> list[str]:
|
|
start = int(face_row.get("edgeStart") or 0)
|
|
count = int(face_row.get("edgeCount") or 0)
|
|
selectors: list[str] = []
|
|
for edge_row_index in index.relations.get("faceEdgeRows", [])[start : start + count]:
|
|
if 0 <= int(edge_row_index) < len(index.edges):
|
|
selectors.append(str(index.edges[int(edge_row_index)].get("id")))
|
|
return selectors
|
|
|
|
|
|
def edge_adjacent_face_selectors(edge_row: dict[str, Any], index: SelectorIndex) -> list[str]:
|
|
start = int(edge_row.get("faceStart") or 0)
|
|
count = int(edge_row.get("faceCount") or 0)
|
|
selectors: list[str] = []
|
|
for face_row_index in index.relations.get("edgeFaceRows", [])[start : start + count]:
|
|
if 0 <= int(face_row_index) < len(index.faces):
|
|
selectors.append(str(index.faces[int(face_row_index)].get("id")))
|
|
return selectors
|
|
|
|
|
|
def edge_adjacent_vertex_selectors(edge_row: dict[str, Any], index: SelectorIndex) -> list[str]:
|
|
start = int(edge_row.get("vertexStart") or 0)
|
|
count = int(edge_row.get("vertexCount") or 0)
|
|
selectors: list[str] = []
|
|
for vertex_row_index in index.relations.get("edgeVertexRows", [])[start : start + count]:
|
|
if 0 <= int(vertex_row_index) < len(index.vertices):
|
|
selectors.append(str(index.vertices[int(vertex_row_index)].get("id")))
|
|
return selectors
|
|
|
|
|
|
def vertex_adjacent_edge_selectors(vertex_row: dict[str, Any], index: SelectorIndex) -> list[str]:
|
|
start = int(vertex_row.get("edgeStart") or 0)
|
|
count = int(vertex_row.get("edgeCount") or 0)
|
|
selectors: list[str] = []
|
|
for edge_row_index in index.relations.get("vertexEdgeRows", [])[start : start + count]:
|
|
if 0 <= int(edge_row_index) < len(index.edges):
|
|
selectors.append(str(index.edges[int(edge_row_index)].get("id")))
|
|
return selectors
|
|
|
|
|
|
def vertex_adjacent_face_selectors(vertex_row: dict[str, Any], index: SelectorIndex) -> list[str]:
|
|
selectors: list[str] = []
|
|
seen: set[str] = set()
|
|
for edge_selector in vertex_adjacent_edge_selectors(vertex_row, index):
|
|
edge_row = index.edge_by_id.get(edge_selector)
|
|
if edge_row is None:
|
|
continue
|
|
for face_selector in edge_adjacent_face_selectors(edge_row, index):
|
|
if face_selector not in seen:
|
|
seen.add(face_selector)
|
|
selectors.append(face_selector)
|
|
return selectors
|
|
|
|
|
|
def topology_payload(index: SelectorIndex) -> dict[str, list[str]]:
|
|
return {
|
|
"occurrences": [display_selector(str(row.get("id")), index) for row in index.occurrences if row.get("id")],
|
|
"shapes": [display_selector(str(row.get("id")), index) for row in index.shapes if row.get("id")],
|
|
"faces": [display_selector(str(row.get("id")), index) for row in index.faces if row.get("id")],
|
|
"edges": [display_selector(str(row.get("id")), index) for row in index.edges if row.get("id")],
|
|
"vertices": [display_selector(str(row.get("id")), index) for row in index.vertices if row.get("id")],
|
|
}
|