export var FuncType;
(function (FuncType) {
FuncType["Byte"] = "Byte";
FuncType["Int"] = "Int";
FuncType["Float"] = "Float";
FuncType["Vec2i"] = "Vec2i";
FuncType["Vec2f"] = "Vec2f";
FuncType["Vec3i"] = "Vec3i";
FuncType["Vec3f"] = "Vec3f";
FuncType["Vec4i"] = "Vec4i";
FuncType["Vec4f"] = "Vec4f";
FuncType["Mat2f"] = "Mat2f";
FuncType["Mat3f"] = "Mat3f";
FuncType["Mat4f"] = "Mat4f";
})(FuncType || (FuncType = {}));
const pointSourceVarType = "_PointSource";
/**
* Instance of a function.
* Function instances may be composed with other functions to build shape graphs.
*/
export class Func {
type;
inputs;
assets;
parameters;
constructor(type, inputs,
/* eslint-disable @typescript-eslint/no-explicit-any */
assets, parameters) {
this.type = type;
this.inputs = inputs;
this.assets = assets;
this.parameters = parameters;
}
/**
* Resolve function call tree to a Metafold shape graph.
*/
json(points) {
if (this.type === pointSourceVarType) {
throw new Error("Failed to resolve point source");
}
const dag = {
nodes: new Set(),
edges: new Map(),
};
const toVisit = [this];
while (toVisit.length > 0) {
const func = toVisit.pop();
if (dag.nodes.has(func))
continue;
dag.nodes.add(func);
for (const [name, source_] of Object.entries(func.inputs ?? {})) {
let source = source_;
if (source_.type === pointSourceVarType) {
if (!points || points.type === pointSourceVarType) {
throw new Error("Expected valid point source");
}
source = points;
}
toVisit.push(source);
if (!dag.edges.has(source))
dag.edges.set(source, []);
dag.edges.get(source).push({ name, func });
}
}
// TODO(ryan): Sort graph in topological order so that edge indices make more sense
const indices = new Map();
const g = {
operators: [],
edges: [],
};
for (const func of dag.nodes.values()) {
const operator = { type: func.type };
if (func.parameters) {
operator.parameters = Object.fromEntries(Object.entries(func.parameters).map(([k, v]) => {
// Convert three.js types to arrays
if (typeof v === "object" && "toArray" in v && typeof v.toArray === "function")
return [k, v.toArray()];
return [k, v];
}));
}
if (func.assets) {
operator.parameters = Object.assign(operator.parameters ?? {}, func.assets);
}
const index = g.operators.push(operator) - 1;
indices.set(func, index);
}
for (const [source, targets] of Array.from(dag.edges.entries())) {
const sourceIndex = indices.get(source);
for (const { func, name: targetName } of targets) {
const targetIndex = indices.get(func);
g.edges.push({
source: sourceIndex,
target: [targetIndex, targetName],
});
}
}
return g;
}
}
/** Typed instance of a function to enable type checking for function compositions. */
export class TypedFunc extends Func {
assets;
parameters;
returnType;
constructor(type, inputs,
/* eslint-disable @typescript-eslint/no-explicit-any */
assets, parameters,
/* eslint-enable @typescript-eslint/no-explicit-any */
// Generic parameters are erased unless they are used in the type
returnType) {
super(type, inputs, parameters);
this.assets = assets;
this.parameters = parameters;
this.returnType = returnType;
}
}
/**
* Dummy point source.
* May be used in place of the (primary) point source for an operator to defer provision of a point
* source until graph resolution. This allows you to re-use a function composition with different
* point sources.
*/
export const POINT_SOURCE = new TypedFunc(pointSourceVarType);