Add Chromium-only Blender WebEngine parity work
This commit is contained in:
78
web/protocol/modifier-graph.ts
Normal file
78
web/protocol/modifier-graph.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import type { MeshSummaryIR, ModifierIR, SceneSnapshotIR } from "./scene-ir";
|
||||
|
||||
export interface ModifierGraphNode {
|
||||
uuid: string;
|
||||
meshId: string;
|
||||
modifier: ModifierIR;
|
||||
dependencies: string[];
|
||||
}
|
||||
|
||||
export interface ModifierDependencyGraph {
|
||||
nodes: ModifierGraphNode[];
|
||||
order: string[];
|
||||
cycles: string[][];
|
||||
missingDependencies: string[];
|
||||
}
|
||||
|
||||
export interface ModifierEvaluationReport {
|
||||
status: "EVALUATED" | "METADATA_ONLY" | "BLOCKED";
|
||||
order: string[];
|
||||
cycles: string[][];
|
||||
blockedBy: string[];
|
||||
}
|
||||
|
||||
export function buildModifierDependencyGraph(snapshot: Pick<SceneSnapshotIR, "meshes">): ModifierDependencyGraph {
|
||||
const nodes: ModifierGraphNode[] = [];
|
||||
const known = new Set<string>();
|
||||
for (const mesh of snapshot.meshes) {
|
||||
let previous: string | undefined;
|
||||
for (const modifier of mesh.modifierStack ?? []) {
|
||||
const dependencies = Array.from(new Set([...(modifier.dependsOn ?? []), ...(previous ? [previous] : [])]));
|
||||
nodes.push({ uuid: modifier.uuid, meshId: mesh.id, modifier, dependencies });
|
||||
known.add(modifier.uuid);
|
||||
previous = modifier.uuid;
|
||||
}
|
||||
}
|
||||
const nodeById = new Map(nodes.map((node) => [node.uuid, node]));
|
||||
const state = new Map<string, 0 | 1 | 2>();
|
||||
const order: string[] = [];
|
||||
const cycles: string[][] = [];
|
||||
const missingDependencies: string[] = [];
|
||||
const path: string[] = [];
|
||||
const visit = (uuid: string): void => {
|
||||
const current = state.get(uuid) ?? 0;
|
||||
if (current === 2) return;
|
||||
if (current === 1) {
|
||||
const start = path.indexOf(uuid);
|
||||
cycles.push(start >= 0 ? [...path.slice(start), uuid] : [uuid]);
|
||||
return;
|
||||
}
|
||||
state.set(uuid, 1);
|
||||
path.push(uuid);
|
||||
for (const dependency of nodeById.get(uuid)?.dependencies ?? []) {
|
||||
if (known.has(dependency)) visit(dependency);
|
||||
else missingDependencies.push(`${uuid}:${dependency}`);
|
||||
}
|
||||
path.pop();
|
||||
state.set(uuid, 2);
|
||||
order.push(uuid);
|
||||
};
|
||||
for (const node of nodes) visit(node.uuid);
|
||||
return { nodes, order, cycles, missingDependencies: Array.from(new Set(missingDependencies)).sort() };
|
||||
}
|
||||
|
||||
export function evaluateModifierDependencyGraph(snapshot: Pick<SceneSnapshotIR, "meshes">): ModifierEvaluationReport {
|
||||
const graph = buildModifierDependencyGraph(snapshot);
|
||||
const blockedBy = graph.nodes.filter((node) => node.modifier.evaluationStatus === "BLOCKED").map((node) => node.uuid);
|
||||
if (graph.cycles.length > 0) return { status: "BLOCKED", order: graph.order, cycles: graph.cycles, blockedBy: [...blockedBy, "MODIFIER_DEPENDENCY_CYCLE"] };
|
||||
if (graph.missingDependencies.length > 0) return { status: "BLOCKED", order: graph.order, cycles: [], blockedBy: [...blockedBy, ...graph.missingDependencies] };
|
||||
if (blockedBy.length > 0) return { status: "BLOCKED", order: graph.order, cycles: [], blockedBy };
|
||||
if (graph.nodes.some((node) => node.modifier.evaluationStatus === "METADATA_ONLY")) {
|
||||
return { status: "METADATA_ONLY", order: graph.order, cycles: [], blockedBy: [] };
|
||||
}
|
||||
return { status: "EVALUATED", order: graph.order, cycles: [], blockedBy: [] };
|
||||
}
|
||||
|
||||
export function evaluateMeshModifierStack(mesh: MeshSummaryIR): ModifierEvaluationReport {
|
||||
return evaluateModifierDependencyGraph({ meshes: [mesh] });
|
||||
}
|
||||
Reference in New Issue
Block a user