-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #404 from nengo/html-nodes
HTMLView component for custom visualizations
- Loading branch information
Showing
8 changed files
with
291 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import struct | ||
import collections | ||
|
||
import nengo | ||
import numpy as np | ||
|
||
from nengo_gui.components.component import Component | ||
|
||
|
||
class HTMLView(Component): | ||
def __init__(self, obj): | ||
super(HTMLView, self).__init__() | ||
self.obj = obj | ||
self.obj_output = obj.output | ||
self.data = collections.deque() | ||
|
||
def attach(self, page, config, uid): | ||
super(HTMLView, self).attach(page, config, uid) | ||
self.label = page.get_label(self.obj) | ||
|
||
def add_nengo_objects(self, page): | ||
with page.model: | ||
self.obj.output = self.gather_data | ||
|
||
def remove_nengo_objects(self, page): | ||
self.obj.output = self.obj_output | ||
|
||
def gather_data(self, t, *x): | ||
value = self.obj_output(t, *x) | ||
data = '%g %s' % (t, self.obj_output._nengo_html_) | ||
self.data.append(data) | ||
return value | ||
|
||
def update_client(self, client): | ||
while len(self.data) > 0: | ||
item = self.data.popleft() | ||
client.write(item) | ||
|
||
def javascript(self): | ||
info = dict(uid=id(self), label=self.label) | ||
json = self.javascript_config(info) | ||
return 'new Nengo.HTMLView(main, sim, %s);' % json | ||
|
||
def code_python_args(self, uids): | ||
return [uids[self.obj]] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# Creating Custom Displays | ||
|
||
# This demonstrates using the special _nengo_html_ variable to create | ||
# custom visualizations. In general, this lets you take values from the model | ||
# and use them to make HTML that displays something useful for your situation. | ||
# | ||
# The basic technique is that if you define a function for a Node, in addition | ||
# to returning a value, you can also set the _nengo_html_ parameter on that | ||
# function. You can set it to any HTML you want, and that HTML will be | ||
# displayed in a custom HTML graph (available by right-clicking on the Node). | ||
|
||
# The first example is a timer function that says "Ready..." for the | ||
# first second of the simulation, then says "Set..." for the next second, | ||
# and then says "Go!". This shows how you can change the HTML based on the | ||
# time of the simulation. | ||
# | ||
# The second example shows that the custom HTML can also be based on the values | ||
# represented by neurons. Here, we make a Node that reads in a value that is | ||
# treated as an amount. If the value is above 0.5, the HTML says "large", | ||
# if the value is below -0.5, it says "small", and otherwise it says "medium". | ||
# | ||
# Finally, the third example uses SVG and trigonometry to create a simple | ||
# visualization of a two-joint arm. The two values represented by the | ||
# Ensemble are the two joint angles (the shoulder and the elbow). From these | ||
# values, the position of the elbow and hand are computed, and lines are | ||
# drawn to show the arm. | ||
|
||
import nengo | ||
import numpy as np | ||
|
||
model = nengo.Network() | ||
with model: | ||
|
||
# Example 1: a timer | ||
def timer_function(t): | ||
if t < 1.0: | ||
timer_function._nengo_html_ = '<h1>Ready...</h1>' | ||
return 0 | ||
elif t < 2.0: | ||
timer_function._nengo_html_ = '<h1>Set...</h1>' | ||
return 0 | ||
else: | ||
timer_function._nengo_html_ = '<h1>Go!</h1>' | ||
return 1 | ||
timer = nengo.Node(timer_function) | ||
|
||
|
||
# Example 2: displaying a value | ||
def amount_function(t, x): | ||
if x < -0.5: | ||
amount_function._nengo_html_ = '<h2>small</h2>' | ||
elif -0.5 < x < 0.5: | ||
amount_function._nengo_html_ = '<h2>medium</h2>' | ||
else: | ||
amount_function._nengo_html_ = '<h2>large</h2>' | ||
stim_amount = nengo.Node(0) | ||
amount = nengo.Ensemble(n_neurons=100, dimensions=1) | ||
display_amount = nengo.Node(amount_function, size_in=1) | ||
nengo.Connection(stim_amount, amount) | ||
nengo.Connection(amount, display_amount) | ||
|
||
|
||
# Example 3: a two-joint arm | ||
def arm_function(t, angles): | ||
len0 = 50 | ||
len1 = 30 | ||
x1 = 50 | ||
y1 = 100 | ||
x2 = x1 + len0 * np.sin(angles[0]) | ||
y2 = y1 - len0 * np.cos(angles[0]) | ||
x3 = x2 + len1 * np.sin(angles[0] + angles[1]) | ||
y3 = y2 - len1 * np.cos(angles[0] + angles[1]) | ||
arm_function._nengo_html_ = ''' | ||
<svg width="100%" height="100%" viewbox="0 0 100 100"> | ||
<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" style="stroke:black"/> | ||
<line x1="{x2}" y1="{y2}" x2="{x3}" y2="{y3}" style="stroke:black"/> | ||
</svg> | ||
'''.format(**locals()) | ||
stim_angles = nengo.Node([0.3, 0.3]) | ||
angles = nengo.Ensemble(n_neurons=200, dimensions=2) | ||
arm = nengo.Node(arm_function, size_in=2) | ||
nengo.Connection(stim_angles, angles) | ||
nengo.Connection(angles, arm) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
_viz_0 = nengo_gui.components.HTMLView(arm) | ||
_viz_config[_viz_0].label_visible = True | ||
_viz_config[_viz_0].width = 0.145178 | ||
_viz_config[_viz_0].height = 0.151002 | ||
_viz_config[_viz_0].y = 0.768603 | ||
_viz_config[_viz_0].x = 0.876166 | ||
_viz_1 = nengo_gui.components.Slider(stim_angles) | ||
_viz_config[_viz_1].label_visible = True | ||
_viz_config[_viz_1].width = 0.124673 | ||
_viz_config[_viz_1].x = 0.233144 | ||
_viz_config[_viz_1].y = 0.790798 | ||
_viz_config[_viz_1].max_value = 1 | ||
_viz_config[_viz_1].min_value = -1 | ||
_viz_config[_viz_1].height = 0.145663 | ||
_viz_2 = nengo_gui.components.HTMLView(timer) | ||
_viz_config[_viz_2].label_visible = True | ||
_viz_config[_viz_2].width = 0.197581 | ||
_viz_config[_viz_2].height = 0.0953106 | ||
_viz_config[_viz_2].y = 0.0978446 | ||
_viz_config[_viz_2].x = 0.833259 | ||
_viz_4 = nengo_gui.components.Value(timer) | ||
_viz_config[_viz_4].label_visible = True | ||
_viz_config[_viz_4].width = 0.145816 | ||
_viz_config[_viz_4].x = 0.497152 | ||
_viz_config[_viz_4].y = 0.0915505 | ||
_viz_config[_viz_4].max_value = 1 | ||
_viz_config[_viz_4].min_value = -1 | ||
_viz_config[_viz_4].height = 0.101605 | ||
_viz_5 = nengo_gui.components.Slider(stim_amount) | ||
_viz_config[_viz_5].label_visible = False | ||
_viz_config[_viz_5].width = 0.0998842 | ||
_viz_config[_viz_5].x = 0.212906 | ||
_viz_config[_viz_5].y = 0.379993 | ||
_viz_config[_viz_5].max_value = 1 | ||
_viz_config[_viz_5].min_value = -1 | ||
_viz_config[_viz_5].height = 0.143865 | ||
_viz_6 = nengo_gui.components.HTMLView(display_amount) | ||
_viz_config[_viz_6].label_visible = True | ||
_viz_config[_viz_6].width = 0.127589 | ||
_viz_config[_viz_6].height = 0.1061 | ||
_viz_config[_viz_6].y = 0.415959 | ||
_viz_config[_viz_6].x = 0.856185 | ||
_viz_ace_editor = nengo_gui.components.AceEditor() | ||
_viz_net_graph = nengo_gui.components.NetGraph() | ||
_viz_sim_control = nengo_gui.components.SimControl() | ||
_viz_config[_viz_sim_control].kept_time = 4 | ||
_viz_config[_viz_sim_control].shown_time = 0.5 | ||
_viz_config[amount].pos=(0.5338306914210958, 0.4086156823527614) | ||
_viz_config[amount].size=(0.09749681440124454, 0.10359036530132232) | ||
_viz_config[angles].pos=(0.5519433694044356, 0.7832269165834734) | ||
_viz_config[angles].size=(0.07919650508520541, 0.09767091585553361) | ||
_viz_config[arm].pos=(0.8780716431486573, 0.7769328184385826) | ||
_viz_config[arm].size=(0.158196444012342, 0.14761158744564548) | ||
_viz_config[display_amount].pos=(0.8561991906510176, 0.41041399610844403) | ||
_viz_config[display_amount].size=(0.1392403185143874, 0.11883856735471768) | ||
_viz_config[model].pos=(-0.04680408247364714, 0.01466241789112259) | ||
_viz_config[model].size=(1.0055632718705836, 1.0055632718705836) | ||
_viz_config[model].expanded=True | ||
_viz_config[model].has_layout=True | ||
_viz_config[stim_amount].pos=(0.21292035569101672, 0.4032207410857123) | ||
_viz_config[stim_amount].size=(0.10132806751847824, 0.11883856735471773) | ||
_viz_config[stim_angles].pos=(0.23084097794972894, 0.786109085846491) | ||
_viz_config[stim_angles].size=(0.1255178668096988, 0.13956290262502596) | ||
_viz_config[timer].pos=(0.20136940789451646, 0.10488798199188551) | ||
_viz_config[timer].size=(0.10038894308056127, 0.08287229224105784) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** | ||
* Decoded pointer display | ||
* @constructor | ||
* | ||
* @param {dict} args - A set of constructor arguments (see Nengo.Component) | ||
* @param {Nengo.SimControl} args.sim - the simulation controller | ||
*/ | ||
|
||
Nengo.HTMLView = function(parent, sim, args) { | ||
Nengo.Component.call(this, parent, args); | ||
var self = this; | ||
|
||
this.sim = sim; | ||
|
||
this.pdiv = document.createElement('div'); | ||
this.pdiv.style.width = '100%'; | ||
this.pdiv.style.height = '100%'; | ||
Nengo.set_transform(this.pdiv, 0, 0); | ||
this.pdiv.style.position = 'fixed'; | ||
this.pdiv.classList.add('htmlview'); | ||
this.div.appendChild(this.pdiv); | ||
|
||
/** for storing the accumulated data */ | ||
this.data_store = new Nengo.DataStore(1, this.sim, 0); | ||
|
||
/** call schedule_update whenever the time is adjusted in the SimControl */ | ||
this.sim.div.addEventListener('adjust_time', | ||
function(e) {self.schedule_update();}, false); | ||
|
||
this.on_resize(this.get_screen_width(), this.get_screen_height()); | ||
|
||
|
||
|
||
}; | ||
Nengo.HTMLView.prototype = Object.create(Nengo.Component.prototype); | ||
Nengo.HTMLView.prototype.constructor = Nengo.Pointer; | ||
|
||
|
||
/** | ||
* Receive new line data from the server | ||
*/ | ||
Nengo.HTMLView.prototype.on_message = function(event) { | ||
var data = event.data.split(" ", 1); | ||
var time = parseFloat(data[0]); | ||
|
||
var msg = event.data.substring(data[0].length + 1); | ||
|
||
this.data_store.push([time, msg]); | ||
this.schedule_update(); | ||
} | ||
|
||
/** | ||
* Redraw the lines and axis due to changed data | ||
*/ | ||
Nengo.HTMLView.prototype.update = function() { | ||
/** let the data store clear out old values */ | ||
this.data_store.update(); | ||
|
||
var data = this.data_store.get_last_data()[0]; | ||
|
||
if (data === undefined) { | ||
data = ''; | ||
} | ||
|
||
this.pdiv.innerHTML = data; | ||
|
||
}; | ||
|
||
/** | ||
* Adjust the graph layout due to changed size | ||
*/ | ||
Nengo.HTMLView.prototype.on_resize = function(width, height) { | ||
if (width < this.minWidth) { | ||
width = this.minWidth; | ||
} | ||
if (height < this.minHeight) { | ||
height = this.minHeight; | ||
}; | ||
|
||
this.width = width; | ||
this.height = height; | ||
//this.div.style.width = width; | ||
//this.div.style.height = height; | ||
|
||
this.label.style.width = width; | ||
|
||
this.update(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters