PHP Classes

File: vendors/chart.js/src/scales/scale.radialLinear.js

Recommend this page to a friend!
  Classes of Jorge Castro   Gentelella BladeOne   vendors/chart.js/src/scales/scale.radialLinear.js   Download  
File: vendors/chart.js/src/scales/scale.radialLinear.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Gentelella BladeOne
Render templates using Bootstrap for presentation
Author: By
Last change:
Date: 2 years ago
Size: 17,274 bytes
 

Contents

Class file image Download
"use strict"; module.exports = function(Chart) { var helpers = Chart.helpers; var globalDefaults = Chart.defaults.global; var defaultConfig = { display: true, //Boolean - Whether to animate scaling the chart from the centre animate: true, lineArc: false, position: "chartArea", angleLines: { display: true, color: "rgba(0, 0, 0, 0.1)", lineWidth: 1 }, // label settings ticks: { //Boolean - Show a backdrop to the scale label showLabelBackdrop: true, //String - The colour of the label backdrop backdropColor: "rgba(255,255,255,0.75)", //Number - The backdrop padding above & below the label in pixels backdropPaddingY: 2, //Number - The backdrop padding to the side of the label in pixels backdropPaddingX: 2 }, pointLabels: { //Number - Point label font size in pixels fontSize: 10, //Function - Used to convert point labels callback: function(label) { return label; } } }; var LinearRadialScale = Chart.Scale.extend({ getValueCount: function() { return this.chart.data.labels.length; }, setDimensions: function() { var options = this.options; // Set the unconstrained dimension before label rotation this.width = this.maxWidth; this.height = this.maxHeight; this.xCenter = Math.round(this.width / 2); this.yCenter = Math.round(this.height / 2); var minSize = helpers.min([this.height, this.width]); var tickFontSize = helpers.getValueOrDefault(options.ticks.fontSize, globalDefaults.defaultFontSize); this.drawingArea = (options.display) ? (minSize / 2) - (tickFontSize / 2 + options.ticks.backdropPaddingY) : (minSize / 2); }, determineDataLimits: function() { this.min = null; this.max = null; helpers.each(this.chart.data.datasets, function(dataset, datasetIndex) { if (this.chart.isDatasetVisible(datasetIndex)) { var meta = this.chart.getDatasetMeta(datasetIndex); helpers.each(dataset.data, function(rawValue, index) { var value = +this.getRightValue(rawValue); if (isNaN(value) || meta.data[index].hidden) { return; } if (this.min === null) { this.min = value; } else if (value < this.min) { this.min = value; } if (this.max === null) { this.max = value; } else if (value > this.max) { this.max = value; } }, this); } }, this); // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, // do nothing since that would make the chart weird. If the user really wants a weird chart // axis, they can manually override it if (this.options.ticks.beginAtZero) { var minSign = helpers.sign(this.min); var maxSign = helpers.sign(this.max); if (minSign < 0 && maxSign < 0) { // move the top up to 0 this.max = 0; } else if (minSign > 0 && maxSign > 0) { // move the botttom down to 0 this.min = 0; } } if (this.options.ticks.min !== undefined) { this.min = this.options.ticks.min; } else if (this.options.ticks.suggestedMin !== undefined) { this.min = Math.min(this.min, this.options.ticks.suggestedMin); } if (this.options.ticks.max !== undefined) { this.max = this.options.ticks.max; } else if (this.options.ticks.suggestedMax !== undefined) { this.max = Math.max(this.max, this.options.ticks.suggestedMax); } if (this.min === this.max) { this.min--; this.max++; } }, buildTicks: function() { this.ticks = []; // Figure out what the max number of ticks we can support it is based on the size of // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on // the graph var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, globalDefaults.defaultFontSize); var maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); maxTicks = Math.max(2, maxTicks); // Make sure we always have at least 2 ticks // To get a "nice" value for the tick spacing, we will use the appropriately named // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks // for details. var niceRange = helpers.niceNum(this.max - this.min, false); var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true); var niceMin = Math.floor(this.min / spacing) * spacing; var niceMax = Math.ceil(this.max / spacing) * spacing; var numSpaces = Math.ceil((niceMax - niceMin) / spacing); // Put the values into the ticks array this.ticks.push(this.options.ticks.min !== undefined ? this.options.ticks.min : niceMin); for (var j = 1; j < numSpaces; ++j) { this.ticks.push(niceMin + (j * spacing)); } this.ticks.push(this.options.ticks.max !== undefined ? this.options.ticks.max : niceMax); // At this point, we need to update our max and min given the tick values since we have expanded the // range of the scale this.max = helpers.max(this.ticks); this.min = helpers.min(this.ticks); if (this.options.ticks.reverse) { this.ticks.reverse(); this.start = this.max; this.end = this.min; } else { this.start = this.min; this.end = this.max; } this.zeroLineIndex = this.ticks.indexOf(0); }, convertTicksToLabels: function() { Chart.Scale.prototype.convertTicksToLabels.call(this); // Point labels this.pointLabels = this.chart.data.labels.map(this.options.pointLabels.callback, this); }, getLabelForIndex: function(index, datasetIndex) { return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); }, fit: function() { /* * Right, this is really confusing and there is a lot of maths going on here * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 * * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif * * Solution: * * We assume the radius of the polygon is half the size of the canvas at first * at each index we check if the text overlaps. * * Where it does, we store that angle and that index. * * After finding the largest index and angle we calculate how much we need to remove * from the shape radius to move the point inwards by that x. * * We average the left and right distances to get the maximum shape radius that can fit in the box * along with labels. * * Once we have that, we can find the centre point for the chart, by taking the x text protrusion * on each side, removing that from the size, halving it and adding the left x protrusion width. * * This will mean we have a shape fitted to the canvas, as large as it can be with the labels * and position it in the most space efficient manner * * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif */ var pointLabels = this.options.pointLabels; var pointLabelFontSize = helpers.getValueOrDefault(pointLabels.fontSize, globalDefaults.defaultFontSize); var pointLabeFontStyle = helpers.getValueOrDefault(pointLabels.fontStyle, globalDefaults.defaultFontStyle); var pointLabeFontFamily = helpers.getValueOrDefault(pointLabels.fontFamily, globalDefaults.defaultFontFamily); var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily); // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points var largestPossibleRadius = helpers.min([(this.height / 2 - pointLabelFontSize - 5), this.width / 2]), pointPosition, i, textWidth, halfTextWidth, furthestRight = this.width, furthestRightIndex, furthestRightAngle, furthestLeft = 0, furthestLeftIndex, furthestLeftAngle, xProtrusionLeft, xProtrusionRight, radiusReductionRight, radiusReductionLeft, maxWidthRadius; this.ctx.font = pointLabeFont; for (i = 0; i < this.getValueCount(); i++) { // 5px to space the text slightly out - similar to what we do in the draw function. pointPosition = this.getPointPosition(i, largestPossibleRadius); textWidth = this.ctx.measureText(this.pointLabels[i] ? this.pointLabels[i] : '').width + 5; if (i === 0 || i === this.getValueCount() / 2) { // If we're at index zero, or exactly the middle, we're at exactly the top/bottom // of the radar chart, so text will be aligned centrally, so we'll half it and compare // w/left and right text sizes halfTextWidth = textWidth / 2; if (pointPosition.x + halfTextWidth > furthestRight) { furthestRight = pointPosition.x + halfTextWidth; furthestRightIndex = i; } if (pointPosition.x - halfTextWidth < furthestLeft) { furthestLeft = pointPosition.x - halfTextWidth; furthestLeftIndex = i; } } else if (i < this.getValueCount() / 2) { // Less than half the values means we'll left align the text if (pointPosition.x + textWidth > furthestRight) { furthestRight = pointPosition.x + textWidth; furthestRightIndex = i; } } else if (i > this.getValueCount() / 2) { // More than half the values means we'll right align the text if (pointPosition.x - textWidth < furthestLeft) { furthestLeft = pointPosition.x - textWidth; furthestLeftIndex = i; } } } xProtrusionLeft = furthestLeft; xProtrusionRight = Math.ceil(furthestRight - this.width); furthestRightAngle = this.getIndexAngle(furthestRightIndex); furthestLeftAngle = this.getIndexAngle(furthestLeftIndex); radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2); radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2); // Ensure we actually need to reduce the size of the chart radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0; radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0; this.drawingArea = Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2); this.setCenterPoint(radiusReductionLeft, radiusReductionRight); }, setCenterPoint: function(leftMovement, rightMovement) { var maxRight = this.width - rightMovement - this.drawingArea, maxLeft = leftMovement + this.drawingArea; this.xCenter = Math.round(((maxLeft + maxRight) / 2) + this.left); // Always vertically in the centre as the text height doesn't change this.yCenter = Math.round((this.height / 2) + this.top); }, getIndexAngle: function(index) { var angleMultiplier = (Math.PI * 2) / this.getValueCount(); // Start from the top instead of right, so remove a quarter of the circle return index * angleMultiplier - (Math.PI / 2); }, getDistanceFromCenterForValue: function(value) { if (value === null) { return 0; // null always in center } // Take into account half font size + the yPadding of the top value var scalingFactor = this.drawingArea / (this.max - this.min); if (this.options.reverse) { return (this.max - value) * scalingFactor; } else { return (value - this.min) * scalingFactor; } }, getPointPosition: function(index, distanceFromCenter) { var thisAngle = this.getIndexAngle(index); return { x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + this.xCenter, y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + this.yCenter }; }, getPointPositionForValue: function(index, value) { return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); }, getBasePosition: function() { var me = this; var min = me.min; var max = me.max; return me.getPointPositionForValue(0, me.beginAtZero? 0: min < 0 && max < 0? max : min > 0 && max > 0? min : 0); }, draw: function() { if (this.options.display) { var ctx = this.ctx; helpers.each(this.ticks, function(label, index) { // Don't draw a centre value (if it is minimum) if (index > 0 || this.options.reverse) { var yCenterOffset = this.getDistanceFromCenterForValue(this.ticks[index]); var yHeight = this.yCenter - yCenterOffset; // Draw circular lines around the scale if (this.options.gridLines.display) { ctx.strokeStyle = this.options.gridLines.color; ctx.lineWidth = this.options.gridLines.lineWidth; if (this.options.lineArc) { // Draw circular arcs between the points ctx.beginPath(); ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI * 2); ctx.closePath(); ctx.stroke(); } else { // Draw straight lines connecting each index ctx.beginPath(); for (var i = 0; i < this.getValueCount(); i++) { var pointPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.ticks[index])); if (i === 0) { ctx.moveTo(pointPosition.x, pointPosition.y); } else { ctx.lineTo(pointPosition.x, pointPosition.y); } } ctx.closePath(); ctx.stroke(); } } if (this.options.ticks.display) { var tickFontColor = helpers.getValueOrDefault(this.options.ticks.fontColor, globalDefaults.defaultFontColor); var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, globalDefaults.defaultFontSize); var tickFontStyle = helpers.getValueOrDefault(this.options.ticks.fontStyle, globalDefaults.defaultFontStyle); var tickFontFamily = helpers.getValueOrDefault(this.options.ticks.fontFamily, globalDefaults.defaultFontFamily); var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); ctx.font = tickLabelFont; if (this.options.ticks.showLabelBackdrop) { var labelWidth = ctx.measureText(label).width; ctx.fillStyle = this.options.ticks.backdropColor; ctx.fillRect( this.xCenter - labelWidth / 2 - this.options.ticks.backdropPaddingX, yHeight - tickFontSize / 2 - this.options.ticks.backdropPaddingY, labelWidth + this.options.ticks.backdropPaddingX * 2, tickFontSize + this.options.ticks.backdropPaddingY * 2 ); } ctx.textAlign = 'center'; ctx.textBaseline = "middle"; ctx.fillStyle = tickFontColor; ctx.fillText(label, this.xCenter, yHeight); } } }, this); if (!this.options.lineArc) { ctx.lineWidth = this.options.angleLines.lineWidth; ctx.strokeStyle = this.options.angleLines.color; for (var i = this.getValueCount() - 1; i >= 0; i--) { if (this.options.angleLines.display) { var outerPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.options.reverse ? this.min : this.max)); ctx.beginPath(); ctx.moveTo(this.xCenter, this.yCenter); ctx.lineTo(outerPosition.x, outerPosition.y); ctx.stroke(); ctx.closePath(); } // Extra 3px out for some label spacing var pointLabelPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.options.reverse ? this.min : this.max) + 5); var pointLabelFontColor = helpers.getValueOrDefault(this.options.pointLabels.fontColor, globalDefaults.defaultFontColor); var pointLabelFontSize = helpers.getValueOrDefault(this.options.pointLabels.fontSize, globalDefaults.defaultFontSize); var pointLabeFontStyle = helpers.getValueOrDefault(this.options.pointLabels.fontStyle, globalDefaults.defaultFontStyle); var pointLabeFontFamily = helpers.getValueOrDefault(this.options.pointLabels.fontFamily, globalDefaults.defaultFontFamily); var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily); ctx.font = pointLabeFont; ctx.fillStyle = pointLabelFontColor; var labelsCount = this.pointLabels.length, halfLabelsCount = this.pointLabels.length / 2, quarterLabelsCount = halfLabelsCount / 2, upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount), exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount); if (i === 0) { ctx.textAlign = 'center'; } else if (i === halfLabelsCount) { ctx.textAlign = 'center'; } else if (i < halfLabelsCount) { ctx.textAlign = 'left'; } else { ctx.textAlign = 'right'; } // Set the correct text baseline based on outer positioning if (exactQuarter) { ctx.textBaseline = 'middle'; } else if (upperHalf) { ctx.textBaseline = 'bottom'; } else { ctx.textBaseline = 'top'; } ctx.fillText(this.pointLabels[i] ? this.pointLabels[i] : '', pointLabelPosition.x, pointLabelPosition.y); } } } } }); Chart.scaleService.registerScaleType("radialLinear", LinearRadialScale, defaultConfig); };