diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 7853e9e..ebabf06 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -17,6 +17,9 @@ framework: php_errors: log: true + profiler: + collect: false + when@test: framework: test: true diff --git a/public/scripts/chart_loader.js b/public/scripts/chart_loader.js index 2a02abd..14e5d7e 100644 --- a/public/scripts/chart_loader.js +++ b/public/scripts/chart_loader.js @@ -1,6 +1,7 @@ class ChartLoader { - constructor(canvas, detectionCanvas, parent, legend, dataDiv) { + constructor(canvas, effectCanvas, detectionCanvas, parent, legend, dataDiv) { this.canvas = canvas + this.effectCanvas = effectCanvas this.detectionCanvas = detectionCanvas this.parent = parent this.legend = legend @@ -38,33 +39,80 @@ class ChartLoader { x: e.clientX, y: e.clientY } - - var ctx = this.detectionCanvas.getContext('2d'); - var pixelData = ctx.getImageData(pos.x, pos.y, 1, 1).data; - if (pixelData[3] === 255) { - let index = (pixelData[0] * 256 + pixelData[1]) * 256 + pixelData[2] - let obj = newChart.objects[index] + let ctx = this.detectionCanvas.getContext('2d'); - let divHitBox = this.dataDiv - divHitBox.style.display = "block" + // Get neigboring pixels + let imageData = Array.from(ctx.getImageData(pos.x - 2, pos.y - 2, 5, 5).data) + + //console.log(imageData) + + // Convert into indexes + let indexes = [] + while (imageData.length > 0) { + let pixel = imageData.splice(0, 4) + + // only if alpha is 100% + if (pixel[3] === 255) { + let index = (pixel[0] * 256 + pixel[1]) * 256 + pixel[2] + indexes.push(index) + } + } + + function mode(array) + { + if(array.length == 0) + return null; + var modeMap = {}; + var maxEl = array[0], maxCount = 1; + for(var i = 0; i < array.length; i++) + { + var el = array[i]; + if(modeMap[el] == null) + modeMap[el] = 1; + else + modeMap[el]++; + if(modeMap[el] > maxCount) + { + maxEl = el; + maxCount = modeMap[el]; + } + } + return maxEl; + } + + // To avoid edge cases because of anti aliasing + let mostCommonIndex = mode(indexes) + + + if (mostCommonIndex !== null) { + let obj = newChart.objects[mostCommonIndex] + + // Effect + let effectCtx = this.effectCanvas.getContext("2d") + effectCtx.clearRect(0, 0, effectCanvas.width, effectCanvas.height) + this.effectCanvas.style.opacity = 1 + newChart.drawEffect(effectCtx, [obj]) + + this.dataDiv.style.display = "block" // make the info fit into the chart div - if (pos.x + divHitBox.clientWidth <= this.parent.clientWidth - 2) - divHitBox.style.left = pos.x + "px" + if (pos.x + this.dataDiv.clientWidth <= this.parent.clientWidth - 2) + this.dataDiv.style.left = pos.x + "px" else - this.dataDiv.style.left = pos.x - divHitBox.clientWidth + "px" + this.dataDiv.style.left = pos.x - this.dataDiv.clientWidth + "px" - if (pos.y + divHitBox.clientHeight <= parent.clientHeight - 2) + if (pos.y + this.dataDiv.clientHeight <= parent.clientHeight - 2) this.dataDiv.style.top = pos.y + "px" else - this.dataDiv.style.top = pos.y - divHitBox.clientHeight + "px" + this.dataDiv.style.top = pos.y - this.dataDiv.clientHeight + "px" - this.dataDiv.style.display = "block" + this.dataDiv.style.display = "block" this.dataDiv.innerHTML = "" + obj.name + "

" + obj.value + "

" } else { this.dataDiv.style.display = "none" + this.effectCanvas.style.opacity = 0 } }) @@ -73,22 +121,22 @@ class ChartLoader { newChart.resizeCanvas(this.parent, this.legend.offsetHeight) newChart.draw() - this.detectionCanvas.width = this.canvas.width - this.detectionCanvas.height = this.canvas.height - this.chart.drawDetectionMap(this.detectionCanvas.getContext("2d")) + setTimeout(() => { + this.effectCanvas.width = this.canvas.width + this.effectCanvas.height = this.canvas.height + this.detectionCanvas.width = this.canvas.width + this.detectionCanvas.height = this.canvas.height + this.chart.drawDetectionMap(this.detectionCanvas.getContext("2d")) + }, 0) }); } drawChart(graphSettings, data) { - //objects = [] this.chart = new Chart(this.canvas, data, graphSettings) - this.chart.updateLegend(graphSettings.displayLegend, this.legend) + this.chart.updateLegend(graphSettings.displayLegend, this.legend) this.chart.resizeCanvas(this.parent, this.legend.offsetHeight) - this.detectionCanvas.width = this.canvas.width - this.detectionCanvas.height = this.canvas.height - //Choose the correct graph switch (graphSettings.type) { case "point": @@ -120,8 +168,20 @@ class ChartLoader { break } + console.time("1") this.chart.draw() - this.chart.drawDetectionMap(this.detectionCanvas.getContext("2d")) + console.timeEnd("1") + + setTimeout(() => { + console.time("2") + this.effectCanvas.width = this.canvas.width + this.effectCanvas.height = this.canvas.height + this.detectionCanvas.width = this.canvas.width + this.detectionCanvas.height = this.canvas.height + this.chart.drawDetectionMap(this.detectionCanvas.getContext("2d")) + console.timeEnd("2") + }, 0); { + } this.addListener(this.chart) } diff --git a/public/scripts/charts/bar_chart.js b/public/scripts/charts/bar_chart.js index e6074f9..ac9a4fd 100644 --- a/public/scripts/charts/bar_chart.js +++ b/public/scripts/charts/bar_chart.js @@ -34,15 +34,18 @@ class BarChart extends Chart { /*if (this.settings.custom_x_values !== "") text = this.settings.custom_x_values.split(';')[i]*/ + this.ctx.beginPath() this.ctx.font = "16px Arial" this.ctx.fillStyle = "black" this.ctx.textAlign = "center" this.ctx.fillText(text, this.bounds.width / this.dataLen * i + size / 2 + this.bounds.left, this.bounds.bottom + 15) this.ctx.stroke() + this.ctx.closePath() } num++ this.ctx.fillStyle = categ.color + this.ctx.lineWidth = 0 let new_object = new Rectangle(this.ctx, value, categ.col_name, left, top, bar_width, bar_height) new_object.draw() this.objects.push(new_object) diff --git a/public/scripts/charts/chart.js b/public/scripts/charts/chart.js index cff1665..293025b 100644 --- a/public/scripts/charts/chart.js +++ b/public/scripts/charts/chart.js @@ -10,8 +10,6 @@ class Shape { this.index = Shape.globalIndex++ } - checkHit() { return false } - draw() { } } @@ -22,15 +20,11 @@ class Rectangle extends Shape { this.h = h } - checkHit(mouseX, mouseY) { - return (mouseX >= this.x && mouseX <= this.x + this.w - && mouseY >= this.y && mouseY <= this.y + this.h) - } - draw(ctx = this.ctx) { ctx.beginPath() - ctx.fillRect(this.x, this.y, this.w, this.h) - ctx.closePath() + ctx.rect(this.x, this.y, this.w, this.h) + ctx.fill() + //ctx.closePath() } } @@ -40,16 +34,11 @@ class Circle extends Shape { this.r = r } - checkHit(mouseX, mouseY) { - return Math.pow((mouseX - this.x), 2) + Math.pow((mouseY - this.y), 2) <= Math.pow(this.r, 2); - } - draw(ctx = this.ctx) { ctx.beginPath() ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI) ctx.fill() - ctx.stroke() - ctx.closePath() + //ctx.closePath() } } @@ -60,26 +49,13 @@ class PieSlice extends Circle { this.eAngle = eAngle } - checkHit(mouseX, mouseY) { - if (super.checkHit(mouseX, mouseY)) { - var dy = mouseY - this.y - var dx = mouseX - this.x - var theta = Math.atan2(dy, dx) // range (-PI, PI] - - if (theta < 0) - theta += 2 * Math.PI - - return (theta > this.sAngle && theta < this.eAngle) - } - return false - } - draw(ctx = this.ctx) { ctx.beginPath() ctx.moveTo(this.x, this.y) ctx.arc(this.x, this.y, this.r, this.sAngle, this.eAngle) + ctx.lineTo(this.x, this.y) ctx.fill() - ctx.closePath() + //ctx.closePath() } } @@ -91,11 +67,12 @@ class DonutSlice extends PieSlice { draw(ctx = this.ctx) { ctx.beginPath() - ctx.moveTo(this.x, this.y) + // move to start of the slice using polar coordinates + ctx.moveTo(this.x + this.r2 * Math.cos(this.sAngle), this.y + this.r2 * Math.sin(this.sAngle)) ctx.arc(this.x, this.y, this.r, this.sAngle, this.eAngle, false) ctx.arc(this.x, this.y, this.r2, this.eAngle, this.sAngle, true) ctx.fill() - ctx.closePath() + //ctx.closePath() } } @@ -166,13 +143,13 @@ class Chart { return result } - checkHit(pos) { + /*checkHit(pos) { for (let i = 0; i < this.objects.length; i++) if (this.objects[i].checkHit(pos.x, pos.y)) return this.objects[i] return null - } + }*/ drawTitle() { let x = this.canvas.width / 2 @@ -221,16 +198,40 @@ class Chart { } drawDetectionMap(ctx) { + ctx.moveTo(0.5, 0.5) + ctx.lineWidth = 3 + this.objects.forEach(object => { let color = "#" + object.index.toString(16).padStart(6, '0') ctx.fillStyle = color ctx.strokeStyle = color - ctx.lineWidth = 3 object.draw(ctx) }) } + drawEffect(ctx, objects) { + objects.forEach(object => { + ctx.globalCompositeOperation = "source-over" + ctx.fillStyle = 'rgba(0,0,0,1)' + ctx.strokeStyle = 'rgba(0,0,0,0)' + object.draw(ctx) + ctx.stroke() + + ctx.globalCompositeOperation = "source-out" + ctx.lineWidth = 3 + ctx.fillStyle = 'rgba(0,0,0,0)' + ctx.strokeStyle = 'rgba(0,0,0,0.7)' + ctx.stroke() + + ctx.globalCompositeOperation = "source-over" + ctx.fillStyle = 'rgba(255,255,255,0.2)' + ctx.strokeStyle = 'rgba(0,0,0,0)' + object.draw(ctx) + ctx.stroke() + }) + } + drawAxis(displayAxisValues = this.settings.displayAxisValues) { this.ctx.font = this.settings.labelFont.size + "px " + this.settings.labelFont.font if (this.settings.yStep <= 0 || !this.settings.yStep) this.settings.yStep = 1 diff --git a/public/scripts/charts/stacked_chart.js b/public/scripts/charts/stacked_chart.js index 8960820..ce49e3b 100644 --- a/public/scripts/charts/stacked_chart.js +++ b/public/scripts/charts/stacked_chart.js @@ -41,11 +41,13 @@ class StackedChart extends Chart { /*if (this.settings.custom_x_values !== "") text = this.settings.custom_x_values.split(';')[i]*/ + this.ctx.beginPath() this.ctx.font = "16px Arial" this.ctx.fillStyle = "black" this.ctx.textAlign = "center" this.ctx.fillText(text, this.bounds.width / this.dataLen * i + size / 2 + this.bounds.left, this.bounds.bottom + 15) this.ctx.stroke() + this.ctx.closePath() } num++ diff --git a/public/styles/chart_style.css b/public/styles/chart_style.css index ab136ac..7678a4e 100644 --- a/public/styles/chart_style.css +++ b/public/styles/chart_style.css @@ -9,6 +9,7 @@ body { } #chartCanvas, +#effectCanvas, #detectionCanvas { grid-column: 1; grid-row: 1; @@ -58,6 +59,8 @@ html { #detectionCanvas { opacity: 0; - image-rendering: pixelated; - image-rendering: crisp-edges; +} + +#effectCanvas { + transition: opacity 0.3s; } \ No newline at end of file diff --git a/templates/chart.html.twig b/templates/chart.html.twig index 5638ec5..87513fa 100644 --- a/templates/chart.html.twig +++ b/templates/chart.html.twig @@ -27,12 +27,13 @@ document.addEventListener("DOMContentLoaded", function(event){ let canvas = document.getElementById("chartCanvas") + let effectCanvas = document.getElementById("effectCanvas") let detectionCanvas = document.getElementById("detectionCanvas") let parent = document.body let legend = document.getElementById("graphLegend") let dataDiv = document.getElementById("dataDiv") - let chartLoader = new ChartLoader(canvas, detectionCanvas, parent, legend, dataDiv) + let chartLoader = new ChartLoader(canvas, effectCanvas, detectionCanvas, parent, legend, dataDiv) chartLoader.loadData({{ code }}) }) @@ -40,6 +41,7 @@ {% block body %} +