8bitworkshop/src/common/script/node.ts

166 lines
3.6 KiB
TypeScript

var lastTimestamp = 0;
function newTimestamp() {
return ++lastTimestamp;
}
export type DependencySet = {[id:string] : ComputeNode | any}
export abstract class ComputeNode {
private src_ts: number = newTimestamp();
private result_ts: number = 0;
private depends: DependencySet = {};
private busy: Promise<void> = null;
modified() {
this.src_ts = newTimestamp();
}
isStale(ts: number) {
return this.result_ts < ts;
}
setDependencies(depends: DependencySet) {
this.depends = depends;
// compute latest timestamp of all dependencies
var ts = 0;
for (let [key, dep] of Object.entries(this.depends)) {
if (dep instanceof ComputeNode && dep.result_ts) {
ts = Math.max(ts, dep.result_ts);
} else {
ts = newTimestamp();
}
}
this.src_ts = ts;
}
getDependencies() {
return this.depends;
}
async update() : Promise<void> {
let maxts = 0;
let dependsComputes = []
for (let [key, dep] of Object.entries(this.depends)) {
if (dep instanceof ComputeNode && dep.isStale(this.src_ts)) {
dependsComputes.push(dep.compute());
}
}
if (dependsComputes.length) {
await Promise.all(dependsComputes);
this.recompute(maxts);
}
}
async recompute(ts: number) : Promise<void> {
// are we currently waiting for a computation to finish?
if (this.busy == null || ts > this.result_ts) {
// wait for previous operation to finish (no-op if null)
await this.busy;
this.result_ts = ts;
this.busy = this.compute();
}
await this.busy;
this.busy = null;
}
abstract compute(): Promise<void>;
}
class ValueNode<T> extends ComputeNode {
private value : T;
constructor(value : T) {
super();
this.set(value);
}
get() : T {
return this.value;
}
set(newValue : T) {
this.value = newValue;
this.modified();
}
async compute() { }
}
class ArrayNode<T> extends ValueNode<T> {
}
class IntegerNode extends ValueNode<number> {
}
abstract class BitmapNode extends ComputeNode {
width: number;
height: number;
}
class RGBABitmapNode extends BitmapNode {
rgba: ArrayNode<Uint32Array>;
compute(): Promise<void> {
throw new Error("Method not implemented.");
}
}
class IndexedBitmapNode extends BitmapNode {
indices: ArrayNode<Uint8Array>;
compute(): Promise<void> {
throw new Error("Method not implemented.");
}
}
class PaletteNode {
colors: ArrayNode<Uint32Array>;
compute(): Promise<void> {
throw new Error("Method not implemented.");
}
}
class PaletteMapNode extends ComputeNode {
palette: PaletteNode;
indices: ArrayNode<Uint8Array>;
compute(): Promise<void> {
throw new Error("Method not implemented.");
}
}
function valueOf<T>(node : ValueNode<T>) : T {
return node.get();
}
class TestNode extends ComputeNode {
value : string;
async compute() {
await new Promise(r => setTimeout(r, 100));
this.value = Object.values(this.getDependencies()).map(valueOf).join('');
}
}
///
async function test() {
var val1 = new ValueNode<number>(1234);
var arr1 = new ValueNode<number[]>([1,2,3]);
var join = new TestNode();
join.setDependencies({a:val1, b:arr1});
await join.update();
console.log(join);
val1.set(9999);
join.update();
val1.set(9989)
await join.update();
console.log(join);
}
test();