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

Added ability to change color palettes #574

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1d87572
Added basic labels to value plot
Seanny123 Oct 1, 2015
8c99b64
added modal to edit labels
Seanny123 Oct 4, 2015
5825cd2
Create plots for the input and the output of the SPA Basal Ganglia
Seanny123 Oct 4, 2015
f328e9b
Added ability to choose different color palette
Seanny123 Oct 14, 2015
4921aa9
added color palette to config
Seanny123 Oct 14, 2015
91c57da
fixed legend setting behaviour
Seanny123 Oct 14, 2015
b83d12d
expanded palette switching to spa_similarity and bg_plot
Seanny123 Oct 16, 2015
74aa0f5
removed left-over ipdb
Seanny123 Oct 16, 2015
ded744a
Fixed id and empty labels error for spasimilarity
Seanny123 Oct 16, 2015
ed4f79e
Removed redundant variable from bg_plot
Seanny123 Oct 20, 2015
a91e6dc
Added basic labels to value plot
Seanny123 Oct 1, 2015
fe5caae
added modal to edit labels
Seanny123 Oct 4, 2015
a57699d
Create plots for the input and the output of the SPA Basal Ganglia
Seanny123 Oct 4, 2015
f64d726
Added ability to choose different color palette
Seanny123 Oct 14, 2015
8c06f90
added color palette to config
Seanny123 Oct 14, 2015
a7b5978
fixed legend setting behaviour
Seanny123 Oct 14, 2015
8683a1e
expanded palette switching to spa_similarity and bg_plot
Seanny123 Oct 16, 2015
0e7cef0
removed left-over ipdb
Seanny123 Oct 16, 2015
30b4e02
Fixed id and empty labels error for spasimilarity
Seanny123 Oct 16, 2015
91e0a31
Removed redundant variable from bg_plot
Seanny123 Oct 20, 2015
a913d86
fixed formatting stuff
Seanny123 Oct 28, 2015
9137f2a
fix bg plot disapearing problem
Seanny123 Oct 30, 2015
7acea6d
Merge branch 'more-colors' of github.com:nengo/nengo_gui into more-co…
Seanny123 Oct 30, 2015
89092b0
delete useless fil
Seanny123 Nov 16, 2015
3daa7ca
Merge remote-tracking branch 'origin/master' into more-colors
Seanny123 Nov 17, 2015
b319286
fixup
Seanny123 Nov 17, 2015
59272f3
fixup config
Seanny123 Nov 17, 2015
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
1 change: 1 addition & 0 deletions nengo_gui/components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .spa_similarity import SpaSimilarity
from .htmlview import HTMLView
from .spike_grid import SpikeGrid
from .bg_plot import BGPlot

# Old versions of the .cfg files used Templates which had slightly different
# names than the Components currently use. This code allows us to
Expand Down
59 changes: 59 additions & 0 deletions nengo_gui/components/bg_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import struct

import nengo
import numpy as np

from nengo_gui.components.value import Value


class BGPlot(Value):
"""The server-side system for the SPA Basal Ganglia plot."""

# the parameters to be stored in the .cfg file
config_defaults = Value.config_defaults
config_defaults["palette_index"] = 1
config_defaults["show_legend"] = True

def __init__(self, obj, **kwargs):
args = kwargs["args"]
super(BGPlot, self).__init__(obj, args["n_lines"])

# default legends to show
self.def_legend_labels = args["legend_labels"]

# the item to connect to
self.probe_target = args["probe_target"]

self.label = "bg " + self.probe_target

def attach(self, page, config, uid):
super(Value, self).attach(page, config, uid)

def add_nengo_objects(self, page):
# create a Node and a Connection so the Node will be given the
# data we want to show while the model is running.
with page.model:
self.node = nengo.Node(self.gather_data,
size_in=self.n_lines)
if self.probe_target == "input":
self.conn = nengo.Connection(self.obj.input, self.node, synapse=0.01)
else:
self.conn = nengo.Connection(self.obj.output, self.node, synapse=0.01)

