645 lines
17 KiB
JavaScript
645 lines
17 KiB
JavaScript
function newChartGrid() {
|
|
|
|
|
|
|
|
var chartGrid = {
|
|
draw: draw
|
|
};
|
|
|
|
return chartGrid;
|
|
|
|
|
|
function draw(container, plotArea) {
|
|
|
|
let periods = [];
|
|
let boxes = [];
|
|
let labels = [];
|
|
|
|
const basePeriod = {
|
|
x: 8 * 60 * 60 * 1000,
|
|
y: plotArea.max.y / 1000 / CHART_ASPECT_RATIO
|
|
};
|
|
|
|
let period = {
|
|
x: basePeriod.x,
|
|
y: basePeriod.y
|
|
};
|
|
|
|
periods.push(period); // 0
|
|
|
|
period = {
|
|
x: basePeriod.x / 8,
|
|
y: basePeriod.y / 10
|
|
};
|
|
|
|
periods.push(period); // 1
|
|
|
|
period = {
|
|
x: basePeriod.x / 8 / 6,
|
|
y: basePeriod.y / 100
|
|
};
|
|
|
|
periods.push(period); // 2
|
|
|
|
period = {
|
|
x: basePeriod.x * 3,
|
|
y: basePeriod.y * 10
|
|
};
|
|
|
|
periods.push(period); // 3
|
|
|
|
period = {
|
|
x: basePeriod.x * 3 * 7,
|
|
y: basePeriod.y * 100
|
|
};
|
|
|
|
periods.push(period); // 4
|
|
|
|
|
|
|
|
|
|
|
|
/* Period #2 Plotting */
|
|
|
|
boxes = [];
|
|
plotGrid(periods[2]);
|
|
labels = [];
|
|
|
|
/* Label Calculation */
|
|
|
|
for (let i = 0; i < boxes.length; i++) {
|
|
|
|
const UPPER_PERIOD = 1;
|
|
|
|
let box = boxes[i];
|
|
let currentValue = box.topLeft;
|
|
|
|
currentValue = unTransformThisPoint(currentValue, container);
|
|
currentValue = plotArea.unInverseTransform(currentValue, container.frame.height);
|
|
|
|
/* We must compare the current value on the current period, against the next period, to know which cell we are in. */
|
|
|
|
let distanceToNextPeriod = Math.trunc((currentValue.x / periods[UPPER_PERIOD].x - Math.trunc(currentValue.x / periods[UPPER_PERIOD].x)) * 1000);
|
|
|
|
switch (distanceToNextPeriod) {
|
|
case 0: {
|
|
labels.push("0");
|
|
break;
|
|
}
|
|
case Math.trunc(1 / 6 * 1000): {
|
|
|
|
}
|
|
labels.push("10");
|
|
break;
|
|
case Math.trunc(2 / 6 * 1000): {
|
|
|
|
}
|
|
labels.push("20");
|
|
break;
|
|
|
|
case Math.trunc(3 / 6 * 1000): {
|
|
|
|
}
|
|
labels.push("30");
|
|
break;
|
|
|
|
case Math.trunc(4 / 6 * 1000): {
|
|
|
|
}
|
|
labels.push("40");
|
|
break;
|
|
|
|
case Math.trunc(5 / 6 * 1000): {
|
|
|
|
}
|
|
labels.push("50");
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printLabels(1, 150);
|
|
|
|
|
|
|
|
/* Period #1 Plotting */
|
|
|
|
boxes = [];
|
|
plotGrid(periods[1]);
|
|
labels = [];
|
|
|
|
/* Label Calculation */
|
|
|
|
for (let i = 0; i < boxes.length; i++) {
|
|
|
|
const UPPER_PERIOD = 3;
|
|
|
|
let box = boxes[i];
|
|
let currentValue = box.topLeft;
|
|
|
|
currentValue = unTransformThisPoint(currentValue, container);
|
|
currentValue = plotArea.unInverseTransform(currentValue, container.frame.height);
|
|
|
|
/* We must compare the current value on the current period, against the next period, to know which cell we are in. */
|
|
|
|
let distanceToNextPeriod = Math.trunc((currentValue.x / periods[UPPER_PERIOD].x - Math.trunc(currentValue.x / periods[UPPER_PERIOD].x)) * 1000);
|
|
|
|
switch (distanceToNextPeriod) {
|
|
case 0: {
|
|
labels.push("0 Hs");
|
|
break;
|
|
}
|
|
case Math.trunc(1 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("1 Hs");
|
|
break;
|
|
case Math.trunc(2 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("2 Hs");
|
|
break;
|
|
|
|
case Math.trunc(3 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("3 Hs");
|
|
break;
|
|
|
|
case Math.trunc(4 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("4 Hs");
|
|
break;
|
|
|
|
case Math.trunc(5 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("5 Hs");
|
|
break;
|
|
|
|
case Math.trunc(6 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("6 Hs");
|
|
break;
|
|
|
|
case Math.trunc(7 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("7 Hs");
|
|
break;
|
|
|
|
case Math.trunc(8 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("8 Hs");
|
|
break;
|
|
|
|
case Math.trunc(9 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("9 Hs");
|
|
break;
|
|
|
|
case Math.trunc(10 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("10 Hs");
|
|
break;
|
|
|
|
case Math.trunc(11 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("11 Hs");
|
|
break;
|
|
|
|
case Math.trunc(12 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("12 Hs");
|
|
break;
|
|
|
|
case Math.trunc(13 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("13 Hs");
|
|
break;
|
|
|
|
case Math.trunc(14 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("14 Hs");
|
|
break;
|
|
|
|
case Math.trunc(15 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("15 Hs");
|
|
break;
|
|
|
|
case Math.trunc(16 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("16 Hs");
|
|
break;
|
|
|
|
case Math.trunc(17 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("17 Hs");
|
|
break;
|
|
|
|
case Math.trunc(18 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("18 Hs");
|
|
break;
|
|
|
|
case Math.trunc(19 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("19 Hs");
|
|
break;
|
|
|
|
case Math.trunc(20 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("20 Hs");
|
|
break;
|
|
|
|
case Math.trunc(21 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("21 Hs");
|
|
break;
|
|
|
|
case Math.trunc(22 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("22 Hs");
|
|
break;
|
|
|
|
case Math.trunc(23 / 24 * 1000): {
|
|
|
|
}
|
|
labels.push("23 Hs");
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
printLabels(5, 250);
|
|
|
|
|
|
|
|
/* Period #0 Plotting */
|
|
|
|
boxes = [];
|
|
plotGrid(periods[0]);
|
|
labels = [];
|
|
|
|
/* Label Calculation */
|
|
|
|
for (let i = 0; i < boxes.length; i++) {
|
|
|
|
const UPPER_PERIOD = 3;
|
|
|
|
let box = boxes[i];
|
|
let currentValue = box.topLeft;
|
|
|
|
currentValue = unTransformThisPoint(currentValue, container);
|
|
currentValue = plotArea.unInverseTransform(currentValue, container.frame.height);
|
|
|
|
/* We must compare the current value on the current period, against the next period, to know which cell we are in. */
|
|
|
|
let distanceToNextPeriod = Math.trunc((currentValue.x / periods[UPPER_PERIOD].x - Math.trunc(currentValue.x / periods[UPPER_PERIOD].x)) * 1000);
|
|
|
|
switch (distanceToNextPeriod) {
|
|
case 0: {
|
|
labels.push("Morning");
|
|
break;
|
|
}
|
|
case Math.trunc(1 / 3 * 1000): {
|
|
|
|
}
|
|
labels.push("Afternoon");
|
|
break;
|
|
case Math.trunc(2 / 3 * 1000): {
|
|
|
|
}
|
|
labels.push("Evening");
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
printLabels(15, 50);
|
|
|
|
|
|
|
|
|
|
|
|
/* Period #3 Plotting */
|
|
|
|
boxes = [];
|
|
plotGrid(periods[3]);
|
|
labels = [];
|
|
|
|
/* Label Calculation */
|
|
|
|
for (let i = 0; i < boxes.length; i++) {
|
|
|
|
const UPPER_PERIOD = 1;
|
|
|
|
let box = boxes[i];
|
|
let currentValue = box.topLeft;
|
|
|
|
currentValue = unTransformThisPoint(currentValue, container);
|
|
currentValue = plotArea.unInverseTransform(currentValue, container.frame.height);
|
|
|
|
let date = new Date(currentValue.x);
|
|
|
|
labels.push(date.toDateString());
|
|
|
|
}
|
|
|
|
printLabels(100, 2250);
|
|
|
|
|
|
|
|
/* Period #4 Plotting */
|
|
|
|
boxes = [];
|
|
plotGrid(periods[4]);
|
|
labels = [];
|
|
|
|
/* Label Calculation */
|
|
|
|
for (let i = 0; i < boxes.length; i++) {
|
|
|
|
const UPPER_PERIOD = 1;
|
|
|
|
let box = boxes[i];
|
|
let currentValue = box.topLeft;
|
|
|
|
currentValue = unTransformThisPoint(currentValue, container);
|
|
currentValue = plotArea.unInverseTransform(currentValue, container.frame.height);
|
|
|
|
let date = new Date(currentValue.x);
|
|
|
|
var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
|
|
labels.push(monthNames[date.getMonth()] + ' ' + date.getFullYear());
|
|
|
|
}
|
|
|
|
printLabels(1000, 10000);
|
|
|
|
|
|
|
|
function plotGrid(period) {
|
|
|
|
|
|
let periodWidth = period.x;
|
|
let periodHeight = period.y;
|
|
|
|
let visiblePoint1 = {
|
|
x: viewPort.visibleArea.topLeft.x,
|
|
y: viewPort.visibleArea.bottomLeft.y
|
|
};
|
|
|
|
visiblePoint1 = unTransformThisPoint(visiblePoint1, container);
|
|
visiblePoint1 = plotArea.unInverseTransform(visiblePoint1, container.frame.height);
|
|
|
|
let visiblePoint2 = {
|
|
x: viewPort.visibleArea.bottomRight.x,
|
|
y: viewPort.visibleArea.topRight.y
|
|
};
|
|
|
|
visiblePoint2 = unTransformThisPoint(visiblePoint2, container);
|
|
visiblePoint2 = plotArea.unInverseTransform(visiblePoint2, container.frame.height);
|
|
|
|
startingPoint = {
|
|
x: Math.trunc(visiblePoint1.x / periodWidth) * periodWidth,
|
|
y: Math.round(Math.trunc(visiblePoint1.y / periodHeight) * periodHeight * 100000) / 100000
|
|
};
|
|
|
|
endingPoint = {
|
|
x: Math.trunc(visiblePoint2.x / periodWidth) * periodWidth,
|
|
y: Math.trunc(visiblePoint2.y / periodHeight) * periodHeight
|
|
};
|
|
|
|
let lineWidth;
|
|
|
|
if (viewPort.zoomLevel > 0) {
|
|
|
|
lineWidth = period.x / basePeriod.x + period.x / basePeriod.x * viewPort.zoomLevel / 20;
|
|
|
|
} else {
|
|
|
|
lineWidth = period.x / basePeriod.x + period.x / basePeriod.x * viewPort.zoomLevel / 80;
|
|
|
|
}
|
|
|
|
|
|
if (lineWidth < 0.7) {
|
|
return;
|
|
}
|
|
|
|
if (lineWidth > 1.4) {
|
|
lineWidth = 1.4;
|
|
}
|
|
|
|
browserCanvasContext.beginPath();
|
|
|
|
let y1;
|
|
let y2;
|
|
|
|
let lastLabelPosition = {
|
|
x: 0,
|
|
y: 0
|
|
};
|
|
|
|
for (var j = startingPoint.y; j <= endingPoint.y + periodHeight; j = j + periodHeight) {
|
|
|
|
if (j >= 0 && j <= plotArea.max.y) {
|
|
|
|
let point1 = {
|
|
x: visiblePoint1.x,
|
|
y: j
|
|
};
|
|
|
|
let point2 = {
|
|
x: visiblePoint2.x,
|
|
y: j
|
|
};
|
|
|
|
point1 = plotArea.inverseTransform(point1, container.frame.height);
|
|
point2 = plotArea.inverseTransform(point2, container.frame.height);
|
|
|
|
point1 = transformThisPoint(point1, container);
|
|
point2 = transformThisPoint(point2, container);
|
|
|
|
if (point1.y >= viewPort.visibleArea.topLeft.y) {
|
|
|
|
/* Here we keep the last two positive valued of y */
|
|
|
|
y2 = y1;
|
|
y1 = point1.y;
|
|
|
|
}
|
|
|
|
point1 = viewPort.fitIntoVisibleArea(point1);
|
|
point2 = viewPort.fitIntoVisibleArea(point2);
|
|
|
|
browserCanvasContext.moveTo(point1.x, point1.y);
|
|
browserCanvasContext.lineTo(point2.x, point2.y);
|
|
|
|
/* Now we put the scale on the right side of the chart */
|
|
|
|
let fontSize = 10;
|
|
|
|
let label = ' ' + Math.round(j * 1000000) / 1000000;
|
|
|
|
let labelPoint = {
|
|
x: viewPort.visibleArea.bottomRight.x + 1,
|
|
y: point1.y + fontSize / 2 * FONT_ASPECT_RATIO
|
|
};
|
|
|
|
if (Math.abs(lastLabelPosition.y - labelPoint.y > fontSize || lastLabelPosition.y === 0)) {
|
|
|
|
if (labelPoint.y > viewPort.visibleArea.topLeft.y + fontSize && labelPoint.y < viewPort.visibleArea.bottomRight.y - fontSize) {
|
|
|
|
browserCanvasContext.font = fontSize + 'px Courier New';
|
|
browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)';
|
|
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
lastLabelPosition.x = labelPoint.x;
|
|
lastLabelPosition.y = labelPoint.y;
|
|
}
|
|
}
|
|
|
|
let x1;
|
|
let x2;
|
|
|
|
for (var i = startingPoint.x; i <= endingPoint.x + periodWidth; i = i + periodWidth) {
|
|
|
|
let point3 = {
|
|
x: i,
|
|
y: 0
|
|
};
|
|
|
|
let point4 = {
|
|
x: i,
|
|
y: plotArea.max.y
|
|
};
|
|
|
|
point3 = plotArea.inverseTransform(point3, container.frame.height);
|
|
point4 = plotArea.inverseTransform(point4, container.frame.height);
|
|
|
|
point3 = transformThisPoint(point3, container);
|
|
point4 = transformThisPoint(point4, container);
|
|
|
|
/* Here we keep the last two positive valued of y */
|
|
|
|
x2 = x1;
|
|
x1 = point3.x;
|
|
|
|
if (x2 !== undefined) {
|
|
|
|
let topLeft = {
|
|
x: x2,
|
|
y: y1
|
|
};
|
|
|
|
let bottomRight = {
|
|
x: x1,
|
|
y: y2
|
|
};
|
|
|
|
let box = {
|
|
topLeft: topLeft,
|
|
bottomRight: bottomRight
|
|
};
|
|
boxes.push(box);
|
|
}
|
|
|
|
|
|
/* Painting the lines */
|
|
|
|
point3 = viewPort.fitIntoVisibleArea(point3);
|
|
point4 = viewPort.fitIntoVisibleArea(point4);
|
|
|
|
browserCanvasContext.moveTo(point3.x, point3.y);
|
|
browserCanvasContext.lineTo(point4.x, point4.y);
|
|
|
|
|
|
|
|
}
|
|
|
|
browserCanvasContext.closePath();
|
|
browserCanvasContext.strokeStyle = 'rgba(0, 37, 224, 0.' + Math.trunc(lineWidth + 1) + ')';
|
|
|
|
browserCanvasContext.lineWidth = lineWidth;
|
|
|
|
browserCanvasContext.stroke();
|
|
|
|
}
|
|
|
|
|
|
function printLabels(baseZoomFilter, maxZoom) {
|
|
|
|
let zoomFilter = viewPort.zoomFontSize(baseZoomFilter);
|
|
|
|
if (zoomFilter < 7 || zoomFilter > maxZoom) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < boxes.length; i++) {
|
|
|
|
let label = labels[i];
|
|
|
|
if (label === undefined) {
|
|
label = "undefined";
|
|
}
|
|
|
|
let fontSize = 10;
|
|
|
|
let labelPoint = {
|
|
x: boxes[i].topLeft.x - label.length / 2 * fontSize * FONT_ASPECT_RATIO,
|
|
y: viewPort.visibleArea.topLeft.y - fontSize / 2 + 1
|
|
};
|
|
|
|
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO;
|
|
|
|
browserCanvasContext.beginPath();
|
|
|
|
browserCanvasContext.rect(labelPoint.x, labelPoint.y - fontSize, xOffset * 2, fontSize );
|
|
browserCanvasContext.fillStyle = 'rgba(255, 255, 255, 1)';
|
|
browserCanvasContext.fill();
|
|
|
|
browserCanvasContext.closePath();
|
|
|
|
browserCanvasContext.font = fontSize + 'px Courier New';
|
|
browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)';
|
|
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y);
|
|
}
|
|
|
|
browserCanvasContext.fillStyle = 'rgba(255, 255, 255, 1)';
|
|
browserCanvasContext.beginPath();
|
|
browserCanvasContext.rect(0, 0, viewPort.visibleArea.topLeft.x, viewPort.visibleArea.topLeft.y);
|
|
browserCanvasContext.rect(viewPort.visibleArea.topRight.x, 0, 500, viewPort.visibleArea.topLeft.y);
|
|
browserCanvasContext.closePath();
|
|
browserCanvasContext.fill();
|
|
}
|
|
}
|
|
|
|
|
|
}
|