mirror of
https://github.com/medialab-prado/poblados-colonizacion-colonias-penitenciarias.git
synced 2024-12-29 14:01:22 +01:00
5444 lines
147 KiB
JavaScript
5444 lines
147 KiB
JavaScript
/*
|
|
* Point class. A GPoint is composed of two coordinates (x, y) and a text label
|
|
*/
|
|
function GPoint() {
|
|
var x, y, label;
|
|
|
|
if (arguments.length === 3) {
|
|
x = arguments[0];
|
|
y = arguments[1];
|
|
label = arguments[2];
|
|
} else if (arguments.length === 2 && arguments[0] instanceof p5.Vector) {
|
|
x = arguments[0].x;
|
|
y = arguments[0].y;
|
|
label = arguments[1];
|
|
} else if (arguments.length === 2) {
|
|
x = arguments[0];
|
|
y = arguments[1];
|
|
label = "";
|
|
} else if (arguments.length === 1 && arguments[0] instanceof GPoint) {
|
|
x = arguments[0].getX();
|
|
y = arguments[0].getY();
|
|
label = arguments[0].getLabel();
|
|
} else if (arguments.length === 1 && arguments[0] instanceof p5.Vector) {
|
|
x = arguments[0].x;
|
|
y = arguments[0].y;
|
|
label = "";
|
|
} else if (arguments.length === 0) {
|
|
x = 0;
|
|
y = 0;
|
|
label = "";
|
|
} else {
|
|
throw new Error("GPoint constructor: signature not supported");
|
|
}
|
|
|
|
this.x = x;
|
|
this.y = y;
|
|
this.label = label;
|
|
this.valid = this.isValidNumber(this.x) && this.isValidNumber(this.y);
|
|
}
|
|
|
|
GPoint.prototype.isValidNumber = function(number) {
|
|
return !isNaN(number) && isFinite(number);
|
|
};
|
|
|
|
GPoint.prototype.set = function() {
|
|
var x, y, label;
|
|
|
|
if (arguments.length === 3) {
|
|
x = arguments[0];
|
|
y = arguments[1];
|
|
label = arguments[2];
|
|
} else if (arguments.length === 2 && arguments[0] instanceof p5.Vector) {
|
|
x = arguments[0].x;
|
|
y = arguments[0].y;
|
|
label = arguments[1];
|
|
} else if (arguments.length === 2) {
|
|
x = arguments[0];
|
|
y = arguments[1];
|
|
label = "";
|
|
} else if (arguments.length === 1 && arguments[0] instanceof GPoint) {
|
|
x = arguments[0].getX();
|
|
y = arguments[0].getY();
|
|
label = arguments[0].getLabel();
|
|
} else if (arguments.length === 1 && arguments[0] instanceof p5.Vector) {
|
|
x = arguments[0].x;
|
|
y = arguments[0].y;
|
|
label = "";
|
|
} else {
|
|
throw new Error("GPoint.set(): signature not supported");
|
|
}
|
|
|
|
this.x = x;
|
|
this.y = y;
|
|
this.label = label;
|
|
this.valid = this.isValidNumber(this.x) && this.isValidNumber(this.y);
|
|
};
|
|
|
|
GPoint.prototype.setX = function(x) {
|
|
this.x = x;
|
|
this.valid = this.isValidNumber(this.x) && this.isValidNumber(this.y);
|
|
};
|
|
|
|
GPoint.prototype.setY = function(y) {
|
|
this.y = y;
|
|
this.valid = this.isValidNumber(this.x) && this.isValidNumber(this.y);
|
|
};
|
|
|
|
GPoint.prototype.setLabel = function(label) {
|
|
this.label = label;
|
|
};
|
|
|
|
GPoint.prototype.setXY = function() {
|
|
var x, y;
|
|
|
|
if (arguments.length === 2) {
|
|
x = arguments[0];
|
|
y = arguments[1];
|
|
} else if (arguments.length === 1 && arguments[0] instanceof GPoint) {
|
|
x = arguments[0].getX();
|
|
y = arguments[0].getY();
|
|
} else if (arguments.length === 1 && arguments[0] instanceof p5.Vector) {
|
|
x = arguments[0].x;
|
|
y = arguments[0].y;
|
|
} else {
|
|
throw new Error("GPoint.setXY(): signature not supported");
|
|
}
|
|
|
|
this.x = x;
|
|
this.y = y;
|
|
this.valid = this.isValidNumber(this.x) && this.isValidNumber(this.y);
|
|
};
|
|
|
|
GPoint.prototype.getX = function() {
|
|
return this.x;
|
|
};
|
|
|
|
GPoint.prototype.getY = function() {
|
|
return this.y;
|
|
};
|
|
|
|
GPoint.prototype.getLabel = function() {
|
|
return this.label;
|
|
};
|
|
|
|
GPoint.prototype.getValid = function() {
|
|
return this.valid;
|
|
};
|
|
|
|
GPoint.prototype.isValid = function() {
|
|
return this.valid;
|
|
};
|
|
/*
|
|
* Title class.
|
|
*/
|
|
function GTitle(parent, dim) {
|
|
// The parent processing object
|
|
this.parent = parent;
|
|
|
|
// General properties
|
|
this.dim = dim.slice();
|
|
this.relativePos = 0.5;
|
|
this.plotPos = this.relativePos * this.dim[0];
|
|
this.offset = 10;
|
|
|
|
// Text properties
|
|
this.text = "";
|
|
this.textAlignment = this.parent.CENTER;
|
|
this.fontName = "Helvetica";
|
|
this.fontColor = this.parent.color(100);
|
|
this.fontStyle = this.parent.BOLD;
|
|
this.fontSize = 13;
|
|
}
|
|
|
|
GTitle.prototype.draw = function() {
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textStyle(this.fontStyle);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.noStroke();
|
|
this.parent.textAlign(this.textAlignment, this.parent.BOTTOM);
|
|
this.parent.text(this.text, this.plotPos, -this.offset - this.dim[1]);
|
|
|
|
// There seems to be a bug in p5.js
|
|
this.parent.textStyle(this.parent.NORMAL);
|
|
this.parent.pop();
|
|
};
|
|
|
|
GTitle.prototype.setDim = function() {
|
|
var xDim, yDim;
|
|
|
|
if (arguments.length === 2) {
|
|
xDim = arguments[0];
|
|
yDim = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
xDim = arguments[0][0];
|
|
yDim = arguments[0][1];
|
|
} else {
|
|
throw new Error("GTitle.setDim(): signature not supported");
|
|
}
|
|
|
|
if (xDim > 0 && yDim > 0) {
|
|
this.dim[0] = xDim;
|
|
this.dim[1] = yDim;
|
|
this.plotPos = this.relativePos * this.dim[0];
|
|
}
|
|
};
|
|
|
|
GTitle.prototype.setRelativePos = function(relativePos) {
|
|
this.relativePos = relativePos;
|
|
this.plotPos = this.relativePos * this.dim[0];
|
|
};
|
|
|
|
GTitle.prototype.setOffset = function(offset) {
|
|
this.offset = offset;
|
|
};
|
|
|
|
GTitle.prototype.setText = function(text) {
|
|
this.text = text;
|
|
};
|
|
|
|
GTitle.prototype.setTextAlignment = function(textAlignment) {
|
|
if (textAlignment === this.parent.CENTER || textAlignment === this.parent.LEFT || textAlignment === this.parent.RIGHT) {
|
|
this.textAlignment = textAlignment;
|
|
}
|
|
};
|
|
|
|
GTitle.prototype.setFontName = function(fontName) {
|
|
this.fontName = fontName;
|
|
};
|
|
|
|
GTitle.prototype.setFontColor = function(fontColor) {
|
|
this.fontColor = fontColor;
|
|
};
|
|
|
|
GTitle.prototype.setFontStyle = function(fontStyle) {
|
|
this.fontStyle = fontStyle;
|
|
};
|
|
|
|
GTitle.prototype.setFontSize = function(fontSize) {
|
|
if (fontSize > 0) {
|
|
this.fontSize = fontSize;
|
|
}
|
|
};
|
|
|
|
GTitle.prototype.setFontProperties = function(fontName, fontColor, fontSize) {
|
|
if (fontSize > 0) {
|
|
this.fontName = fontName;
|
|
this.fontColor = fontColor;
|
|
this.fontSize = fontSize;
|
|
}
|
|
};
|
|
/*
|
|
* Axis label class.
|
|
*/
|
|
function GAxisLabel(parent, type, dim) {
|
|
// The parent processing object
|
|
this.parent = parent;
|
|
|
|
// General properties
|
|
this.type = (type === this.parent.BOTTOM || type === this.parent.TOP || type === this.parent.LEFT || type === this.parent.RIGHT) ? type : this.parent.BOTTOM;
|
|
this.dim = dim.slice();
|
|
this.relativePos = 0.5;
|
|
this.plotPos = (this.type === this.parent.BOTTOM || this.type === this.parent.TOP) ? this.relativePos * this.dim[0] : -this.relativePos * this.dim[1];
|
|
this.offset = 35;
|
|
this.rotate = (this.type === this.parent.BOTTOM || this.type === this.parent.TOP) ? false : true;
|
|
|
|
// Text properties
|
|
this.text = "";
|
|
this.textAlignment = this.parent.CENTER;
|
|
this.fontName = "Helvetica";
|
|
this.fontColor = this.parent.color(0);
|
|
this.fontSize = 13;
|
|
}
|
|
|
|
GAxisLabel.prototype.draw = function() {
|
|
switch (this.type) {
|
|
case this.parent.BOTTOM:
|
|
this.drawAsXLabel();
|
|
break;
|
|
case this.parent.LEFT:
|
|
this.drawAsYLabel();
|
|
break;
|
|
case this.parent.TOP:
|
|
this.drawAsTopLabel();
|
|
break;
|
|
case this.parent.RIGHT:
|
|
this.drawAsRightLabel();
|
|
break;
|
|
}
|
|
};
|
|
|
|
GAxisLabel.prototype.drawAsXLabel = function() {
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.noStroke();
|
|
|
|
if (this.rotate) {
|
|
this.parent.textAlign(this.parent.RIGHT, this.parent.CENTER);
|
|
this.parent.translate(this.plotPos, this.offset);
|
|
this.parent.rotate(-0.5 * Math.PI);
|
|
this.parent.text(this.text, 0, 0);
|
|
} else {
|
|
this.parent.textAlign(this.textAlignment, this.parent.TOP);
|
|
this.parent.text(this.text, this.plotPos, this.offset);
|
|
}
|
|
|
|
this.parent.pop();
|
|
};
|
|
|
|
GAxisLabel.prototype.drawAsYLabel = function() {
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.noStroke();
|
|
|
|
if (this.rotate) {
|
|
this.parent.textAlign(this.textAlignment, this.parent.BOTTOM);
|
|
this.parent.translate(-this.offset, this.plotPos);
|
|
this.parent.rotate(-0.5 * Math.PI);
|
|
this.parent.text(this.text, 0, 0);
|
|
} else {
|
|
this.parent.textAlign(this.parent.RIGHT, this.parent.CENTER);
|
|
this.parent.text(this.text, -this.offset, this.plotPos);
|
|
}
|
|
|
|
this.parent.pop();
|
|
};
|
|
|
|
GAxisLabel.prototype.drawAsTopLabel = function() {
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.noStroke();
|
|
|
|
if (this.rotate) {
|
|
this.parent.textAlign(this.parent.LEFT, this.parent.CENTER);
|
|
this.parent.translate(this.plotPos, -this.offset - this.dim[1]);
|
|
this.parent.rotate(-0.5 * Math.PI);
|
|
this.parent.text(this.text, 0, 0);
|
|
} else {
|
|
this.parent.textAlign(this.textAlignment, this.parent.BOTTOM);
|
|
this.parent.text(this.text, this.plotPos, -this.offset - this.dim[1]);
|
|
}
|
|
|
|
this.parent.pop();
|
|
};
|
|
|
|
GAxisLabel.prototype.drawAsRightLabel = function() {
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.noStroke();
|
|
|
|
if (this.rotate) {
|
|
this.parent.textAlign(this.textAlignment, this.parent.TOP);
|
|
this.parent.translate(this.offset + this.dim[0], this.plotPos);
|
|
this.parent.rotate(-0.5 * Math.PI);
|
|
this.parent.text(this.text, 0, 0);
|
|
} else {
|
|
this.parent.textAlign(this.parent.LEFT, this.parent.CENTER);
|
|
this.parent.text(this.text, this.offset + this.dim[0], this.plotPos);
|
|
}
|
|
|
|
this.parent.pop();
|
|
};
|
|
|
|
GAxisLabel.prototype.setDim = function() {
|
|
var xDim, yDim;
|
|
|
|
if (arguments.length === 2) {
|
|
xDim = arguments[0];
|
|
yDim = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
xDim = arguments[0][0];
|
|
yDim = arguments[0][1];
|
|
} else {
|
|
throw new Error("GAxisLabel.setDim(): signature not supported");
|
|
}
|
|
|
|
if (xDim > 0 && yDim > 0) {
|
|
this.dim[0] = xDim;
|
|
this.dim[1] = yDim;
|
|
this.plotPos = (this.type === this.parent.BOTTOM || this.type === this.parent.TOP) ? this.relativePos * this.dim[0] : -this.relativePos * this.dim[1];
|
|
}
|
|
};
|
|
|
|
GAxisLabel.prototype.setRelativePos = function(relativePos) {
|
|
this.relativePos = relativePos;
|
|
this.plotPos = (this.type === this.parent.BOTTOM || this.type === this.parent.TOP) ? this.relativePos * this.dim[0] : -this.relativePos * this.dim[1];
|
|
};
|
|
|
|
GAxisLabel.prototype.setOffset = function(offset) {
|
|
this.offset = offset;
|
|
};
|
|
|
|
GAxisLabel.prototype.setRotate = function(rotate) {
|
|
this.rotate = rotate;
|
|
};
|
|
|
|
GAxisLabel.prototype.setText = function(text) {
|
|
this.text = text;
|
|
};
|
|
|
|
GAxisLabel.prototype.setTextAlignment = function(textAlignment) {
|
|
if (textAlignment === this.parent.CENTER || textAlignment === this.parent.LEFT || textAlignment === this.parent.RIGHT) {
|
|
this.textAlignment = textAlignment;
|
|
}
|
|
};
|
|
|
|
GAxisLabel.prototype.setFontName = function(fontName) {
|
|
this.fontName = fontName;
|
|
};
|
|
|
|
GAxisLabel.prototype.setFontColor = function(fontColor) {
|
|
this.fontColor = fontColor;
|
|
};
|
|
|
|
GAxisLabel.prototype.setFontSize = function(fontSize) {
|
|
if (fontSize > 0) {
|
|
this.fontSize = fontSize;
|
|
}
|
|
};
|
|
|
|
GAxisLabel.prototype.setFontProperties = function(fontName, fontColor, fontSize) {
|
|
if (fontSize > 0) {
|
|
this.fontName = fontName;
|
|
this.fontColor = fontColor;
|
|
this.fontSize = fontSize;
|
|
}
|
|
};
|
|
/*
|
|
* Axis class.
|
|
*/
|
|
function GAxis(parent, type, dim, lim, log) {
|
|
// The parent processing object
|
|
this.parent = parent;
|
|
|
|
// General properties
|
|
this.type = (type === this.parent.BOTTOM || type === this.parent.TOP || type === this.parent.LEFT || type === this.parent.RIGHT) ? type : this.parent.BOTTOM;
|
|
this.dim = dim.slice();
|
|
this.lim = lim.slice();
|
|
this.log = log;
|
|
|
|
// Do some sanity checks
|
|
if (this.log && (this.lim[0] <= 0 || this.lim[1] <= 0)) {
|
|
console.log("The limits are negative. This is not allowed in logarithmic scale.");
|
|
console.log("Will set them to (0.1, 10)");
|
|
|
|
if (this.lim[1] > this.lim[0]) {
|
|
this.lim[0] = 0.1;
|
|
this.lim[1] = 10;
|
|
} else {
|
|
this.lim[0] = 10;
|
|
this.lim[1] = 0.1;
|
|
}
|
|
}
|
|
|
|
// Format properties
|
|
this.offset = 5;
|
|
this.lineColor = this.parent.color(0);
|
|
this.lineWidth = 1;
|
|
|
|
// Ticks properties
|
|
this.nTicks = 5;
|
|
this.ticksSeparation = -1;
|
|
this.ticks = [];
|
|
this.plotTicks = [];
|
|
this.ticksInside = [];
|
|
this.tickLabels = [];
|
|
this.fixedTicks = false;
|
|
this.tickLength = 3;
|
|
this.smallTickLength = 2;
|
|
this.expTickLabels = false;
|
|
this.rotateTickLabels = (this.type === this.parent.BOTTOM || this.type === this.parent.TOP) ? false : true;
|
|
this.drawTickLabels = (this.type === this.parent.BOTTOM || this.type === this.parent.LEFT) ? true : false;
|
|
this.tickLabelOffset = 7;
|
|
this.ticksPrecision = undefined;
|
|
|
|
// Label properties
|
|
this.lab = new GAxisLabel(this.parent, this.type, this.dim);
|
|
this.drawAxisLabel = true;
|
|
|
|
// Text properties
|
|
this.fontName = "Helvetica";
|
|
this.fontColor = this.parent.color(0);
|
|
this.fontSize = 11;
|
|
|
|
// Update the arrays
|
|
this.updateTicks();
|
|
this.updatePlotTicks();
|
|
this.updateTicksInside();
|
|
this.updateTickLabels();
|
|
}
|
|
|
|
GAxis.prototype.obtainSigDigits = function(number) {
|
|
return Math.round(-Math.log(0.5 * Math.abs(number)) / Math.LN10);
|
|
};
|
|
|
|
GAxis.prototype.roundPlus = function(number, sigDigits) {
|
|
// Old way of doing it
|
|
// var bd = new BigDecimal(number);
|
|
// roundedNumber = parseFloat(bd.setScale(sigDigits, RoundingMode.HALF_UP()).longValue().toFixed(sigDigits));
|
|
|
|
number = Math.round(number * Math.pow(10, sigDigits)) / Math.pow(10, sigDigits);
|
|
|
|
if (sigDigits <= 0) {
|
|
number = Math.round(number);
|
|
}
|
|
|
|
return number;
|
|
};
|
|
|
|
GAxis.prototype.adaptSize = function(a, n) {
|
|
if (n < a.length) {
|
|
a.splice(n, Number.MAX_VALUE);
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.updateTicks = function() {
|
|
if (this.log) {
|
|
this.obtainLogarithmicTicks();
|
|
} else {
|
|
this.obtainLinearTicks();
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.obtainLogarithmicTicks = function() {
|
|
// Get the exponents of the first and last ticks in increasing order
|
|
var firstExp, lastExp;
|
|
|
|
if (this.lim[1] > this.lim[0]) {
|
|
firstExp = Math.floor(Math.log(this.lim[0]) / Math.LN10);
|
|
lastExp = Math.ceil(Math.log(this.lim[1]) / Math.LN10);
|
|
} else {
|
|
firstExp = Math.floor(Math.log(this.lim[1]) / Math.LN10);
|
|
lastExp = Math.ceil(Math.log(this.lim[0]) / Math.LN10);
|
|
}
|
|
|
|
// Calculate the ticks
|
|
var n = (lastExp - firstExp) * 9 + 1;
|
|
this.adaptSize(this.ticks, n);
|
|
|
|
for (var exp = firstExp; exp < lastExp; exp++) {
|
|
var base = this.roundPlus(Math.exp(exp * Math.LN10), -exp);
|
|
|
|
for (var i = 0; i < 9; i++) {
|
|
this.ticks[(exp - firstExp) * 9 + i] = (i + 1) * base;
|
|
}
|
|
|
|
}
|
|
|
|
this.ticks[this.ticks.length - 1] = this.roundPlus(Math.exp(lastExp * Math.LN10), -exp);
|
|
|
|
// Change the ticks order if necessary
|
|
if (this.lim[1] < this.lim[0]) {
|
|
this.ticks.reverse();
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.obtainLinearTicks = function() {
|
|
// Obtain the required precision for the ticks
|
|
var step = 0;
|
|
var nSteps = 0;
|
|
var sigDigits = 0;
|
|
|
|
if (this.ticksSeparation > 0) {
|
|
step = (this.lim[1] > this.lim[0]) ? this.ticksSeparation : -this.ticksSeparation;
|
|
sigDigits = this.obtainSigDigits(step);
|
|
|
|
while (this.roundPlus(step, sigDigits) - step !== 0) {
|
|
sigDigits++;
|
|
}
|
|
|
|
nSteps = Math.floor((this.lim[1] - this.lim[0]) / step);
|
|
} else if (this.nTicks > 0) {
|
|
step = (this.lim[1] - this.lim[0]) / this.nTicks;
|
|
sigDigits = this.obtainSigDigits(step);
|
|
step = this.roundPlus(step, sigDigits);
|
|
|
|
if (step === 0 || Math.abs(step) > Math.abs(this.lim[1] - this.lim[0])) {
|
|
sigDigits++;
|
|
step = this.roundPlus((this.lim[1] - this.lim[0]) / this.nTicks, sigDigits);
|
|
}
|
|
|
|
nSteps = Math.floor((this.lim[1] - this.lim[0]) / step);
|
|
}
|
|
|
|
// Calculate the linear ticks
|
|
if (nSteps > 0) {
|
|
// Obtain the first tick
|
|
var firstTick = this.lim[0] + ((this.lim[1] - this.lim[0]) - nSteps * step) / 2;
|
|
|
|
// Subtract some steps to be sure we have all
|
|
firstTick = this.roundPlus(firstTick - 2 * step, sigDigits);
|
|
|
|
while ((this.lim[1] - firstTick) * (this.lim[0] - firstTick) > 0) {
|
|
firstTick = this.roundPlus(firstTick + step, sigDigits);
|
|
}
|
|
|
|
// Calculate the rest of the ticks
|
|
var n = Math.floor(Math.abs((this.lim[1] - firstTick) / step)) + 1;
|
|
this.adaptSize(this.ticks, n);
|
|
this.ticks[0] = firstTick;
|
|
|
|
for (var i = 1; i < n; i++) {
|
|
this.ticks[i] = this.roundPlus(this.ticks[i - 1] + step, sigDigits);
|
|
}
|
|
|
|
// Save the ticks precision
|
|
this.ticksPrecision = sigDigits;
|
|
} else {
|
|
this.ticks = [];
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.updatePlotTicks = function() {
|
|
var scaleFactor, i;
|
|
var n = this.ticks.length;
|
|
this.adaptSize(this.plotTicks, n);
|
|
|
|
if (this.log) {
|
|
if (this.type === this.parent.BOTTOM || this.type === this.parent.TOP) {
|
|
scaleFactor = this.dim[0] / Math.log(this.lim[1] / this.lim[0]);
|
|
} else {
|
|
scaleFactor = -this.dim[1] / Math.log(this.lim[1] / this.lim[0]);
|
|
}
|
|
|
|
for ( i = 0; i < n; i++) {
|
|
this.plotTicks[i] = Math.log(this.ticks[i] / this.lim[0]) * scaleFactor;
|
|
}
|
|
} else {
|
|
if (this.type === this.parent.BOTTOM || this.type === this.parent.TOP) {
|
|
scaleFactor = this.dim[0] / (this.lim[1] - this.lim[0]);
|
|
} else {
|
|
scaleFactor = -this.dim[1] / (this.lim[1] - this.lim[0]);
|
|
}
|
|
|
|
for ( i = 0; i < n; i++) {
|
|
this.plotTicks[i] = (this.ticks[i] - this.lim[0]) * scaleFactor;
|
|
}
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.updateTicksInside = function() {
|
|
var i;
|
|
var n = this.ticks.length;
|
|
this.adaptSize(this.ticksInside, n);
|
|
|
|
if (this.type === this.parent.BOTTOM || this.type === this.parent.TOP) {
|
|
for ( i = 0; i < n; i++) {
|
|
this.ticksInside[i] = (this.plotTicks[i] >= 0) && (this.plotTicks[i] <= this.dim[0]);
|
|
}
|
|
} else {
|
|
for ( i = 0; i < n; i++) {
|
|
this.ticksInside[i] = (-this.plotTicks[i] >= 0) && (-this.plotTicks[i] <= this.dim[1]);
|
|
}
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.updateTickLabels = function() {
|
|
var tick, logValue, isExactLogValue, i;
|
|
var n = this.ticks.length;
|
|
this.adaptSize(this.tickLabels, n);
|
|
|
|
if (this.log) {
|
|
for ( i = 0; i < n; i++) {
|
|
tick = this.ticks[i];
|
|
|
|
if (tick > 0) {
|
|
logValue = Math.log(tick) / Math.LN10;
|
|
isExactLogValue = Math.abs(logValue - Math.round(logValue)) < 0.0001;
|
|
|
|
if (isExactLogValue) {
|
|
logValue = Math.round(logValue);
|
|
|
|
if (this.expTickLabels) {
|
|
this.tickLabels[i] = "1e" + logValue;
|
|
} else {
|
|
if (logValue > -3.1 && logValue < 3.1) {
|
|
this.tickLabels[i] = (logValue >= 0) ? "" + Math.round(tick) : "" + tick;
|
|
} else {
|
|
this.tickLabels[i] = "1e" + logValue;
|
|
}
|
|
}
|
|
} else {
|
|
this.tickLabels[i] = "";
|
|
}
|
|
} else {
|
|
this.tickLabels[i] = "";
|
|
}
|
|
}
|
|
} else {
|
|
for ( i = 0; i < n; i++) {
|
|
tick = this.ticks[i];
|
|
|
|
if (tick % 1 === 0) {
|
|
this.tickLabels[i] = "" + Math.round(tick);
|
|
} else if ( typeof this.ticksPrecision !== "undefined" && this.ticksPrecision >= 0) {
|
|
this.tickLabels[i] = "" + parseFloat(tick).toFixed(this.ticksPrecision);
|
|
} else {
|
|
this.tickLabels[i] = "" + tick;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.moveLim = function(newLim) {
|
|
if (newLim[1] !== newLim[0]) {
|
|
// Check that the new limit makes sense
|
|
if (this.log && (newLim[0] <= 0 || newLim[1] <= 0)) {
|
|
console.log("The limits are negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.lim[0] = newLim[0];
|
|
this.lim[1] = newLim[1];
|
|
|
|
// Calculate the new ticks if they are not fixed
|
|
if (!this.fixedTicks) {
|
|
var n = this.ticks.length;
|
|
|
|
if (this.log) {
|
|
this.obtainLogarithmicTicks();
|
|
} else if (n > 0) {
|
|
// Obtain the ticks precision and the tick separation
|
|
var step = 0;
|
|
var sigDigits = 0;
|
|
|
|
if (this.ticksSeparation > 0) {
|
|
step = (this.lim[1] > this.lim[0]) ? this.ticksSeparation : -this.ticksSeparation;
|
|
sigDigits = this.obtainSigDigits(step);
|
|
|
|
while (this.roundPlus(step, sigDigits) - step !== 0) {
|
|
sigDigits++;
|
|
}
|
|
} else {
|
|
step = (n === 1) ? this.lim[1] - this.lim[0] : this.ticks[1] - this.ticks[0];
|
|
sigDigits = this.obtainSigDigits(step);
|
|
step = this.roundPlus(step, sigDigits);
|
|
|
|
if (step === 0 || Math.abs(step) > Math.abs(this.lim[1] - this.lim[0])) {
|
|
sigDigits++;
|
|
step = (n === 1) ? this.lim[1] - this.lim[0] : this.ticks[1] - this.ticks[0];
|
|
step = this.roundPlus(step, sigDigits);
|
|
}
|
|
|
|
step = (this.lim[1] > this.lim[0]) ? Math.abs(step) : -Math.abs(step);
|
|
}
|
|
|
|
// Obtain the first tick
|
|
var firstTick = this.ticks[0] + step * Math.ceil((this.lim[0] - this.ticks[0]) / step);
|
|
firstTick = this.roundPlus(firstTick, sigDigits);
|
|
|
|
if ((this.lim[1] - firstTick) * (this.lim[0] - firstTick) > 0) {
|
|
firstTick = this.ticks[0] + step * Math.floor((this.lim[0] - this.ticks[0]) / step);
|
|
firstTick = this.roundPlus(firstTick, sigDigits);
|
|
}
|
|
|
|
// Calculate the rest of the ticks
|
|
n = Math.floor(Math.abs((this.lim[1] - firstTick) / step)) + 1;
|
|
this.adaptSize(this.ticks, n);
|
|
this.ticks[0] = firstTick;
|
|
|
|
for (var i = 1; i < n; i++) {
|
|
this.ticks[i] = this.roundPlus(this.ticks[i - 1] + step, sigDigits);
|
|
}
|
|
|
|
// A sanity check
|
|
if (this.ticksPrecision !== sigDigits) {
|
|
console.log("There is a problem in the axis ticks precision calculation");
|
|
}
|
|
}
|
|
|
|
// Obtain the new tick labels
|
|
this.updateTickLabels();
|
|
}
|
|
|
|
// Update the rest of the arrays
|
|
this.updatePlotTicks();
|
|
this.updateTicksInside();
|
|
}
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.draw = function() {
|
|
switch (this.type) {
|
|
case this.parent.BOTTOM:
|
|
this.drawAsXAxis();
|
|
break;
|
|
case this.parent.LEFT:
|
|
this.drawAsYAxis();
|
|
break;
|
|
case this.parent.TOP:
|
|
this.drawAsTopAxis();
|
|
break;
|
|
case this.parent.RIGHT:
|
|
this.drawAsRightAxis();
|
|
break;
|
|
}
|
|
|
|
if (this.drawAxisLabel) {
|
|
this.lab.draw();
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.drawAsXAxis = function() {
|
|
var i;
|
|
|
|
// Draw the ticks
|
|
this.parent.push();
|
|
this.parent.stroke(this.lineColor);
|
|
this.parent.strokeWeight(this.lineWidth);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
|
|
this.parent.line(0, this.offset, this.dim[0], this.offset);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i]) {
|
|
if (this.log && this.tickLabels[i] === "") {
|
|
this.parent.line(this.plotTicks[i], this.offset, this.plotTicks[i], this.offset + this.smallTickLength);
|
|
} else {
|
|
this.parent.line(this.plotTicks[i], this.offset, this.plotTicks[i], this.offset + this.tickLength);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
|
|
// Draw the tick labels
|
|
if (this.drawTickLabels) {
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.noStroke();
|
|
|
|
if (this.rotateTickLabels) {
|
|
var halfPI = 0.5 * Math.PI;
|
|
this.parent.textAlign(this.parent.RIGHT, this.parent.CENTER);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i] && this.tickLabels[i] !== "") {
|
|
this.parent.push();
|
|
this.parent.translate(this.plotTicks[i], this.offset + this.tickLabelOffset);
|
|
this.parent.rotate(-halfPI);
|
|
this.parent.text(this.tickLabels[i], 0, 0);
|
|
this.parent.pop();
|
|
}
|
|
}
|
|
} else {
|
|
this.parent.textAlign(this.parent.CENTER, this.parent.TOP);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i] && this.tickLabels[i] !== "") {
|
|
this.parent.text(this.tickLabels[i], this.plotTicks[i], this.offset + this.tickLabelOffset);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.drawAsYAxis = function() {
|
|
var i;
|
|
|
|
// Draw the ticks
|
|
this.parent.push();
|
|
this.parent.stroke(this.lineColor);
|
|
this.parent.strokeWeight(this.lineWidth);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
|
|
this.parent.line(-this.offset, 0, -this.offset, -this.dim[1]);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i]) {
|
|
if (this.log && this.tickLabels[i] === "") {
|
|
this.parent.line(-this.offset, this.plotTicks[i], -this.offset - this.smallTickLength, this.plotTicks[i]);
|
|
} else {
|
|
this.parent.line(-this.offset, this.plotTicks[i], -this.offset - this.tickLength, this.plotTicks[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
|
|
// Draw the tick labels
|
|
if (this.drawTickLabels) {
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.noStroke();
|
|
|
|
if (this.rotateTickLabels) {
|
|
var halfPI = 0.5 * Math.PI;
|
|
this.parent.textAlign(this.parent.CENTER, this.parent.BOTTOM);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i] && this.tickLabels[i] !== "") {
|
|
this.parent.push();
|
|
this.parent.translate(-this.offset - this.tickLabelOffset, this.plotTicks[i]);
|
|
this.parent.rotate(-halfPI);
|
|
this.parent.text(this.tickLabels[i], 0, 0);
|
|
this.parent.pop();
|
|
}
|
|
}
|
|
} else {
|
|
this.parent.textAlign(this.parent.RIGHT, this.parent.CENTER);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i] && this.tickLabels[i] !== "") {
|
|
this.parent.text(this.tickLabels[i], -this.offset - this.tickLabelOffset, this.plotTicks[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.drawAsTopAxis = function() {
|
|
var i;
|
|
|
|
// Draw the ticks
|
|
this.parent.push();
|
|
this.parent.stroke(this.lineColor);
|
|
this.parent.strokeWeight(this.lineWidth);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
this.parent.translate(0, -this.dim[1]);
|
|
|
|
this.parent.line(0, -this.offset, this.dim[0], -this.offset);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i]) {
|
|
if (this.log && this.tickLabels[i] === "") {
|
|
this.parent.line(this.plotTicks[i], -this.offset, this.plotTicks[i], -this.offset - this.smallTickLength);
|
|
} else {
|
|
this.parent.line(this.plotTicks[i], -this.offset, this.plotTicks[i], -this.offset - this.tickLength);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
|
|
// Draw the tick labels
|
|
if (this.drawTickLabels) {
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.noStroke();
|
|
this.parent.translate(0, -this.dim[1]);
|
|
|
|
if (this.rotateTickLabels) {
|
|
var halfPI = 0.5 * Math.PI;
|
|
this.parent.textAlign(this.parent.LEFT, this.parent.CENTER);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i] && this.tickLabels[i] !== "") {
|
|
this.parent.push();
|
|
this.parent.translate(this.plotTicks[i], -this.offset - this.tickLabelOffset);
|
|
this.parent.rotate(-halfPI);
|
|
this.parent.text(this.tickLabels[i], 0, 0);
|
|
this.parent.pop();
|
|
}
|
|
}
|
|
} else {
|
|
this.parent.textAlign(this.parent.CENTER, this.parent.BOTTOM);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i] && this.tickLabels[i] !== "") {
|
|
this.parent.text(this.tickLabels[i], this.plotTicks[i], -this.offset - this.tickLabelOffset);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.drawAsRightAxis = function() {
|
|
var i;
|
|
|
|
// Draw the ticks
|
|
this.parent.push();
|
|
this.parent.stroke(this.lineColor);
|
|
this.parent.strokeWeight(this.lineWidth);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
this.parent.translate(this.dim[0], 0);
|
|
|
|
this.parent.line(this.offset, 0, this.offset, -this.dim[1]);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i]) {
|
|
if (this.log && this.tickLabels[i] === "") {
|
|
this.parent.line(this.offset, this.plotTicks[i], this.offset + this.smallTickLength, this.plotTicks[i]);
|
|
} else {
|
|
this.parent.line(this.offset, this.plotTicks[i], this.offset + this.tickLength, this.plotTicks[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
|
|
// Draw the tick labels
|
|
if (this.drawTickLabels) {
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.noStroke();
|
|
this.parent.translate(this.dim[0], 0);
|
|
|
|
if (this.rotateTickLabels) {
|
|
var halfPI = 0.5 * Math.PI;
|
|
this.parent.textAlign(this.parent.CENTER, this.parent.TOP);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i] && this.tickLabels[i] !== "") {
|
|
this.parent.push();
|
|
this.parent.translate(this.offset + this.tickLabelOffset, this.plotTicks[i]);
|
|
this.parent.rotate(-halfPI);
|
|
this.parent.text(this.tickLabels[i], 0, 0);
|
|
this.parent.pop();
|
|
}
|
|
}
|
|
} else {
|
|
this.parent.textAlign(this.parent.LEFT, this.parent.CENTER);
|
|
|
|
for ( i = 0; i < this.plotTicks.length; i++) {
|
|
if (this.ticksInside[i] && this.tickLabels[i] !== "") {
|
|
this.parent.text(this.tickLabels[i], this.offset + this.tickLabelOffset, this.plotTicks[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setDim = function() {
|
|
var xDim, yDim;
|
|
|
|
if (arguments.length === 2) {
|
|
xDim = arguments[0];
|
|
yDim = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
xDim = arguments[0][0];
|
|
yDim = arguments[0][1];
|
|
} else {
|
|
throw new Error("GAxis.setDim(): signature not supported");
|
|
}
|
|
|
|
if (xDim > 0 && yDim > 0) {
|
|
this.dim[0] = xDim;
|
|
this.dim[1] = yDim;
|
|
this.updatePlotTicks();
|
|
this.lab.setDim(this.dim);
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setLim = function(lim) {
|
|
if (lim[1] !== lim[0]) {
|
|
// Make sure the new limits makes sense
|
|
if (this.log && (lim[0] <= 0 || lim[1] <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.lim[0] = lim[0];
|
|
this.lim[1] = lim[1];
|
|
|
|
if (!this.fixedTicks) {
|
|
this.updateTicks();
|
|
this.updateTickLabels();
|
|
}
|
|
|
|
this.updatePlotTicks();
|
|
this.updateTicksInside();
|
|
}
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setLimAndLog = function(lim, log) {
|
|
if (lim[1] !== lim[0]) {
|
|
// Make sure the new limits makes sense
|
|
if (log && (lim[0] <= 0 || lim[1] <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.lim[0] = lim[0];
|
|
this.lim[1] = lim[1];
|
|
this.log = log;
|
|
|
|
if (!this.fixedTicks) {
|
|
this.updateTicks();
|
|
this.updateTickLabels();
|
|
}
|
|
|
|
this.updatePlotTicks();
|
|
this.updateTicksInside();
|
|
}
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setLog = function(log) {
|
|
if (log !== this.log) {
|
|
this.log = log;
|
|
|
|
// Check if the old limits still make sense
|
|
if (this.log && (this.lim[0] <= 0 || this.lim[1] <= 0)) {
|
|
console.log("The limits are negative. This is not allowed in logarithmic scale.");
|
|
console.log("Will set them to (0.1, 10)");
|
|
|
|
if (this.lim[1] > this.lim[0]) {
|
|
this.lim[0] = 0.1;
|
|
this.lim[1] = 10;
|
|
} else {
|
|
this.lim[0] = 10;
|
|
this.lim[1] = 0.1;
|
|
}
|
|
}
|
|
|
|
if (!this.fixedTicks) {
|
|
this.updateTicks();
|
|
this.updateTickLabels();
|
|
}
|
|
|
|
this.updatePlotTicks();
|
|
this.updateTicksInside();
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setOffset = function(offset) {
|
|
this.offset = offset;
|
|
};
|
|
|
|
GAxis.prototype.setLineColor = function(lineColor) {
|
|
this.lineColor = lineColor;
|
|
};
|
|
|
|
GAxis.prototype.setLineWidth = function(lineWidth) {
|
|
if (lineWidth > 0) {
|
|
this.lineWidth = lineWidth;
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setNTicks = function(nTicks) {
|
|
if (nTicks >= 0) {
|
|
this.nTicks = nTicks;
|
|
this.ticksSeparation = -1;
|
|
|
|
if (!this.log) {
|
|
this.fixedTicks = false;
|
|
this.updateTicks();
|
|
this.updatePlotTicks();
|
|
this.updateTicksInside();
|
|
this.updateTickLabels();
|
|
}
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setTicksSeparation = function(ticksSeparation) {
|
|
this.ticksSeparation = ticksSeparation;
|
|
|
|
if (!this.log) {
|
|
this.fixedTicks = false;
|
|
this.updateTicks();
|
|
this.updatePlotTicks();
|
|
this.updateTicksInside();
|
|
this.updateTickLabels();
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setTicks = function(ticks) {
|
|
var n = ticks.length;
|
|
this.adaptSize(this.ticks, n);
|
|
|
|
for (var i = 0; i < n; i++) {
|
|
this.ticks[i] = ticks[i];
|
|
}
|
|
|
|
this.fixedTicks = true;
|
|
|
|
// Set the tick precision to undefined
|
|
this.ticksPrecision = undefined;
|
|
|
|
this.updatePlotTicks();
|
|
this.updateTicksInside();
|
|
this.updateTickLabels();
|
|
};
|
|
|
|
GAxis.prototype.setTickLabels = function(tickLabels) {
|
|
if (tickLabels.length === this.tickLabels.length) {
|
|
for (var i = 0; i < this.tickLabels.length; i++) {
|
|
this.tickLabels[i] = tickLabels[i];
|
|
}
|
|
|
|
this.fixedTicks = true;
|
|
|
|
// Set the tick precision to undefined
|
|
this.ticksPrecision = undefined;
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setFixedTicks = function(fixedTicks) {
|
|
if (fixedTicks !== this.fixedTicks) {
|
|
this.fixedTicks = fixedTicks;
|
|
|
|
if (!this.fixedTicks) {
|
|
this.updateTicks();
|
|
this.updatePlotTicks();
|
|
this.updateTicksInside();
|
|
this.updateTickLabels();
|
|
}
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setTickLength = function(tickLength) {
|
|
this.tickLength = tickLength;
|
|
};
|
|
|
|
GAxis.prototype.setSmallTickLength = function(smallTickLength) {
|
|
this.smallTickLength = smallTickLength;
|
|
};
|
|
|
|
GAxis.prototype.setExpTickLabels = function(expTickLabels) {
|
|
if (expTickLabels !== this.expTickLabels) {
|
|
this.expTickLabels = expTickLabels;
|
|
this.updateTickLabels();
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setRotateTickLabels = function(rotateTickLabels) {
|
|
this.rotateTickLabels = rotateTickLabels;
|
|
};
|
|
|
|
GAxis.prototype.setDrawTickLabels = function(drawTickLabels) {
|
|
this.drawTickLabels = drawTickLabels;
|
|
};
|
|
|
|
GAxis.prototype.setTickLabelOffset = function(tickLabelOffset) {
|
|
this.tickLabelOffset = tickLabelOffset;
|
|
};
|
|
|
|
GAxis.prototype.setDrawAxisLabel = function(drawAxisLabel) {
|
|
this.drawAxisLabel = drawAxisLabel;
|
|
};
|
|
|
|
GAxis.prototype.setAxisLabelText = function(axisLabelText) {
|
|
this.lab.setText(axisLabelText);
|
|
};
|
|
|
|
GAxis.prototype.setFontName = function(fontName) {
|
|
this.fontName = fontName;
|
|
};
|
|
|
|
GAxis.prototype.setFontColor = function(fontColor) {
|
|
this.fontColor = fontColor;
|
|
};
|
|
|
|
GAxis.prototype.setFontSize = function(fontSize) {
|
|
if (fontSize > 0) {
|
|
this.fontSize = fontSize;
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setFontProperties = function(fontName, fontColor, fontSize) {
|
|
if (fontSize > 0) {
|
|
this.fontName = fontName;
|
|
this.fontColor = fontColor;
|
|
this.fontSize = fontSize;
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.setAllFontProperties = function(fontName, fontColor, fontSize) {
|
|
this.setFontProperties(fontName, fontColor, fontSize);
|
|
this.lab.setFontProperties(fontName, fontColor, fontSize);
|
|
};
|
|
|
|
GAxis.prototype.getTicks = function() {
|
|
if (this.fixedTicks) {
|
|
return this.ticks.slice();
|
|
} else {
|
|
// Return only the ticks that are visible
|
|
var validTicks = [];
|
|
var counter = 0;
|
|
|
|
for (var i = 0; i < this.ticksInside.length; i++) {
|
|
if (this.ticksInside[i]) {
|
|
validTicks[counter] = this.ticks[i];
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
return validTicks;
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.getTicksRef = function() {
|
|
return this.ticks;
|
|
};
|
|
|
|
GAxis.prototype.getPlotTicks = function() {
|
|
if (this.fixedTicks) {
|
|
return this.plotTicks.slice();
|
|
} else {
|
|
var validPlotTicks = [];
|
|
var counter = 0;
|
|
|
|
for (var i = 0; i < this.ticksInside.length; i++) {
|
|
if (this.ticksInside[i]) {
|
|
validPlotTicks[counter] = this.plotTicks[i];
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
return validPlotTicks;
|
|
}
|
|
};
|
|
|
|
GAxis.prototype.getPlotTicksRef = function() {
|
|
return this.plotTicks;
|
|
};
|
|
|
|
GAxis.prototype.getAxisLabel = function() {
|
|
return this.lab;
|
|
};
|
|
/*
|
|
* Histogram class.
|
|
*/
|
|
function GHistogram(parent, type, dim, plotPoints) {
|
|
// The parent processing object
|
|
this.parent = parent;
|
|
|
|
// General properties
|
|
this.type = (type === GPlot.VERTICAL || type === GPlot.HORIZONTAL) ? type : GPlot.VERTICAL;
|
|
this.dim = dim.slice();
|
|
this.plotPoints = [];
|
|
|
|
// Copy the plot points
|
|
for (var i = 0; i < plotPoints.length; i++) {
|
|
this.plotPoints[i] = new GPoint(plotPoints[i]);
|
|
}
|
|
|
|
this.visible = true;
|
|
this.separations = [2];
|
|
this.bgColors = [this.parent.color(150, 150, 255)];
|
|
this.lineColors = [this.parent.color(100, 100, 255)];
|
|
this.lineWidths = [1];
|
|
this.differences = [];
|
|
this.leftSides = [];
|
|
this.rightSides = [];
|
|
this.updateArrays();
|
|
|
|
// Labels properties
|
|
this.labelsOffset = 8;
|
|
this.drawLabels = false;
|
|
this.rotateLabels = false;
|
|
this.fontName = "Helvetica";
|
|
this.fontColor = this.parent.color(0);
|
|
this.fontSize = 11;
|
|
}
|
|
|
|
GHistogram.prototype.updateArrays = function() {
|
|
var i;
|
|
var nPoints = this.plotPoints.length;
|
|
|
|
// Remove unused points
|
|
if (this.differences.length > nPoints) {
|
|
this.differences.splice(nPoints, Number.MAX_VALUE);
|
|
this.leftSides.splice(nPoints, Number.MAX_VALUE);
|
|
this.rightSides.splice(nPoints, Number.MAX_VALUE);
|
|
}
|
|
|
|
// Update the arrays
|
|
if (nPoints === 1) {
|
|
this.leftSides[0] = (this.type === GPlot.VERTICAL) ? 0.2 * this.dim[0] : 0.2 * this.dim[1];
|
|
this.rightSides[0] = this.leftSides[0];
|
|
} else if (nPoints > 1) {
|
|
// Calculate the differences between consecutive points
|
|
for ( i = 0; i < nPoints - 1; i++) {
|
|
if (this.plotPoints[i].isValid() && this.plotPoints[i + 1].isValid()) {
|
|
var separation = this.separations[i % this.separations.length];
|
|
var diff;
|
|
|
|
if (this.type === GPlot.VERTICAL) {
|
|
diff = this.plotPoints[i + 1].getX() - this.plotPoints[i].getX();
|
|
} else {
|
|
diff = this.plotPoints[i + 1].getY() - this.plotPoints[i].getY();
|
|
}
|
|
|
|
if (diff > 0) {
|
|
this.differences[i] = (diff - separation) / 2;
|
|
} else {
|
|
this.differences[i] = (diff + separation) / 2;
|
|
}
|
|
} else {
|
|
this.differences[i] = 0;
|
|
}
|
|
}
|
|
|
|
// Fill the leftSides and rightSides arrays
|
|
this.leftSides[0] = this.differences[0];
|
|
this.rightSides[0] = this.differences[0];
|
|
|
|
for ( i = 1; i < nPoints - 1; i++) {
|
|
this.leftSides[i] = this.differences[i - 1];
|
|
this.rightSides[i] = this.differences[i];
|
|
}
|
|
|
|
this.leftSides[nPoints - 1] = this.differences[nPoints - 2];
|
|
this.rightSides[nPoints - 1] = this.differences[nPoints - 2];
|
|
}
|
|
};
|
|
|
|
GHistogram.prototype.draw = function(plotBasePoint) {
|
|
if (this.visible) {
|
|
// Calculate the baseline for the histogram
|
|
var baseline = 0;
|
|
|
|
if (plotBasePoint.isValid()) {
|
|
baseline = (this.type === GPlot.VERTICAL) ? plotBasePoint.getY() : plotBasePoint.getX();
|
|
}
|
|
|
|
// Draw the rectangles
|
|
var point, x1, x2, y1, y2, lw;
|
|
var nPoints = this.plotPoints.length;
|
|
|
|
this.parent.push();
|
|
this.parent.rectMode(this.parent.CORNERS);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
|
|
for (var i = 0; i < nPoints; i++) {
|
|
point = this.plotPoints[i];
|
|
|
|
if (point.isValid()) {
|
|
// Obtain the corners
|
|
if (this.type === GPlot.VERTICAL) {
|
|
x1 = point.getX() - this.leftSides[i];
|
|
x2 = point.getX() + this.rightSides[i];
|
|
y1 = point.getY();
|
|
y2 = baseline;
|
|
} else {
|
|
x1 = baseline;
|
|
x2 = point.getX();
|
|
y1 = point.getY() - this.leftSides[i];
|
|
y2 = point.getY() + this.rightSides[i];
|
|
}
|
|
|
|
if (x1 < 0) {
|
|
x1 = 0;
|
|
} else if (x1 > this.dim[0]) {
|
|
x1 = this.dim[0];
|
|
}
|
|
|
|
if (-y1 < 0) {
|
|
y1 = 0;
|
|
} else if (-y1 > this.dim[1]) {
|
|
y1 = -this.dim[1];
|
|
}
|
|
|
|
if (x2 < 0) {
|
|
x2 = 0;
|
|
} else if (x2 > this.dim[0]) {
|
|
x2 = this.dim[0];
|
|
}
|
|
|
|
if (-y2 < 0) {
|
|
y2 = 0;
|
|
} else if (-y2 > this.dim[1]) {
|
|
y2 = -this.dim[1];
|
|
}
|
|
|
|
// Draw the rectangle
|
|
lw = this.lineWidths[i % this.lineWidths.length];
|
|
this.parent.fill(this.bgColors[i % this.bgColors.length]);
|
|
this.parent.stroke(this.lineColors[i % this.lineColors.length]);
|
|
this.parent.strokeWeight(lw);
|
|
|
|
if (Math.abs(x2 - x1) > 2 * lw && Math.abs(y2 - y1) > 2 * lw) {
|
|
this.parent.rect(x1, y1, x2, y2);
|
|
} else if ((this.type === GPlot.VERTICAL && x2 !== x1 && !(y1 === y2 && (y1 === 0 || y1 === -this.dim[1]))) || (this.type === GPlot.HORIZONTAL && y2 !== y1 && !(x1 === x2 && (x1 === 0 || x1 === this.dim[0])))) {
|
|
this.parent.rect(x1, y1, x2, y2);
|
|
this.parent.line(x1, y1, x1, y2);
|
|
this.parent.line(x2, y1, x2, y2);
|
|
this.parent.line(x1, y1, x2, y1);
|
|
this.parent.line(x1, y2, x2, y2);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
|
|
// Draw the labels
|
|
if (this.drawLabels) {
|
|
this.drawHistLabels();
|
|
}
|
|
}
|
|
};
|
|
|
|
GHistogram.prototype.drawHistLabels = function() {
|
|
var point, i;
|
|
var nPoints = this.plotPoints.length;
|
|
var halfPI = 0.5 * Math.PI;
|
|
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.noStroke();
|
|
|
|
if (this.type === GPlot.VERTICAL) {
|
|
if (this.rotateLabels) {
|
|
this.parent.textAlign(this.parent.RIGHT, this.parent.CENTER);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = this.plotPoints[i];
|
|
|
|
if (point.isValid() && point.getX() >= 0 && point.getX() <= this.dim[0]) {
|
|
this.parent.push();
|
|
this.parent.translate(point.getX(), this.labelsOffset);
|
|
this.parent.rotate(-halfPI);
|
|
this.parent.text(point.getLabel(), 0, 0);
|
|
this.parent.pop();
|
|
}
|
|
}
|
|
} else {
|
|
this.parent.textAlign(this.parent.CENTER, this.parent.TOP);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = this.plotPoints[i];
|
|
|
|
if (point.isValid() && point.getX() >= 0 && point.getX() <= this.dim[0]) {
|
|
this.parent.text(point.getLabel(), point.getX(), this.labelsOffset);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (this.rotateLabels) {
|
|
this.parent.textAlign(this.parent.CENTER, this.parent.BOTTOM);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = this.plotPoints[i];
|
|
|
|
if (point.isValid() && -point.getY() >= 0 && -point.getY() <= this.dim[1]) {
|
|
this.parent.push();
|
|
this.parent.translate(-this.labelsOffset, point.getY());
|
|
this.parent.rotate(-halfPI);
|
|
this.parent.text(point.getLabel(), 0, 0);
|
|
this.parent.pop();
|
|
}
|
|
}
|
|
} else {
|
|
this.parent.textAlign(this.parent.RIGHT, this.parent.CENTER);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = this.plotPoints[i];
|
|
|
|
if (point.isValid() && -point.getY() >= 0 && -point.getY() <= this.dim[1]) {
|
|
this.parent.text(point.getLabel(), -this.labelsOffset, point.getY());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
};
|
|
|
|
GHistogram.prototype.setType = function(type) {
|
|
if (type !== this.type && (type === GPlot.VERTICAL || type === GPlot.HORIZONTAL)) {
|
|
this.type = type;
|
|
this.updateArrays();
|
|
}
|
|
};
|
|
|
|
GHistogram.prototype.setDim = function() {
|
|
var xDim, yDim;
|
|
|
|
if (arguments.length === 2) {
|
|
xDim = arguments[0];
|
|
yDim = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
xDim = arguments[0][0];
|
|
yDim = arguments[0][1];
|
|
} else {
|
|
throw new Error("GHistogram.setDim(): signature not supported");
|
|
}
|
|
|
|
if (xDim > 0 && yDim > 0) {
|
|
this.dim[0] = xDim;
|
|
this.dim[1] = yDim;
|
|
this.updateArrays();
|
|
}
|
|
};
|
|
|
|
GHistogram.prototype.setPlotPoints = function(plotPoints) {
|
|
var i;
|
|
var nPoints = plotPoints.length;
|
|
|
|
if (this.plotPoints.length === nPoints) {
|
|
for ( i = 0; i < nPoints; i++) {
|
|
this.plotPoints[i].set(plotPoints[i]);
|
|
}
|
|
} else if (this.plotPoints.length > nPoints) {
|
|
for ( i = 0; i < nPoints; i++) {
|
|
this.plotPoints[i].set(plotPoints[i]);
|
|
}
|
|
|
|
this.plotPoints.splice(nPoints, Number.MAX_VALUE);
|
|
} else {
|
|
for ( i = 0; i < this.plotPoints.length; i++) {
|
|
this.plotPoints[i].set(plotPoints[i]);
|
|
}
|
|
|
|
for ( i = this.plotPoints.lengh; i < nPoints; i++) {
|
|
this.plotPoints[i] = new GPoint(plotPoints[i]);
|
|
}
|
|
}
|
|
|
|
this.updateArrays();
|
|
};
|
|
|
|
GHistogram.prototype.setPlotPoint = function(index, plotPoint) {
|
|
if (index < this.plotPoints.length) {
|
|
this.plotPoints[index].set(plotPoint);
|
|
} else if (index === this.plotPoints.length) {
|
|
this.plotPoints[index] = new GPoint(plotPoint);
|
|
} else {
|
|
throw new Error("GHistogram.setPlotPoint(): the index position is outside the array size");
|
|
}
|
|
|
|
this.updateArrays();
|
|
};
|
|
|
|
GHistogram.prototype.addPlotPoint = function() {
|
|
if (arguments.length === 2) {
|
|
this.plotPoints.push(new GPoint(arguments[0], arguments[1]));
|
|
} else if (arguments.length === 1) {
|
|
this.plotPoints.push(new GPoint(arguments[0]));
|
|
} else {
|
|
throw new Error("GHistogram.addPlotPoint(): signature not supported");
|
|
}
|
|
|
|
this.updateArrays();
|
|
};
|
|
|
|
GHistogram.prototype.removePlotPoint = function(index) {
|
|
if (index < this.plotPoints.length) {
|
|
this.plotPoints.splice(index, 1);
|
|
} else {
|
|
throw new Error("GHistogram.removePlotPoint(): the index position is outside the array size");
|
|
}
|
|
|
|
this.updateArrays();
|
|
};
|
|
|
|
GHistogram.prototype.setSeparations = function(separations) {
|
|
this.separations = separations.slice();
|
|
this.updateArrays();
|
|
};
|
|
|
|
GHistogram.prototype.setBgColors = function(bgColors) {
|
|
this.bgColors = bgColors.slice();
|
|
};
|
|
|
|
GHistogram.prototype.setLineColors = function(lineColors) {
|
|
this.lineColors = lineColors.slice();
|
|
};
|
|
|
|
GHistogram.prototype.setLineWidths = function(lineWidths) {
|
|
this.lineWidths = lineWidths.slice();
|
|
};
|
|
|
|
GHistogram.prototype.setVisible = function(visible) {
|
|
this.visible = visible;
|
|
};
|
|
|
|
GHistogram.prototype.setLabelsOffset = function(labelsOffset) {
|
|
this.labelsOffset = labelsOffset;
|
|
};
|
|
|
|
GHistogram.prototype.setDrawLabels = function(drawLabels) {
|
|
this.drawLabels = drawLabels;
|
|
};
|
|
|
|
GHistogram.prototype.setRotateLabels = function(rotateLabels) {
|
|
this.rotateLabels = rotateLabels;
|
|
};
|
|
|
|
GHistogram.prototype.setFontName = function(fontName) {
|
|
this.fontName = fontName;
|
|
};
|
|
|
|
GHistogram.prototype.setFontColor = function(fontColor) {
|
|
this.fontColor = fontColor;
|
|
};
|
|
|
|
GHistogram.prototype.setFontSize = function(fontSize) {
|
|
if (fontSize > 0) {
|
|
this.fontSize = fontSize;
|
|
}
|
|
};
|
|
|
|
GHistogram.prototype.setFontProperties = function(fontName, fontColor, fontSize) {
|
|
if (fontSize > 0) {
|
|
this.fontName = fontName;
|
|
this.fontColor = fontColor;
|
|
this.fontSize = fontSize;
|
|
}
|
|
};
|
|
/*
|
|
* Layer class. A GLayer usually contains an array of points and a histogram
|
|
*/
|
|
function GLayer(parent, id, dim, xLim, yLim, xLog, yLog) {
|
|
// The parent processing object
|
|
this.parent = parent;
|
|
|
|
// General properties
|
|
this.id = id;
|
|
this.dim = dim.slice();
|
|
this.xLim = xLim.slice();
|
|
this.yLim = yLim.slice();
|
|
this.xLog = xLog;
|
|
this.yLog = yLog;
|
|
|
|
// Do some sanity checks
|
|
if (this.xLog && (this.xLim[0] <= 0 || this.xLim[1] <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
console.log("Will set horizontal limits to (0.1, 10)");
|
|
this.xLim[0] = 0.1;
|
|
this.xLim[1] = 10;
|
|
}
|
|
|
|
if (this.yLog && (this.yLim[0] <= 0 || this.yLim[1] <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
console.log("Will set vertical limits to (0.1, 10)");
|
|
this.yLim[0] = 0.1;
|
|
this.yLim[1] = 10;
|
|
}
|
|
|
|
// Points properties
|
|
this.points = [];
|
|
this.plotPoints = [];
|
|
this.inside = [];
|
|
this.pointColors = [this.parent.color(255, 0, 0, 150)];
|
|
this.pointSizes = [7];
|
|
|
|
// Line properties
|
|
this.lineColor = this.parent.color(0, 150);
|
|
this.lineWidth = 1;
|
|
|
|
// Histogram properties
|
|
this.hist = undefined;
|
|
this.histBasePoint = new GPoint(0, 0);
|
|
|
|
// Labels properties
|
|
this.labelBgColor = this.parent.color(255, 200);
|
|
this.labelSeparation = [7, 7];
|
|
this.fontName = "Helvetica";
|
|
this.fontColor = this.parent.color(0);
|
|
this.fontSize = 11;
|
|
|
|
// Helper variable
|
|
this.cuts = [[0, 0], [0, 0], [0, 0], [0, 0]];
|
|
}
|
|
|
|
GLayer.prototype.isValidNumber = function(number) {
|
|
return !isNaN(number) && isFinite(number);
|
|
};
|
|
|
|
GLayer.prototype.isId = function(someId) {
|
|
return this.id === someId;
|
|
};
|
|
|
|
GLayer.prototype.valueToXPlot = function(x) {
|
|
if (this.xLog) {
|
|
return this.dim[0] * Math.log(x / this.xLim[0]) / Math.log(this.xLim[1] / this.xLim[0]);
|
|
} else {
|
|
return this.dim[0] * (x - this.xLim[0]) / (this.xLim[1] - this.xLim[0]);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.valueToYPlot = function(y) {
|
|
if (this.yLog) {
|
|
return -this.dim[1] * Math.log(y / this.yLim[0]) / Math.log(this.yLim[1] / this.yLim[0]);
|
|
} else {
|
|
return -this.dim[1] * (y - this.yLim[0]) / (this.yLim[1] - this.yLim[0]);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.valueToPlot = function() {
|
|
if (arguments.length === 2) {
|
|
return [this.valueToXPlot(arguments[0]), this.valueToYPlot(arguments[1])];
|
|
} else if (arguments.length === 1 && arguments[0] instanceof GPoint) {
|
|
return new GPoint(this.valueToXPlot(arguments[0].getX()), this.valueToYPlot(arguments[0].getY()), arguments[0].getLabel());
|
|
} else if (arguments.length === 1 && arguments[0] instanceof Array && arguments[0][0] instanceof GPoint) {
|
|
var xScalingFactor, yScalingFactor, point, xPlot, yPlot, i;
|
|
var nPoints = arguments[0].length;
|
|
var plotPts = [];
|
|
|
|
// Go case by case. More code, but it's faster
|
|
if (this.xLog && this.yLog) {
|
|
xScalingFactor = this.dim[0] / Math.log(this.xLim[1] / this.xLim[0]);
|
|
yScalingFactor = -this.dim[1] / Math.log(this.yLim[1] / this.yLim[0]);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = arguments[0][i];
|
|
xPlot = Math.log(point.getX() / this.xLim[0]) * xScalingFactor;
|
|
yPlot = Math.log(point.getY() / this.yLim[0]) * yScalingFactor;
|
|
plotPts[i] = new GPoint(xPlot, yPlot, point.getLabel());
|
|
}
|
|
} else if (this.xLog) {
|
|
xScalingFactor = this.dim[0] / Math.log(this.xLim[1] / this.xLim[0]);
|
|
yScalingFactor = -this.dim[1] / (this.yLim[1] - this.yLim[0]);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = arguments[0][i];
|
|
xPlot = Math.log(point.getX() / this.xLim[0]) * xScalingFactor;
|
|
yPlot = (point.getY() - this.yLim[0]) * yScalingFactor;
|
|
plotPts[i] = new GPoint(xPlot, yPlot, point.getLabel());
|
|
}
|
|
} else if (this.yLog) {
|
|
xScalingFactor = this.dim[0] / (this.xLim[1] - this.xLim[0]);
|
|
yScalingFactor = -this.dim[1] / Math.log(this.yLim[1] / this.yLim[0]);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = arguments[0][i];
|
|
xPlot = (point.getX() - this.xLim[0]) * xScalingFactor;
|
|
yPlot = Math.log(point.getY() / this.yLim[0]) * yScalingFactor;
|
|
plotPts[i] = new GPoint(xPlot, yPlot, point.getLabel());
|
|
}
|
|
} else {
|
|
xScalingFactor = this.dim[0] / (this.xLim[1] - this.xLim[0]);
|
|
yScalingFactor = -this.dim[1] / (this.yLim[1] - this.yLim[0]);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = arguments[0][i];
|
|
xPlot = (point.getX() - this.xLim[0]) * xScalingFactor;
|
|
yPlot = (point.getY() - this.yLim[0]) * yScalingFactor;
|
|
plotPts[i] = new GPoint(xPlot, yPlot, point.getLabel());
|
|
}
|
|
}
|
|
|
|
return plotPts;
|
|
} else {
|
|
throw new Error("GLayer.valueToPlot(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.updatePlotPoints = function() {
|
|
var xScalingFactor, yScalingFactor, point, xPlot, yPlot, i;
|
|
var nPoints = this.points.length;
|
|
|
|
// Update the plotPoints array size if necessary
|
|
if (this.plotPoints.length < nPoints) {
|
|
for ( i = this.plotPoints.length; i < nPoints; i++) {
|
|
this.plotPoints[i] = new GPoint();
|
|
}
|
|
} else if (this.plotPoints.length > nPoints) {
|
|
this.plotPoints.splice(nPoints, Number.MAX_VALUE);
|
|
}
|
|
|
|
// Go case by case. More code, but it should be faster
|
|
if (this.xLog && this.yLog) {
|
|
xScalingFactor = this.dim[0] / Math.log(this.xLim[1] / this.xLim[0]);
|
|
yScalingFactor = -this.dim[1] / Math.log(this.yLim[1] / this.yLim[0]);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = this.points[i];
|
|
xPlot = Math.log(point.getX() / this.xLim[0]) * xScalingFactor;
|
|
yPlot = Math.log(point.getY() / this.yLim[0]) * yScalingFactor;
|
|
this.plotPoints[i].set(xPlot, yPlot, point.getLabel());
|
|
}
|
|
} else if (this.xLog) {
|
|
xScalingFactor = this.dim[0] / Math.log(this.xLim[1] / this.xLim[0]);
|
|
yScalingFactor = -this.dim[1] / (this.yLim[1] - this.yLim[0]);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = this.points[i];
|
|
xPlot = Math.log(point.getX() / this.xLim[0]) * xScalingFactor;
|
|
yPlot = (point.getY() - this.yLim[0]) * yScalingFactor;
|
|
this.plotPoints[i].set(xPlot, yPlot, point.getLabel());
|
|
}
|
|
} else if (this.yLog) {
|
|
xScalingFactor = this.dim[0] / (this.xLim[1] - this.xLim[0]);
|
|
yScalingFactor = -this.dim[1] / Math.log(this.yLim[1] / this.yLim[0]);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = this.points[i];
|
|
xPlot = (point.getX() - this.xLim[0]) * xScalingFactor;
|
|
yPlot = Math.log(point.getY() / this.yLim[0]) * yScalingFactor;
|
|
this.plotPoints[i].set(xPlot, yPlot, point.getLabel());
|
|
}
|
|
} else {
|
|
xScalingFactor = this.dim[0] / (this.xLim[1] - this.xLim[0]);
|
|
yScalingFactor = -this.dim[1] / (this.yLim[1] - this.yLim[0]);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = this.points[i];
|
|
xPlot = (point.getX() - this.xLim[0]) * xScalingFactor;
|
|
yPlot = (point.getY() - this.yLim[0]) * yScalingFactor;
|
|
this.plotPoints[i].set(xPlot, yPlot, point.getLabel());
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.xPlotToValue = function(xPlot) {
|
|
if (this.xLog) {
|
|
return Math.exp(Math.log(this.xLim[0]) + Math.log(this.xLim[1] / this.xLim[0]) * xPlot / this.dim[0]);
|
|
} else {
|
|
return this.xLim[0] + (this.xLim[1] - this.xLim[0]) * xPlot / this.dim[0];
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.yPlotToValue = function(yPlot) {
|
|
if (this.yLog) {
|
|
return Math.exp(Math.log(this.yLim[0]) - Math.log(this.yLim[1] / this.yLim[0]) * yPlot / this.dim[1]);
|
|
} else {
|
|
return this.yLim[0] - (this.yLim[1] - this.yLim[0]) * yPlot / this.dim[1];
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.plotToValue = function(xPlot, yPlot) {
|
|
return [this.xPlotToValue(xPlot), this.yPlotToValue(yPlot)];
|
|
};
|
|
|
|
GLayer.prototype.isInside = function() {
|
|
var xPlot, yPlot, valid;
|
|
|
|
if (arguments.length === 2) {
|
|
xPlot = arguments[0];
|
|
yPlot = arguments[1];
|
|
valid = this.isValidNumber(xPlot) && this.isValidNumber(yPlot);
|
|
} else if (arguments.length === 1 && arguments[0] instanceof GPoint) {
|
|
xPlot = arguments[0].getX();
|
|
yPlot = arguments[0].getY();
|
|
valid = arguments[0].isValid();
|
|
} else {
|
|
throw new Error("GLayer.isInside(): signature not supported");
|
|
}
|
|
|
|
return (valid) ? (xPlot >= 0) && (xPlot <= this.dim[0]) && (-yPlot >= 0) && (-yPlot <= this.dim[1]) : false;
|
|
};
|
|
|
|
GLayer.prototype.updateInsideList = function() {
|
|
var point;
|
|
var nPoints = this.plotPoints.length;
|
|
|
|
for (var i = 0; i < nPoints; i++) {
|
|
point = this.plotPoints[i];
|
|
this.inside[i] = (point.isValid()) ? (point.getX() >= 0) && (point.getX() <= this.dim[0]) && (-point.getY() >= 0) && (-point.getY() <= this.dim[1]) : false;
|
|
}
|
|
|
|
// Remove the unused elements
|
|
if (this.inside.length > nPoints) {
|
|
this.inside.splice(nPoints, Number.MAX_VALUE);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.getPointIndexAtPlotPos = function(xPlot, yPlot) {
|
|
var pointIndex;
|
|
|
|
if (this.isInside(xPlot, yPlot)) {
|
|
var point, distSq;
|
|
var minDistSq = 25;
|
|
var nPoints = this.plotPoints.length;
|
|
|
|
for (var i = 0; i < nPoints; i++) {
|
|
if (this.inside[i]) {
|
|
point = this.plotPoints[i];
|
|
distSq = Math.pow(point.getX() - xPlot, 2) + Math.pow(point.getY() - yPlot, 2);
|
|
|
|
if (distSq < minDistSq) {
|
|
minDistSq = distSq;
|
|
pointIndex = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return pointIndex;
|
|
};
|
|
|
|
GLayer.prototype.getPointAtPlotPos = function(xPlot, yPlot) {
|
|
return this.points[this.getPointIndexAtPlotPos(xPlot, yPlot)];
|
|
};
|
|
|
|
GLayer.prototype.obtainBoxIntersections = function(plotPoint1, plotPoint2) {
|
|
var nCuts = 0;
|
|
|
|
if (plotPoint1.isValid() && plotPoint2.isValid()) {
|
|
var x1 = plotPoint1.getX();
|
|
var y1 = plotPoint1.getY();
|
|
var x2 = plotPoint2.getX();
|
|
var y2 = plotPoint2.getY();
|
|
var inside1 = this.isInside(x1, y1);
|
|
var inside2 = this.isInside(x2, y2);
|
|
|
|
// Check if the line between the two points could cut the box borders
|
|
var dontCut = (inside1 && inside2) || (x1 < 0 && x2 < 0) || (x1 > this.dim[0] && x2 > this.dim[0]) || (-y1 < 0 && -y2 < 0) || (-y1 > this.dim[1] && -y2 > this.dim[1]);
|
|
|
|
if (!dontCut) {
|
|
// Obtain the axis cuts of the line that cross the two points
|
|
var deltaX = x2 - x1;
|
|
var deltaY = y2 - y1;
|
|
|
|
if (deltaX === 0) {
|
|
nCuts = 2;
|
|
this.cuts[0][0] = x1;
|
|
this.cuts[0][1] = 0;
|
|
this.cuts[1][0] = x1;
|
|
this.cuts[1][1] = -this.dim[1];
|
|
} else if (deltaY === 0) {
|
|
nCuts = 2;
|
|
this.cuts[0][0] = 0;
|
|
this.cuts[0][1] = y1;
|
|
this.cuts[1][0] = this.dim[0];
|
|
this.cuts[1][1] = y1;
|
|
} else {
|
|
// Obtain the straight line (y = yCut + slope*x) that
|
|
// crosses the two points
|
|
var slope = deltaY / deltaX;
|
|
var yCut = y1 - slope * x1;
|
|
|
|
// Calculate the axis cuts of that line
|
|
nCuts = 4;
|
|
this.cuts[0][0] = -yCut / slope;
|
|
this.cuts[0][1] = 0;
|
|
this.cuts[1][0] = (-this.dim[1] - yCut) / slope;
|
|
this.cuts[1][1] = -this.dim[1];
|
|
this.cuts[2][0] = 0;
|
|
this.cuts[2][1] = yCut;
|
|
this.cuts[3][0] = this.dim[0];
|
|
this.cuts[3][1] = yCut + slope * this.dim[0];
|
|
}
|
|
|
|
// Select only the cuts that fall inside the box and are located
|
|
// between the two points
|
|
nCuts = this.getValidCuts(this.cuts, nCuts, plotPoint1, plotPoint2);
|
|
|
|
// Make sure we have the correct number of cuts
|
|
if (inside1 || inside2) {
|
|
// One of the points is inside. We should have one cut only
|
|
if (nCuts !== 1) {
|
|
var pointInside = (inside1) ? plotPoint1 : plotPoint2;
|
|
|
|
// If too many cuts
|
|
if (nCuts > 1) {
|
|
nCuts = this.removeDuplicatedCuts(this.cuts, nCuts, 0);
|
|
|
|
if (nCuts > 1) {
|
|
nCuts = this.removePointFromCuts(this.cuts, nCuts, pointInside, 0);
|
|
|
|
// In case of rounding number errors
|
|
if (nCuts > 1) {
|
|
nCuts = this.removeDuplicatedCuts(this.cuts, nCuts, 0.001);
|
|
|
|
if (nCuts > 1) {
|
|
nCuts = this.removePointFromCuts(this.cuts, nCuts, pointInside, 0.001);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the cut is missing, then it must be equal to the point
|
|
// inside
|
|
if (nCuts === 0) {
|
|
nCuts = 1;
|
|
this.cuts[0][0] = pointInside.getX();
|
|
this.cuts[1][0] = pointInside.getY();
|
|
}
|
|
}
|
|
} else {
|
|
// Both points are outside. We should have either two cuts or
|
|
// none
|
|
if (nCuts > 2) {
|
|
nCuts = this.removeDuplicatedCuts(this.cuts, nCuts, 0);
|
|
|
|
// In case of rounding number errors
|
|
if (nCuts > 2) {
|
|
nCuts = this.removeDuplicatedCuts(this.cuts, nCuts, 0.001);
|
|
}
|
|
}
|
|
|
|
// If we have two cuts, order them (the closest to the first
|
|
// point goes first)
|
|
if (nCuts === 2) {
|
|
if ((Math.pow(this.cuts[0][0] - x1, 2) + Math.pow(this.cuts[0][1] - y1), 2) > (Math.pow(this.cuts[1][0] - x1, 2) + Math.pow(this.cuts[1][1] - y1, 2))) {
|
|
this.cuts[2][0] = this.cuts[0][0];
|
|
this.cuts[2][1] = this.cuts[0][1];
|
|
this.cuts[0][0] = this.cuts[1][0];
|
|
this.cuts[0][1] = this.cuts[1][1];
|
|
this.cuts[1][0] = this.cuts[2][0];
|
|
this.cuts[1][1] = this.cuts[2][1];
|
|
}
|
|
}
|
|
|
|
// If one cut is missing, add the same one twice
|
|
if (nCuts === 1) {
|
|
nCuts = 2;
|
|
this.cuts[1][0] = this.cuts[0][0];
|
|
this.cuts[1][1] = this.cuts[0][1];
|
|
}
|
|
}
|
|
|
|
// Some sanity checks
|
|
if ((inside1 || inside2) && nCuts !== 1) {
|
|
console.log("There should be one cut!!!");
|
|
} else if (!inside1 && !inside2 && nCuts !== 0 && nCuts !== 2) {
|
|
console.log("There should be either 0 or 2 cuts!!! " + nCuts + " were found");
|
|
}
|
|
}
|
|
}
|
|
|
|
return nCuts;
|
|
};
|
|
|
|
GLayer.prototype.getValidCuts = function(cuts, nCuts, plotPoint1, plotPoint2) {
|
|
var x1 = plotPoint1.getX();
|
|
var y1 = plotPoint1.getY();
|
|
var x2 = plotPoint2.getX();
|
|
var y2 = plotPoint2.getY();
|
|
var deltaX = Math.abs(x2 - x1);
|
|
var deltaY = Math.abs(y2 - y1);
|
|
var counter = 0;
|
|
|
|
for (var i = 0; i < nCuts; i++) {
|
|
// Check that the cut is inside the inner plotting area
|
|
if (this.isInside(cuts[i][0], cuts[i][1])) {
|
|
// Check that the cut falls between the two points
|
|
if (Math.abs(cuts[i][0] - x1) <= deltaX && Math.abs(cuts[i][1] - y1) <= deltaY && Math.abs(cuts[i][0] - x2) <= deltaX && Math.abs(cuts[i][1] - y2) <= deltaY) {
|
|
cuts[counter][0] = cuts[i][0];
|
|
cuts[counter][1] = cuts[i][1];
|
|
counter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return counter;
|
|
};
|
|
|
|
GLayer.prototype.removeDuplicatedCuts = function(cuts, nCuts, tolerance) {
|
|
var repeated;
|
|
var counter = 0;
|
|
|
|
for (var i = 0; i < nCuts; i++) {
|
|
repeated = false;
|
|
|
|
for (var j = 0; j < counter; j++) {
|
|
if (Math.abs(cuts[j][0] - cuts[i][0]) <= tolerance && Math.abs(cuts[j][1] - cuts[i][1]) <= tolerance) {
|
|
repeated = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!repeated) {
|
|
cuts[counter][0] = cuts[i][0];
|
|
cuts[counter][1] = cuts[i][1];
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
return counter;
|
|
};
|
|
|
|
GLayer.prototype.removePointFromCuts = function(cuts, nCuts, plotPoint, tolerance) {
|
|
var x = plotPoint.getX();
|
|
var y = plotPoint.getY();
|
|
var counter = 0;
|
|
|
|
for (var i = 0; i < nCuts; i++) {
|
|
if (Math.abs(cuts[i][0] - x) > tolerance || Math.abs(cuts[i][1] - y) > tolerance) {
|
|
cuts[counter][0] = cuts[i][0];
|
|
cuts[counter][1] = cuts[i][1];
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
return counter;
|
|
};
|
|
|
|
GLayer.prototype.startHistogram = function(histType) {
|
|
this.hist = new GHistogram(this.parent, histType, this.dim, this.plotPoints);
|
|
};
|
|
|
|
GLayer.prototype.drawPoints = function() {
|
|
var nPoints, i;
|
|
|
|
if (arguments.length === 0) {
|
|
nPoints = this.plotPoints.length;
|
|
var nColors = this.pointColors.length;
|
|
var nSizes = this.pointSizes.length;
|
|
|
|
this.parent.push();
|
|
this.parent.ellipseMode(this.parent.CENTER);
|
|
this.parent.stroke(50);
|
|
|
|
if (nColors === 1 && nSizes === 1) {
|
|
this.parent.fill(this.pointColors[0]);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
if (this.inside[i]) {
|
|
this.parent.ellipse(this.plotPoints[i].getX(), this.plotPoints[i].getY(), this.pointSizes[0], this.pointSizes[0]);
|
|
}
|
|
}
|
|
} else if (nColors === 1) {
|
|
this.parent.fill(this.pointColors[0]);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
if (this.inside[i]) {
|
|
this.parent.fill(0);
|
|
this.parent.noStroke();
|
|
this.parent.ellipse(this.plotPoints[i].getX(), this.plotPoints[i].getY(),4,4);
|
|
this.parent.stroke(100);
|
|
this.parent.fill(this.pointColors[0]);
|
|
this.parent.ellipse(this.plotPoints[i].getX(), this.plotPoints[i].getY(), this.pointSizes[i % nSizes], this.pointSizes[i % nSizes]);
|
|
}
|
|
}
|
|
} else if (nSizes === 1) {
|
|
for ( i = 0; i < nPoints; i++) {
|
|
if (this.inside[i]) {
|
|
this.parent.fill(this.pointColors[i % nColors]);
|
|
this.parent.ellipse(this.plotPoints[i].getX(), this.plotPoints[i].getY(), this.pointSizes[0], this.pointSizes[0]);
|
|
}
|
|
}
|
|
} else {
|
|
for ( i = 0; i < nPoints; i++) {
|
|
if (this.inside[i]) {
|
|
this.parent.fill(this.pointColors[i % nColors]);
|
|
this.parent.ellipse(this.plotPoints[i].getX(), this.plotPoints[i].getY(), this.pointSizes[i % nSizes], this.pointSizes[i % nSizes]);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
} else if (arguments.length === 1 && arguments[0] instanceof p5.Image) {
|
|
nPoints = this.plotPoints.length;
|
|
|
|
this.parent.push();
|
|
this.parent.imageMode(this.parent.CENTER);
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
if (this.inside[i]) {
|
|
this.parent.image(arguments[0], this.plotPoints[i].getX(), this.plotPoints[i].getY());
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
} else {
|
|
throw new Error("GLayer.drawPoints(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.drawPoint = function() {
|
|
var point, pointColor, pointSize, pointImg;
|
|
|
|
if (arguments.length === 3) {
|
|
point = arguments[0];
|
|
pointColor = arguments[1];
|
|
pointSize = arguments[2];
|
|
} else if (arguments.length === 2 && arguments[1] instanceof p5.Image) {
|
|
point = arguments[0];
|
|
pointImg = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
point = arguments[0];
|
|
pointColor = this.pointColors[0];
|
|
pointSize = this.pointSizes[0];
|
|
} else {
|
|
throw new Error("GLayer.drawPoint(): signature not supported");
|
|
}
|
|
|
|
var xPlot = this.valueToXPlot(point.getX());
|
|
var yPlot = this.valueToYPlot(point.getY());
|
|
|
|
if (this.isInside(xPlot, yPlot)) {
|
|
this.parent.push();
|
|
|
|
if ( typeof pointImg !== "undefined") {
|
|
this.parent.imageMode(this.parent.CENTER);
|
|
this.parent.image(pointImg, xPlot, yPlot);
|
|
} else {
|
|
this.parent.ellipseMode(this.parent.CENTER);
|
|
this.parent.fill(pointColor);
|
|
this.parent.noStroke();
|
|
this.parent.ellipse(xPlot, yPlot, pointSize, pointSize);
|
|
}
|
|
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.drawLines = function() {
|
|
var nPoints = this.plotPoints.length;
|
|
|
|
this.parent.push();
|
|
this.parent.noFill();
|
|
this.parent.stroke(this.lineColor);
|
|
this.parent.strokeWeight(this.lineWidth);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
|
|
for (var i = 0; i < nPoints - 1; i++) {
|
|
if (this.inside[i] && this.inside[i + 1]) {
|
|
this.parent.line(this.plotPoints[i].getX(), this.plotPoints[i].getY(), this.plotPoints[i + 1].getX(), this.plotPoints[i + 1].getY());
|
|
} else if (this.plotPoints[i].isValid() && this.plotPoints[i + 1].isValid()) {
|
|
// At least one of the points is outside the inner region.
|
|
// Obtain the valid line box intersections
|
|
var nCuts = this.obtainBoxIntersections(this.plotPoints[i], this.plotPoints[i + 1]);
|
|
|
|
if (this.inside[i]) {
|
|
this.parent.line(this.plotPoints[i].getX(), this.plotPoints[i].getY(), this.cuts[0][0], this.cuts[0][1]);
|
|
} else if (this.inside[i + 1]) {
|
|
this.parent.line(this.cuts[0][0], this.cuts[0][1], this.plotPoints[i + 1].getX(), this.plotPoints[i + 1].getY());
|
|
} else if (nCuts >= 2) {
|
|
this.parent.line(this.cuts[0][0], this.cuts[0][1], this.cuts[1][0], this.cuts[1][1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
};
|
|
|
|
GLayer.prototype.drawLine = function() {
|
|
var point1, point2, lc, lw, slope, ycut;
|
|
|
|
if (arguments.length === 4 && arguments[0] instanceof GPoint) {
|
|
point1 = arguments[0];
|
|
point2 = arguments[1];
|
|
lc = arguments[2];
|
|
lw = arguments[3];
|
|
} else if (arguments.length === 4) {
|
|
slope = arguments[0];
|
|
ycut = arguments[1];
|
|
lc = arguments[2];
|
|
lw = arguments[3];
|
|
} else if (arguments.length === 2 && arguments[0] instanceof GPoint) {
|
|
point1 = arguments[0];
|
|
point2 = arguments[1];
|
|
lc = this.lineColor;
|
|
lw = this.lineWidth;
|
|
} else if (arguments.length === 2) {
|
|
slope = arguments[0];
|
|
ycut = arguments[1];
|
|
lc = this.lineColor;
|
|
lw = this.lineWidth;
|
|
} else {
|
|
throw new Error("GLayer.drawLine(): signature not supported");
|
|
}
|
|
|
|
if ( typeof slope !== "undefined") {
|
|
if (this.xLog && this.yLog) {
|
|
point1 = new GPoint(this.xLim[0], Math.pow(10, slope * Math.log(this.xLim[0]) / Math.LN10 + yCut));
|
|
point2 = new GPoint(this.xLim[1], Math.pow(10, slope * Math.log(this.xLim[1]) / Math.LN10 + yCut));
|
|
} else if (this.xLog) {
|
|
point1 = new GPoint(this.xLim[0], slope * Math.log(this.xLim[0]) / Math.LN10 + yCut);
|
|
point2 = new GPoint(this.xLim[1], slope * Math.log(this.xLim[1]) / Math.LN10 + yCut);
|
|
} else if (this.yLog) {
|
|
point1 = new GPoint(this.xLim[0], Math.pow(10, slope * this.xLim[0] + yCut));
|
|
point2 = new GPoint(this.xLim[1], Math.pow(10, slope * this.xLim[1] + yCut));
|
|
} else {
|
|
point1 = new GPoint(this.xLim[0], slope * this.xLim[0] + yCut);
|
|
point2 = new GPoint(this.xLim[1], slope * this.xLim[1] + yCut);
|
|
}
|
|
}
|
|
|
|
var plotPoint1 = this.valueToPlot(point1);
|
|
var plotPoint2 = this.valueToPlot(point2);
|
|
|
|
if (plotPoint1.isValid() && plotPoint2.isValid()) {
|
|
var inside1 = this.isInside(plotPoint1);
|
|
var inside2 = this.isInside(plotPoint2);
|
|
|
|
this.parent.push();
|
|
this.parent.noFill();
|
|
this.parent.stroke(lc);
|
|
this.parent.strokeWeight(lw);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
|
|
if (inside1 && inside2) {
|
|
this.parent.line(plotPoint1.getX(), plotPoint1.getY(), plotPoint2.getX(), plotPoint2.getY());
|
|
} else {
|
|
// At least one of the points is outside the inner region.
|
|
// Obtain the valid line box intersections
|
|
var nCuts = this.obtainBoxIntersections(plotPoint1, plotPoint2);
|
|
|
|
if (inside1) {
|
|
this.parent.line(plotPoint1.getX(), plotPoint1.getY(), this.cuts[0][0], this.cuts[0][1]);
|
|
} else if (inside2) {
|
|
this.parent.line(this.cuts[0][0], this.cuts[0][1], plotPoint2.getX(), plotPoint2.getY());
|
|
} else if (nCuts >= 2) {
|
|
this.parent.line(this.cuts[0][0], this.cuts[0][1], this.cuts[1][0], this.cuts[1][1]);
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.drawHorizontalLine = function() {
|
|
var value, lc, lw;
|
|
|
|
if (arguments.length === 3) {
|
|
value = arguments[0];
|
|
lc = arguments[1];
|
|
lw = arguments[2];
|
|
} else if (arguments.length === 1) {
|
|
value = arguments[0];
|
|
lc = this.lineColor;
|
|
lw = this.lineWidth;
|
|
} else {
|
|
throw new Error("GLayer.drawHorizontalLine(): signature not supported");
|
|
}
|
|
|
|
var yPlot = this.valueToYPlot(value);
|
|
|
|
if (this.isValidNumber(yPlot) && -yPlot >= 0 && -yPlot <= this.dim[1]) {
|
|
this.parent.push();
|
|
this.parent.noFill();
|
|
this.parent.stroke(lc);
|
|
this.parent.strokeWeight(lw);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
this.parent.line(0, yPlot, this.dim[0], yPlot);
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.drawVerticalLine = function() {
|
|
var value, lc, lw;
|
|
|
|
if (arguments.length === 3) {
|
|
value = arguments[0];
|
|
lc = arguments[1];
|
|
lw = arguments[2];
|
|
} else if (arguments.length === 1) {
|
|
value = arguments[0];
|
|
lc = this.lineColor;
|
|
lw = this.lineWidth;
|
|
} else {
|
|
throw new Error("GLayer.drawVerticalLine(): signature not supported");
|
|
}
|
|
|
|
var xPlot = this.valueToXPlot(value);
|
|
|
|
if (this.isValidNumber(xPlot) && xPlot >= 0 && xPlot <= this.dim[0]) {
|
|
this.parent.push();
|
|
this.parent.noFill();
|
|
this.parent.stroke(lc);
|
|
this.parent.strokeWeight(lw);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
this.parent.line(xPlot, 0, xPlot, -this.dim[1]);
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.drawFilledContour = function(contourType, referenceValue) {
|
|
// Get the points that compose the shape
|
|
var shapePoints;
|
|
|
|
if (contourType === GPlot.HORIZONTAL) {
|
|
shapePoints = this.getHorizontalShape(referenceValue);
|
|
} else if (contourType === GPlot.VERTICAL) {
|
|
shapePoints = this.getVerticalShape(referenceValue);
|
|
}
|
|
|
|
// Draw the shape
|
|
if ( typeof shapePoints !== "undefined" && shapePoints.length > 0) {
|
|
this.parent.push();
|
|
this.parent.fill(this.lineColor);
|
|
this.parent.noStroke();
|
|
this.parent.beginShape();
|
|
|
|
for (var i = 0; i < shapePoints.length; i++) {
|
|
if (shapePoints[i].isValid()) {
|
|
this.parent.vertex(shapePoints[i].getX(), shapePoints[i].getY());
|
|
}
|
|
}
|
|
|
|
this.parent.endShape(this.parent.CLOSE);
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.getHorizontalShape = function(referenceValue) {
|
|
// Collect the points and cuts inside the box
|
|
var point, addedPoints, nextIndex;
|
|
var nPoints = this.plotPoints.length;
|
|
var shapePoints = [];
|
|
var indexFirstPoint = -1;
|
|
var indexLastPoint = -1;
|
|
|
|
for (var i = 0; i < nPoints; i++) {
|
|
point = this.plotPoints[i];
|
|
|
|
if (point.isValid()) {
|
|
addedPoints = false;
|
|
|
|
// Add the point if it's inside the box
|
|
if (this.inside[i]) {
|
|
shapePoints.push(new GPoint(point.getX(), point.getY(), "normal point"));
|
|
addedPoints = true;
|
|
} else if (point.getX() >= 0 && point.getX() <= this.dim[0]) {
|
|
// If it's outside, add the projection of the point on the
|
|
// horizontal axes
|
|
if (-point.getY() < 0) {
|
|
shapePoints.push(new GPoint(point.getX(), 0, "projection"));
|
|
addedPoints = true;
|
|
} else {
|
|
shapePoints.push(new GPoint(point.getX(), -this.dim[1], "projection"));
|
|
addedPoints = true;
|
|
}
|
|
}
|
|
|
|
// Add the box cuts if there is any
|
|
nextIndex = i + 1;
|
|
|
|
while (nextIndex < nPoints - 1 && !this.plotPoints[nextIndex].isValid()) {
|
|
nextIndex++;
|
|
}
|
|
|
|
if (nextIndex < nPoints && this.plotPoints[nextIndex].isValid()) {
|
|
var nCuts = this.obtainBoxIntersections(point, this.plotPoints[nextIndex]);
|
|
|
|
for (var j = 0; j < nCuts; j++) {
|
|
shapePoints.push(new GPoint(this.cuts[j][0], this.cuts[j][1], "cut"));
|
|
addedPoints = true;
|
|
}
|
|
}
|
|
|
|
if (addedPoints) {
|
|
if (indexFirstPoint < 0) {
|
|
indexFirstPoint = i;
|
|
}
|
|
|
|
indexLastPoint = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Continue if there are points in the shape
|
|
if (shapePoints.length > 0) {
|
|
// Calculate the starting point
|
|
var startPoint = new GPoint(shapePoints[0]);
|
|
|
|
if (startPoint.getX() !== 0 && startPoint.getX() !== this.dim[0]) {
|
|
if (startPoint.getLabel() === "cut") {
|
|
if (this.plotPoints[indexFirstPoint].getX() < 0) {
|
|
startPoint.setX(0);
|
|
startPoint.setLabel("extreme");
|
|
} else {
|
|
startPoint.setX(this.dim[0]);
|
|
startPoint.setLabel("extreme");
|
|
}
|
|
} else if (indexFirstPoint !== 0) {
|
|
// Get the previous valid point
|
|
var prevIndex = indexFirstPoint - 1;
|
|
|
|
while (prevIndex > 0 && !this.plotPoints[prevIndex].isValid()) {
|
|
prevIndex--;
|
|
}
|
|
|
|
if (this.plotPoints[prevIndex].isValid()) {
|
|
if (this.plotPoints[prevIndex].getX() < 0) {
|
|
startPoint.setX(0);
|
|
startPoint.setLabel("extreme");
|
|
} else {
|
|
startPoint.setX(this.dim[0]);
|
|
startPoint.setLabel("extreme");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate the end point
|
|
var endPoint = new GPoint(shapePoints[shapePoints.length - 1]);
|
|
|
|
if (endPoint.getX() !== 0 && endPoint.getX() !== this.dim[0] && indexLastPoint !== nPoints - 1) {
|
|
nextIndex = indexLastPoint + 1;
|
|
|
|
while (nextIndex < nPoints - 1 && !this.plotPoints[nextIndex].isValid()) {
|
|
nextIndex++;
|
|
}
|
|
|
|
if (this.plotPoints[nextIndex].isValid()) {
|
|
if (this.plotPoints[nextIndex].getX() < 0) {
|
|
endPoint.setX(0);
|
|
endPoint.setLabel("extreme");
|
|
} else {
|
|
endPoint.setX(this.dim[0]);
|
|
endPoint.setLabel("extreme");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the end point if it's a new extreme
|
|
if (endPoint.getLabel() === "extreme") {
|
|
shapePoints.push(endPoint);
|
|
}
|
|
|
|
// Add the reference connections
|
|
if (this.yLog && referenceValue <= 0) {
|
|
referenceValue = Math.min(this.yLim[0], this.yLim[1]);
|
|
}
|
|
|
|
var plotReference = this.valueToPlot(1, referenceValue);
|
|
|
|
if (-plotReference[1] < 0) {
|
|
shapePoints.push(new GPoint(endPoint.getX(), 0));
|
|
shapePoints.push(new GPoint(startPoint.getX(), 0));
|
|
} else if (-plotReference[1] > this.dim[1]) {
|
|
shapePoints.push(new GPoint(endPoint.getX(), -this.dim[1]));
|
|
shapePoints.push(new GPoint(startPoint.getX(), -this.dim[1]));
|
|
} else {
|
|
shapePoints.push(new GPoint(endPoint.getX(), plotReference[1]));
|
|
shapePoints.push(new GPoint(startPoint.getX(), plotReference[1]));
|
|
}
|
|
|
|
// Add the starting point if it's a new extreme
|
|
if (startPoint.getLabel() === "extreme") {
|
|
shapePoints.push(startPoint);
|
|
}
|
|
}
|
|
|
|
return shapePoints;
|
|
};
|
|
|
|
GLayer.prototype.getVerticalShape = function(referenceValue) {
|
|
// Collect the points and cuts inside the box
|
|
var point, addedPoints, nextIndex;
|
|
var nPoints = this.plotPoints.length;
|
|
var shapePoints = [];
|
|
var indexFirstPoint = -1;
|
|
var indexLastPoint = -1;
|
|
|
|
for (var i = 0; i < nPoints; i++) {
|
|
point = this.plotPoints[i];
|
|
|
|
if (point.isValid()) {
|
|
addedPoints = false;
|
|
|
|
// Add the point if it's inside the box
|
|
if (this.inside[i]) {
|
|
shapePoints.push(new GPoint(point.getX(), point.getY(), "normal point"));
|
|
addedPoints = true;
|
|
} else if (-point.getY() >= 0 && -point.getY() <= this.dim[1]) {
|
|
// If it's outside, add the projection of the point on the
|
|
// vertical axes
|
|
if (point.getX() < 0) {
|
|
shapePoints.push(new GPoint(0, point.getY(), "projection"));
|
|
addedPoints = true;
|
|
} else {
|
|
shapePoints.push(new GPoint(this.dim[0], point.getY(), "projection"));
|
|
addedPoints = true;
|
|
}
|
|
}
|
|
|
|
// Add the box cuts if there is any
|
|
nextIndex = i + 1;
|
|
|
|
while (nextIndex < nPoints - 1 && !this.plotPoints[nextIndex].isValid()) {
|
|
nextIndex++;
|
|
}
|
|
|
|
if (nextIndex < nPoints && this.plotPoints[nextIndex].isValid()) {
|
|
var nCuts = this.obtainBoxIntersections(point, this.plotPoints[nextIndex]);
|
|
|
|
for (var j = 0; j < nCuts; j++) {
|
|
shapePoints.push(new GPoint(this.cuts[j][0], this.cuts[j][1], "cut"));
|
|
addedPoints = true;
|
|
}
|
|
}
|
|
|
|
if (addedPoints) {
|
|
if (indexFirstPoint < 0) {
|
|
indexFirstPoint = i;
|
|
}
|
|
|
|
indexLastPoint = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Continue if there are points in the shape
|
|
if (shapePoints.length > 0) {
|
|
// Calculate the starting point
|
|
var startPoint = new GPoint(shapePoints[0]);
|
|
|
|
if (startPoint.getY() !== 0 && startPoint.getY() !== -this.dim[1]) {
|
|
if (startPoint.getLabel() === "cut") {
|
|
if (-this.plotPoints[indexFirstPoint].getY() < 0) {
|
|
startPoint.setY(0);
|
|
startPoint.setLabel("extreme");
|
|
} else {
|
|
startPoint.setY(-this.dim[1]);
|
|
startPoint.setLabel("extreme");
|
|
}
|
|
} else if (indexFirstPoint !== 0) {
|
|
// Get the previous valid point
|
|
var prevIndex = indexFirstPoint - 1;
|
|
|
|
while (prevIndex > 0 && !this.plotPoints[prevIndex].isValid()) {
|
|
prevIndex--;
|
|
}
|
|
|
|
if (this.plotPoints[prevIndex].isValid()) {
|
|
if (-this.plotPoints[prevIndex].getY() < 0) {
|
|
startPoint.setY(0);
|
|
startPoint.setLabel("extreme");
|
|
} else {
|
|
startPoint.setY(-this.dim[1]);
|
|
startPoint.setLabel("extreme");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate the end point
|
|
var endPoint = new GPoint(shapePoints[shapePoints.length - 1]);
|
|
|
|
if (endPoint.getY() !== 0 && endPoint.getY() !== -this.dim[1] && indexLastPoint !== nPoints - 1) {
|
|
nextIndex = indexLastPoint + 1;
|
|
|
|
while (nextIndex < nPoints - 1 && !this.plotPoints[nextIndex].isValid()) {
|
|
nextIndex++;
|
|
}
|
|
|
|
if (this.plotPoints[nextIndex].isValid()) {
|
|
if (-this.plotPoints[nextIndex].getY() < 0) {
|
|
endPoint.setY(0);
|
|
endPoint.setLabel("extreme");
|
|
} else {
|
|
endPoint.setY(-this.dim[1]);
|
|
endPoint.setLabel("extreme");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the end point if it's a new extreme
|
|
if (endPoint.getLabel() === "extreme") {
|
|
shapePoints.push(endPoint);
|
|
}
|
|
|
|
// Add the reference connections
|
|
if (this.xLog && referenceValue <= 0) {
|
|
referenceValue = Math.min(this.xLim[0], this.xLim[1]);
|
|
}
|
|
|
|
var plotReference = this.valueToPlot(referenceValue, 1);
|
|
|
|
if (plotReference[0] < 0) {
|
|
shapePoints.push(new GPoint(0, endPoint.getY()));
|
|
shapePoints.push(new GPoint(0, startPoint.getY()));
|
|
} else if (plotReference[0] > this.dim[0]) {
|
|
shapePoints.push(new GPoint(this.dim[0], endPoint.getY()));
|
|
shapePoints.push(new GPoint(this.dim[0], startPoint.getY()));
|
|
} else {
|
|
shapePoints.push(new GPoint(plotReference[0], endPoint.getY()));
|
|
shapePoints.push(new GPoint(plotReference[0], startPoint.getY()));
|
|
}
|
|
|
|
// Add the starting point if it's a new extreme
|
|
if (startPoint.getLabel() === "extreme") {
|
|
shapePoints.push(startPoint);
|
|
}
|
|
}
|
|
|
|
return shapePoints;
|
|
};
|
|
|
|
GLayer.prototype.drawAllLabels = function() {
|
|
for (var n = 0; n < this.points.length; n++) {
|
|
this.drawLabel(this.points[n]);
|
|
}
|
|
}
|
|
|
|
GLayer.prototype.drawLabel = function(point) {
|
|
var xPlot = this.valueToXPlot(point.getX());
|
|
var yPlot = this.valueToYPlot(point.getY());
|
|
|
|
if (this.isValidNumber(xPlot) && this.isValidNumber(yPlot)) {
|
|
var xLabelPos = xPlot + this.labelSeparation[0];
|
|
var yLabelPos = yPlot - 0*this.labelSeparation[1] + 6;
|
|
var delta = this.fontSize / 4;
|
|
|
|
this.parent.push();
|
|
this.parent.rectMode(this.parent.CORNER);
|
|
this.parent.noStroke();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.textAlign(this.parent.LEFT, this.parent.BOTTOM);
|
|
|
|
// Draw the background
|
|
this.parent.fill(this.labelBgColor);
|
|
this.parent.rect(xLabelPos - delta, yLabelPos - this.fontSize - delta, this.parent.textWidth(point.getLabel()) + 2 * delta, this.fontSize + 2 * delta);
|
|
|
|
// Draw the text
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.text(point.getLabel(), xLabelPos, yLabelPos);
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.drawLabelAtPlotPos = function(xPlot, yPlot) {
|
|
var point = this.getPointAtPlotPos(xPlot, yPlot);
|
|
|
|
if ( typeof point !== "undefined") {
|
|
this.drawLabel(point);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.drawHistogram = function() {
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.draw(this.valueToPlot(this.histBasePoint));
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.drawPolygon = function(polygonPoints, polygonColor) {
|
|
var i;
|
|
|
|
if (polygonPoints.length > 2) {
|
|
// Remove the polygon invalid points
|
|
var plotPolygonPoints = this.valueToPlot(polygonPoints);
|
|
var counter = 0;
|
|
|
|
for ( i = 0; i < plotPolygonPoints.length; i++) {
|
|
if (plotPolygonPoints[i].isValid()) {
|
|
plotPolygonPoints[counter] = plotPolygonPoints[i];
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
plotPolygonPoints.splice(counter, Number.MAX_VALUE);
|
|
|
|
// Create a temporal array with the points inside the plotting area
|
|
// and the valid box cuts
|
|
var point;
|
|
var nPoints = plotPolygonPoints.length;
|
|
var tmp = [];
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
point = plotPolygonPoints[i];
|
|
|
|
if (this.isInside(point)) {
|
|
tmp.push(new GPoint(point.getX(), point.getY(), "normal point"));
|
|
}
|
|
|
|
// Obtain the cuts with the next point
|
|
var nextIndex = (i + 1 < nPoints) ? i + 1 : 0;
|
|
var nCuts = this.obtainBoxIntersections(point, plotPolygonPoints[nextIndex]);
|
|
|
|
if (nCuts === 1) {
|
|
tmp.push(new GPoint(this.cuts[0][0], this.cuts[0][1], "single cut"));
|
|
} else if (nCuts > 1) {
|
|
tmp.push(new GPoint(this.cuts[0][0], this.cuts[0][1], "double cut"));
|
|
tmp.push(new GPoint(this.cuts[1][0], this.cuts[1][1], "double cut"));
|
|
}
|
|
}
|
|
|
|
// Final version of the polygon
|
|
nPoints = tmp.length;
|
|
var croppedPoly = [];
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
// Add the point
|
|
croppedPoly.push(tmp[i]);
|
|
|
|
// Add new points in case we have two consecutive cuts, one of
|
|
// them is single, and they are in consecutive axes
|
|
var next = (i + 1 < nPoints) ? i + 1 : 0;
|
|
var label = tmp[i].getLabel();
|
|
var nextLabel = tmp[next].getLabel();
|
|
|
|
var cond = (label === "single cut" && nextLabel === "single cut") || (label === "single cut" && nextLabel === "double cut") || (label === "double cut" && nextLabel === "single cut");
|
|
|
|
if (cond) {
|
|
var x1 = tmp[i].getX();
|
|
var y1 = tmp[i].getY();
|
|
var x2 = tmp[next].getX();
|
|
var y2 = tmp[next].getY();
|
|
var deltaX = Math.abs(x2 - x1);
|
|
var deltaY = Math.abs(y2 - y1);
|
|
|
|
// Check that they come from consecutive axes
|
|
if (deltaX > 0 && deltaY > 0 && deltaX !== this.dim[0] && deltaY !== this.dim[1]) {
|
|
var x = (x1 === 0 || x1 === this.dim[0]) ? x1 : x2;
|
|
var y = (y1 === 0 || y1 === -this.dim[1]) ? y1 : y2;
|
|
croppedPoly.push(new GPoint(x, y, "special cut"));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw the cropped polygon
|
|
if (croppedPoly.length > 2) {
|
|
this.parent.push();
|
|
this.parent.fill(polygonColor);
|
|
this.parent.noStroke();
|
|
this.parent.beginShape();
|
|
|
|
for ( i = 0; i < croppedPoly.length; i++) {
|
|
this.parent.vertex(croppedPoly[i].getX(), croppedPoly[i].getY());
|
|
}
|
|
|
|
this.parent.endShape(this.parent.CLOSE);
|
|
this.parent.pop();
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.drawAnnotation = function(text, x, y, horAlign, verAlign) {
|
|
var xPlot = this.valueToXPlot(x);
|
|
var yPlot = this.valueToYPlot(y);
|
|
|
|
if (this.isValidNumber(xPlot) && this.isValidNumber(yPlot) && this.isInside(xPlot, yPlot)) {
|
|
if (horAlign !== this.parent.CENTER && horAlign !== this.parent.RIGHT && horAlign !== this.parent.LEFT) {
|
|
horAlign = this.parent.LEFT;
|
|
}
|
|
|
|
if (verAlign !== this.parent.CENTER && verAlign !== this.parent.TOP && verAlign !== this.parent.BOTTOM) {
|
|
verAlign = this.parent.CENTER;
|
|
}
|
|
|
|
// A trick to really center the text vertically
|
|
if (verAlign === this.parent.CENTER) {
|
|
verAlign = this.parent.BOTTOM;
|
|
yPlot += this.fontSize / 2;
|
|
}
|
|
|
|
this.parent.push();
|
|
this.parent.textFont(this.fontName);
|
|
this.parent.textSize(this.fontSize);
|
|
this.parent.fill(this.fontColor);
|
|
this.parent.textAlign(horAlign, verAlign);
|
|
this.parent.text(text, xPlot, yPlot);
|
|
this.parent.pop();
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setDim = function() {
|
|
var xDim, yDim;
|
|
|
|
if (arguments.length === 2) {
|
|
xDim = arguments[0];
|
|
yDim = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
xDim = arguments[0][0];
|
|
yDim = arguments[0][1];
|
|
} else {
|
|
throw new Error("GLayer.setDim(): signature not supported");
|
|
}
|
|
|
|
if (xDim > 0 && yDim > 0) {
|
|
this.dim[0] = xDim;
|
|
this.dim[1] = yDim;
|
|
this.updatePlotPoints();
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setDim(this.dim);
|
|
this.hist.setPlotPoints(this.plotPoints);
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setXLim = function() {
|
|
var xMin, xMax;
|
|
|
|
if (arguments.length === 2) {
|
|
xMin = arguments[0];
|
|
xMax = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
xMin = arguments[0][0];
|
|
xMax = arguments[0][1];
|
|
} else {
|
|
throw new Error("GLayer.setXLim(): signature not supported");
|
|
}
|
|
|
|
if (xMin !== xMax && this.isValidNumber(xMin) && this.isValidNumber(xMax)) {
|
|
// Make sure the new limits makes sense
|
|
if (this.xLog && (xMin <= 0 || xMax <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.xLim[0] = xMin;
|
|
this.xLim[1] = xMax;
|
|
this.updatePlotPoints();
|
|
this.updateInsideList();
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setPlotPoints(this.plotPoints);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setYLim = function() {
|
|
var yMin, yMax;
|
|
|
|
if (arguments.length === 2) {
|
|
yMin = arguments[0];
|
|
yMax = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
yMin = arguments[0][0];
|
|
yMax = arguments[0][1];
|
|
} else {
|
|
throw new Error("GLayer.setYLim(): signature not supported");
|
|
}
|
|
|
|
if (yMin !== yMax && this.isValidNumber(yMin) && this.isValidNumber(yMax)) {
|
|
// Make sure the new limits makes sense
|
|
if (this.yLog && (yMin <= 0 || yMax <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.yLim[0] = yMin;
|
|
this.yLim[1] = yMax;
|
|
this.updatePlotPoints();
|
|
this.updateInsideList();
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setPlotPoints(this.plotPoints);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setXYLim = function() {
|
|
var xMin, xMax, yMin, yMax;
|
|
|
|
if (arguments.length === 4) {
|
|
xMin = arguments[0];
|
|
xMax = arguments[1];
|
|
yMin = arguments[2];
|
|
yMax = arguments[3];
|
|
} else if (arguments.length === 2) {
|
|
xMin = arguments[0][0];
|
|
xMax = arguments[0][1];
|
|
yMin = arguments[1][0];
|
|
yMax = arguments[1][1];
|
|
} else {
|
|
throw new Error("GLayer.setXYLim(): signature not supported");
|
|
}
|
|
|
|
if (xMin !== xMax && yMin !== yMax && this.isValidNumber(xMin) && this.isValidNumber(xMax) && this.isValidNumber(yMin) && this.isValidNumber(yMax)) {
|
|
// Make sure the new limits make sense
|
|
if (this.xLog && (xMin <= 0 || xMax <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.xLim[0] = xMin;
|
|
this.xLim[1] = xMax;
|
|
}
|
|
|
|
if (this.yLog && (yMin <= 0 || yMax <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.yLim[0] = yMin;
|
|
this.yLim[1] = yMax;
|
|
}
|
|
|
|
this.updatePlotPoints();
|
|
this.updateInsideList();
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setPlotPoints(this.plotPoints);
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setLimAndLog = function() {
|
|
var xMin, xMax, yMin, yMax, xLog, yLog;
|
|
|
|
if (arguments.length === 6) {
|
|
xMin = arguments[0];
|
|
xMax = arguments[1];
|
|
yMin = arguments[2];
|
|
yMax = arguments[3];
|
|
xLog = arguments[4];
|
|
yLog = arguments[5];
|
|
} else if (arguments.length === 4) {
|
|
xMin = arguments[0][0];
|
|
xMax = arguments[0][1];
|
|
yMin = arguments[1][0];
|
|
yMax = arguments[1][1];
|
|
xLog = arguments[2];
|
|
yLog = arguments[3];
|
|
} else {
|
|
throw new Error("GLayer.setLimAndLog(): signature not supported");
|
|
}
|
|
|
|
if (xMin !== xMax && yMin !== yMax && this.isValidNumber(xMin) && this.isValidNumber(xMax) && this.isValidNumber(yMin) && this.isValidNumber(yMax)) {
|
|
// Make sure the new limits make sense
|
|
if (xLog && (xMin <= 0 || xMax <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.xLim[0] = xMin;
|
|
this.yLim[1] = xMax;
|
|
this.xLog = xLog;
|
|
}
|
|
|
|
if (yLog && (yMin <= 0 || yMax <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.yLim[0] = yMin;
|
|
this.yLim[1] = yMax;
|
|
this.yLog = yLog;
|
|
}
|
|
|
|
this.updatePlotPoints();
|
|
this.updateInsideList();
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setPlotPoints(this.plotPoints);
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setXLog = function(xLog) {
|
|
if (xLog !== this.xLog) {
|
|
if (xLog && (this.xLim[0] <= 0 || this.xLim[1] <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
console.log("Will set horizontal limits to (0.1, 10)");
|
|
this.xLim[0] = 0.1;
|
|
this.xLim[1] = 10;
|
|
}
|
|
|
|
this.xLog = xLog;
|
|
this.updatePlotPoints();
|
|
this.updateInsideList();
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setPlotPoints(this.plotPoints);
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setYLog = function(yLog) {
|
|
if (yLog !== this.yLog) {
|
|
if (yLog && (this.yLim[0] <= 0 || this.yLim[1] <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
console.log("Will set horizontal limits to (0.1, 10)");
|
|
this.yLim[0] = 0.1;
|
|
this.yLim[1] = 10;
|
|
}
|
|
|
|
this.yLog = yLog;
|
|
this.updatePlotPoints();
|
|
this.updateInsideList();
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setPlotPoints(this.plotPoints);
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setPoints = function(points) {
|
|
var i;
|
|
var nPoints = points.length;
|
|
|
|
if (this.points.length > nPoints) {
|
|
this.points.splice(nPoints, Number.MAX_VALUE);
|
|
} else {
|
|
for ( i = this.points.length; i < nPoints; i++) {
|
|
this.points[i] = new GPoint();
|
|
}
|
|
}
|
|
|
|
for ( i = 0; i < nPoints; i++) {
|
|
this.points[i].set(points[i]);
|
|
}
|
|
|
|
this.updatePlotPoints();
|
|
this.updateInsideList();
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setPlotPoints(this.plotPoints);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setPoint = function() {
|
|
var index, x, y, label;
|
|
var nPoints = this.points.length;
|
|
|
|
if (arguments.length === 4) {
|
|
index = arguments[0];
|
|
x = arguments[1];
|
|
y = arguments[2];
|
|
label = arguments[3];
|
|
} else if (arguments.length === 3) {
|
|
index = arguments[0];
|
|
x = arguments[1];
|
|
y = arguments[2];
|
|
label = (index < nPoins) ? this.points[index].getLabel() : "";
|
|
} else if (arguments.length === 2) {
|
|
index = arguments[0];
|
|
x = arguments[1].getX();
|
|
y = arguments[1].getY();
|
|
label = arguments[1].getLabel();
|
|
} else {
|
|
throw new Error("GLayer.setPoint(): signature not supported");
|
|
}
|
|
|
|
if (index < nPoints) {
|
|
this.points[index].set(x, y, label);
|
|
this.plotPoints[index].set(this.valueToXPlot(x), this.valueToYPlot(y), label);
|
|
this.inside[index] = this.isInside(this.plotPoints[index]);
|
|
} else if (index === nPoints) {
|
|
this.points[index] = new GPoint(x, y, label);
|
|
this.plotPoints[index] = new GPoint(this.valueToXPlot(x), this.valueToYPlot(y), label);
|
|
this.inside[index] = this.isInside(this.plotPoints[index]);
|
|
} else {
|
|
throw new Error("GLayer.setPoint(): the index position is outside the array size");
|
|
}
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setPlotPoint(index, this.plotPoints[index]);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.addPoint = function() {
|
|
var x, y, label;
|
|
|
|
if (arguments.length === 3) {
|
|
x = arguments[0];
|
|
y = arguments[1];
|
|
label = arguments[2];
|
|
} else if (arguments.length === 2) {
|
|
x = arguments[0];
|
|
y = arguments[1];
|
|
label = "";
|
|
} else if (arguments.length === 1) {
|
|
x = arguments[0].getX();
|
|
y = arguments[0].getY();
|
|
label = arguments[0].getLabel();
|
|
} else {
|
|
throw new Error("GLayer.addPoint(): signature not supported");
|
|
}
|
|
|
|
this.points.push(new GPoint(x, y, label));
|
|
this.plotPoints.push(new GPoint(this.valueToXPlot(x), this.valueToYPlot(y), label));
|
|
this.inside.push(this.isInside(this.plotPoints[this.plotPoints.length - 1]));
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.addPlotPoint(this.plotPoints[this.plotPoints.length - 1]);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.addPoints = function(newPoints) {
|
|
var newPoint;
|
|
var nNewPoints = newPoints.length;
|
|
|
|
for (var i = 0; i < nNewPoints; i++) {
|
|
newPoint = newPoints[i];
|
|
this.points.push(new GPoint(newPoint));
|
|
this.plotPoints.push(new GPoint(this.valueToXPlot(newPoint.getX()), this.valueToYPlot(newPoint.getY()), newPoint.getLabel()));
|
|
this.inside.push(this.isInside(this.plotPoints[this.plotPoints.length - 1]));
|
|
}
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setPlotPoints(this.plotPoints);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.addPointAtIndexPos = function() {
|
|
var index, x, y, label;
|
|
|
|
if (arguments.length === 4) {
|
|
index = arguments[0];
|
|
x = arguments[1];
|
|
y = arguments[2];
|
|
label = arguments[3];
|
|
} else if (arguments.length === 3) {
|
|
index = arguments[0];
|
|
x = arguments[1];
|
|
y = arguments[2];
|
|
label = "";
|
|
} else if (arguments.length === 2) {
|
|
index = arguments[0];
|
|
x = arguments[1].getX();
|
|
y = arguments[1].getY();
|
|
label = arguments[1].getLabel();
|
|
} else {
|
|
throw new Error("GLayer.addPointAtIndexPos(): signature not supported");
|
|
}
|
|
|
|
if (index <= this.points.length) {
|
|
this.points.splice(index, 0, new GPoint(x, y, label));
|
|
this.plotPoints.splice(index, 0, new GPoint(this.valueToXPlot(x), this.valueToYPlot(y), label));
|
|
this.inside.splice(index, 0, this.isInside(this.plotPoints[0]));
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setPlotPoints(this.plotPoints);
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.removePoint = function(index) {
|
|
if (index < this.points.length) {
|
|
this.points.splice(index, 1);
|
|
this.plotPoints.splice(index, 1);
|
|
this.inside.splice(index, 1);
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.removePlotPoint(index);
|
|
}
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setInside = function(inside) {
|
|
if (inside.length === this.inside.length) {
|
|
this.inside = inside.slice();
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setPointColors = function(pointColors) {
|
|
if (pointColors.length > 0) {
|
|
this.pointColors = pointColors.slice();
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setPointColor = function(pointColor) {
|
|
this.pointColors = [pointColor];
|
|
};
|
|
|
|
GLayer.prototype.setPointSizes = function(pointSizes) {
|
|
if (pointSizes.length > 0) {
|
|
this.pointSizes = pointSizes.slice();
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setPointSize = function(pointSize) {
|
|
this.pointSizes = [pointSize];
|
|
};
|
|
|
|
GLayer.prototype.setLineColor = function(lineColor) {
|
|
this.lineColor = lineColor;
|
|
};
|
|
|
|
GLayer.prototype.setLineWidth = function(lineWidth) {
|
|
if (lineWidth > 0) {
|
|
this.lineWidth = lineWidth;
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setHistBasePoint = function(histBasePoint) {
|
|
this.histBasePoint.set(histBasePoint);
|
|
};
|
|
|
|
GLayer.prototype.setHistType = function(histType) {
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setType(histType);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setHistVisible = function(visible) {
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setVisible(visible);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setDrawHistLabels = function(drawHistLabels) {
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setDrawLabels(drawHistLabels);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setLabelBgColor = function(labelBgColor) {
|
|
this.labelBgColor = labelBgColor;
|
|
};
|
|
|
|
GLayer.prototype.setLabelSeparation = function(labelSeparation) {
|
|
this.labelSeparation[0] = labelSeparation[0];
|
|
this.labelSeparation[1] = labelSeparation[1];
|
|
};
|
|
|
|
GLayer.prototype.setFontName = function(fontName) {
|
|
this.fontName = fontName;
|
|
};
|
|
|
|
GLayer.prototype.setFontColor = function(fontColor) {
|
|
this.fontColor = fontColor;
|
|
};
|
|
|
|
GLayer.prototype.setFontSize = function(fontSize) {
|
|
if (fontSize > 0) {
|
|
this.fontSize = fontSize;
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setFontProperties = function(fontName, fontColor, fontSize) {
|
|
if (fontSize > 0) {
|
|
this.fontName = fontName;
|
|
this.fontColor = fontColor;
|
|
this.fontSize = fontSize;
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.setAllFontProperties = function(fontName, fontColor, fontSize) {
|
|
this.setFontProperties(fontName, fontColor, fontSize);
|
|
|
|
if ( typeof this.hist !== "undefined") {
|
|
this.hist.setFontProperties(fontName, fontColor, fontSize);
|
|
}
|
|
};
|
|
|
|
GLayer.prototype.getId = function() {
|
|
return this.id;
|
|
};
|
|
|
|
GLayer.prototype.getDim = function() {
|
|
return this.dim.slice();
|
|
};
|
|
|
|
GLayer.prototype.getXLim = function() {
|
|
return this.xLim.slice();
|
|
};
|
|
|
|
GLayer.prototype.getYLim = function() {
|
|
return this.yLim.slice();
|
|
};
|
|
|
|
GLayer.prototype.getXLog = function() {
|
|
return this.xLog;
|
|
};
|
|
|
|
GLayer.prototype.getYLog = function() {
|
|
return this.yLog;
|
|
};
|
|
|
|
GLayer.prototype.getPoints = function() {
|
|
var points = [];
|
|
|
|
for (var i = 0; i < this.points.length; i++) {
|
|
points[i] = new GPoint(this.points[i]);
|
|
}
|
|
|
|
return points;
|
|
};
|
|
|
|
GLayer.prototype.getPointsRef = function() {
|
|
return this.points;
|
|
};
|
|
|
|
GLayer.prototype.getPointColors = function() {
|
|
return this.pointColors.slice();
|
|
};
|
|
|
|
GLayer.prototype.getPointSizes = function() {
|
|
return this.pointSizes.slice();
|
|
};
|
|
|
|
GLayer.prototype.getLineColor = function() {
|
|
return this.lineColor;
|
|
};
|
|
|
|
GLayer.prototype.getLineWidth = function() {
|
|
return this.lineWidth;
|
|
};
|
|
|
|
GLayer.prototype.getHistogram = function() {
|
|
return this.hist;
|
|
};
|
|
/*
|
|
* Plot class. It controls the rest of the graphical elements (layers, axes,
|
|
* title, limits).
|
|
*/
|
|
function GPlot() {
|
|
var parent, xPos, yPos, plotWidth, plotHeight;
|
|
|
|
if (arguments.length === 5) {
|
|
parent = arguments[0];
|
|
xPos = arguments[1];
|
|
yPos = arguments[2];
|
|
plotWidth = arguments[3];
|
|
plotHeight = arguments[4];
|
|
} else if (arguments.length === 3) {
|
|
parent = arguments[0];
|
|
xPos = arguments[1];
|
|
yPos = arguments[2];
|
|
plotWidth = 450;
|
|
plotHeight = 300;
|
|
} else if (arguments.length === 1) {
|
|
parent = arguments[0];
|
|
xPos = 0;
|
|
yPos = 0;
|
|
plotWidth = 450;
|
|
plotHeight = 300;
|
|
} else {
|
|
throw new Error("GPlot constructor: signature not supported");
|
|
}
|
|
|
|
// The parent processing object
|
|
this.parent = parent;
|
|
|
|
// General properties
|
|
this.pos = [xPos, yPos];
|
|
this.outerDim = [plotWidth, plotHeight];
|
|
this.mar = [60, 70, 40, 30];
|
|
this.dim = [this.outerDim[0] - this.mar[1] - this.mar[3], this.outerDim[1] - this.mar[0] - this.mar[2]];
|
|
this.xLim = [0, 1];
|
|
this.yLim = [0, 1];
|
|
this.fixedXLim = false;
|
|
this.fixedYLim = false;
|
|
this.xLog = false;
|
|
this.yLog = false;
|
|
this.invertedXScale = false;
|
|
this.invertedYScale = false;
|
|
this.includeAllLayersInLim = true;
|
|
this.expandLimFactor = 0.1;
|
|
|
|
// Format properties
|
|
this.bgColor = this.parent.color(255);
|
|
this.boxBgColor = this.parent.color(245);
|
|
this.boxLineColor = this.parent.color(210);
|
|
this.boxLineWidth = 1;
|
|
this.gridLineColor = this.parent.color(210);
|
|
this.gridLineWidth = 1;
|
|
|
|
// Layers
|
|
this.mainLayer = new GLayer(this.parent, GPlot.MAINLAYERID, this.dim, this.xLim, this.yLim, this.xLog, this.yLog);
|
|
this.layerList = [];
|
|
|
|
// Axes and title
|
|
this.xAxis = new GAxis(this.parent, this.parent.BOTTOM, this.dim, this.xLim, this.xLog);
|
|
this.topAxis = new GAxis(this.parent, this.parent.TOP, this.dim, this.xLim, this.xLog);
|
|
this.yAxis = new GAxis(this.parent, this.parent.LEFT, this.dim, this.yLim, this.yLog);
|
|
this.rightAxis = new GAxis(this.parent, this.parent.RIGHT, this.dim, this.yLim, this.yLog);
|
|
this.title = new GTitle(this.parent, this.dim);
|
|
|
|
// Setup for the mouse events
|
|
this.zoomingIsActive = false;
|
|
this.zoomFactor = 1.3;
|
|
this.increaseZoomButton = this.parent.LEFT;
|
|
this.decreaseZoomButton = this.parent.RIGHT;
|
|
this.increaseZoomKeyModifier = GPlot.NONE;
|
|
this.decreaseZoomKeyModifier = GPlot.NONE;
|
|
this.centeringIsActive = false;
|
|
this.centeringButton = this.parent.LEFT;
|
|
this.centeringKeyModifier = GPlot.NONE;
|
|
this.panningIsActive = false;
|
|
this.panningButton = this.parent.LEFT;
|
|
this.panningKeyModifier = GPlot.NONE;
|
|
this.panningReferencePoint = undefined;
|
|
this.panningIntervalId = undefined;
|
|
this.labelingIsActive = false;
|
|
this.labelingButton = this.parent.LEFT;
|
|
this.labelingKeyModifier = GPlot.NONE;
|
|
this.mousePos = undefined;
|
|
this.resetIsActive = false;
|
|
this.resetButton = this.parent.RIGHT;
|
|
this.resetKeyModifier = this.parent.CONTROL;
|
|
this.xLimReset = undefined;
|
|
this.yLimReset = undefined;
|
|
|
|
// Add the event listeners
|
|
window.addEventListener("click", this.clickEvent.bind(this), false);
|
|
window.addEventListener("mousedown", this.mouseDownEvent.bind(this), false);
|
|
window.addEventListener("touchstart", this.mouseDownEvent.bind(this), false);
|
|
window.addEventListener("mouseup", this.mouseUpEvent.bind(this), false);
|
|
window.addEventListener("touchend", this.mouseUpEvent.bind(this), false);
|
|
window.addEventListener("wheel", this.wheelEvent.bind(this), false);
|
|
}
|
|
|
|
// Constants
|
|
GPlot.MAINLAYERID = "main layer";
|
|
GPlot.VERTICAL = 0;
|
|
GPlot.HORIZONTAL = 1;
|
|
GPlot.BOTH = 2;
|
|
GPlot.NONE = 0;
|
|
|
|
GPlot.prototype.addLayer = function() {
|
|
var id, layer;
|
|
|
|
if (arguments.length === 2) {
|
|
id = arguments[0];
|
|
layer = new GLayer(this.parent, id, this.dim, this.xLim, this.yLim, this.xLog, this.yLog);
|
|
layer.setPoints(arguments[1]);
|
|
} else if (arguments.length === 1) {
|
|
id = arguments[0].getId();
|
|
layer = arguments[0];
|
|
} else {
|
|
throw new Error("GPlot.addLayer(): signature not supported");
|
|
}
|
|
|
|
// Check that it is the only layer with that id
|
|
var sameId = false;
|
|
|
|
if (this.mainLayer.isId(id)) {
|
|
sameId = true;
|
|
} else {
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
if (this.layerList[i].isId(id)) {
|
|
sameId = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the layer to the list
|
|
if (!sameId) {
|
|
layer.setDim(this.dim);
|
|
layer.setLimAndLog(this.xLim, this.yLim, this.xLog, this.yLog);
|
|
this.layerList.push(layer);
|
|
|
|
// Calculate and update the new plot limits if necessary
|
|
if (this.includeAllLayersInLim) {
|
|
this.updateLimits();
|
|
}
|
|
} else {
|
|
console.log("A layer with the same id exists. Please change the id and try to add it again.");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.removeLayer = function(id) {
|
|
var index;
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
if (this.layerList[i].isId(id)) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( typeof index !== "undefined") {
|
|
this.layerList.splice(index, 1);
|
|
|
|
// Calculate and update the new plot limits if necessary
|
|
if (this.includeAllLayersInLim) {
|
|
this.updateLimits();
|
|
}
|
|
} else {
|
|
console.log("Couldn't find a layer in the plot with id = " + id);
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.getPlotPosAt = function(xScreen, yScreen) {
|
|
var xPlot = xScreen - (this.pos[0] + this.mar[1]);
|
|
var yPlot = yScreen - (this.pos[1] + this.mar[2] + this.dim[1]);
|
|
|
|
return [xPlot, yPlot];
|
|
};
|
|
|
|
GPlot.prototype.getScreenPosAtValue = function(xValue, yValue) {
|
|
var xScreen = this.mainLayer.valueToXPlot(xValue) + (this.pos[0] + this.mar[1]);
|
|
var yScreen = this.mainLayer.valueToYPlot(yValue) + (this.pos[1] + this.mar[2] + this.dim[1]);
|
|
|
|
return [xScreen, yScreen];
|
|
};
|
|
|
|
GPlot.prototype.getPointAt = function() {
|
|
var xScreen, yScreen, layer;
|
|
|
|
if (arguments.length === 3) {
|
|
xScreen = arguments[0];
|
|
yScreen = arguments[1];
|
|
layer = this.getLayer(arguments[2]);
|
|
} else if (arguments.length === 2) {
|
|
xScreen = arguments[0];
|
|
yScreen = arguments[1];
|
|
layer = this.mainLayer;
|
|
} else {
|
|
throw new Error("GPlot.getPointAt(): signature not supported");
|
|
}
|
|
|
|
var plotPos = this.getPlotPosAt(xScreen, yScreen);
|
|
return layer.getPointAtPlotPos(plotPos[0], plotPos[1]);
|
|
};
|
|
|
|
GPlot.prototype.addPointAt = function() {
|
|
var xScreen, yScreen, layerId;
|
|
|
|
if (arguments.length === 3) {
|
|
xScreen = arguments[0];
|
|
yScreen = arguments[1];
|
|
layerId = arguments[2];
|
|
} else if (arguments.length === 2) {
|
|
xScreen = arguments[0];
|
|
yScreen = arguments[1];
|
|
layerId = GPlot.MAINLAYERID;
|
|
} else {
|
|
throw new Error("GPlot.addPointAt(): signature not supported");
|
|
}
|
|
|
|
var value = this.getValueAt(xScreen, yScreen);
|
|
this.addPoint(value[0], value[1], "", layerId);
|
|
};
|
|
|
|
GPlot.prototype.removePointAt = function() {
|
|
var xScreen, yScreen, layerId;
|
|
|
|
if (arguments.length === 3) {
|
|
xScreen = arguments[0];
|
|
yScreen = arguments[1];
|
|
layerId = arguments[2];
|
|
} else if (arguments.length === 2) {
|
|
xScreen = arguments[0];
|
|
yScreen = arguments[1];
|
|
layerId = GPlot.MAINLAYERID;
|
|
} else {
|
|
throw new Error("GPlot.removePointAt(): signature not supported");
|
|
}
|
|
|
|
var plotPos = this.getPlotPosAt(xScreen, yScreen);
|
|
var pointIndex = this.getLayer(layerId).getPointIndexAtPlotPos(plotPos[0], plotPos[1]);
|
|
|
|
if ( typeof pointIndex !== "undefined") {
|
|
this.removePoint(pointIndex, layerId);
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.getValueAt = function(xScreen, yScreen) {
|
|
var plotPos = this.getPlotPosAt(xScreen, yScreen);
|
|
return this.mainLayer.plotToValue(plotPos[0], plotPos[1]);
|
|
};
|
|
|
|
GPlot.prototype.getRelativePlotPosAt = function(xScreen, yScreen) {
|
|
var plotPos = this.getPlotPosAt(xScreen, yScreen);
|
|
return [plotPos[0] / this.dim[0], -plotPos[1] / this.dim[1]];
|
|
};
|
|
|
|
GPlot.prototype.isOverPlot = function() {
|
|
var xScreen, yScreen;
|
|
|
|
if (arguments.length === 2) {
|
|
xScreen = arguments[0];
|
|
yScreen = arguments[1];
|
|
} else if (arguments.length === 0) {
|
|
xScreen = this.parent.mouseX;
|
|
yScreen = this.parent.mouseY;
|
|
} else {
|
|
throw new Error("GPlot.isOverPlot(): signature not supported");
|
|
}
|
|
|
|
return (xScreen >= this.pos[0]) && (xScreen <= this.pos[0] + this.outerDim[0]) && (yScreen >= this.pos[1]) && (yScreen <= this.pos[1] + this.outerDim[1]);
|
|
};
|
|
|
|
GPlot.prototype.isOverBox = function() {
|
|
var xScreen, yScreen;
|
|
|
|
if (arguments.length === 2) {
|
|
xScreen = arguments[0];
|
|
yScreen = arguments[1];
|
|
} else if (arguments.length === 0) {
|
|
xScreen = this.parent.mouseX;
|
|
yScreen = this.parent.mouseY;
|
|
} else {
|
|
throw new Error("GPlot.isOverBox(): signature not supported");
|
|
}
|
|
|
|
return (xScreen >= this.pos[0] + this.mar[1]) && (xScreen <= this.pos[0] + this.outerDim[0] - this.mar[3]) && (yScreen >= this.pos[1] + this.mar[2]) && (yScreen <= this.pos[1] + this.outerDim[1] - this.mar[0]);
|
|
};
|
|
|
|
GPlot.prototype.updateLimits = function() {
|
|
// Calculate the new limits and update the axes if needed
|
|
if (!this.fixedXLim) {
|
|
this.xLim = this.calculatePlotXLim();
|
|
this.xAxis.setLim(this.xLim);
|
|
this.topAxis.setLim(this.xLim);
|
|
}
|
|
|
|
if (!this.fixedYLim) {
|
|
this.yLim = this.calculatePlotYLim();
|
|
this.yAxis.setLim(this.yLim);
|
|
this.rightAxis.setLim(this.yLim);
|
|
}
|
|
|
|
// Update the layers
|
|
this.mainLayer.setXYLim(this.xLim, this.yLim);
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].setXYLim(this.xLim, this.yLim);
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.calculatePlotXLim = function() {
|
|
// Find the limits for the main layer
|
|
var lim = this.calculatePointsXLim(this.mainLayer.getPointsRef());
|
|
|
|
// Include the other layers in the limit calculation if necessary
|
|
if (this.includeAllLayersInLim) {
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
var newLim = this.calculatePointsXLim(this.layerList[i].getPointsRef());
|
|
|
|
if ( typeof newLim !== "undefined") {
|
|
if ( typeof lim !== "undefined") {
|
|
lim[0] = Math.min(lim[0], newLim[0]);
|
|
lim[1] = Math.max(lim[1], newLim[1]);
|
|
} else {
|
|
lim = newLim;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( typeof lim !== "undefined") {
|
|
// Expand the axis limits a bit
|
|
var delta = (lim[0] === 0) ? 0.1 : 0.1 * lim[0];
|
|
|
|
if (this.xLog) {
|
|
if (lim[0] !== lim[1]) {
|
|
delta = Math.exp(this.expandLimFactor * Math.log(lim[1] / lim[0]));
|
|
}
|
|
|
|
lim[0] = lim[0] / delta;
|
|
lim[1] = lim[1] * delta;
|
|
} else {
|
|
if (lim[0] !== lim[1]) {
|
|
delta = this.expandLimFactor * (lim[1] - lim[0]);
|
|
}
|
|
|
|
lim[0] = lim[0] - delta;
|
|
lim[1] = lim[1] + delta;
|
|
}
|
|
} else {
|
|
if (this.xLog && (this.xLim[0] <= 0 || this.xLim[1] <= 0)) {
|
|
lim = [0.1, 10];
|
|
} else {
|
|
lim = this.xLim.slice();
|
|
}
|
|
}
|
|
|
|
// Invert the limits if necessary
|
|
if (this.invertedXScale && lim[0] < lim[1]) {
|
|
lim = [lim[1], lim[0]];
|
|
}
|
|
|
|
return lim;
|
|
};
|
|
|
|
GPlot.prototype.calculatePlotYLim = function() {
|
|
// Find the limits for the main layer
|
|
var lim = this.calculatePointsYLim(this.mainLayer.getPointsRef());
|
|
|
|
// Include the other layers in the limit calculation if necessary
|
|
if (this.includeAllLayersInLim) {
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
var newLim = this.calculatePointsYLim(this.layerList[i].getPointsRef());
|
|
|
|
if ( typeof newLim !== "undefined") {
|
|
if ( typeof lim !== "undefined") {
|
|
lim[0] = Math.min(lim[0], newLim[0]);
|
|
lim[1] = Math.max(lim[1], newLim[1]);
|
|
} else {
|
|
lim = newLim;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( typeof lim !== "undefined") {
|
|
// Expand the axis limits a bit
|
|
var delta = (lim[0] === 0) ? 0.1 : 0.1 * lim[0];
|
|
|
|
if (this.yLog) {
|
|
if (lim[0] !== lim[1]) {
|
|
delta = Math.exp(this.expandLimFactor * Math.log(lim[1] / lim[0]));
|
|
}
|
|
|
|
lim[0] = lim[0] / delta;
|
|
lim[1] = lim[1] * delta;
|
|
} else {
|
|
if (lim[0] !== lim[1]) {
|
|
delta = this.expandLimFactor * (lim[1] - lim[0]);
|
|
}
|
|
|
|
lim[0] = lim[0] - delta;
|
|
lim[1] = lim[1] + delta;
|
|
}
|
|
} else {
|
|
if (this.yLog && (this.yLim[0] <= 0 || this.yLim[1] <= 0)) {
|
|
lim = [0.1, 10];
|
|
} else {
|
|
lim = this.yLim.slice();
|
|
}
|
|
}
|
|
|
|
// Invert the limits if necessary
|
|
if (this.invertedYScale && lim[0] < lim[1]) {
|
|
lim = [lim[1], lim[0]];
|
|
}
|
|
|
|
return lim;
|
|
};
|
|
|
|
GPlot.prototype.calculatePointsXLim = function(points) {
|
|
// Find the points limits
|
|
var lim = [Number.MAX_VALUE, -Number.MAX_VALUE];
|
|
|
|
for (var i = 0; i < points.length; i++) {
|
|
if (points[i].isValid()) {
|
|
// Use the point if it's inside, and it's not negative if
|
|
// the scale is logarithmic
|
|
var x = points[i].getX();
|
|
var y = points[i].getY();
|
|
var isInside = true;
|
|
|
|
if (this.fixedYLim) {
|
|
isInside = ((this.yLim[1] >= this.yLim[0]) && (y >= this.yLim[0]) && (y <= this.yLim[1])) || ((this.yLim[1] < this.yLim[0]) && (y <= this.yLim[0]) && (y >= this.yLim[1]));
|
|
}
|
|
|
|
if (isInside && !(this.xLog && x <= 0)) {
|
|
if (x < lim[0]) {
|
|
lim[0] = x;
|
|
}
|
|
|
|
if (x > lim[1]) {
|
|
lim[1] = x;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check that the new limits make sense
|
|
if (lim[1] < lim[0]) {
|
|
lim = undefined;
|
|
}
|
|
|
|
return lim;
|
|
};
|
|
|
|
GPlot.prototype.calculatePointsYLim = function(points) {
|
|
// Find the points limits
|
|
var lim = [Number.MAX_VALUE, -Number.MAX_VALUE];
|
|
|
|
for (var i = 0; i < points.length; i++) {
|
|
if (points[i].isValid()) {
|
|
// Use the point if it's inside, and it's not negative if
|
|
// the scale is logarithmic
|
|
var x = points[i].getX();
|
|
var y = points[i].getY();
|
|
var isInside = true;
|
|
|
|
if (this.fixedXLim) {
|
|
isInside = ((this.xLim[1] >= this.xLim[0]) && (x >= this.xLim[0]) && (x <= this.xLim[1])) || ((this.xLim[1] < this.xLim[0]) && (x <= this.xLim[0]) && (x >= this.xLim[1]));
|
|
}
|
|
|
|
if (isInside && !(this.yLog && y <= 0)) {
|
|
if (y < lim[0]) {
|
|
lim[0] = y;
|
|
}
|
|
if (y > lim[1]) {
|
|
lim[1] = y;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check that the new limits make sense
|
|
if (lim[1] < lim[0]) {
|
|
lim = undefined;
|
|
}
|
|
|
|
return lim;
|
|
};
|
|
|
|
GPlot.prototype.moveHorizontalAxesLim = function(delta) {
|
|
// Obtain the new x limits
|
|
var deltaLim;
|
|
|
|
if (this.xLog) {
|
|
deltaLim = Math.exp(Math.log(this.xLim[1] / this.xLim[0]) * delta / this.dim[0]);
|
|
this.xLim[0] *= deltaLim;
|
|
this.xLim[1] *= deltaLim;
|
|
} else {
|
|
deltaLim = (this.xLim[1] - this.xLim[0]) * delta / this.dim[0];
|
|
this.xLim[0] += deltaLim;
|
|
this.xLim[1] += deltaLim;
|
|
}
|
|
|
|
// Fix the limits
|
|
this.fixedXLim = true;
|
|
|
|
// Move the horizontal axes
|
|
this.xAxis.moveLim(this.xLim);
|
|
this.topAxis.moveLim(this.xLim);
|
|
|
|
// Update the plot limits
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.moveVerticalAxesLim = function(delta) {
|
|
// Obtain the new y limits
|
|
var deltaLim;
|
|
|
|
if (this.yLog) {
|
|
deltaLim = Math.exp(Math.log(this.yLim[1] / this.yLim[0]) * delta / this.dim[1]);
|
|
this.yLim[0] *= deltaLim;
|
|
this.yLim[1] *= deltaLim;
|
|
} else {
|
|
deltaLim = (this.yLim[1] - this.yLim[0]) * delta / this.dim[1];
|
|
this.yLim[0] += deltaLim;
|
|
this.yLim[1] += deltaLim;
|
|
}
|
|
|
|
// Fix the limits
|
|
this.fixedYLim = true;
|
|
|
|
// Move the vertical axes
|
|
this.yAxis.moveLim(this.yLim);
|
|
this.rightAxis.moveLim(this.yLim);
|
|
|
|
// Update the plot limits
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.centerAndZoom = function(factor, xValue, yValue) {
|
|
// Calculate the new limits
|
|
var deltaLim;
|
|
|
|
if (this.xLog) {
|
|
deltaLim = Math.exp(Math.log(this.xLim[1] / this.xLim[0]) / (2 * factor));
|
|
this.xLim = [xValue / deltaLim, xValue * deltaLim];
|
|
} else {
|
|
deltaLim = (this.xLim[1] - this.xLim[0]) / (2 * factor);
|
|
this.xLim = [xValue - deltaLim, xValue + deltaLim];
|
|
}
|
|
|
|
if (this.yLog) {
|
|
deltaLim = Math.exp(Math.log(this.yLim[1] / this.yLim[0]) / (2 * factor));
|
|
this.yLim = [yValue / deltaLim, yValue * deltaLim];
|
|
} else {
|
|
deltaLim = (this.yLim[1] - this.yLim[0]) / (2 * factor);
|
|
this.yLim = [yValue - deltaLim, yValue + deltaLim];
|
|
}
|
|
|
|
// Fix the limits
|
|
this.fixedXLim = true;
|
|
this.fixedYLim = true;
|
|
|
|
// Update the horizontal and vertical axes
|
|
this.xAxis.setLim(this.xLim);
|
|
this.topAxis.setLim(this.xLim);
|
|
this.yAxis.setLim(this.yLim);
|
|
this.rightAxis.setLim(yLim);
|
|
|
|
// Update the plot limits (the layers, because the limits are fixed)
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.zoom = function() {
|
|
var factor, deltaLim, offset;
|
|
|
|
if (arguments.length === 3) {
|
|
factor = arguments[0];
|
|
var xScreen = arguments[1];
|
|
var yScreen = arguments[2];
|
|
|
|
var plotPos = this.getPlotPosAt(xScreen, yScreen);
|
|
var value = this.mainLayer.plotToValue(plotPos[0], plotPos[1]);
|
|
|
|
if (this.xLog) {
|
|
deltaLim = Math.exp(Math.log(this.xLim[1] / this.xLim[0]) / (2 * factor));
|
|
offset = Math.exp((Math.log(this.xLim[1] / this.xLim[0]) / factor) * (0.5 - plotPos[0] / this.dim[0]));
|
|
this.xLim = [value[0] * offset / deltaLim, value[0] * offset * deltaLim];
|
|
} else {
|
|
deltaLim = (this.xLim[1] - this.xLim[0]) / (2 * factor);
|
|
offset = 2 * deltaLim * (0.5 - plotPos[0] / this.dim[0]);
|
|
this.xLim = [value[0] + offset - deltaLim, value[0] + offset + deltaLim];
|
|
}
|
|
|
|
if (this.yLog) {
|
|
deltaLim = Math.exp(Math.log(this.yLim[1] / this.yLim[0]) / (2 * factor));
|
|
offset = Math.exp((Math.log(this.yLim[1] / this.yLim[0]) / factor) * (0.5 + plotPos[1] / this.dim[1]));
|
|
this.yLim = [value[1] * offset / deltaLim, value[1] * offset * deltaLim];
|
|
} else {
|
|
deltaLim = (this.yLim[1] - this.yLim[0]) / (2 * factor);
|
|
offset = 2 * deltaLim * (0.5 + plotPos[1] / this.dim[1]);
|
|
this.yLim = [value[1] + offset - deltaLim, value[1] + offset + deltaLim];
|
|
}
|
|
|
|
// Fix the limits
|
|
this.fixedXLim = true;
|
|
this.fixedYLim = true;
|
|
|
|
// Update the horizontal and vertical axes
|
|
this.xAxis.setLim(this.xLim);
|
|
this.topAxis.setLim(this.xLim);
|
|
this.yAxis.setLim(this.yLim);
|
|
this.rightAxis.setLim(this.yLim);
|
|
|
|
// Update the plot limits (the layers, because the limits are fixed)
|
|
this.updateLimits();
|
|
} else if (arguments.length === 1) {
|
|
factor = arguments[0];
|
|
var centerValue = this.mainLayer.plotToValue(this.dim[0] / 2, -this.dim[1] / 2);
|
|
this.centerAndZoom(factor, centerValue[0], centerValue[1]);
|
|
} else {
|
|
throw new Error("GPlot.zoom(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.shiftPlotPos = function(valuePlotPos, newPlotPos) {
|
|
// Calculate the new limits
|
|
var deltaLim;
|
|
var deltaXPlot = valuePlotPos[0] - newPlotPos[0];
|
|
var deltaYPlot = valuePlotPos[1] - newPlotPos[1];
|
|
|
|
if (this.xLog) {
|
|
deltaLim = Math.exp(Math.log(this.xLim[1] / this.xLim[0]) * deltaXPlot / this.dim[0]);
|
|
this.xLim = [this.xLim[0] * deltaLim, this.xLim[1] * deltaLim];
|
|
} else {
|
|
deltaLim = (this.xLim[1] - this.xLim[0]) * deltaXPlot / this.dim[0];
|
|
this.xLim = [this.xLim[0] + deltaLim, this.xLim[1] + deltaLim];
|
|
}
|
|
|
|
if (this.yLog) {
|
|
deltaLim = Math.exp(-Math.log(this.yLim[1] / this.yLim[0]) * deltaYPlot / this.dim[1]);
|
|
this.yLim = [this.yLim[0] * deltaLim, this.yLim[1] * deltaLim];
|
|
} else {
|
|
deltaLim = -(this.yLim[1] - this.yLim[0]) * deltaYPlot / this.dim[1];
|
|
this.yLim = [this.yLim[0] + deltaLim, this.yLim[1] + deltaLim];
|
|
}
|
|
|
|
// Fix the limits
|
|
this.fixedXLim = true;
|
|
this.fixedYLim = true;
|
|
|
|
// Move the horizontal and vertical axes
|
|
this.xAxis.moveLim(this.xLim);
|
|
this.topAxis.moveLim(this.xLim);
|
|
this.yAxis.moveLim(this.yLim);
|
|
this.rightAxis.moveLim(this.yLim);
|
|
|
|
// Update the plot limits (the layers, because the limits are fixed)
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.align = function() {
|
|
var xValue, yValue, xScreen, yScreen;
|
|
|
|
if (arguments.length === 4) {
|
|
xValue = arguments[0];
|
|
yValue = arguments[1];
|
|
xScreen = arguments[2];
|
|
yScreen = arguments[3];
|
|
} else if (arguments.length === 3) {
|
|
xValue = arguments[0][0];
|
|
yValue = arguments[0][1];
|
|
xScreen = arguments[1];
|
|
yScreen = arguments[2];
|
|
} else {
|
|
throw new Error("GPlot.align(): signature not supported");
|
|
}
|
|
|
|
var valuePlotPos = this.mainLayer.valueToPlot(xValue, yValue);
|
|
var newPlotPos = this.getPlotPosAt(xScreen, yScreen);
|
|
this.shiftPlotPos(valuePlotPos, newPlotPos);
|
|
};
|
|
|
|
GPlot.prototype.center = function(xScreen, yScreen) {
|
|
var valuePlotPos = this.getPlotPosAt(xScreen, yScreen);
|
|
var newPlotPos = [this.dim[0] / 2, -this.dim[1] / 2];
|
|
this.shiftPlotPos(valuePlotPos, newPlotPos);
|
|
};
|
|
|
|
GPlot.prototype.startHistograms = function(histType) {
|
|
this.mainLayer.startHistogram(histType);
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].startHistogram(histType);
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.defaultDraw = function() {
|
|
this.beginDraw();
|
|
this.drawBackground();
|
|
this.drawBox();
|
|
this.drawXAxis();
|
|
this.drawYAxis();
|
|
this.drawTitle();
|
|
this.drawLines();
|
|
this.drawPoints();
|
|
this.endDraw();
|
|
};
|
|
|
|
GPlot.prototype.beginDraw = function() {
|
|
this.parent.push();
|
|
this.parent.translate(this.pos[0] + this.mar[1], this.pos[1] + this.mar[2] + this.dim[1]);
|
|
};
|
|
|
|
GPlot.prototype.endDraw = function() {
|
|
this.parent.pop();
|
|
};
|
|
|
|
GPlot.prototype.drawBackground = function() {
|
|
this.parent.push();
|
|
this.parent.rectMode(this.parent.CORNER);
|
|
this.parent.fill(this.bgColor);
|
|
this.parent.noStroke();
|
|
this.parent.rect(-this.mar[1], -this.mar[2] - this.dim[1], this.outerDim[0], this.outerDim[1]);
|
|
this.parent.pop();
|
|
};
|
|
|
|
GPlot.prototype.drawBox = function() {
|
|
this.parent.push();
|
|
this.parent.rectMode(this.parent.CORNER);
|
|
this.parent.fill(this.boxBgColor);
|
|
this.parent.stroke(this.boxLineColor);
|
|
this.parent.strokeWeight(this.boxLineWidth);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
this.parent.rect(0, -this.dim[1], this.dim[0], this.dim[1]);
|
|
this.parent.pop();
|
|
};
|
|
|
|
GPlot.prototype.drawXAxis = function() {
|
|
this.xAxis.draw();
|
|
};
|
|
|
|
GPlot.prototype.drawYAxis = function() {
|
|
this.yAxis.draw();
|
|
};
|
|
|
|
GPlot.prototype.drawTopAxis = function() {
|
|
this.topAxis.draw();
|
|
};
|
|
|
|
GPlot.prototype.drawRightAxis = function() {
|
|
this.rightAxis.draw();
|
|
};
|
|
|
|
GPlot.prototype.drawTitle = function() {
|
|
this.title.draw();
|
|
};
|
|
|
|
GPlot.prototype.drawPoints = function() {
|
|
var i;
|
|
|
|
if (arguments.length === 1) {
|
|
this.mainLayer.drawPoints(arguments[0]);
|
|
|
|
for ( i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[0].drawPoints(arguments[0]);
|
|
}
|
|
} else if (arguments.length === 0) {
|
|
this.mainLayer.drawPoints();
|
|
|
|
for ( i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].drawPoints();
|
|
}
|
|
} else {
|
|
throw new Error("GPlot.drawPoints(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.drawPoint = function() {
|
|
if (arguments.length === 3) {
|
|
this.mainLayer.drawPoint(arguments[0], arguments[1], arguments[2]);
|
|
} else if (arguments.length === 2) {
|
|
this.mainLayer.drawPoint(arguments[0], arguments[1]);
|
|
} else if (arguments.length === 1) {
|
|
this.mainLayer.drawPoint(arguments[0]);
|
|
} else {
|
|
throw new Error("GPlot.drawPoint(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.drawLines = function() {
|
|
this.mainLayer.drawLines();
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].drawLines();
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.drawLine = function() {
|
|
if (arguments.length === 4) {
|
|
this.mainLayer.drawLine(arguments[0], arguments[1], arguments[2], arguments[3]);
|
|
} else if (arguments.length === 2) {
|
|
this.mainLayer.drawLine(arguments[0], arguments[1]);
|
|
} else {
|
|
throw new Error("GPlot.drawLine(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.drawHorizontalLine = function() {
|
|
if (arguments.length === 3) {
|
|
this.mainLayer.drawHorizontalLine(arguments[0], arguments[1], arguments[2]);
|
|
} else if (arguments.length === 1) {
|
|
this.mainLayer.drawHorizontalLine(arguments[0]);
|
|
} else {
|
|
throw new Error("GPlot.drawHorizontalLine(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.drawVerticalLine = function() {
|
|
if (arguments.length === 3) {
|
|
this.mainLayer.drawVerticalLine(arguments[0], arguments[1], arguments[2]);
|
|
} else if (arguments.length === 1) {
|
|
this.mainLayer.drawVerticalLine(arguments[0]);
|
|
} else {
|
|
throw new Error("GPlot.drawVerticalLine(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.drawFilledContours = function(contourType, referenceValue) {
|
|
this.mainLayer.drawFilledContour(contourType, referenceValue);
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].drawFilledContour(contourType, referenceValue);
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.drawAllLabels = function() {
|
|
this.mainLayer.drawAllLabels();
|
|
};
|
|
|
|
GPlot.prototype.drawLabel = function(point) {
|
|
this.mainLayer.drawLabel(point);
|
|
};
|
|
|
|
GPlot.prototype.drawLabelsAt = function(xScreen, yScreen) {
|
|
var plotPos = this.getPlotPosAt(xScreen, yScreen);
|
|
this.mainLayer.drawLabelAtPlotPos(plotPos[0], plotPos[1]);
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].drawLabelAtPlotPos(plotPos[0], plotPos[1]);
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.drawLabels = function() {
|
|
if (this.labelingIsActive && typeof this.mousePos !== "undefined") {
|
|
this.drawLabelsAt(this.mousePos[0], this.mousePos[1]);
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.drawGridLines = function(gridType) {
|
|
var i;
|
|
|
|
this.parent.push();
|
|
this.parent.noFill();
|
|
this.parent.stroke(this.gridLineColor);
|
|
this.parent.strokeWeight(this.gridLineWidth);
|
|
this.parent.strokeCap(this.parent.SQUARE);
|
|
|
|
if (gridType === GPlot.BOTH || gridType === GPlot.VERTICAL) {
|
|
var xPlotTicks = this.xAxis.getPlotTicksRef();
|
|
|
|
for ( i = 0; i < xPlotTicks.length; i++) {
|
|
if (xPlotTicks[i] >= 0 && xPlotTicks[i] <= this.dim[0]) {
|
|
this.parent.line(xPlotTicks[i], 0, xPlotTicks[i], -this.dim[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gridType === GPlot.BOTH || gridType === GPlot.HORIZONTAL) {
|
|
var yPlotTicks = this.yAxis.getPlotTicksRef();
|
|
|
|
for ( i = 0; i < yPlotTicks.length; i++) {
|
|
if (-yPlotTicks[i] >= 0 && -yPlotTicks[i] <= this.dim[1]) {
|
|
this.parent.line(0, yPlotTicks[i], this.dim[0], yPlotTicks[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
};
|
|
|
|
GPlot.prototype.drawHistograms = function() {
|
|
this.mainLayer.drawHistogram();
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].drawHistogram();
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.drawPolygon = function(polygonPoints, polygonColor) {
|
|
this.mainLayer.drawPolygon(polygonPoints, polygonColor);
|
|
};
|
|
|
|
GPlot.prototype.drawAnnotation = function(text, x, y, horAlign, verAlign) {
|
|
this.mainLayer.drawAnnotation(text, x, y, horAlign, verAlign);
|
|
};
|
|
|
|
GPlot.prototype.drawLegend = function(text, xRelativePos, yRelativePos) {
|
|
var rectSize = 14;
|
|
|
|
this.parent.push();
|
|
this.parent.rectMode(this.parent.CENTER);
|
|
this.parent.noStroke();
|
|
|
|
for (var i = 0; i < text.length; i++) {
|
|
var plotPosition = [xRelativePos[i] * this.dim[0], -yRelativePos[i] * this.dim[1]];
|
|
var position = this.mainLayer.plotToValue(plotPosition[0] + rectSize, plotPosition[1]);
|
|
|
|
if (i === 0) {
|
|
this.parent.fill(this.mainLayer.getLineColor());
|
|
this.parent.rect(plotPosition[0], plotPosition[1], rectSize, rectSize);
|
|
this.mainLayer.drawAnnotation(text[i], position[0], position[1], this.parent.LEFT, this.parent.CENTER);
|
|
} else {
|
|
this.parent.fill(this.layerList[i - 1].getLineColor());
|
|
this.parent.rect(plotPosition[0], plotPosition[1], rectSize, rectSize);
|
|
this.layerList[i - i].drawAnnotation(text[i], position[0], position[1], this.parent.LEFT, this.parent.CENTER);
|
|
}
|
|
}
|
|
|
|
this.parent.pop();
|
|
};
|
|
|
|
GPlot.prototype.setPos = function() {
|
|
if (arguments.length === 2) {
|
|
this.pos[0] = arguments[0];
|
|
this.pos[1] = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
this.pos[0] = arguments[0][0];
|
|
this.pos[1] = arguments[0][1];
|
|
} else {
|
|
throw new Error("GPlot.setPos(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setOuterDim = function() {
|
|
var xOuterDim, yOuterDim;
|
|
|
|
if (arguments.length === 2) {
|
|
xOuterDim = arguments[0];
|
|
yOuterDim = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
xOuterDim = arguments[0][0];
|
|
yOuterDim = arguments[0][1];
|
|
} else {
|
|
throw new Error("GPlot.setOuterDim(): signature not supported");
|
|
}
|
|
|
|
if (xOuterDim > 0 && yOuterDim > 0) {
|
|
// Make sure that the new plot dimensions are positive
|
|
var xDim = xOuterDim - this.mar[1] - this.mar[3];
|
|
var yDim = yOuterDim - this.mar[0] - this.mar[2];
|
|
|
|
if (xDim > 0 && yDim > 0) {
|
|
this.outerDim[0] = xOuterDim;
|
|
this.outerDim[1] = yOuterDim;
|
|
this.dim[0] = xDim;
|
|
this.dim[1] = yDim;
|
|
this.xAxis.setDim(this.dim);
|
|
this.topAxis.setDim(this.dim);
|
|
this.yAxis.setDim(this.dim);
|
|
this.rightAxis.setDim(this.dim);
|
|
this.title.setDim(this.dim);
|
|
|
|
// Update the layers
|
|
this.mainLayer.setDim(this.dim);
|
|
|
|
for (var i = 0; i < this.layerList.lenght; i++) {
|
|
this.layerList[i].setDim(this.dim);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setMar = function() {
|
|
var bottomMargin, leftMargin, topMargin, rightMargin;
|
|
|
|
if (arguments.length === 4) {
|
|
bottomMargin = arguments[0];
|
|
leftMargin = arguments[1];
|
|
topMargin = arguments[2];
|
|
rightMargin = arguments[3];
|
|
} else if (arguments.length === 1) {
|
|
bottomMargin = arguments[0][0];
|
|
leftMargin = arguments[0][1];
|
|
topMargin = arguments[0][2];
|
|
rightMargin = arguments[0][3];
|
|
} else {
|
|
throw new Error("GPlot.setMar(): signature not supported");
|
|
}
|
|
|
|
var xOuterDim = this.dim[0] + leftMargin + rightMargin;
|
|
var yOuterDim = this.dim[1] + bottomMargin + topMargin;
|
|
|
|
if (xOuterDim > 0 && yOuterDim > 0) {
|
|
this.mar[0] = bottomMargin;
|
|
this.mar[1] = leftMargin;
|
|
this.mar[2] = topMargin;
|
|
this.mar[3] = rightMargin;
|
|
this.outerDim[0] = xOuterDim;
|
|
this.outerDim[1] = yOuterDim;
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setDim = function() {
|
|
var xDim, yDim;
|
|
|
|
if (arguments.length === 2) {
|
|
xDim = arguments[0];
|
|
yDim = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
xDim = arguments[0][0];
|
|
yDim = arguments[0][1];
|
|
} else {
|
|
throw new Error("GPlot.setDim(): signature not supported");
|
|
}
|
|
|
|
if (xDim > 0 && yDim > 0) {
|
|
// Make sure that the new outer dimensions are positive
|
|
var xOuterDim = xDim + this.mar[1] + this.mar[3];
|
|
var yOuterDim = yDim + this.mar[0] + this.mar[2];
|
|
|
|
if (xOuterDim > 0 && yOuterDim > 0) {
|
|
this.outerDim[0] = xOuterDim;
|
|
this.outerDim[1] = yOuterDim;
|
|
this.dim[0] = xDim;
|
|
this.dim[1] = yDim;
|
|
this.xAxis.setDim(this.dim);
|
|
this.topAxis.setDim(this.dim);
|
|
this.yAxis.setDim(this.dim);
|
|
this.rightAxis.setDim(this.dim);
|
|
this.title.setDim(this.dim);
|
|
|
|
// Update the layers
|
|
this.mainLayer.setDim(this.dim);
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].setDim(this.dim);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setXLim = function() {
|
|
var lowerLim, upperLim;
|
|
|
|
if (arguments.length === 2) {
|
|
lowerLim = arguments[0];
|
|
upperLim = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
lowerLim = arguments[0][0];
|
|
upperLim = arguments[0][1];
|
|
} else {
|
|
throw new Error("GPlot.setXLim(): signature not supported");
|
|
}
|
|
|
|
if (lowerLim !== upperLim) {
|
|
// Make sure the new limits makes sense
|
|
if (this.xLog && (lowerLim <= 0 || upperLim <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.xLim[0] = lowerLim;
|
|
this.xLim[1] = upperLim;
|
|
this.invertedXScale = this.xLim[0] > this.xLim[1];
|
|
|
|
// Fix the limits
|
|
this.fixedXLim = true;
|
|
|
|
// Update the axes
|
|
this.xAxis.setLim(this.xLim);
|
|
this.topAxis.setLim(this.xLim);
|
|
|
|
// Update the plot limits
|
|
this.updateLimits();
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setYLim = function() {
|
|
var lowerLim, upperLim;
|
|
|
|
if (arguments.length === 2) {
|
|
lowerLim = arguments[0];
|
|
upperLim = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
lowerLim = arguments[0][0];
|
|
upperLim = arguments[0][1];
|
|
} else {
|
|
throw new Error("GPlot.setYLim(): signature not supported");
|
|
}
|
|
|
|
if (lowerLim !== upperLim) {
|
|
// Make sure the new limits makes sense
|
|
if (this.yLog && (lowerLim <= 0 || upperLim <= 0)) {
|
|
console.log("One of the limits is negative. This is not allowed in logarithmic scale.");
|
|
} else {
|
|
this.yLim[0] = lowerLim;
|
|
this.yLim[1] = upperLim;
|
|
this.invertedYScale = this.yLim[0] > this.yLim[1];
|
|
|
|
// Fix the limits
|
|
this.fixedYLim = true;
|
|
|
|
// Update the axes
|
|
this.yAxis.setLim(this.yLim);
|
|
this.rightAxis.setLim(this.yLim);
|
|
|
|
// Update the plot limits
|
|
this.updateLimits();
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setFixedXLim = function(fixedXLim) {
|
|
this.fixedXLim = fixedXLim;
|
|
|
|
// Update the plot limits
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.setFixedYLim = function(fixedYLim) {
|
|
this.fixedYLim = fixedYLim;
|
|
|
|
// Update the plot limits
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.setLogScale = function(logType) {
|
|
var newXLog = this.xLog;
|
|
var newYLog = this.yLog;
|
|
|
|
if (logType === "xy" || logType === "yx") {
|
|
newXLog = true;
|
|
newYLog = true;
|
|
} else if (logType === "x") {
|
|
newXLog = true;
|
|
newYLog = false;
|
|
} else if (logType === "y") {
|
|
newXLog = false;
|
|
newYLog = true;
|
|
} else if (logType === "") {
|
|
newXLog = false;
|
|
newYLog = false;
|
|
}
|
|
|
|
// Do something only if the scale changed
|
|
if (newXLog !== this.xLog || newYLog !== this.yLog) {
|
|
// Set the new log scales
|
|
this.xLog = newXLog;
|
|
this.yLog = newYLog;
|
|
|
|
// Unfix the limits if the old ones don't make sense
|
|
if (this.xLog && this.fixedXLim && (this.xLim[0] <= 0 || this.xLim[1] <= 0)) {
|
|
this.fixedXLim = false;
|
|
}
|
|
|
|
if (this.yLog && this.fixedYLim && (this.yLim[0] <= 0 || this.yLim[1] <= 0)) {
|
|
this.fixedYLim = false;
|
|
}
|
|
|
|
// Calculate the new limits if needed
|
|
if (!this.fixedXLim) {
|
|
this.xLim = this.calculatePlotXLim();
|
|
}
|
|
|
|
if (!this.fixedYLim) {
|
|
this.yLim = this.calculatePlotYLim();
|
|
}
|
|
|
|
// Update the axes
|
|
this.xAxis.setLimAndLog(this.xLim, this.xLog);
|
|
this.topAxis.setLimAndLog(this.xLim, this.xLog);
|
|
this.yAxis.setLimAndLog(this.yLim, this.yLog);
|
|
this.rightAxis.setLimAndLog(this.yLim, this.yLog);
|
|
|
|
// Update the layers
|
|
this.mainLayer.setLimAndLog(this.xLim, this.yLim, this.xLog, this.yLog);
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].setLimAndLog(this.xLim, this.yLim, this.xLog, this.yLog);
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setInvertedXScale = function(invertedXScale) {
|
|
if (invertedXScale !== this.invertedXScale) {
|
|
this.invertedXScale = invertedXScale;
|
|
var temp = this.xLim[0];
|
|
this.xLim[0] = this.xLim[1];
|
|
this.xLim[1] = temp;
|
|
|
|
// Update the axes
|
|
this.xAxis.setLim(this.xLim);
|
|
this.topAxis.setLim(this.xLim);
|
|
|
|
// Update the layers
|
|
this.mainLayer.setXLim(this.xLim);
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].setXLim(this.xLim);
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.invertXScale = function() {
|
|
this.setInvertedXScale(!this.invertedXScale);
|
|
};
|
|
|
|
GPlot.prototype.setInvertedYScale = function(invertedYScale) {
|
|
if (invertedYScale !== this.invertedYScale) {
|
|
this.invertedYScale = invertedYScale;
|
|
var temp = this.yLim[0];
|
|
this.yLim[0] = this.yLim[1];
|
|
this.yLim[1] = temp;
|
|
|
|
// Update the axes
|
|
this.yAxis.setLim(this.yLim);
|
|
this.rightAxis.setLim(this.yLim);
|
|
|
|
// Update the layers
|
|
this.mainLayer.setYLim(this.yLim);
|
|
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
this.layerList[i].setYLim(this.yLim);
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.invertYScale = function() {
|
|
this.setInvertedYScale(!this.invertedYScale);
|
|
};
|
|
|
|
GPlot.prototype.setIncludeAllLayersInLim = function(includeAllLayers) {
|
|
if (includeAllLayers !== this.includeAllLayersInLim) {
|
|
this.includeAllLayersInLim = includeAllLayers;
|
|
|
|
// Update the plot limits
|
|
this.updateLimits();
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setExpandLimFactor = function(expandFactor) {
|
|
if (expandFactor >= 0 && expandFactor !== this.expandLimFactor) {
|
|
this.expandLimFactor = expandFactor;
|
|
|
|
// Update the plot limits
|
|
this.updateLimits();
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setBgColor = function(bgColor) {
|
|
this.bgColor = bgColor;
|
|
};
|
|
|
|
GPlot.prototype.setBoxBgColor = function(boxBgColor) {
|
|
this.boxBgColor = boxBgColor;
|
|
};
|
|
|
|
GPlot.prototype.setBoxLineColor = function(boxLineColor) {
|
|
this.boxLineColor = boxLineColor;
|
|
};
|
|
|
|
GPlot.prototype.setBoxLineWidth = function(boxLineWidth) {
|
|
if (boxLineWidth > 0) {
|
|
this.boxLineWidth = boxLineWidth;
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setGridLineColor = function(gridLineColor) {
|
|
this.gridLineColor = gridLineColor;
|
|
};
|
|
|
|
GPlot.prototype.setGridLineWidth = function(gridLineWidth) {
|
|
if (gridLineWidth > 0) {
|
|
this.gridLineWidth = gridLineWidth;
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.setPoints = function() {
|
|
if (arguments.length === 2) {
|
|
this.getLayer(arguments[1]).setPoints(arguments[0]);
|
|
} else if (arguments.length === 1) {
|
|
this.mainLayer.setPoints(arguments[0]);
|
|
} else {
|
|
throw new Error("GPlot.setPoints(): signature not supported");
|
|
}
|
|
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.setPoint = function() {
|
|
if (arguments.length === 5) {
|
|
this.getLayer(arguments[4]).setPoint(arguments[0], arguments[1], arguments[2], arguments[3]);
|
|
} else if (arguments.length === 4) {
|
|
this.mainLayer.setPoint(arguments[0], arguments[1], arguments[2], arguments[3]);
|
|
} else if (arguments.length === 3 && arguments[1] instanceof GPoint) {
|
|
this.getLayer(arguments[2]).setPoint(arguments[0], arguments[1]);
|
|
} else if (arguments.length === 3) {
|
|
this.mainLayer.setPoint(arguments[0], arguments[1], arguments[2]);
|
|
} else if (arguments.length === 2) {
|
|
this.mainLayer.setPoint(arguments[0], arguments[1]);
|
|
} else {
|
|
throw new Error("GPlot.setPoint(): signature not supported");
|
|
}
|
|
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.addPoint = function() {
|
|
if (arguments.length === 4) {
|
|
this.getLayer(arguments[3]).addPoint(arguments[0], arguments[1], arguments[2]);
|
|
} else if (arguments.length === 3) {
|
|
this.mainLayer.addPoint(arguments[0], arguments[1], arguments[2]);
|
|
} else if (arguments.length === 2 && arguments[0] instanceof GPoint) {
|
|
this.getLayer(arguments[1]).addPoint(arguments[0]);
|
|
} else if (arguments.length === 2) {
|
|
this.mainLayer.addPoint(arguments[0], arguments[1]);
|
|
} else if (arguments.length === 1) {
|
|
this.mainLayer.addPoint(arguments[0]);
|
|
} else {
|
|
throw new Error("GPlot.addPoint(): signature not supported");
|
|
}
|
|
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.addPoints = function() {
|
|
if (arguments.length === 2) {
|
|
this.getLayer(arguments[1]).addPoints(arguments[0]);
|
|
} else if (arguments.length === 1) {
|
|
this.mainLayer.addPoints(arguments[0]);
|
|
} else {
|
|
throw new Error("GPlot.addPoints(): signature not supported");
|
|
}
|
|
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.removePoint = function() {
|
|
if (arguments.length === 2) {
|
|
this.getLayer(arguments[1]).removePoint(arguments[0]);
|
|
} else if (arguments.length === 1) {
|
|
this.mainLayer.removePoint(arguments[0]);
|
|
} else {
|
|
throw new Error("GPlot.removePoint(): signature not supported");
|
|
}
|
|
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.addPointAtIndexPos = function() {
|
|
if (arguments.length === 5) {
|
|
this.getLayer(arguments[4]).addPointAtIndexPos(arguments[0], arguments[1], arguments[2], arguments[3]);
|
|
} else if (arguments.length === 4) {
|
|
this.mainLayer.addPointAtIndexPos(arguments[0], arguments[1], arguments[2], arguments[3]);
|
|
} else if (arguments.length === 3 && arguments[1] instanceof GPoint) {
|
|
this.getLayer(arguments[2]).addPointAtIndexPos(arguments[0], arguments[1]);
|
|
} else if (arguments.length === 3) {
|
|
this.mainLayer.addPointAtIndexPos(arguments[0], arguments[1], arguments[2]);
|
|
} else if (arguments.length === 2) {
|
|
this.mainLayer.addPointAtIndexPos(arguments[0], arguments[1]);
|
|
} else {
|
|
throw new Error("GPlot.addPointAtIndexPos(): signature not supported");
|
|
}
|
|
|
|
this.updateLimits();
|
|
};
|
|
|
|
GPlot.prototype.setPointColors = function(pointColors) {
|
|
this.mainLayer.setPointColors(pointColors);
|
|
};
|
|
|
|
GPlot.prototype.setPointColor = function(pointColor) {
|
|
this.mainLayer.setPointColor(pointColor);
|
|
};
|
|
|
|
GPlot.prototype.setPointSizes = function(pointSizes) {
|
|
this.mainLayer.setPointSizes(pointSizes);
|
|
};
|
|
|
|
GPlot.prototype.setPointSize = function(pointSize) {
|
|
this.mainLayer.setPointSize(pointSize);
|
|
};
|
|
|
|
GPlot.prototype.setLineColor = function(lineColor) {
|
|
this.mainLayer.setLineColor(lineColor);
|
|
};
|
|
|
|
GPlot.prototype.setLineWidth = function(lineWidth) {
|
|
this.mainLayer.setLineWidth(lineWidth);
|
|
};
|
|
|
|
GPlot.prototype.setHistBasePoint = function(basePoint) {
|
|
this.mainLayer.setHistBasePoint(basePoint);
|
|
};
|
|
|
|
GPlot.prototype.setHistType = function(histType) {
|
|
this.mainLayer.setHistType(histType);
|
|
};
|
|
|
|
GPlot.prototype.setHistVisible = function(histVisible) {
|
|
this.mainLayer.setHistVisible(histVisible);
|
|
};
|
|
|
|
GPlot.prototype.setDrawHistLabels = function(drawHistLabels) {
|
|
this.mainLayer.setDrawHistLabels(drawHistLabels);
|
|
};
|
|
|
|
GPlot.prototype.setLabelBgColor = function(labelBgColor) {
|
|
this.mainLayer.setLabelBgColor(labelBgColor);
|
|
};
|
|
|
|
GPlot.prototype.setLabelSeparation = function(labelSeparation) {
|
|
this.mainLayer.setLabelSeparation(labelSeparation);
|
|
};
|
|
|
|
GPlot.prototype.setTitleText = function(text) {
|
|
this.title.setText(text);
|
|
};
|
|
|
|
GPlot.prototype.setAxesOffset = function(offset) {
|
|
this.xAxis.setOffset(offset);
|
|
this.topAxis.setOffset(offset);
|
|
this.yAxis.setOffset(offset);
|
|
this.rightAxis.setOffset(offset);
|
|
};
|
|
|
|
GPlot.prototype.setTicksLength = function(tickLength) {
|
|
this.xAxis.setTickLength(tickLength);
|
|
this.topAxis.setTickLength(tickLength);
|
|
this.yAxis.setTickLength(tickLength);
|
|
this.rightAxis.setTickLength(tickLength);
|
|
};
|
|
|
|
GPlot.prototype.setHorizontalAxesNTicks = function(nTicks) {
|
|
this.xAxis.setNTicks(nTicks);
|
|
this.topAxis.setNTicks(nTicks);
|
|
};
|
|
|
|
GPlot.prototype.setHorizontalAxesTicksSeparation = function(ticksSeparation) {
|
|
this.xAxis.setTicksSeparation(ticksSeparation);
|
|
this.topAxis.setTicksSeparation(ticksSeparation);
|
|
};
|
|
|
|
GPlot.prototype.setHorizontalAxesTicks = function(ticks) {
|
|
this.xAxis.setTicks(ticks);
|
|
this.topAxis.setTicks(ticks);
|
|
};
|
|
|
|
GPlot.prototype.setVerticalAxesNTicks = function(nTicks) {
|
|
this.yAxis.setNTicks(nTicks);
|
|
this.rightAxis.setNTicks(nTicks);
|
|
};
|
|
|
|
GPlot.prototype.setVerticalAxesTicksSeparation = function(ticksSeparation) {
|
|
this.yAxis.setTicksSeparation(ticksSeparation);
|
|
this.rightAxis.setTicksSeparation(ticksSeparation);
|
|
};
|
|
|
|
GPlot.prototype.setVerticalAxesTicks = function(ticks) {
|
|
this.yAxis.setTicks(ticks);
|
|
this.rightAxis.setTicks(ticks);
|
|
};
|
|
|
|
GPlot.prototype.setFontName = function(fontName) {
|
|
this.maniLayer.setFontName(fontName);
|
|
};
|
|
|
|
GPlot.prototype.setFontColor = function(fontColor) {
|
|
this.maniLayer.setFontColor(fontColor);
|
|
};
|
|
|
|
GPlot.prototype.setFontSize = function(fontSize) {
|
|
this.maniLayer.setFontSize(fontSize);
|
|
};
|
|
|
|
GPlot.prototype.setFontProperties = function(fontName, fontColor, fontSize) {
|
|
this.maniLayer.setFontProperties(fontName, fontColor, fontSize);
|
|
};
|
|
|
|
GPlot.prototype.setAllFontProperties = function(fontName, fontColor, fontSize) {
|
|
this.xAxis.setAllFontProperties(fontName, fontColor, fontSize);
|
|
this.topAxis.setAllFontProperties(fontName, fontColor, fontSize);
|
|
this.yAxis.setAllFontProperties(fontName, fontColor, fontSize);
|
|
this.rightAxis.setAllFontProperties(fontName, fontColor, fontSize);
|
|
this.title.setFontProperties(fontName, fontColor, fontSize);
|
|
|
|
this.mainLayer.setAllFontProperties(fontName, fontColor, fontSize);
|
|
|
|
for (var i = 0; i < layerList.length; i++) {
|
|
this.layerList[i].setAllFontProperties(fontName, fontColor, fontSize);
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.getPos = function() {
|
|
return this.pos.slice();
|
|
};
|
|
|
|
GPlot.prototype.getOuterDim = function() {
|
|
return this.outerDim.slice();
|
|
};
|
|
|
|
GPlot.prototype.getMar = function() {
|
|
return this.mar.slice();
|
|
};
|
|
|
|
GPlot.prototype.getDim = function() {
|
|
return this.dim.slice();
|
|
};
|
|
|
|
GPlot.prototype.getXLim = function() {
|
|
return this.xLim.slice();
|
|
};
|
|
|
|
GPlot.prototype.getYLim = function() {
|
|
return this.yLim.slice();
|
|
};
|
|
|
|
GPlot.prototype.getFixedXLim = function() {
|
|
return this.fixedXLim;
|
|
};
|
|
|
|
GPlot.prototype.getFixedYLim = function() {
|
|
return this.fixedYLim;
|
|
};
|
|
|
|
GPlot.prototype.getXLog = function() {
|
|
return this.xLog;
|
|
};
|
|
|
|
GPlot.prototype.getYLog = function() {
|
|
return this.yLog;
|
|
};
|
|
|
|
GPlot.prototype.getInvertedXScale = function() {
|
|
return this.invertedXScale;
|
|
};
|
|
|
|
GPlot.prototype.getInvertedYScale = function() {
|
|
return this.invertedYScale;
|
|
};
|
|
|
|
GPlot.prototype.getMainLayer = function() {
|
|
return this.mainLayer;
|
|
};
|
|
|
|
GPlot.prototype.getLayer = function(id) {
|
|
var layer;
|
|
|
|
if (this.mainLayer.isId(id)) {
|
|
layer = this.mainLayer;
|
|
} else {
|
|
for (var i = 0; i < this.layerList.length; i++) {
|
|
if (this.layerList[i].isId(id)) {
|
|
layer = this.layerList[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( typeof layer === "undefined") {
|
|
console.log("Couldn't find a layer in the plot with id = " + id);
|
|
}
|
|
|
|
return layer;
|
|
};
|
|
|
|
GPlot.prototype.getXAxis = function() {
|
|
return this.xAxis;
|
|
};
|
|
|
|
GPlot.prototype.getYAxis = function() {
|
|
return this.yAxis;
|
|
};
|
|
|
|
GPlot.prototype.getTopAxis = function() {
|
|
return this.topAxis;
|
|
};
|
|
|
|
GPlot.prototype.getRightAxis = function() {
|
|
return this.rightAxis;
|
|
};
|
|
|
|
GPlot.prototype.getTitle = function() {
|
|
return this.title;
|
|
};
|
|
|
|
GPlot.prototype.getPoints = function() {
|
|
if (arguments.length === 1) {
|
|
return this.getLayer(arguments[0]).getPoints();
|
|
} else if (arguments.length === 0) {
|
|
return this.mainLayer.getPoints();
|
|
} else {
|
|
throw new Error("GPlot.getPoints(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.getPointsRef = function() {
|
|
if (arguments.length === 1) {
|
|
return this.getLayer(arguments[0]).getPointsRef();
|
|
} else if (arguments.length === 0) {
|
|
return this.mainLayer.getPointsRef();
|
|
} else {
|
|
throw new Error("GPlot.getPointsRef(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.getHistogram = function() {
|
|
if (arguments.length === 1) {
|
|
return this.getLayer(arguments[0]).getHistogram();
|
|
} else if (arguments.length === 0) {
|
|
return this.mainLayer.getHistogram();
|
|
} else {
|
|
throw new Error("GPlot.getHistogram(): signature not supported");
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.activateZooming = function() {
|
|
var zoomFactor, increaseButton, decreaseButton, increaseKeyModifier, decreaseKeyModifier;
|
|
|
|
if (arguments.length === 5) {
|
|
zoomFactor = arguments[0];
|
|
increaseButton = arguments[1];
|
|
decreaseButton = arguments[2];
|
|
increaseKeyModifier = arguments[3];
|
|
decreaseKeyModifier = arguments[4];
|
|
} else if (arguments.length === 3) {
|
|
zoomFactor = arguments[0];
|
|
increaseButton = arguments[1];
|
|
decreaseButton = arguments[2];
|
|
increaseKeyModifier = GPlot.NONE;
|
|
decreaseKeyModifier = GPlot.NONE;
|
|
} else if (arguments.length === 1) {
|
|
zoomFactor = arguments[0];
|
|
increaseButton = this.parent.LEFT;
|
|
decreaseButton = this.parent.RIGHT;
|
|
increaseKeyModifier = GPlot.NONE;
|
|
decreaseKeyModifier = GPlot.NONE;
|
|
} else if (arguments.length === 0) {
|
|
zoomFactor = 1.3;
|
|
increaseButton = this.parent.LEFT;
|
|
decreaseButton = this.parent.RIGHT;
|
|
increaseKeyModifier = GPlot.NONE;
|
|
decreaseKeyModifier = GPlot.NONE;
|
|
} else {
|
|
throw new Error("GPlot.activateZooming(): signature not supported");
|
|
}
|
|
|
|
this.zoomingIsActive = true;
|
|
|
|
if (zoomFactor > 0) {
|
|
this.zoomFactor = zoomFactor;
|
|
}
|
|
|
|
if (increaseButton === this.parent.LEFT || increaseButton === this.parent.RIGHT || increaseButton === this.parent.CENTER) {
|
|
this.increaseZoomButton = increaseButton;
|
|
}
|
|
|
|
if (decreaseButton === this.parent.LEFT || decreaseButton === this.parent.RIGHT || decreaseButton === this.parent.CENTER) {
|
|
this.decreaseZoomButton = decreaseButton;
|
|
}
|
|
|
|
if (increaseKeyModifier === this.parent.SHIFT || increaseKeyModifier === this.parent.CONTROL || increaseKeyModifier === this.parent.ALT || increaseKeyModifier === GPlot.NONE) {
|
|
this.increaseZoomKeyModifier = increaseKeyModifier;
|
|
}
|
|
|
|
if (decreaseKeyModifier === this.parent.SHIFT || decreaseKeyModifier === this.parent.CONTROL || decreaseKeyModifier === this.parent.ALT || decreaseKeyModifier === GPlot.NONE) {
|
|
this.decreaseZoomKeyModifier = decreaseKeyModifier;
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.deactivateZooming = function() {
|
|
this.zoomingIsActive = false;
|
|
};
|
|
|
|
GPlot.prototype.activateCentering = function() {
|
|
var button, keyModifier;
|
|
|
|
if (arguments.length === 2) {
|
|
button = arguments[0];
|
|
keyModifier = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
button = arguments[0];
|
|
keyModifier = GPlot.NONE;
|
|
} else if (arguments.length === 0) {
|
|
button = this.parent.LEFT;
|
|
keyModifier = GPlot.NONE;
|
|
} else {
|
|
throw new Error("GPlot.activateCentering(): signature not supported");
|
|
}
|
|
|
|
this.centeringIsActive = true;
|
|
|
|
if (button === this.parent.LEFT || button === this.parent.RIGHT || button === this.parent.CENTER) {
|
|
this.centeringButton = button;
|
|
}
|
|
|
|
if (keyModifier === this.parent.SHIFT || keyModifier === this.parent.CONTROL || keyModifier === this.parent.ALT || keyModifier === GPlot.NONE) {
|
|
this.centeringKeyModifier = keyModifier;
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.deactivateCentering = function() {
|
|
this.centeringIsActive = false;
|
|
};
|
|
|
|
GPlot.prototype.activatePanning = function() {
|
|
var button, keyModifier;
|
|
|
|
if (arguments.length === 2) {
|
|
button = arguments[0];
|
|
keyModifier = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
button = arguments[0];
|
|
keyModifier = GPlot.NONE;
|
|
} else if (arguments.length === 0) {
|
|
button = this.parent.LEFT;
|
|
keyModifier = GPlot.NONE;
|
|
} else {
|
|
throw new Error("GPlot.activatePanning(): signature not supported");
|
|
}
|
|
|
|
this.panningIsActive = true;
|
|
|
|
if (button === this.parent.LEFT || button === this.parent.RIGHT || button === this.parent.CENTER) {
|
|
this.panningButton = button;
|
|
}
|
|
|
|
if (keyModifier === this.parent.SHIFT || keyModifier === this.parent.CONTROL || keyModifier === this.parent.ALT || keyModifier === GPlot.NONE) {
|
|
this.panningKeyModifier = keyModifier;
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.deactivatePanning = function() {
|
|
this.panningIsActive = false;
|
|
this.panningReferencePoint = undefined;
|
|
};
|
|
|
|
GPlot.prototype.activatePointLabels = function() {
|
|
var button, keyModifier;
|
|
|
|
if (arguments.length === 2) {
|
|
button = arguments[0];
|
|
keyModifier = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
button = arguments[0];
|
|
keyModifier = GPlot.NONE;
|
|
} else if (arguments.length === 0) {
|
|
button = this.parent.LEFT;
|
|
keyModifier = GPlot.NONE;
|
|
} else {
|
|
throw new Error("GPlot.activatePointLabels(): signature not supported");
|
|
}
|
|
|
|
this.labelingIsActive = true;
|
|
|
|
if (button === this.parent.LEFT || button === this.parent.RIGHT || button === this.parent.CENTER) {
|
|
this.labelingButton = button;
|
|
}
|
|
|
|
if (keyModifier === this.parent.SHIFT || keyModifier === this.parent.CONTROL || keyModifier === this.parent.ALT || keyModifier === GPlot.NONE) {
|
|
this.labelingKeyModifier = keyModifier;
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.deactivatePointLabels = function() {
|
|
this.labelingIsActive = false;
|
|
this.mousePos = undefined;
|
|
};
|
|
|
|
GPlot.prototype.activateReset = function() {
|
|
var button, keyModifier;
|
|
|
|
if (arguments.length === 2) {
|
|
button = arguments[0];
|
|
keyModifier = arguments[1];
|
|
} else if (arguments.length === 1) {
|
|
button = arguments[0];
|
|
keyModifier = GPlot.NONE;
|
|
} else if (arguments.length === 0) {
|
|
button = this.parent.RIGHT;
|
|
keyModifier = GPlot.NONE;
|
|
} else {
|
|
throw new Error("GPlot.activateReset(): signature not supported");
|
|
}
|
|
|
|
this.resetIsActive = true;
|
|
this.xLimReset = undefined;
|
|
this.yLimReset = undefined;
|
|
|
|
if (button === this.parent.LEFT || button === this.parent.RIGHT || button === this.parent.CENTER) {
|
|
this.resetButton = button;
|
|
}
|
|
|
|
if (keyModifier === this.parent.SHIFT || keyModifier === this.parent.CONTROL || keyModifier === this.parent.ALT || keyModifier === GPlot.NONE) {
|
|
this.resetKeyModifier = keyModifier;
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.deactivateReset = function() {
|
|
this.resetIsActive = false;
|
|
this.xLimReset = undefined;
|
|
this.yLimReset = undefined;
|
|
};
|
|
|
|
GPlot.prototype.getButton = function(event) {
|
|
var button;
|
|
|
|
if (event.button === 0) {
|
|
button = this.parent.LEFT;
|
|
} else if (event.button === 1) {
|
|
button = this.parent.CENTER;
|
|
} else if (event.button === 2) {
|
|
button = this.parent.RIGHT;
|
|
} else if ( typeof event.button === "undefined") {
|
|
button = this.parent.LEFT;
|
|
}
|
|
|
|
return button;
|
|
};
|
|
|
|
GPlot.prototype.getModifier = function(event) {
|
|
var modifier;
|
|
|
|
if (event.altKey) {
|
|
modifier = this.parent.ALT;
|
|
} else if (event.ctrlKey) {
|
|
modifier = this.parent.CONTROL;
|
|
} else if (event.shiftKey) {
|
|
modifier = this.parent.SHIFT;
|
|
} else {
|
|
modifier = GPlot.NONE;
|
|
}
|
|
|
|
return modifier;
|
|
};
|
|
|
|
GPlot.prototype.saveResetLimits = function() {
|
|
if ( typeof this.xLimReset === "undefined" || typeof this.yLimReset === "undefined") {
|
|
this.xLimReset = this.xLim.slice();
|
|
this.yLimReset = this.yLim.slice();
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.panningFunction = function() {
|
|
if ( typeof this.panningReferencePoint !== "undefined") {
|
|
this.align(this.panningReferencePoint, this.parent.mouseX, this.parent.mouseY);
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.clickEvent = function(event) {
|
|
e = event || window.event;
|
|
|
|
if (this.isOverBox()) {
|
|
var button = this.getButton(e);
|
|
var modifier = this.getModifier(e);
|
|
|
|
if (this.zoomingIsActive) {
|
|
if (button === this.increaseZoomButton && modifier === this.increaseZoomKeyModifier) {
|
|
// Save the axes limits
|
|
if (this.resetIsActive) {
|
|
this.saveResetLimits();
|
|
}
|
|
|
|
this.zoom(this.zoomFactor, this.parent.mouseX, this.parent.mouseY);
|
|
} else if (button === this.decreaseZoomButton && modifier === this.decreaseZoomKeyModifier) {
|
|
// Save the axes limits
|
|
if (this.resetIsActive) {
|
|
this.saveResetLimits();
|
|
}
|
|
|
|
this.zoom(1 / this.zoomFactor, this.parent.mouseX, this.parent.mouseY);
|
|
}
|
|
}
|
|
|
|
if (this.centeringIsActive && button === this.centeringButton && modifier === this.centeringKeyModifier) {
|
|
// Save the axes limits
|
|
if (this.resetIsActive) {
|
|
this.saveResetLimits();
|
|
}
|
|
|
|
this.center(this.parent.mouseX, this.parent.mouseY);
|
|
}
|
|
|
|
if (this.resetIsActive && button === this.resetButton && modifier === this.resetKeyModifier) {
|
|
if ( typeof this.xLimReset !== "undefined" && typeof this.yLimReset !== "undefined") {
|
|
this.setXLim(this.xLimReset);
|
|
this.setYLim(this.yLimReset);
|
|
this.xLimReset = undefined;
|
|
this.yLimReset = undefined;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.mouseDownEvent = function(event) {
|
|
e = event || window.event;
|
|
|
|
if (this.isOverBox()) {
|
|
var button = this.getButton(e);
|
|
var modifier = this.getModifier(e);
|
|
|
|
if (this.panningIsActive && button === this.panningButton && modifier === this.panningKeyModifier) {
|
|
// Save the axes limits
|
|
if (this.resetIsActive) {
|
|
this.saveResetLimits();
|
|
}
|
|
|
|
// Calculate the panning reference point
|
|
this.panningReferencePoint = this.getValueAt(this.parent.mouseX, this.parent.mouseY);
|
|
|
|
// Execute the panning function every 100ms
|
|
this.panningIntervalId = setInterval(this.panningFunction.bind(this), 100);
|
|
}
|
|
|
|
if (this.labelingIsActive && button === this.labelingButton && modifier === this.labelingKeyModifier) {
|
|
this.mousePos = [this.parent.mouseX, this.parent.mouseY];
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.mouseUpEvent = function(event) {
|
|
e = event || window.event;
|
|
var button = this.getButton(e);
|
|
var modifier = this.getModifier(e);
|
|
|
|
if (this.panningIsActive && button === this.panningButton && typeof this.panningIntervalId !== "undefined") {
|
|
// Stop executing the panning function
|
|
clearInterval(this.panningIntervalId);
|
|
|
|
// Reset the panning variables
|
|
this.panningIntervalId = undefined;
|
|
this.panningReferencePoint = undefined;
|
|
}
|
|
|
|
if (this.labelingIsActive && button === this.labelingButton) {
|
|
this.mousePos = undefined;
|
|
}
|
|
|
|
if (button === this.parent.RIGHT) {
|
|
// This is a right click!
|
|
this.clickEvent(e);
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.wheelEvent = function(event) {
|
|
e = event || window.event;
|
|
|
|
if (this.isOverBox()) {
|
|
var deltaY = e.deltaY;
|
|
var button = this.parent.CENTER;
|
|
var modifier = this.getModifier(e);
|
|
|
|
if (this.zoomingIsActive) {
|
|
if (button === this.increaseZoomButton && modifier === this.increaseZoomKeyModifier && deltaY > 0) {
|
|
// Save the axes limits
|
|
if (this.resetIsActive) {
|
|
this.saveResetLimits();
|
|
}
|
|
|
|
this.zoom(this.zoomFactor, this.parent.mouseX, this.parent.mouseY);
|
|
} else if (button === this.decreaseZoomButton && modifier === this.decreaseZoomKeyModifier && deltaY < 0) {
|
|
// Save the axes limits
|
|
if (this.resetIsActive) {
|
|
this.saveResetLimits();
|
|
}
|
|
|
|
this.zoom(1 / this.zoomFactor, this.parent.mouseX, this.parent.mouseY);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.preventDefaultEvent = function(event) {
|
|
e = event || window.event;
|
|
|
|
if (this.isOverBox()) {
|
|
// Don't show the menu inside the plot area
|
|
if (e.preventDefault) {
|
|
e.preventDefault();
|
|
} else {
|
|
e.returnValue = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
GPlot.prototype.preventWheelDefault = function() {
|
|
window.addEventListener("wheel", this.preventDefaultEvent.bind(this), false);
|
|
};
|
|
|
|
GPlot.prototype.preventRightClickDefault = function() {
|
|
window.addEventListener("contextmenu", this.preventDefaultEvent.bind(this), false);
|
|
};
|