Commit 1be18090 authored by Mathieu Giraud's avatar Mathieu Giraud

Merge branch 'feature-c/2700-bar-x-as-grid' into 'dev'

Features 2700, 2905 - bar x positions as in grid

Closes #2905 and #2699

See merge request !275
parents 1ae6a8e8 8bb8361d
Pipeline #36768 failed with stages
in 5 minutes and 56 seconds
......@@ -34,7 +34,8 @@ function GenericAxis (reverse, can_undefined) {
this.values = []
this.value_mapping = {};
this.can_undefined = true;
this.MAX_NB_STEPS_IN_AXIS = 8; // Number (max) of labels per numerical axis
this.MAX_NB_STEPS_IN_AXIS = 14; // Number (max) of labels per numerical axis
this.MAX_NB_BARS_IN_AXIS = 200
this.NB_STEPS_BAR = 18; // Number (max) of labels per numerical axis in histograms
if(typeof can_undefined !== "undefined")
this.can_undefined = can_undefined;
......@@ -113,33 +114,39 @@ GenericAxis.prototype = {
}
},
populateValueMapping: function() {
var values = this.values;
var value_mapping = this.value_mapping;
var label_mapping = this.label_mapping;
populateValueMapping: function(round) {
for (var idx in this.values) {
var value = this.values[idx];
// if (value.isVirtual())
// continue ; // ? pas toujours ?
var label_array = Object.keys(label_mapping);
for (var i = 0; i < values.length; i++) {
var value = values[i];
var convert = this.applyConverter(value);
if (typeof label_mapping[convert] !== 'undefined') {
var index = label_array.indexOf(convert);
if (typeof value_mapping[index] === 'undefined') {
value_mapping[index] = [];
if (typeof round !== 'undefined')
convert = nice_ceil(convert - round/2, round)
if (typeof convert == "undefined" || convert == undefined || convert == "undefined" || Number.isNaN(convert)) {
if (this.can_undefined) {
if (typeof this.value_mapping["?"] === 'undefined')
this.value_mapping["?"] = [];
this.value_mapping["?"].push(value);
}
value_mapping[index].push(value);
} else if (this.can_undefined){
if (typeof value_mapping["?"] === 'undefined')
value_mapping["?"] = [];
value_mapping["?"].push(value);
}
else
{
this.value_mapping[convert] = this.value_mapping[convert] || []
this.value_mapping[convert].push(value);
}
}
},
sortValueMapping: function() {
var keys = Object.keys(this.value_mapping).sort()
var temp = {}
for (var key_pos in Object.keys(this.value_mapping).sort()){
key = Object.keys(this.value_mapping).sort()[key_pos]
for (var key_pos in keys){
key = keys[key_pos]
temp[key] = this.value_mapping[key]
}
this.value_mapping = temp
......@@ -180,10 +187,19 @@ GenericAxis.prototype = {
},
pos: function(element) {
var value = this.label_mapping[this.applyConverter(element)];
if (typeof value === 'undefined')
value = this.label_mapping["?"];
return value;
value = this.applyConverter(element);
return this.pos_from_value(value)
},
pos_from_value: function(value) {
var pos = this.label_mapping[value]
if (typeof pos === 'undefined')
pos = this.label_mapping["?"];
return pos ;
},
computeLabels: function(values, sort) {
......@@ -212,43 +228,11 @@ GenericAxis.prototype = {
}
},
/**
* add labels for barplot <br>
* @param {Array} tab - barplot descriptor like the one made by Model.computeBarTab()
* */
computeBarLabels : function () {
this.labels = [];
this.label_mapping = {};
var length = Object.keys(this.value_mapping).length;
var step = 1 + Math.floor(length / this.NB_STEPS_BAR)
var text;
var i=1
for (var e in this.value_mapping){
if (i%step === 0 || (e == '?' && this.value_mapping[e].length > 0)){
text = this.getLabelText(e);
if (e == '?')
text = e;
var pos = this.posBarLabel(i, length);
if (this.reverse) pos = 1 - pos;
this.addLabel("line", text, pos, text);
}
i++;
}
},
getLabelText: function(value) {
return value;
},
computeBarTab: function(ref) {
ref.barTab = {};
for (var key in this.value_mapping) {
ref.barTab[key] = this.value_mapping[key];
}
},
posBarLabel : function (i) {
var length = Object.keys(this.value_mapping).length;
return (i-0.5)/length ;
......
......@@ -33,7 +33,7 @@
function NumericalAxis (model, reverse, can_undefined) {
this.m = model;
this.labels = [];
this.clones = [];
this.values = [];
this.value_mapping = {};
this.reverse = reverse;
GenericAxis.call(this, reverse, can_undefined);
......@@ -54,7 +54,7 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
* */
NumericalAxis.prototype.init = function(clones, fct, labels, sort, default_min, default_max, use_log, display_label){
this.reset();
this.clones = clones;
this.values = clones;
use_log = typeof use_log !== 'undefined' ? use_log : false;
this.use_log = use_log ;
display_label = typeof display_label !== 'undefined' ? display_label : true;
......@@ -64,6 +64,7 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
this.nb_steps_special = this.can_undefined ? 1 : 0
this.nb_steps_normal = this.nb_steps - this.nb_steps_special
// Sets this.converter
if (typeof fct === 'function') {
this.converter = fct;
} else {
......@@ -71,16 +72,17 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
return elem[fct];
}
}
// Compute min / max
var min = default_min;
var max = default_max;
if (typeof min === 'function') min = min();
if (typeof max === 'function') max = max();
if (typeof labels == "undefined") {
for (var i in this.clones){
if (! this.clones[i].isVirtual()) {
var tmp = this.applyConverter(this.clones[i]);
for (var i in this.values){
if (! this.values[i].isVirtual()) {
var tmp = this.applyConverter(this.values[i]);
if ( typeof tmp != "undefined" && !isNaN(tmp)){
if ( tmp > max || typeof max == "undefined") max = tmp;
......@@ -88,28 +90,16 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
}
}
}
for (var j=min; j<=max; j++){
this.value_mapping[j]=[];
}
} else {
for (var k in labels) {
var val = labels[k];
this.value_mapping[val] = [];
}
}
if(this.can_undefined)
this.value_mapping["?"] = [];
// insert all the values into the valuemapping object
this.insert_values()
if (typeof min == "undefined"){
min = 0;
max = 1;
this.nb_steps_normal = 1
}
else if (typeof max === 'undefined') {
else if (typeof max == 'undefined') {
max = min + 1;
this.nb_steps_normal = 1
} else {
if (use_log)
{
......@@ -121,11 +111,12 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
nice = nice_min_max_steps(min, max, this.nb_steps_normal)
min = nice.min
max = nice.max
this.nb_steps_normal = nice.nb_steps
this.nb_steps = this.nb_steps_normal + this.nb_steps_special
this.nb_steps_normal = this.limitSteps(min, max, nice.nb_steps);
}
}
this.nb_steps = this.nb_steps_normal + this.nb_steps_special
if (this.can_undefined && ! use_log) {
max = max + (max - min) / (this.nb_steps_normal)
}
......@@ -144,15 +135,24 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
.domain([min, max])
.range(range);
}
// Prepare value_mapping
this.value_mapping = {};
if(this.can_undefined)
this.value_mapping["?"] = [];
this.insert_values()
// Set labels
this.computeLabels(min, max, use_log, display_label, this.can_undefined)
}
NumericalAxis.prototype.pos = function(clone) {
var value, pos;
value = this.applyConverter(clone);
NumericalAxis.prototype.pos_from_value = function(value) {
if (typeof value != "undefined" && value != 'undefined'){
if (typeof value != "undefined" && value != 'undefined' && value != "?"){
pos = this.sizeScale(value);
}else{
pos = this.sizeScale(this.use_log ? this.min : this.max) ;
......@@ -165,19 +165,15 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
* This function allow to insert all the values getted into the value_mapping object.
*/
NumericalAxis.prototype.insert_values = function() {
for(var idx in this.clones) {
var clone = this.clones[idx];
if(!clone.isVirtual()) {
var value = this.applyConverter(clone);
if (typeof value == "undefined" || value == undefined || value == "undefined") {
if (this.can_undefined)
this.value_mapping["?"].push(clone);
}else{
this.value_mapping[value] = this.value_mapping[value] || []
this.value_mapping[value].push(clone);
}
}
}
this.step_bar = nice_1_2_5_ceil((this.max - this.min) / this.MAX_NB_BARS_IN_AXIS)
// Init value_mapping
for (var m = this.min; m < this.max; m += this.step_bar)
this.value_mapping[nice_ceil(m, this.step_bar)] = []
// Fill value_mapping
this.populateValueMapping(this.step_bar)
this.sortValueMapping()
}
......@@ -198,6 +194,10 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
display_label = true;
if (typeof has_undefined == 'undefined')
has_undefined = false
// Re-compute nb_steps (will not change the min/max here, only the label display)
this.nb_steps_special = has_undefined ? 1 : 0
this.nb_steps = this.nb_steps_normal + this.nb_steps_special
var text, pos, h;
if (use_log){
......@@ -211,8 +211,6 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
h = h / 10;
}
}else{
this.nb_steps = this.computeSteps(min, max, this.nb_steps);
if (has_undefined){
this.labels.push(this.label("line", (this.reverse) ? 0 : 1, "?"))
......@@ -234,12 +232,12 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
}
}
NumericalAxis.prototype.computeSteps = function(min, max, nb_steps) {
NumericalAxis.prototype.limitSteps = function(min, max, nb_steps) {
var steps = nb_steps;
// if (Math.abs(max - min) < nb_steps) {
// steps = Math.abs(max - min)
// }
if (Math.abs(max - min) < nb_steps) {
steps = Math.abs(max - min)
}
return steps;
}
......@@ -257,23 +255,6 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
return value;
}
function PercentAxis (model, reverse, can_undefined) {
this.m = model;
this.labels = [];
this.reverse = reverse;
NumericalAxis.call(this, model, reverse, can_undefined);
}
PercentAxis.prototype = Object.create(NumericalAxis.prototype);
PercentAxis.prototype.computeSteps = function(min, max, nb_steps) {
return nb_steps;
}
PercentAxis.prototype.getLabelText = function(value) {
return parseFloat(value*100).toFixed(nice_number_digits(100 * (this.max - this.min), 2)) + "%"
}
/**
* Axis object contain labels and their position on an axis (from 0 to 1) <br>
* can provide the position of a clone on it
......@@ -281,6 +262,10 @@ PercentAxis.prototype = Object.create(NumericalAxis.prototype);
* @param {Model} model
* @reverse {boolean} reverse - by default axis go from low to high but can be revsersed
* */
// FloatAxis
function FloatAxis (model, reverse, can_undefined) {
this.m = model;
this.labels = [];
......@@ -294,4 +279,21 @@ FloatAxis.prototype = Object.create(NumericalAxis.prototype);
return parseFloat(value).toFixed(nice_number_digits(this.max - this.min, 2))
}
FloatAxis.prototype.limitSteps = function(min, max, nb_steps) {
return nb_steps;
}
// PercentAxis
function PercentAxis (model, reverse, can_undefined) {
this.m = model;
this.labels = [];
this.reverse = reverse;
NumericalAxis.call(this, model, reverse, can_undefined);
}
PercentAxis.prototype = Object.create(FloatAxis.prototype);
PercentAxis.prototype.getLabelText = function(value) {
return parseFloat(value*100).toFixed(nice_number_digits(100 * (this.max - this.min), 2)) + "%"
}
......@@ -49,7 +49,7 @@ function ScatterPlot(id, model, database, default_preset) {
this.gridSizeH = 1; //grid height
//Margins (css style : top/right/bottom/left)
this.default_margin = [75,10,25,120];
this.default_margin = [45,10,15,90];
this.graph_margin = [25,25,25,25];
this.margin = this.default_margin;
......@@ -564,8 +564,6 @@ ScatterPlot.prototype = {
//split clones into bar (axisX)
this.axisX.init(this.m.clones, this.available_axis[this.splitX].fct);
this.axisX.computeBarTab(this)
//sort each bar (axisY)
......@@ -615,8 +613,8 @@ ScatterPlot.prototype = {
if (va.constructor === Number) return (vb.constructor === Number ) ? (va-vb) : -1;
}
for (var i in this.barTab) {
this.barTab[i].sort(compare);
for (var i in this.axisX.value_mapping) {
this.axisX.value_mapping[i].sort(compare);
}
return this;
......@@ -635,10 +633,10 @@ ScatterPlot.prototype = {
* */
computeBarMax : function () {
var bar_max = 0;
for (var i in this.barTab) {
for (var i in this.axisX.value_mapping) {
var tmp = 0;
for (var j in this.barTab[i]) {
var clone = this.barTab[i][j]
for (var j in this.axisX.value_mapping[i]) {
var clone = this.axisX.value_mapping[i][j]
if (this.includeBar(clone)){
tmp += clone.getSize();
}
......@@ -654,7 +652,7 @@ ScatterPlot.prototype = {
* */
computeBarTab : function () {
var bar_max = nice_ceil(this.computeBarMax());
var tab_length = Object.keys(this.barTab).length;
var tab_length = Object.keys(this.axisX.value_mapping).length;
var width = Math.min(0.08, 0.8 / tab_length);
......@@ -667,14 +665,12 @@ ScatterPlot.prototype = {
}
k=1 ;
for (var i in this.barTab) {
val = this.barTab[i];
for (var i in this.axisX.value_mapping) {
var y_pos = 0
var x_pos = this.axisX.posBarLabel(k);
var x_pos = this.axisX.pos_from_value(i).pos
for (var j in this.barTab[i]){
var clone = this.barTab[i][j]
for (var j in this.axisX.value_mapping[i]){
var clone = this.axisX.value_mapping[i][j]
var cloneID = clone.index;
if (this.includeBar(clone)){
height = clone.getSize()/bar_max;
......@@ -695,7 +691,6 @@ ScatterPlot.prototype = {
}
this.axisY.reverse = true;
this.axisY.computeLabels(0, bar_max);
this.axisX.computeBarLabels(this.barTab)
this.initGrid();
this.drawBarTab(500);
......@@ -765,7 +760,7 @@ ScatterPlot.prototype = {
return "circle_hidden";
})
self.update()
},500)
},400)
},
......@@ -1371,7 +1366,7 @@ ScatterPlot.prototype = {
var className = "sp_legend"
if (space < 1.1) {
this.rotation_x = 320;
this.text_position_x = 60;
this.text_position_x = 40;
this.sub_text_position_x = 80;
className = "sp_rotated_legend";
} else {
......
......@@ -370,7 +370,11 @@ function nice_floor(x, force_pow10)
function nice_min_max_steps(min, max, nb_max_steps)
{
if (min == max)
return {min: min, max: max, step: 0, nb_steps: 0}
{
min = nice_floor(min)
max = nice_ceil(max + 1)
nb_max_steps = 1
}
var basic_step = nice_1_2_5_ceil((max - min) / nb_max_steps)
......
......@@ -97,10 +97,11 @@ QUnit.test("axis", function(assert) {
0,25)
assert.equal(axis.pos(m.clone(0)).pos.toPrecision(3), 0.00, "custom : clone 0 (nlength = 0) position -> 0.00")
assert.equal(axis.pos(m.clone(1)).pos.toPrecision(3), 0.30, "custom : clone 1 (nlength = 9) position -> 0.30")
assert.equal(axis.pos(m.clone(1)).pos.toPrecision(3), 0.321, "custom : clone 1 (nlength = 9) position")
//sequenceLength
axis = new NumericalAxis(m)
axis.MAX_NB_STEPS_IN_AXIS = 8
axis.init(m.clones,
function(clone) {
return clone.getSequenceLength();
......@@ -161,7 +162,7 @@ QUnit.test("axis", function(assert) {
function(clone) {
return undefined;
}, "V", true, 0, 0);
assert.equal(axis.labels.length, 2, "Just two labels: 0, undefined");
assert.equal(axis.labels.length, 3, "Just three labels: 0, 1, undefined");
// undefined values
axis = new NumericalAxis(m);
......@@ -169,6 +170,11 @@ QUnit.test("axis", function(assert) {
function(clone) {
return 'undefined';
});
assert.equal(axis.labels[0].pos, 1, "Just two labels: undefined, 0");
assert.equal(axis.labels[1].pos, 0, "Just two labels: undefined, 0");
assert.equal(axis.labels[0].pos, 1, "Just three labels: undefined, 0, 1");
assert.equal(axis.labels[1].pos, 0, "Just three labels: undefined, 0, 1");
assert.equal(axis.labels[2].pos, 0.5, "Just three labels: undefined, 0, 1");
assert.equal(axis.labels[2].text, "1", "Just three labels: undefined, 0, 1");
});
......@@ -47,7 +47,8 @@ QUnit.test("grid", function(assert) {
$(document.getElementsByClassName("sp_legend")[0]).d3Click() //click label ighv4
assert.deepEqual(m.getSelected(), [2], "check click label");
sp.available_axis["nLength"].axis.MAX_NB_STEPS_IN_AXIS = 8
sp.changeSplitMethod("nLength", "size", sp.MODE_BAR);
sp.update()
......@@ -55,8 +56,9 @@ QUnit.test("grid", function(assert) {
assert.equal(sp.select_y.selectedIndex, 14, 'select_y index');
assert.equal(sp.nodes[1].bar_h , 0.3333333333333333, "node 1, bar h position")
assert.equal(sp.nodes[1].bar_x , sp.axisX.labels[9].pos ,"node 1, bar x position is on '9'")
assert.equal(sp.axisX.labels[9].text, "9", "10th label for 'n' axis' is '9'")
assert.equal(sp.nodes[1].bar_x , (sp.axisX.labels[5].pos + sp.axisX.labels[6].pos)/2,
"node 1, bar x position is between labels 5th ('8') and 6th ('10')")
assert.equal(sp.axisX.labels[5].text, "8", "Correct 5th label ('10')")
assert.equal(sp.nodes[1].bar_y , 0.3333333333333333, "node 1, bar y position")
assert.approx(sp.nodes[2].bar_h, 0.40, 0.05, "node 2, bar h is about 0.40")
......
......@@ -73,7 +73,7 @@ QUnit.test("test nice_min_max_steps", function(assert) {
assert.deepEqual(nice_min_max_steps(43, 103, 5), {min: 40, max: 120, step: 20, nb_steps: 4}, "43..103 (5)");
assert.deepEqual(nice_min_max_steps(43, 103, 3), {min: 0, max: 150, step: 50, nb_steps: 3}, "43..103 (3)");
assert.deepEqual(nice_min_max_steps(42, 42, 20), {min: 42, max: 42, step: 0, nb_steps: 0}, "42..42 (20)");
assert.deepEqual(nice_min_max_steps(42, 42, 20), {min: 40, max: 50, step: 10, nb_steps: 1}, "42..42 (20)");
});
QUnit.test("prepend_path_if_not_web", function(assert) {
......
......@@ -25,7 +25,10 @@ class TestSimple < BrowserTest
def test_01_legend_scatterplot
assert ($b.scatterplot_x_legend(0).text == "TRGV5"), "First legend should be TRGV5"
assert ($b.scatterplot_y_legend(0).text == "TRGJ1"), "First legend should be TRGJ1"
assert ($b.scatterplot_x_legend(0, 2).text == "119"), "First legend should be 119, it is " + $b.scatterplot_x_legend(0, 2).text
assert ($b.scatterplot_x_legend(0, 2).text == "?"), "Legend should be ?, it is " + $b.scatterplot_x_legend(0, 2).text
assert ($b.scatterplot_x_legend(1, 2).text == "100"), "Legend should be 100, it is " + $b.scatterplot_x_legend(1, 2).text
assert ($b.scatterplot_x_legend(2, 2).text == "150"), "Legend should be 150, it is " + $b.scatterplot_x_legend(2, 2).text
assert ($b.scatterplot_y_legend(0, 2).text == "0%"), "First legend sould be 0%"
end
......
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