def javascript(self):
# generate the javascript that will create the client-side object
info = dict(uid=id(self), label=self.label,
n_lines=self.n_lines, synapse=0)

if getattr(self.config, "legend_labels") == []:
self.config.legend_labels = self.def_legend_labels

json = self.javascript_config(info)
return 'new Nengo.Value(main, sim, %s);' % json

def code_python_args(self, uids):
return [
uids[self.obj],
' args=dict(n_lines=%s, legend_labels=%s, probe_target="%s")'
% (self.n_lines, self.config.legend_labels, self.probe_target,)
]
9 changes: 9 additions & 0 deletions nengo_gui/components/netgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,15 @@ def get_extra_info(self, obj):
elif isinstance(obj, nengo.Ensemble):
info['dimensions'] = int(obj.size_out)
info['n_neurons'] = int(obj.n_neurons)
# TODO: Add the same functionality for the BasalGanglia non-spa net
elif isinstance(obj, spa.BasalGanglia):
info['bg_inputs'] = obj.input.size_in
info['input_labels'] = []
for ac in obj.actions.actions:
if ac.name == None:
info['input_labels'].append(ac.condition.expression.__str__())
else:
info['input_labels'].append(ac.name)
elif Value.default_output(obj) is not None:
info['default_output'] = True

Expand Down
8 changes: 3 additions & 5 deletions nengo_gui/components/spa_similarity.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
class SpaSimilarity(SpaPlot):
"""Line graph showing semantic pointer decoded values over time"""

config_defaults = dict(max_value=1,
min_value=-1,
show_pairs=False,
config_defaults = dict(max_value=1.5, min_value=-1.5,
palette_index=1, show_pairs=False,
**Component.config_defaults)

def __init__(self, obj, **kwargs):
Expand Down Expand Up @@ -80,8 +79,7 @@ def update_legend(self, vocab):
def javascript(self):
"""Generate the javascript that will create the client-side object"""
info = dict(uid=id(self), label=self.label, n_lines=len(self.labels),
synapse=0, min_value=-1.5, max_value=1.5,
pointer_labels=self.labels)
synapse=0, pointer_labels=self.labels)
json = self.javascript_config(info)
return 'new Nengo.SpaSimilarity(main, sim, %s);' % json

Expand Down
1 change: 1 addition & 0 deletions nengo_gui/components/value.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Value(Component):

# the parameters to be stored in the .cfg file
config_defaults = dict(max_value=1, min_value=-1,
show_legend=False, legend_labels=[], palette_index=0,
**Component.config_defaults)

def __init__(self, obj):
Expand Down
45 changes: 45 additions & 0 deletions nengo_gui/static/color.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Generate a color sequence of a given length.
*/

/**
* Generate a color sequence of a given length.
*
* Colors are defined using a color blind-friendly palette.
*/
Nengo.make_colors = function(N) {
// Color blind palette with blue, green, red, magenta, yellow, cyan
var palette = ["#1c73b3", "#039f74", "#d65e00", "#cd79a7", "#f0e542", "#56b4ea"];
var c = [];

for (var i = 0; i < N; i++) {
c.push(palette[i % palette.length]);
}
return c;
}

/**
* Color blind-friendly palette.
*/
Nengo.default_colors = function() {
// Color blind palette with blue, green, red, magenta, yellow, cyan
var palette = ["#1c73b3", "#039f74", "#d65e00", "#cd79a7", "#f0e542", "#56b4ea"];
return function(i){ return palette[i%palette.length] };
}

/**
* Color palette use by Google for graphics, trends, etc...
*/
Nengo.google_colors = function() {
var palette = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"];
return function(i){ return palette[i%palette.length] };
}

