diff --git a/lib/graph3d/Graph3d.js b/lib/graph3d/Graph3d.js index 9a1d4414..95be0634 100644 --- a/lib/graph3d/Graph3d.js +++ b/lib/graph3d/Graph3d.js @@ -831,21 +831,34 @@ Graph3d.prototype.redraw = function() { this._redrawClear(); this._redrawAxis(); - if (this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE) { - this._redrawDataGrid(); - } - else if (this.style === Graph3d.STYLE.LINE) { - this._redrawDataLine(); - } - else if (this.style === Graph3d.STYLE.BAR || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - this._redrawDataBar(); + var pointDrawingMethod = undefined; + switch (this.style) { + case Graph3d.STYLE.BAR: + pointDrawingMethod = Graph3d.prototype._redrawBarGraphPoint; + break; } - else { - // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE - this._redrawDataDot(); + + if (pointDrawingMethod !== undefined) { + // Use generic drawing loop + // Pass the method reference here + this._redrawDataGraph(pointDrawingMethod); + } else { + // Use the old style drawing methods + + if (this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE) { + this._redrawDataGrid(); + } + else if (this.style === Graph3d.STYLE.LINE) { + this._redrawDataLine(); + } else if (this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + this._redrawDataBar(); + } + else { + // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE + this._redrawDataDot(); + } } this._redrawInfo(); @@ -1563,6 +1576,137 @@ Graph3d.prototype._redrawDataDot = function() { } }; + +/** + * Draw a bar element in the view with the given properties. + */ +Graph3d.prototype._redrawBar = function(ctx, point, xWidth, yWidth, color, borderColor) { + var i, j, surface, corners; + + // calculate all corner points + var me = this; + var point3d = point.point; + var top = [ + {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z)}, + {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z)}, + {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z)}, + {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z)} + ]; + var bottom = [ + {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin)}, + {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin)}, + {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin)}, + {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin)} + ]; + + // calculate screen location of the points + top.forEach(function (obj) { + obj.screen = me._convert3Dto2D(obj.point); + }); + bottom.forEach(function (obj) { + obj.screen = me._convert3Dto2D(obj.point); + }); + + // create five sides, calculate both corner points and center points + var surfaces = [ + {corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point)}, + {corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point)}, + {corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point)}, + {corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point)}, + {corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point)} + ]; + point.surfaces = surfaces; + + // calculate the distance of each of the surface centers to the camera + for (j = 0; j < surfaces.length; j++) { + surface = surfaces[j]; + var transCenter = this._convertPointToTranslation(surface.center); + surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z; + // TODO: this dept calculation doesn't work 100% of the cases due to perspective, + // but the current solution is fast/simple and works in 99.9% of all cases + // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9}) + } + + // order the surfaces by their (translated) depth + surfaces.sort(function (a, b) { + var diff = b.dist - a.dist; + if (diff) return diff; + + // if equal depth, sort the top surface last + if (a.corners === top) return 1; + if (b.corners === top) return -1; + + // both are equal + return 0; + }); + + // draw the ordered surfaces + ctx.lineWidth = this._getStrokeWidth(point); + ctx.strokeStyle = borderColor; + ctx.fillStyle = color; + // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside + for (j = 2; j < surfaces.length; j++) { + surface = surfaces[j]; + corners = surface.corners; + ctx.beginPath(); + ctx.moveTo(corners[3].screen.x, corners[3].screen.y); + ctx.lineTo(corners[0].screen.x, corners[0].screen.y); + ctx.lineTo(corners[1].screen.x, corners[1].screen.y); + ctx.lineTo(corners[2].screen.x, corners[2].screen.y); + ctx.lineTo(corners[3].screen.x, corners[3].screen.y); + ctx.fill(); + ctx.stroke(); + } +}; + + +/** + * Draw single datapoint for graph style 'Bar'. + */ +Graph3d.prototype._redrawBarGraphPoint = function(ctx, point) { + var i, j, surface, corners; + + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + + var xWidth = this.xBarWidth / 2; + var yWidth = this.yBarWidth / 2; + + + // determine color + var hue, color, borderColor; + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + + this._redrawBar(ctx, point, xWidth, yWidth, color, borderColor); +}; + + +/** + * Draw all datapoints for currently selected graph style. + * + * @param pointDrawMethod - method reference to draw a point in a specific graph style. + */ +Graph3d.prototype._redrawDataGraph = function(pointDrawMethod) { + var ctx = this._getContext(); + var i; + + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? + + this._calcTranslations(this.dataPoints); + + for (i = 0; i < this.dataPoints.length; i++) { + var point = this.dataPoints[i]; + + // Using call() ensures that the correct context is used + pointDrawMethod.call(this, ctx, point); + } +}; + + /** * Draw all datapoints as bars. * This function can be used when the style is 'bar', 'bar-color', or 'bar-size' @@ -1585,6 +1729,8 @@ Graph3d.prototype._redrawDataBar = function() { for (i = 0; i < this.dataPoints.length; i++) { var point = this.dataPoints[i]; + // TODO: Remove code for style `Bar` here - it has been refactored to separate routine + // determine color var hue, color, borderColor; if (this.style === Graph3d.STYLE.BARCOLOR ) {