Skip to content

Commit

Permalink
reduce
Browse files Browse the repository at this point in the history
  • Loading branch information
felixroos committed Nov 27, 2024
1 parent 2cdb3d1 commit ef28f9b
Showing 1 changed file with 54 additions and 160 deletions.
214 changes: 54 additions & 160 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,22 +72,36 @@
}
}

const node = (type, value) => new Node(type, value);
function getNode(type, args, schema) {
const next = new Node(type);
next.schema = schema;
next.ins = args.map((arg) => parseInput(arg, next));
return next;
}

Node.prototype.withIns = function (...ins) {
this.ins = ins;
return this;
let register = (name, fn) => {
Node.prototype[name] = function (...args) {
return fn(this, ...args);
};
return fn;
};

function getNode(type, ...args) {
const next = node(type);
return next.withIns(...args.map((arg) => parseInput(arg, next)));
}
let registerNode = (type, schema) =>
register(type, (...args) => getNode(type, args, schema));

let n = register("n", (value) => {
if (typeof value === "object") {
return value;
}
return new Node("n", value);
});

function parseInput(input, node) {
// array = sequence of floats, function = value setter function
if (Array.isArray(input) || typeof input === "function") {
return new Node("float", input);
const floatNode = float();
floatNode.value = input;
return floatNode;
}
if (typeof input === "object") {
return input;
Expand All @@ -107,30 +121,6 @@
return 0;
}

let nodeRegistry = new Map();

let register = (name, fn, schema) => {
schema && nodeRegistry.set(name, schema);
Node.prototype[name] = function (...args) {
return fn(this, ...args);
};
return fn;
};

let registerNode = (type, schema) =>
register(type, (...args) => getNode(type, ...args), schema);

let n = register(
"n",
(value) => {
if (typeof value === "object") {
return value;
}
return node("n", value);
},
{} // the compiler has a special case for n, so no need to pass compile function
);

Node.prototype.apply = function (fn) {
return fn(this);
};
Expand All @@ -151,65 +141,51 @@
return node;
};

function flatten(node) {
const flat = [];
dfs(node, (node) => {
flat.push(node);
return node;
});
return flat.map((node) => {
let clone = {
...node,
type: node.type,
ins: node.ins.map((child) => flat.indexOf(child) + ""),
};
node.value !== undefined && (clone.value = node.value);
node.to !== undefined && (clone.to = flat.indexOf(node.to));
return clone;
});
// sort nodes by dependencies
function topoSort(graph) {
const sorted = [];
const visited = new Set();
function dfs(node) {
if (!(node instanceof Node) || visited.has(node)) {
return; // constant values or already visited nodes
}
visited.add(node);
for (let i in node.ins) {
dfs(node.ins[i]);
}
sorted.push(node);
}
dfs(graph);
return sorted;
}

function compile(node, options = {}) {
const {
log = false,
fallbackType = "thru",
constType = "n",
varPrefix = "n",
} = options;
const { log = false, constType = "n", varPrefix = "n" } = options;
log && console.log("compile", node);
const nodes = flatten(node);
// log && console.log("flat", nodes);
const sorted = topoSort(nodes);
const nodes = topoSort(node);
let lines = [];
let v = (id) => {
if (nodes[id].type !== constType) {
let v = (node) => {
if (node.type !== constType) {
const id = nodes.indexOf(node);
return `${varPrefix}${id}`;
}
if (typeof nodes[id].value === "string") {
return `"${nodes[id].value}"`;
if (typeof node.value === "string") {
return `"${node.value}"`;
}
return nodes[id].value;
return node.value;
};
for (let id of sorted) {
for (let id in nodes) {
const node = nodes[id];
const vars = nodes[id].ins.map((inlet) => v(inlet));

let schema = nodeRegistry.get(node.type);
if (!schema) {
console.warn(
`unhandled node type "${nodes[id].type}". falling back to "${fallbackType}"`
);
schema = nodeRegistry.get(fallbackType);
}
const meta = {
vars,
node,
nodes,
id,
name: v(id),
name: v(node),
};
if (schema.compile) {
lines.push(schema.compile(meta));
if (node.schema && node.schema.compile) {
lines.push(node.schema.compile(meta));
}
}

Expand All @@ -225,86 +201,6 @@
return compile(this, options);
};

// taken from noisecraft
// https://github.com/maximecb/noisecraft
// LICENSE: GPL-2.0

function topoSort(nodes) {
// Count the number of input edges going into a node
function countInEdges(nodeId) {
let node = nodes[nodeId];
let numIns = 0;

for (let i = 0; i < node.ins.length; ++i) {
let edge = node.ins[i];
if (!edge) continue;
if (remEdges.has(edge)) continue;
numIns++;
}

return numIns;
}

let S = []; // Set of nodes with no incoming edges
let L = []; // List sorted in reverse topological order
let remEdges = new Set(); // Map of input-side edges removed from the graph
let outEdges = new Map(); // Map of each node to a list of outgoing edges

// Populate the initial list of nodes without input edges
for (let nodeId in nodes) {
if (countInEdges(nodeId) == 0) {
S.push(nodeId);
}
}
// Initialize the set of list of output edges for each node
for (let nodeId in nodes) {
outEdges.set(nodeId, []);
}

// Populate the list of output edges for each node
for (let nodeId in nodes) {
let node = nodes[nodeId];

// For each input of this node
for (let i = 0; i < node.ins.length; ++i) {
let edge = node.ins[i];
if (edge === undefined) continue;

let srcId = node.ins[i];
let srcOuts = outEdges.get(srcId);
srcOuts.push([nodeId, edge]);
}
}

// While we have nodes with no inputs
while (S.length > 0) {
// Remove a node from S, add it at the end of L
var nodeId = S.pop();
L.push(nodeId);

// Get the list of output edges for this node
let nodeOuts = outEdges.get(nodeId);

// For each outgoing edge
for (let [dstId, edge] of nodeOuts) {
// Mark the edge as removed
remEdges.add(edge);

// If the node has no more incoming edges
if (countInEdges(dstId) == 0) S.push(dstId);
}
}
L = Array.from(new Set(L)); // <--- had to add this to make .apply(x=>x.mul(x)) work
// hopefully doesn't break anything

// If the topological ordering doesn't include all the nodes
if (L.length != Object.keys(nodes).length) {
throw SyntaxError("graph contains cycles");
}

return L;
}

//// Hydra specific logic

let glslNode = (functionName, schema) => {
Expand All @@ -314,14 +210,12 @@
if (!schema.args) {
throw new Error(`no "schema.args" set for node ${functionName}`);
}
return register(
functionName,
(...args) => getNode(functionName, ...schema.args(...args)),
{
return register(functionName, (...args) =>
getNode(functionName, schema.args(...args), {
compile: ({ vars, name }) =>
`${schema.type} ${name} = ${functionName}(${glslArgs(...vars)});`,
...schema,
}
})
);
};

Expand Down

0 comments on commit ef28f9b

Please sign in to comment.