/** list of valid color choices */
Nengo.color_choices = [
["Nengo Color-Blind Friendly (6 colors)", {"func":Nengo.default_colors(), "mod":6}],
["Google (20 colors)", {"func":Nengo.google_colors(), "mod":20}],
["D3.js A (20 colors)", {"func":d3.scale.category20(), "mod":20}],
["D3.js B (20 colors)", {"func":d3.scale.category20b(), "mod":20}],
["D3.js C (20 colors)", {"func":d3.scale.category20c(), "mod":20}]
];
22 changes: 22 additions & 0 deletions nengo_gui/static/components/netgraph_item.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ Nengo.NetGraphItem = function(ng, info, minimap, mini_item) {
this.g_items = ng.g_items_mini;
}

// SPA network specific parameter
this.sp_targets = info.sp_targets;
// Basal Ganglia network specific parameters
this.bg_inputs = info.bg_inputs;
this.input_labels = info.input_labels;

/** if this is a network, the children list is the set of NetGraphItems
* and NetGraphConnections that are inside this network */
this.children = [];
Expand Down Expand Up @@ -367,6 +373,22 @@ Nengo.NetGraphItem.prototype.generate_menu = function () {
items.push(['Semantic pointer plot',
function() {self.create_graph('SpaSimilarity', self.sp_targets[0]);}]);
}
if (this.bg_inputs) {
items.push(['Input Plot',
function () {
self.create_graph('BGPlot',
{"n_lines":self.bg_inputs, "legend_labels":self.input_labels, "probe_target":"input"}
);
}
]);
items.push(['Output Plot',
function () {
self.create_graph('BGPlot',
{"n_lines":self.bg_inputs, "legend_labels":self.input_labels, "probe_target":"output"}
);
}
]);
}
items.push(['Details ...', function() {self.create_modal();}]);
return items;
};
Expand Down
81 changes: 64 additions & 17 deletions nengo_gui/static/components/spa_similarity.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,14 @@ Nengo.SpaSimilarity = function(parent, sim, args) {

var self = this;

this.colors = Nengo.make_colors(6);
this.color_func = function(d, i) {return self.colors[i % 6]};

this.line.defined(function(d) { return !isNaN(d)});

// create the legend from label args
this.pointer_labels = args.pointer_labels;
this.legend_labels = args.pointer_labels;
this.legend = document.createElement('div');
this.legend.classList.add('legend', 'unselectable');
this.div.appendChild(this.legend);
this.legend_svg = Nengo.draw_legend(this.legend, args.pointer_labels, this.color_func);
this.legend_svg = Nengo.draw_legend(this.legend, this.legend_labels, this.color_func, this.uid);
};

