Commit 6df3dc15 authored by Mathieu Giraud's avatar Mathieu Giraud
Browse files

merge -- test multiple selection

Refactor and tests by @heto, see merge request !27.
Closes #2093.
parents 7a274ccc dd99c914
......@@ -93,7 +93,7 @@ function ScatterPlot(id, model, database) {
this.time1 = this.time0; //Frames computed
this.fpsqueue = []; //Numbers of frames computed, according to the 20th last values
//Booléen pour le sélecteur de nodes
//Boolean for the nodes selector
this.active_selector = false
//axis X text position
......@@ -1814,9 +1814,18 @@ ScatterPlot.prototype = {
* start drawing a selector at this position
* */
activeSelector: function() {
var self = this;
this.coordinates = d3.mouse(d3.select("#" + this.id + "_svg")
.node());
this.activeSelectorAt(
d3.mouse(d3.select("#" + this.id + "_svg")
.node())
);
},
/**
* start drawing a selector at this position
* @param {integer[]} coord - the [x,y] coordinates at which start the selection
* */
activeSelectorAt: function(coord) {
this.coordinates = coord;
if (this.active_move) {
......@@ -1846,13 +1855,25 @@ ScatterPlot.prototype = {
this.active_selector = true;
},
/**
* onmousemove event <br>
* redraw the selector every time the mouse move till the click release
* */
updateSelector: function() {
this.coordinates = d3.mouse(d3.select("#" + this.id + "_svg")
.node());
this.updateSelectorAt(
d3.mouse(d3.select("#" + this.id + "_svg")
.node())
);
},
/**
* redraw the selector every time the mouse move till the click release
* @param {integer[]} coord - the [x,y] coordinates at which update the selector
* */
updateSelectorAt: function(coord) {
this.coordinates = coord;
//Active selector -> activeSelector() function
if (this.active_selector) {
......@@ -1923,6 +1944,9 @@ ScatterPlot.prototype = {
}
},
/**
* onmouserelease event<br>
* detect and select all clones under the selector
......@@ -1930,58 +1954,77 @@ ScatterPlot.prototype = {
* */
stopSelector: function(e) {
if (this.active_selector) {
this.stopSelectorAt(
d3.mouse(d3.select("#" + this.id + "_svg")
.node())
);
}
},
this.coordinates = d3.mouse(d3.select("#" + this.id + "_svg")
.node());
/**
* detect and select all clones under the selector
* @param {integer[]} coord - the [x,y] coordinates at which stop the selector
* */
stopSelectorAt: function(coord) {
if (this.active_move) {
this.coordinates = coord;
if (this.active_move) {
//Selector disabled
this.cancelSelector();
//Set the CSS of the mouse to "default"
document.body.style.cursor = "default";
}
/*Selection*/
else {
var nodes_selected = []
var x1 = parseInt(this.selector.attr("x"))
var x2 = x1 + parseInt(this.selector.attr("width"))
var y1 = parseInt(this.selector.attr("y"))
var y2 = y1 + parseInt(this.selector.attr("height"))
for (var i = 0; i < this.nodes.length; i++) {
var node = this.nodes[i]
var clone = this.m.clone(i)
var node_x, node_y;
if (this.mode != this.MODE_BAR) {
node_x = node.x + this.margin[3]
node_y = node.y + this.margin[0]
} else {
// bar_x and bar_y are both ratio values (between 0 and 1), need to multiply by the size of the grid
node_x = node.bar_x * this.gridSizeW + this.margin[3];
var mid_y = node.bar_y - node.bar_h / 2; // bar_x represents the middle of the rectangle, but not bar_y
// bar_y starts from bottom, so we need to substract the y value from the height of the grid
node_y = this.gridSizeH - mid_y * this.gridSizeH + this.margin[0];
}
if (clone.isActive() && (clone.getSize() || clone.getSequenceSize()) && node_x > x1 && node_x < x2 && node_y > y1 && node_y < y2)
nodes_selected.push(i);
}
this.selector
.style("display", "none")
.attr("width", 0)
.attr("height", 0)
this.active_selector = false;
this.m.unselectAllUnlessKey(d3.event)
this.m.multiSelect(nodes_selected)
}
},
//Selector disabled
this.cancelSelector();
//Set the CSS of the mouse to "default"
document.body.style.cursor = "default";
}
/*Sélection*/
else {
var nodes_selected = []
var x1 = parseInt(this.selector.attr("x"))
var x2 = x1 + parseInt(this.selector.attr("width"))
var y1 = parseInt(this.selector.attr("y"))
var y2 = y1 + parseInt(this.selector.attr("height"))
for (var i = 0; i < this.nodes.length; i++) {
var node = this.nodes[i]
var clone = this.m.clone(i)
var node_x, node_y;
if (this.mode != this.MODE_BAR) {
node_x = node.x + this.margin[3]
node_y = node.y + this.margin[0]
} else {
// bar_x and bar_y are both ratio values (between 0 and 1), need to multiply by the size of the grid
node_x = node.bar_x * this.gridSizeW + this.margin[3];
var mid_y = node.bar_y - node.bar_h / 2; // bar_x represents the middle of the rectangle, but not bar_y
// bar_y starts from bottom, so we need to substract the y value from the height of the grid
node_y = this.gridSizeH - mid_y * this.gridSizeH + this.margin[0];
}
if (clone.isActive() && (clone.getSize() || clone.getSequenceSize()) && node_x > x1 && node_x < x2 && node_y > y1 && node_y < y2)
nodes_selected.push(i);
}
this.selector
.style("display", "none")
.attr("width", 0)
.attr("height", 0)
this.active_selector = false;
this.m.unselectAllUnlessKey(d3.event)
this.m.multiSelect(nodes_selected)
}
}
},
/**
* click event, select/unselect clones <br>
......
......@@ -133,8 +133,8 @@ Shortcut.prototype = {
}
//system shortcuts
if (! e.ctrlKey && ! e.metaKey
&& typeof this.system_shortcuts[key] != "undefined") {
if (! e.ctrlKey && ! e.metaKey &&
typeof this.system_shortcuts[key] != "undefined") {
var germlines = this.system_shortcuts[key].filter(function(g) {return m.system_available.indexOf(g) != -1})
if (germlines.length === 0)
......
......@@ -79,3 +79,73 @@ QUnit.test("node sizes", function(assert) {
assert.equal(sp.nodes[0].s, 0.10, "node 0 (not quantifiable), size")
})
QUnit.test("multiple selection", function(assert) {
var m = new Model(m);
m.parseJsonData(json_data, 100);
m.loadGermline();
m.initClones();
var sp = new ScatterPlot("visu", m);
sp.shouldRefresh();
/* Expected clones distribution in the scatterplot (numbers are ratios for width en height) :
0-0.5 0.5-0.8 0.8-1
0-0.5 test3 x test{1,2,4}
0.5-1 x unseg sequence x
*/
var done = assert.async();
var sp_svg;
var sp_width;
var sp_height;
setTimeout(function() {
/*
NB: .getComputedStyle() is not the way used by the actual selection functions.
If one (ever) gets different values, causing the tests to fail,
it may come frome here.
*/
sp_style = window.getComputedStyle(d3.select("#"+ sp.id).node());
sp_width = parseInt(sp_style.width);
sp_height = parseInt(sp_style.height);
// This 'event' is created to save m.unselectAllUnlessKey from failing (called by .stopSelectorAt)
// There might be a better solution
d3.event = new Event("");
test1();
test2();
done();
});
function test1() {
sp.activeSelectorAt([sp_width*0.5, sp_height*0.5]);
sp.updateSelectorAt([sp_width*0.8, sp_height]);
sp.stopSelectorAt([sp_width*0.8, sp_height]);
assert.equal(m.getSelected().length, 1, "only one clone is selected");
assert.equal(m.clone(m.getSelected()[0]).name, "unseg sequence", "the selected clone is 'unseg sequence' (test5)");
}
function test2() {
sp.activeSelectorAt([sp_width*0.8, 0]);
sp.updateSelectorAt([sp_width, sp_height*0.5]);
sp.stopSelectorAt([sp_width, sp_height*0.5]);
assert.equal(m.getSelected().length, 3, "three clones are selected");
var expected_cloneIDs = [];
for (var index in m.clones) {
if (["test1","test2","test4"].includes(m.clone(index).name))
expected_cloneIDs.push(parseInt(index));
}
assert.deepEqual(m.getSelected().sort(), expected_cloneIDs.sort(), "the selected clones are test{1,2,4}");
}
})
......@@ -112,7 +112,6 @@
<script src="./testFiles/segmenter_test.js"></script>
<script src="./testFiles/tools_test.js"></script>
<script src="./testFiles/germline_test.js"></script>
<script src="./testFiles/model_loader_test.js"></script>
<script src="./testFiles/url_test.js"></script>
<script src="./testFiles/speed_test.js"></script>
</body>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment