You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

528 lines
17 KiB

function Rect(value, name, x, y, w, h){
this.value = value;
this.name = name;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.checkHit = function (mouseX, mouseY) {
return (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY <= y + h);
}
}
function Circle(value, name, x, y, r){
this.value = value;
this.name = name;
this.x = x;
this.y = y;
this.r = r;
this.checkHit = function (mouseX, mouseY) {
return Math.pow((mouseX - x),2) + Math.pow((mouseY - y),2) <= Math.pow(r, 2);
}
}
function PieSlice(value, name, x, y, r, sAngle, eAngle){
this.value = value;
this.name = name;
this.x = x;
this.y = y;
this.r = r;
this.sAngle = sAngle;
this.eAngle = eAngle;
this.checkHit = function (mouseX, mouseY) {
if (Math.pow((mouseX - x),2) + Math.pow((mouseY - y),2) <= Math.pow(r, 2)){
var dy = mouseY - y;
var dx = mouseX - x;
var theta = Math.atan2(dy, dx); // range (-PI, PI]
if (theta < 0) theta += 2*Math.PI;
return (theta > sAngle && theta < eAngle);
}
}
}
let objects = [];
function checkHit(pos) {
for (let i = 0; i < objects.length; i++){
if (objects[i].checkHit(pos.x, pos.y)){
return objects[i];
}
}
return null;
}
function getLargest(data) {
let largest = data[0].values[0];
data.forEach(function (categ) {
for (let i = 0; i < categ.values.length; i++)
if (categ.values[i] > largest) {
largest = categ.values[i];
}
});
return largest;
}
function getSmallest(data) {
let smallest = data[0].values[0];
data.forEach(function (categ) {
for (let i = 0; i < categ.values.length; i++)
if (categ.values[i] < smallest) {
smallest = categ.values[i];
}
});
return smallest;
}
function drawAxis(bounds, largest, smallest, arrayLen, ctx, graphSettings, drawValues = true) {
ctx.font = "16px Arial";
if (graphSettings.y_step <= 0) graphSettings.y_step = 1;
ctx.beginPath();
for (let i = (smallest < 0)?smallest:0; i <= ((largest>=0)?largest:0); i += parseFloat(graphSettings.y_step)){
ctx.strokeStyle = "#BBB";
ctx.lineWidth = 1;
let scale = bounds.height - ((largest >= 0)?(bounds.bottom - bounds.xAxis):0);
let extreme = (largest<=0)?Math.abs(smallest):Math.abs(largest);
let yPos = Math.round((bounds.xAxis - i * scale / extreme));
//support line
if (graphSettings.display_support_lines) {
ctx.moveTo( bounds.left, yPos );
ctx.lineTo( bounds.right, yPos );
}
//Y axis value
ctx.fillStyle = "black";
ctx.textAlign = "center";
ctx.textAlign = "end";
ctx.fillText( i, bounds.left - 3, yPos);
ctx.stroke();
}
//X axis value
if (drawValues) {
ctx.fillStyle = "black";
ctx.textAlign = "center";
for (let i = 0; i < arrayLen; i++){
let x = bounds.left + bounds.width / (arrayLen - 1) * i;
let text = (i + 1).toString();
if (graphSettings.custom_x_values !== "")
text = graphSettings.custom_x_values.split(';')[i];
ctx.fillText(text, x, bounds.bottom + 18);
}
ctx.closePath();
}
//X and Y axis
ctx.strokeStyle = "black";
ctx.lineWidth = "2px";
ctx.beginPath();
ctx.moveTo( bounds.left, bounds.top );
ctx.lineTo( bounds.left, bounds.bottom );
ctx.moveTo( bounds.left, bounds.xAxis);
ctx.lineTo( bounds.right, bounds.xAxis );
ctx.stroke();
//Axis labels
//X axis text
ctx.beginPath();
ctx.font = "20px Arial";
ctx.fillStyle = "black";
ctx.textAlign = "center";
ctx.fillText(graphSettings.x_label, bounds.width/2 + bounds.left, bounds.height + 2*bounds.top - 5);
//Y axis text
ctx.save();
ctx.rotate(-Math.PI / 2);
ctx.textAlign = "center";
ctx.fillText(graphSettings.y_label, -(bounds.left + bounds.height/2), 18);
ctx.restore();
ctx.stroke();
}
function drawTitle(canvas, graphSettings) {
let ctx = canvas.getContext("2d");
let x = canvas.width / 2;
let y = 25;
ctx.font = "30px Arial";
ctx.fillStyle = "black";
ctx.textAlign = "center";
ctx.fillText( graphSettings.title, x, y);
}
function drawPoints(ctx, bounds, values, name, arrayLen, largest, color) {
ctx.fillStyle = color;
let radius = 3;
for( let i = 0; i < arrayLen; i++ ){
ctx.beginPath();
if(values[i] === null) continue;
let scale = bounds.height - ((largest >= 0)?(bounds.bottom - bounds.xAxis):0);
let extreme = (largest<=0)?Math.abs(smallest):Math.abs(largest);
let x = bounds.left + bounds.width / (arrayLen - 1) * i;
let y = (bounds.xAxis - values[i] / extreme * scale);
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.closePath();
let new_object = new Circle(values[i], name, x, y, radius);
objects.push(new_object);
}
}
function drawSlice(ctx, centerX, centerY, radius, startAngle, endAngle, color ){
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(centerX,centerY);
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.fill();
ctx.closePath();
}
function getBounds(canvas, graphMargin) {
return {
top: graphMargin,
bottom: canvas.height - graphMargin,
left: graphMargin,
right: canvas.width - graphMargin,
height: canvas.height - 2*graphMargin,
width: canvas.width - 2*graphMargin,
xAxis: canvas.height - graphMargin
};
}
function drawPieChart(canvas, data, graphSettings) {
let ctx = canvas.getContext("2d");
let index = 0;
let start_angle = 0;
let total_value = 0;
let bounds = getBounds(canvas, graphSettings.margin);
data.forEach(function (categ) {
let val = categ.values[0];
if (val !== null)
total_value += val;
});
data.forEach(function (categ) {
let val = categ.values[0];
let slice_angle = 2 * Math.PI * val / total_value;
let x = canvas.width/2;
let y = canvas.height/2;
let r = Math.min(bounds.width/2, bounds.height/2);
let end_angle = start_angle + slice_angle;
drawSlice(ctx, x, y, r, start_angle, end_angle, categ.color);
let new_object = new PieSlice(val + " (" + Math.round(val/total_value*100) + "%)", categ.name, x, y, r, start_angle, end_angle);
objects.push(new_object);
start_angle = end_angle;
index++;
});
}
function drawPointChart(canvas, data, graphSettings){
let ctx = canvas.getContext( "2d" );
let bounds = getBounds(canvas, graphSettings.margin);
let largest = getLargest(data);
let smallest = getSmallest(data);
if (smallest < 0)
bounds.xAxis = bounds.bottom - (bounds.height / (((largest<=0)?0:Math.abs(largest)) + Math.abs(smallest)) * Math.abs(smallest));
let arrayLen = data[0].values.length;
drawAxis(bounds, largest, smallest, arrayLen, ctx, graphSettings);
data.display_points = true;
data.forEach(function (categ) {
//Points
if (graphSettings.display_points)
drawPoints(ctx, bounds, categ.values, categ.name, arrayLen, largest, categ.color);
});
}
function drawLineChart(canvas, data, graphSettings){
let ctx = canvas.getContext( "2d" );
let bounds = getBounds(canvas, graphSettings.margin);
let largest = getLargest(data);
let smallest = getSmallest(data);
if (smallest < 0)
bounds.xAxis = bounds.bottom - (bounds.height / (((largest<=0)?0:Math.abs(largest)) + Math.abs(smallest)) * Math.abs(smallest));
let arrayLen = data[0].values.length;
drawAxis(bounds, largest, smallest, arrayLen, ctx, graphSettings);
data.forEach(function (categ) {
//Lines
ctx.beginPath();
ctx.lineJoin = "round";
ctx.strokeStyle = categ.color;
for (let i = 0; i < arrayLen; i++) {
if (categ.values[i] === null) continue;
let scale = bounds.height - ((largest >= 0)?(bounds.bottom - bounds.xAxis):0);
let extreme = (largest<=0)?Math.abs(smallest):Math.abs(largest);
let x = bounds.left + bounds.width / (arrayLen - 1) * i;
let y = (bounds.xAxis - categ.values[i] / extreme * scale);
ctx.lineTo(x, y);
}
ctx.stroke();
ctx.closePath();
//Points
if (graphSettings.display_points)
drawPoints(ctx, bounds, categ.values, categ.name, arrayLen, largest, categ.color);
});
}
function drawAreaChart(canvas, data, graphSettings){
let ctx = canvas.getContext( "2d" );
let bounds = getBounds(canvas, graphSettings.margin);
let largest = getLargest(data);
let smallest = getSmallest(data);
if (smallest < 0)
bounds.xAxis = bounds.bottom - (bounds.height / (((largest<=0)?0:Math.abs(largest)) + Math.abs(smallest)) * Math.abs(smallest));
let arrayLen = data[0].values.length;
drawAxis(bounds, largest, smallest, arrayLen, ctx, graphSettings);
data.forEach(function (categ) {
//Lines
ctx.beginPath();
ctx.lineJoin = "round";
ctx.strokeStyle = categ.color;
let xmax = 0;
for (let i = 0; i < arrayLen; i++) {
if (categ.values[i] === null) continue;
let scale = bounds.height - ((largest >= 0)?(bounds.bottom - bounds.xAxis):0);
let extreme = (largest<=0)?Math.abs(smallest):Math.abs(largest);
let x = bounds.left + bounds.width / (arrayLen - 1) * i;
let y = (bounds.xAxis - categ.values[i] / extreme * scale);
xmax = x;
ctx.lineTo(x, y);
}
ctx.stroke();
ctx.lineTo(xmax, bounds.xAxis);
ctx.lineTo(bounds.left, bounds.xAxis);
ctx.globalAlpha = 0.5;
ctx.fillStyle = categ.color;
ctx.closePath();
ctx.fill();
ctx.globalAlpha = 1;
//Points
if (graphSettings.display_points)
drawPoints(ctx, bounds, categ.values, categ.name, arrayLen, largest, categ.color);
});
}
function drawBarChart(canvas, data, graphSettings) {
let ctx = canvas.getContext( "2d" );
let bounds = getBounds(canvas, graphSettings.margin);
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 15;
ctx.shadowBlur = 4;
let largest = getLargest(data);
let barCount = data.length;
let dataCount = data[0].values.length;
let smallest = getSmallest(data);
if (smallest < 0)
bounds.xAxis = bounds.bottom - (bounds.height / (((largest<=0)?0:Math.abs(largest)) + Math.abs(smallest)) * Math.abs(smallest));
drawAxis(bounds, largest, smallest, dataCount, ctx, graphSettings, false);
let size = bounds.width / dataCount;
let innerSize = size * 0.8;
let bar_width = innerSize * 0.7 / barCount;
for (let i = 0; i < dataCount; i++) {
let num = 0;
data.forEach(function (categ) {
ctx.beginPath();
let value = categ.values[i];
let left = bounds.left + (size * (i + 0.15) + (innerSize * num / barCount));
let scale = bounds.height - ((largest >= 0)?(bounds.bottom - bounds.xAxis):0);
let extreme = (largest<=0)?Math.abs(smallest):Math.abs(largest);
let bar_height = value / extreme * scale;
let top = (bounds.xAxis - categ.values[i] / extreme * scale);
ctx.fillStyle = categ.color;
ctx.fillRect(left, top, bar_width, bar_height);
//x value
if (num === 0){
let text = (i + 1).toString();
if (graphSettings.custom_x_values !== ""){
text = graphSettings.custom_x_values.split(';')[i];
}
ctx.font = "16px Arial";
ctx.fillStyle = "black";
ctx.textAlign = "center";
ctx.fillText(text, bounds.width / dataCount * i + size / 2 + bounds.left, bounds.bottom + 15);
ctx.stroke();
}
num++;
let new_object = new Rect(value, categ.name, left, top, bar_width, bar_height);
objects.push(new_object);
});
}
}
function stackedChart(canvas, data, graphSettings) {
let ctx = canvas.getContext( "2d" );
let bounds = getBounds(canvas, graphSettings.margin);
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 15;
ctx.shadowBlur = 4;
let dataCount = data[0].values.length;
let largest = 0;
for (let i = 0; i < dataCount; i++){
let sum = 0;
data.forEach(function (categ) {
categ.values[i] = Math.abs(categ.values[i]);
sum += categ.values[i];
});
if (sum > largest) largest = sum;
}
let smallest = getSmallest(data);
if (smallest < 0)
bounds.xAxis = bounds.bottom - (bounds.height / (((largest<=0)?0:Math.abs(largest)) + Math.abs(smallest)) * Math.abs(smallest));
drawAxis(bounds, largest, smallest, dataCount, ctx, graphSettings, false);
let size = bounds.width / dataCount;
let bar_width = size * 0.7;
for (let i = 0; i < dataCount; i++) {
let last_top = bounds.xAxis;
let num = 0;
data.forEach(function (categ) {
ctx.beginPath();
let value = categ.values[i];
let bar_height = value / largest * bounds.height;
let left = bounds.left + (size * (i + 0.15));
let top = last_top - bar_height;
ctx.fillStyle = categ.color;
ctx.fillRect(left, top, bar_width, bar_height);
last_top = top;
//x value
if (num === 0){
let text = (i + 1).toString();
if (graphSettings.custom_x_values !== ""){
text = graphSettings.custom_x_values.split(';')[i];
}
ctx.font = "16px Arial";
ctx.fillStyle = "black";
ctx.textAlign = "center";
ctx.fillText(text, bounds.width / dataCount * i + size / 2 + bounds.left, bounds.bottom + 15);
ctx.stroke();
}
num++;
let new_object = new Rect(value, categ.name, left, top, bar_width, bar_height);
objects.push(new_object);
});
}
}
function resizeCanvas(canvas, parent, legendHeight = 0, bgColor) {
if (legendHeight > 0) legendHeight += 3.1;
//set size
canvas.style.width = parent.clientWidth.toString();
canvas.style.height = (parent.clientHeight - legendHeight).toString();
canvas.width = parent.clientWidth;
canvas.height = parent.clientHeight - legendHeight;
//reset canvas color
let ctx = canvas.getContext( "2d" );
if (bgColor == null){
ctx.clearRect(0, 0, canvas.width, canvas.height);
} else {
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
}
function updateLegend(displayLegend, data) {
if (displayLegend){
let legendHTML = "";
data.forEach(function (categ) {
legendHTML += "<div><span style='display:inline-block;width:20px;background-color:" + categ.color + ";'>&nbsp;</span> "+categ.name+"</div>";
});
legend.innerHTML = legendHTML;
legend.style.display = "block";
} else {
legend.style.display = "none";
}
}
function drawChart(graphSettings, data) {
updateLegend(graphSettings.display_legend, data);
resizeCanvas(canvas, parent, legend.offsetHeight, graphSettings.b_color);
objects = [];
//Choose the correct graph
switch (graphSettings.type) {
case "point":
drawPointChart(canvas, data, graphSettings);
break;
case "line":
drawLineChart(canvas, data, graphSettings);
break;
case "pie":
drawPieChart(canvas, data, graphSettings);
break;
case "bar":
drawBarChart(canvas, data, graphSettings);
break;
case "area":
drawAreaChart(canvas, data, graphSettings);
break;
case "stacked":
stackedChart(canvas, data, graphSettings);
break;
}
if (graphSettings.display_title)
drawTitle(canvas, graphSettings);
}

Powered by TurnKey Linux.