Commit 27ffaf74 authored by Mathieu Giraud's avatar Mathieu Giraud

Merge branch 'feature-c/2699-nicer-numerical-values' into 'dev'

Feature c/2699 nicer numerical values

Closes #2699

See merge request !274
parents 61571169 75644d2d
Pipeline #36403 passed with stages
in 4 minutes and 33 seconds
......@@ -34,7 +34,7 @@ function GenericAxis (reverse, can_undefined) {
this.values = []
this.value_mapping = {};
this.can_undefined = true;
this.NB_STEPS_IN_AXIS = 6; // Number (max) of labels per numerical axis
this.MAX_NB_STEPS_IN_AXIS = 8; // Number (max) of labels per numerical axis
this.NB_STEPS_BAR = 18; // Number (max) of labels per numerical axis in histograms
if(typeof can_undefined !== "undefined")
this.can_undefined = can_undefined;
......
......@@ -60,6 +60,10 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
display_label = typeof display_label !== 'undefined' ? display_label : true;
var self = this;
this.nb_steps = this.MAX_NB_STEPS_IN_AXIS
this.nb_steps_special = this.can_undefined ? 1 : 0
this.nb_steps_normal = this.nb_steps - this.nb_steps_special
if (typeof fct === 'function') {
this.converter = fct;
} else {
......@@ -107,12 +111,23 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
else if (typeof max === 'undefined') {
max = min + 1;
} else {
min = nice_floor(min)
max = nice_ceil(max)
if (use_log)
{
min = nice_floor(min)
max = nice_ceil(max)
}
else
{
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
}
}
if (this.can_undefined && ! use_log) {
max = max + (max - min)/this.NB_STEPS_IN_AXIS
max = max + (max - min) / (this.nb_steps_normal)
}
this.min = min;
......@@ -196,29 +211,20 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
h = h / 10;
}
}else{
var nb_steps = this.NB_STEPS_IN_AXIS-1
undefined_min = min
// Recover the initial min value
nb_steps = this.computeSteps(min, max, nb_steps);
this.nb_steps = this.computeSteps(min, max, this.nb_steps);
if (has_undefined) {
nb_steps = nb_steps -1
}
if (has_undefined){
this.labels.push(this.label("line", (this.reverse) ? 0 : 1, "?"))
h = (max-min)/(nb_steps+1)
} else {
h = (max-min)/(nb_steps)
}
h = (max - min) / this.nb_steps
// Computed so that pos <= 1 (in the loop below)
var delta = (min - max)/((min - undefined_min)/(max-undefined_min) - 1)
// Shift the start when there is an undefined value
var start_shift = (min - undefined_min)/(max-undefined_min)
for (var j = 0; j <= nb_steps; j++) {
pos = (h*j)*(1/delta);
var delta = max - min
for (var j = 0; j <= this.nb_steps_normal ; j++) {
pos = j / this.nb_steps
text = this.getLabelText(min + h*j);
if (this.reverse) pos = 1 - pos;
......@@ -230,9 +236,10 @@ NumericalAxis.prototype = Object.create(GenericAxis.prototype);
NumericalAxis.prototype.computeSteps = 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;
}
......@@ -264,7 +271,7 @@ PercentAxis.prototype = Object.create(NumericalAxis.prototype);
}
PercentAxis.prototype.getLabelText = function(value) {
return floatToFixed(value*100, 4) + "%"
return parseFloat(value*100).toFixed(nice_number_digits(100 * (this.max - this.min), 2)) + "%"
}
/**
......
......@@ -570,6 +570,7 @@ ScatterPlot.prototype = {
//sort each bar (axisY)
this.axisY = new PercentAxis(this.m, true);
this.axisY.MAX_NB_STEPS_IN_AXIS = 5;
this.axisY.init(this.m.clones, this.available_axis[this.splitY].fct);
this.sortBarTab(this.axisY.converter);
......@@ -1723,8 +1724,8 @@ ScatterPlot.prototype = {
* @param {Axis} axis
* @param {string} splitMethod
* */
updateAxis: function(splitMethod, reverse) {
if (typeof reverse === "undefined") reverse = false;
updateAxis: function(splitMethod, is_Y) {
if (typeof is_Y === "undefined") is_Y = false;
var axis;
var aa = this.available_axis[splitMethod]
if (aa == undefined) {
......@@ -1733,7 +1734,9 @@ ScatterPlot.prototype = {
}
axis = aa.axis;
axis.reverse = reverse;
axis.reverse = is_Y;
if (is_Y)
axis.MAX_NB_STEPS_IN_AXIS = 6
axis.init(this.m.clones, aa.fct, aa.labels, aa.sort, aa.min, aa.max, aa.log, aa.display_label);
return axis;
......
......@@ -296,12 +296,12 @@ function floor_pow10(x)
* nice_ceil(23.4) -> 30
**/
function nice_ceil(x)
function nice_ceil(x, force_pow10)
{
if (x <= 0) return x
try {
var floor_power10 = floor_pow10(x)
var floor_power10 = (typeof force_pow10 == 'undefined') ? floor_pow10(x) : force_pow10
var xx = x / floor_power10
return (xx == 1 ? 1 : xx <= 1.5 ? 1.5 : Math.ceil(xx)) * floor_power10
......@@ -313,18 +313,41 @@ function nice_ceil(x)
}
/**
* Give a nice decimal number (to 1/2/5) above the given number
**/
function nice_1_2_5_ceil(x)
{
if (x <= 0) return x
try {
var floor_power10 = floor_pow10(x)
var xx = x / floor_power10
return (xx == 1 ? 1 : xx <= 2 ? 2 : xx <= 5 ? 5 : 10) * floor_power10
}
catch(e) {
// Always return something
return x;
}
}
/**
* Give a nice decimal number under the given number
* nice_floor(0.14) -> 0.1
* nice_floor(23.4) -> 20
* nice_floor(23.4, 1) -> 23
* nice_floor(23.4, 100) -> 0
**/
function nice_floor(x)
function nice_floor(x, force_pow10)
{
if (x <= 0) return x
try {
var floor_power10 = floor_pow10(x)
var floor_power10 = (typeof force_pow10 == 'undefined') ? floor_pow10(x) : force_pow10
return Math.floor(x / floor_power10) * floor_power10
}
catch(e) {
......@@ -334,6 +357,43 @@ function nice_floor(x)
}
/**
* Give nice min/max/step numbers including the given [min, max] interval in order that steps are also nice,
* See examples in tools_test.js
* nice_min_max_steps(0.03, 19.24, 5) -> {min: 0, max: 20, step: 4}
* nice_min_max_steps(0, 7, 4) -> {min: 0, max:8, step: 2}
**/
function nice_min_max_steps(min, max, nb_max_steps)
{
if (min == max)
return {min: min, max: max, step: 0, nb_steps: 0}
var basic_step = nice_1_2_5_ceil((max - min) / nb_max_steps)
var n_min = nice_floor(min, basic_step)
var n_max = nice_ceil(max, basic_step)
var step = nice_1_2_5_ceil((n_max - n_min) / nb_max_steps)
var nb_steps = Math.ceil((n_max - n_min) / step)
// In some rare cases, we try another loop of rounding
var overlength = nb_steps * step - (n_max - n_min)
if (overlength)
{
n_min = nice_floor(min, step)
n_max = nice_ceil(max, step)
nb_steps = Math.ceil((n_max - n_min) / step)
}
return {min: n_min, max: n_max, step: step, nb_steps: nb_steps}
}
/**
* Give a minimial number of digits to represent 'x' with at least 'sd' significant digits
* nice_number_digits(14.5, 2) -> 1
......
......@@ -97,7 +97,7 @@ 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.257, "custom : clone 1 (nlength = 9) position -> 0.30")
assert.equal(axis.pos(m.clone(1)).pos.toPrecision(3), 0.30, "custom : clone 1 (nlength = 9) position -> 0.30")
//sequenceLength
axis = new NumericalAxis(m)
......@@ -107,18 +107,18 @@ QUnit.test("axis", function(assert) {
},
undefined,
false)
assert.equal(axis.pos(m.clone(0)).pos.toPrecision(3), 0.0325, "custom 2 : clone 0 (sequenceLength = 21) position -> 0.0325")
assert.equal(axis.pos(m.clone(1)).pos.toPrecision(3), 0.0236, "custom 2 : clone 1 (sequenceLength = 18) position -> 0.0236")
assert.equal(axis.pos(m.clone(3)).pos.toPrecision(3), 0.683, "custom 2 : clone 3 (sequenceLength = 241) position -> 0.683")
assert.equal(axis.label_mapping.hasOwnProperty('0'), false, "axis have not label 0")
assert.equal(axis.label_mapping.hasOwnProperty('10'), true, "axis have label 10")
assert.equal(axis.label_mapping.hasOwnProperty('281'), true, "axis have label 281")
assert.equal(axis.label_mapping['10'].pos.toPrecision(3), 0, "pos of axis.label 10 is 0")
assert.equal(axis.label_mapping['281'].pos.toPrecision(3), 0.800, "pos of axis.label 281 is 0.800")
assert.equal(axis.pos(m.clone(0)).pos.toPrecision(3), 0.0700, "custom 2 : clone 0 (sequenceLength = 21) position")
assert.equal(axis.pos(m.clone(1)).pos.toPrecision(3), 0.0600, "custom 2 : clone 1 (sequenceLength = 18) position")
assert.equal(axis.pos(m.clone(3)).pos.toPrecision(3), 0.803, "custom 2 : clone 3 (sequenceLength = 241) position")
assert.equal(axis.label_mapping.hasOwnProperty('0'), true, "axis has label 0")
assert.equal(axis.label_mapping.hasOwnProperty('10'), false, "axis does not have label 10")
assert.equal(axis.label_mapping.hasOwnProperty('250'), true, "axis has label 250")
assert.equal(axis.label_mapping['0'].pos.toPrecision(3), 0, "pos of axis.label 0 is 0")
assert.equal(axis.label_mapping['150'].pos.toPrecision(3), 0.500, "pos of axis.label 150 is 0.500")
assert.equal(axis.labels[0].pos, 1, "pos of axis label 0 ('?' value) is 1")
assert.approx(axis.labels[axis.labels.length-1].pos, 0.8, 0.01, "pos of last axis label is about 0.80")
assert.equal( (axis.labels.length != axis.label_mapping.length), true, "axis.labels length != axis.label_mapping length (du to '?')")
assert.approx(axis.labels[axis.labels.length-1].pos, 0.833, 0.01, "pos of last axis label is about 0.833")
assert.equal((axis.labels.length != axis.label_mapping.length), true, "axis.labels length != axis.label_mapping length (du to '?')")
// sequenceLength with undefined
axis = new NumericalAxis(m)
......@@ -137,7 +137,7 @@ QUnit.test("axis", function(assert) {
assert.equal(axis.pos(m.clone(0)).pos.toPrecision(3), 0.0476, "custom (percent) : clone 0 (gc = 1/21) position -> 0.0476")
assert.equal(axis.pos(m.clone(1)).pos.toPrecision(3), 0.778, "custom (percent) : clone 1 (gc = 14/18) position -> 0.944")
assert.deepEqual(axis.labels[0].text, "0.00%", "custom (percent) : check label 0.00%")
assert.deepEqual(axis.labels[0].text, "0%", "custom (percent) : check label 0%")
//gc + log
axis.init(m.clones, 'GCContent', undefined, false, 0.001, 1, true)
......@@ -160,7 +160,7 @@ QUnit.test("axis", function(assert) {
axis.init(m.clones,
function(clone) {
return undefined;
}, "V", true, 0);
}, "V", true, 0, 0);
assert.equal(axis.labels.length, 2, "Just two labels: 0, undefined");
// undefined values
......
......@@ -46,6 +46,10 @@ QUnit.test("test rounding functions", function(assert) {
assert.equal(nice_floor(100), 100, "rounding 100");
assert.equal(nice_floor(451), 400, "rounding 451");
assert.equal(nice_floor(23.4), 20, "rounding 23.4");
assert.equal(nice_floor(23.4, 1), 23, "rounding 23.4 (base 1)");
assert.equal(nice_floor(23.4, 100), 0, "rounding 23.4 (base 100)");
assert.equal(nice_number_digits(42, 1), 0, "nice_number_digits 42");
assert.equal(nice_number_digits(45.1, 2), 0, "nice_number_digits 45.1");
assert.equal(nice_number_digits(4.51, 2), 1, "nice_number_digits 4.51");
......@@ -54,6 +58,24 @@ QUnit.test("test rounding functions", function(assert) {
}
);
QUnit.test("test nice_min_max_steps", function(assert) {
assert.deepEqual(nice_min_max_steps(347.34, 354.23, 10), {min: 347, max: 355, step: 1, nb_steps: 8}, "347.34..354.23 (10)");
assert.deepEqual(nice_min_max_steps(17, 305, 20), {min: 0, max: 320, step: 20, nb_steps: 16}, "17..305 (20)");
assert.deepEqual(nice_min_max_steps(17, 305, 10), {min: 0, max: 350, step: 50, nb_steps: 7}, "17..305 (10)");
assert.deepEqual(nice_min_max_steps(17, 305, 4), {min: 0, max: 400, step: 100, nb_steps: 4}, "17..305 (4)");
assert.deepEqual(nice_min_max_steps(190, 310, 15), {min: 190, max: 310, step: 10, nb_steps: 12}, "190..310 (15)");
assert.deepEqual(nice_min_max_steps(190, 310, 3), {min: 150, max: 350, step: 100, nb_steps: 2}, "190..310 (3)");
assert.deepEqual(nice_min_max_steps(0.03, 19.24, 5), {min: 0, max: 20, step: 5, nb_steps: 4}, "0.03..19.24 (5)");
assert.deepEqual(nice_min_max_steps(0, 7, 4), {min: 0, max: 8, step: 2, nb_steps: 4}, "0..7 (4)");
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)");
});
QUnit.test("prepend_path_if_not_web", function(assert) {
assert.equal(prepend_path_if_not_web('/tata', 'toto'), 'toto/tata');
assert.equal(prepend_path_if_not_web('http://toto', 'toto'), 'http://toto');
......
......@@ -26,7 +26,7 @@ class TestSimple < BrowserTest
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_y_legend(0, 2).text == "0.00%"), "First legend sould be 0.00%"
assert ($b.scatterplot_y_legend(0, 2).text == "0%"), "First legend sould be 0%"
end
def test_02_deactivate_locus
......
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