Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update signal graph nodes in topological order. #658

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libraries/Native/Show.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Elm.Native.Show.make = function(elm) {
return v.ctor + output;
}
}
if (type === 'object' && 'recv' in v) return '<signal>';
if (type === 'object' && 'update' in v) return '<signal>';
return "<internal structure>";
};

Expand Down
49 changes: 17 additions & 32 deletions libraries/Native/Signal/Keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@ Elm.Native.Keyboard.make = function(elm) {
elm.Native.Keyboard = elm.Native.Keyboard || {};
if (elm.Native.Keyboard.values) return elm.Native.Keyboard.values;

// Duplicated from Native.Signal
function send(node, timestep, changed) {
var kids = node.kids;
for (var i = kids.length; i--; ) {
kids[i].recv(timestep, changed, node.id);
}
}

var Signal = Elm.Signal.make(elm);
var NList = Elm.Native.List.make(elm);
var Utils = Elm.Native.Utils.make(elm);
Expand All @@ -39,35 +31,28 @@ Elm.Native.Keyboard.make = function(elm) {
// Ignore starting values here
this.value = NList.Nil
this.kids = [];

var n = args.length;
var count = 0;
var isChanged = false;

this.recv = function(timestep, changed, parentID) {
++count;
if (changed) {
// We know this a change must only be one of the following cases
if (parentID === down.id && !(NList.member(down.value)(this.value))) {
isChanged = true;
this.value = NList.Cons(down.value, this.value);
}
if (parentID === up.id) {
isChanged = true;
var notEq = function(kc) { return kc !== up.value };
this.value = NList.filter(notEq)(this.value);
}
if (parentID === blur.id) {
isChanged = true;
this.value = NList.Nil;
}
}
if (count == n) {
send(this, timestep, isChanged);
isChanged = false;
count = 0;
this.update = function(timestep, parentID) {
// We know this a change must only be one of the following cases
if (parentID === down.id && !(NList.member(down.value)(this.value))) {
this.value = NList.Cons(down.value, this.value);
return true;
}
if (parentID === up.id) {
var notEq = function(kc) { return kc !== up.value };
this.value = NList.filter(notEq)(this.value);
return true;
}
if (parentID === blur.id) {
this.value = NList.Nil;
return true;
}
};
return false;
}

for (var i = n; i--; ) { args[i].kids.push(this); }

Expand Down
95 changes: 33 additions & 62 deletions libraries/Native/Signal/Signal.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,11 @@ Elm.Native.Signal.make = function(elm) {
var Utils = Elm.Native.Utils.make(elm);
var foldr1 = Elm.List.make(elm).foldr1;

function send(node, timestep, changed) {
var kids = node.kids;
for (var i = kids.length; i--; ) {
kids[i].recv(timestep, changed, node.id);
}
}

function Input(base) {
this.id = Utils.guid();
this.value = base;
this.kids = [];
this.defaultNumberOfKids = 0;
this.recv = function(timestep, eid, v) {
var changed = eid === this.id;
if (changed) { this.value = v; }
send(this, timestep, changed);
return changed;
};
elm.inputs.push(this);
}

Expand All @@ -39,15 +26,9 @@ Elm.Native.Signal.make = function(elm) {
var count = 0;
var isChanged = false;

this.recv = function(timestep, changed, parentID) {
++count;
if (changed) { isChanged = true; }
if (count == n) {
if (isChanged) { this.value = update(); }
send(this, timestep, isChanged);
isChanged = false;
count = 0;
}
this.update = function(timestep, parentID) {
this.value = update();
return true;
};
for (var i = n; i--; ) { args[i].kids.push(this); }
}
Expand Down Expand Up @@ -90,11 +71,9 @@ Elm.Native.Signal.make = function(elm) {
this.value = state;
this.kids = [];

this.recv = function(timestep, changed, parentID) {
if (changed) {
this.value = A2( step, input.value, this.value );
}
send(this, timestep, changed);
this.update = function(timestep, parentID) {
this.value = A2( step, input.value, this.value );
return true;
};
input.kids.push(this);
}
Expand All @@ -107,10 +86,13 @@ Elm.Native.Signal.make = function(elm) {
this.id = Utils.guid();
this.value = pred(input.value) ? base : input.value;
this.kids = [];
this.recv = function(timestep, changed, parentID) {
var chng = changed && !pred(input.value);
if (chng) { this.value = input.value; }
send(this, timestep, chng);
this.update = function(timestep, parentID) {
if (!pred(input.value)) {
this.value = input.value;
return true;
} else {
return false;
}
};
input.kids.push(this);
}
Expand All @@ -119,11 +101,14 @@ Elm.Native.Signal.make = function(elm) {
this.id = Utils.guid();
this.value = input.value;
this.kids = [];
this.recv = function(timestep, changed, parentID) {
var chng = changed && !Utils.eq(this.value,input.value);
if (chng) { this.value = input.value; }
send(this, timestep, chng);
};
this.update = function(timestep, parentID) {
if (!Utils.eq(this.value,input.value)) {
this.value = input.value;
return true;
} else {
return false;
}
}
input.kids.push(this);
}

Expand All @@ -140,18 +125,11 @@ Elm.Native.Signal.make = function(elm) {
var count = 0;
var isChanged = false;

this.recv = function(timestep, changed, parentID) {
if (parentID === s1.id) isChanged = changed;
++count;
if (count == 2) {
if (isChanged) { this.value = s2.value; }
send(this, timestep, isChanged);
count = 0;
isChanged = false;
}
};
this.update = function(timestep, parentID) {
this.value = s2.value;
return true;
}
s1.kids.push(this);
s2.kids.push(this);
}

function sampleOn(s1,s2) { return new SampleOn(s1,s2); }
Expand All @@ -171,26 +149,19 @@ Elm.Native.Signal.make = function(elm) {
this.id = Utils.guid();
this.value = s1.value;
this.kids = [];
this.parentRank = {};
this.parentRank[s1.id] = 1;
this.parentRank[s2.id] = 2;

var next = null;
var count = 0;
var isChanged = false;

this.recv = function(timestep, changed, parentID) {
++count;
if (changed) {
isChanged = true;
if (parentID == s2.id && next === null) { next = s2.value; }
if (parentID == s1.id) { next = s1.value; }
}

if (count == 2) {
if (isChanged) { this.value = next; next = null; }
send(this, timestep, isChanged);
isChanged = false;
count = 0;
}
};
this.update = function(timestep, parentID) {
if (s1.id == parentID) this.value = s1.value;
if (s2.id == parentID) this.value = s2.value;
return true;
}
s1.kids.push(this);
s2.kids.push(this);
}
Expand Down
57 changes: 53 additions & 4 deletions runtime/Init.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,46 @@ function init(display, container, module, ports, moduleToReplace) {
var inputs = [];

var updateInProgress = false;
function propagate(startNode, timestep) {
var parentMap = {};
var heap = new ElmRuntime.BinaryHeap(function (i) { return i.node.id; });
for (var i = 0, l = startNode.kids.length; i < l; i++) {
var node = startNode.kids[i];
if (node.parentRank) {
parentMap[node.id] = startNode.id;
}
heap.push({node: node, parent: startNode.id});
}

var prev = null;
while (heap.size() > 0) {
var next = heap.pop(), node = next.node;
if (node.id == prev) {
continue;
}
var goOn = false;
if (node.parentRank) {
if (parentMap[next.node.id]) {
goOn = node.update(timestep, parentMap[node.id]);
}
} else {
goOn = node.update(timestep, next.parent);
}
if (goOn) {
for (var i = 0, l = node.kids.length; i < l; i++) {
var child = node.kids[i];
if (child.parentRank) {
if (child.parentRank[parentMap[child.id]] > child.parentRank[node.id]) {
parentMap[child.id] = node.id;
}
}
heap.push({node: child, parent: node.id});
}
}
prev = node.id;
}
}

function notify(id, v) {
if (updateInProgress) {
throw new Error(
Expand All @@ -40,10 +80,17 @@ function init(display, container, module, ports, moduleToReplace) {
'Definitely report this to <https://github.com/elm-lang/Elm/issues>\n');
}
updateInProgress = true;
var timestep = Date.now();
var timestep = Date.now(), input = null;

for (var i = inputs.length; i--; ) {
inputs[i].recv(timestep, id, v);
if (inputs[i].id == id) {
input = inputs[i];
break;
}
}

input.value = v;
propagate(input, timestep);
updateInProgress = false;
}

Expand Down Expand Up @@ -105,7 +152,9 @@ function init(display, container, module, ports, moduleToReplace) {

// rerender scene if graphics are enabled.
if (typeof graphicsNode !== 'undefined') {
graphicsNode.recv(0, true, 0);
if (graphicsNode.update(0, 0)) {
propagate(graphicsNode, 0);
}
}
}

Expand Down Expand Up @@ -210,7 +259,7 @@ function initGraphics(elm, Module) {

// make sure the signal graph is actually a signal & extract the visual model
var Signal = Elm.Signal.make(elm);
if (!('recv' in signalGraph)) {
if (!('update' in signalGraph)) {
signalGraph = Signal.constant(signalGraph);
}
var currentScene = signalGraph.value;
Expand Down
Loading