Nengo.SpaSimilarity.prototype = Object.create(Nengo.Value.prototype);
Expand All @@ -42,10 +39,10 @@ Nengo.SpaSimilarity.prototype.reset_legend_and_data = function(new_labels){
while(this.legend.lastChild){
this.legend.removeChild(this.legend.lastChild);
}
this.legend_svg = d3.select(this.legend).append("svg");
this.legend_svg = d3.select(this.legend).append("svg").attr("id", "id"+this.uid);

// redraw all the legends if they exist
this.pointer_labels = [];
this.legend_labels = [];
if(new_labels[0] != ""){
this.update_legend(new_labels);
}
Expand All @@ -71,35 +68,35 @@ Nengo.SpaSimilarity.prototype.data_msg = function(push_data){
Nengo.SpaSimilarity.prototype.update_legend = function(new_labels){

var self = this;
this.pointer_labels = this.pointer_labels.concat(new_labels);
this.legend_labels = this.legend_labels.concat(new_labels);

// expand the height of the svg, where "20" is around the height of the font
this.legend_svg.attr("height", 20 * this.pointer_labels.length);
this.legend_svg.attr("height", 20 * this.legend_labels.length);


// Data join
var recs = this.legend_svg.selectAll("rect").data(this.pointer_labels);
var legend_labels = this.legend_svg.selectAll(".legend-label").data(this.pointer_labels);
var val_texts = this.legend_svg.selectAll(".val").data(this.pointer_labels);
var recs = this.legend_svg.selectAll("rect").data(this.legend_labels);
var legend_labels = this.legend_svg.selectAll(".legend-label").data(this.legend_labels);
var val_texts = this.legend_svg.selectAll(".val").data(this.legend_labels);
// enter to append remaining lines
recs.enter()
.append("rect")
.attr("x", 0)
.attr("y", function(d, i){ return i * 20;})
.attr("width", 10)
.attr("height", 10)
.style("fill", this.color_func);
.style("fill", function(d, i) {return self.color_func(i)});

legend_labels.enter().append("text")
.attr("x", 15)
.attr("y", function(d, i){ return i * 20 + 9;})
.attr("class", "legend-label")
.html(function(d, i) {
return self.pointer_labels[i];
return self.legend_labels[i];
});

// expand the width of the svg of the longest string
var label_list = $(".legend-label").toArray();
var label_list = $("#id"+this.uid+" .legend-label").toArray();
var longest_label = label_list.sort(
function (a, b) { return b.getBBox().width - a.getBBox().width; }
)[0];
Expand Down Expand Up @@ -152,7 +149,7 @@ Nengo.SpaSimilarity.prototype.update = function() {
this.path.enter()
.append('path')
.attr('class', 'line')
.style('stroke', this.color_func)
.style('stroke', function(d, i) {return self.color_func(i)})
.attr('d', self.line);
// remove any lines that aren't needed anymore
this.path.exit().remove();
Expand All @@ -166,7 +163,7 @@ Nengo.SpaSimilarity.prototype.update = function() {
}

// update the text in the legend
var texts = this.legend_svg.selectAll(".val").data(this.pointer_labels);
var texts = this.legend_svg.selectAll(".val").data(this.legend_labels);

texts.html(function(d, i) {
var sign = '';
Expand All @@ -190,10 +187,60 @@ Nengo.SpaSimilarity.prototype.generate_menu = function() {
items.push(['Show pairs', function() {self.set_show_pairs(true);}]);
}

items.push(['Change color palette', function() {self.set_color_func();}])

// add the parent's menu items to this
return $.merge(items, Nengo.Component.prototype.generate_menu.call(this));
};

Nengo.SpaSimilarity.prototype.set_color_func = function() {
var self = this;

Nengo.modal.clear_body();
// TODO: Let the user define their own palette
Nengo.modal.title('Choose a palette');

// Create a radio button form with the available palettes
var body = Nengo.modal.$body;
var radio_html = "<form id='palette'>";
radio_html += "<input type='radio' name='pal' value='" + 0 + "' checked='checked'>"+Nengo.color_choices[0][0]+"</input><br>"
for (i = 1; i < Nengo.color_choices.length; i++) {
radio_html += "<input type='radio' name='pal' value='" + i + "'>"+Nengo.color_choices[i][0]+"</input><br>"
}
radio_html += "</form>";
body.append(radio_html);

// TODO: Make this thing easier to select for the user
Nengo.modal.footer('ok_cancel', function(e) {
self.palette_index = Number($("#palette input:radio[name='pal']:checked").val());
self.color_func = Nengo.color_choices[self.palette_index][1]["func"];

self.path.style('stroke', function(d, i){return self.color_func(i)});
self.legend_svg.selectAll("rect").remove();
var recs = self.legend_svg.selectAll("rect").data(self.legend_labels);
recs.enter()
.append("rect")
.attr("x", 0)
.attr("y", function(d, i){ return i * 20;})
.attr("width", 10)
.attr("height", 10)
.style("fill", function(d, i) {return self.color_func(i)});
self.save_layout();
$('#OK').attr('data-dismiss', 'modal');
});

// allow "Enter" keypress
$("#palette").keypress(function(event) {
if (event.which == 13) {
event.preventDefault();
$('#OK').click();
}
});

Nengo.modal.show();
}


Nengo.SpaSimilarity.prototype.set_show_pairs = function(value) {
if (this.show_pairs !== value) {
this.show_pairs = value;
Expand Down
Loading