func-types.js

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);