/doctor/004_doctor/doctor/node_modules/highcharts/js/highcharts-3d.src.js |
@@ -0,0 +1,3726 @@ |
/** |
* @license Highcharts JS v5.0.14 (2017-07-28) |
* |
* 3D features for Highcharts JS |
* |
* @license: www.highcharts.com/license |
*/ |
'use strict'; |
(function(factory) { |
if (typeof module === 'object' && module.exports) { |
module.exports = factory; |
} else { |
factory(Highcharts); |
} |
}(function(Highcharts) { |
(function(H) { |
/** |
* (c) 2010-2017 Torstein Honsi |
* |
* License: www.highcharts.com/license |
*/ |
/** |
* Mathematical Functionility |
*/ |
var deg2rad = H.deg2rad, |
pick = H.pick; |
/** |
* Apply 3-D rotation |
* Euler Angles (XYZ): cosA = cos(Alfa|Roll), cosB = cos(Beta|Pitch), cosG = cos(Gamma|Yaw) |
* |
* Composite rotation: |
* | cosB * cosG | cosB * sinG | -sinB | |
* | sinA * sinB * cosG - cosA * sinG | sinA * sinB * sinG + cosA * cosG | sinA * cosB | |
* | cosA * sinB * cosG + sinA * sinG | cosA * sinB * sinG - sinA * cosG | cosA * cosB | |
* |
* Now, Gamma/Yaw is not used (angle=0), so we assume cosG = 1 and sinG = 0, so we get: |
* | cosB | 0 | - sinB | |
* | sinA * sinB | cosA | sinA * cosB | |
* | cosA * sinB | - sinA | cosA * cosB | |
* |
* But in browsers, y is reversed, so we get sinA => -sinA. The general result is: |
* | cosB | 0 | - sinB | | x | | px | |
* | - sinA * sinB | cosA | - sinA * cosB | x | y | = | py | |
* | cosA * sinB | sinA | cosA * cosB | | z | | pz | |
*/ |
function rotate3D(x, y, z, angles) { |
return { |
x: angles.cosB * x - angles.sinB * z, |
y: -angles.sinA * angles.sinB * x + angles.cosA * y - angles.cosB * angles.sinA * z, |
z: angles.cosA * angles.sinB * x + angles.sinA * y + angles.cosA * angles.cosB * z |
}; |
} |
|
function perspective3D(coordinate, origin, distance) { |
var projection = ((distance > 0) && (distance < Number.POSITIVE_INFINITY)) ? distance / (coordinate.z + origin.z + distance) : 1; |
return { |
x: coordinate.x * projection, |
y: coordinate.y * projection |
}; |
} |
|
/** |
* Transforms a given array of points according to the angles in chart.options. |
* Parameters: |
* - points: the array of points |
* - chart: the chart |
* - insidePlotArea: wether to verifiy the points are inside the plotArea |
* Returns: |
* - an array of transformed points |
*/ |
H.perspective = function(points, chart, insidePlotArea) { |
var options3d = chart.options.chart.options3d, |
inverted = insidePlotArea ? chart.inverted : false, |
origin = { |
x: chart.plotWidth / 2, |
y: chart.plotHeight / 2, |
z: options3d.depth / 2, |
vd: pick(options3d.depth, 1) * pick(options3d.viewDistance, 0) |
}, |
scale = chart.scale3d || 1, |
beta = deg2rad * options3d.beta * (inverted ? -1 : 1), |
alpha = deg2rad * options3d.alpha * (inverted ? -1 : 1), |
angles = { |
cosA: Math.cos(alpha), |
cosB: Math.cos(-beta), |
sinA: Math.sin(alpha), |
sinB: Math.sin(-beta) |
}; |
|
if (!insidePlotArea) { |
origin.x += chart.plotLeft; |
origin.y += chart.plotTop; |
} |
|
// Transform each point |
return H.map(points, function(point) { |
var rotated = rotate3D( |
(inverted ? point.y : point.x) - origin.x, |
(inverted ? point.x : point.y) - origin.y, |
(point.z || 0) - origin.z, |
angles |
), |
coordinate = perspective3D(rotated, origin, origin.vd); // Apply perspective |
|
// Apply translation |
coordinate.x = coordinate.x * scale + origin.x; |
coordinate.y = coordinate.y * scale + origin.y; |
coordinate.z = rotated.z * scale + origin.z; |
|
return { |
x: (inverted ? coordinate.y : coordinate.x), |
y: (inverted ? coordinate.x : coordinate.y), |
z: coordinate.z |
}; |
}); |
}; |
|
/** |
* Calculate a distance from camera to points - made for calculating zIndex of scatter points. |
* Parameters: |
* - coordinates: The coordinates of the specific point |
* - chart: the chart |
* Returns: |
* - a distance from camera to point |
*/ |
H.pointCameraDistance = function(coordinates, chart) { |
var options3d = chart.options.chart.options3d, |
cameraPosition = { |
x: chart.plotWidth / 2, |
y: chart.plotHeight / 2, |
z: pick(options3d.depth, 1) * pick(options3d.viewDistance, 0) + options3d.depth |
}, |
distance = Math.sqrt(Math.pow(cameraPosition.x - coordinates.plotX, 2) + Math.pow(cameraPosition.y - coordinates.plotY, 2) + Math.pow(cameraPosition.z - coordinates.plotZ, 2)); |
return distance; |
}; |
|
/** |
* Calculate area of a 2D polygon using Shoelace algorithm |
* http://en.wikipedia.org/wiki/Shoelace_formula |
*/ |
H.shapeArea = function(vertexes) { |
var area = 0, |
i, |
j; |
for (i = 0; i < vertexes.length; i++) { |
j = (i + 1) % vertexes.length; |
area += vertexes[i].x * vertexes[j].y - vertexes[j].x * vertexes[i].y; |
} |
return area / 2; |
}; |
|
/** |
* Calculate area of a 3D polygon after perspective projection |
*/ |
H.shapeArea3d = function(vertexes, chart, insidePlotArea) { |
return H.shapeArea(H.perspective(vertexes, chart, insidePlotArea)); |
}; |
|
|
}(Highcharts)); |
(function(H) { |
/** |
* (c) 2010-2017 Torstein Honsi |
* |
* License: www.highcharts.com/license |
*/ |
var cos = Math.cos, |
PI = Math.PI, |
sin = Math.sin; |
|
|
var animObject = H.animObject, |
charts = H.charts, |
color = H.color, |
defined = H.defined, |
deg2rad = H.deg2rad, |
each = H.each, |
extend = H.extend, |
inArray = H.inArray, |
map = H.map, |
merge = H.merge, |
perspective = H.perspective, |
pick = H.pick, |
SVGElement = H.SVGElement, |
SVGRenderer = H.SVGRenderer, |
wrap = H.wrap; |
/*** |
EXTENSION TO THE SVG-RENDERER TO ENABLE 3D SHAPES |
***/ |
////// HELPER METHODS ////// |
|
var dFactor = (4 * (Math.sqrt(2) - 1) / 3) / (PI / 2); |
|
/** Method to construct a curved path |
* Can 'wrap' around more then 180 degrees |
*/ |
function curveTo(cx, cy, rx, ry, start, end, dx, dy) { |
var result = [], |
arcAngle = end - start; |
if ((end > start) && (end - start > Math.PI / 2 + 0.0001)) { |
result = result.concat(curveTo(cx, cy, rx, ry, start, start + (Math.PI / 2), dx, dy)); |
result = result.concat(curveTo(cx, cy, rx, ry, start + (Math.PI / 2), end, dx, dy)); |
return result; |
} |
if ((end < start) && (start - end > Math.PI / 2 + 0.0001)) { |
result = result.concat(curveTo(cx, cy, rx, ry, start, start - (Math.PI / 2), dx, dy)); |
result = result.concat(curveTo(cx, cy, rx, ry, start - (Math.PI / 2), end, dx, dy)); |
return result; |
} |
return [ |
'C', |
cx + (rx * Math.cos(start)) - ((rx * dFactor * arcAngle) * Math.sin(start)) + dx, |
cy + (ry * Math.sin(start)) + ((ry * dFactor * arcAngle) * Math.cos(start)) + dy, |
cx + (rx * Math.cos(end)) + ((rx * dFactor * arcAngle) * Math.sin(end)) + dx, |
cy + (ry * Math.sin(end)) - ((ry * dFactor * arcAngle) * Math.cos(end)) + dy, |
|
cx + (rx * Math.cos(end)) + dx, |
cy + (ry * Math.sin(end)) + dy |
]; |
} |
|
|
/** |
* Override the SVGRenderer initiator to add definitions used by brighter and |
* darker faces of the cuboids. |
*/ |
wrap(SVGRenderer.prototype, 'init', function(proceed) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
|
each([{ |
name: 'darker', |
slope: 0.6 |
}, { |
name: 'brighter', |
slope: 1.4 |
}], function(cfg) { |
this.definition({ |
tagName: 'filter', |
id: 'highcharts-' + cfg.name, |
children: [{ |
tagName: 'feComponentTransfer', |
children: [{ |
tagName: 'feFuncR', |
type: 'linear', |
slope: cfg.slope |
}, { |
tagName: 'feFuncG', |
type: 'linear', |
slope: cfg.slope |
}, { |
tagName: 'feFuncB', |
type: 'linear', |
slope: cfg.slope |
}] |
}] |
}); |
}, this); |
}); |
|
|
SVGRenderer.prototype.toLinePath = function(points, closed) { |
var result = []; |
|
// Put "L x y" for each point |
each(points, function(point) { |
result.push('L', point.x, point.y); |
}); |
|
if (points.length) { |
// Set the first element to M |
result[0] = 'M'; |
|
// If it is a closed line, add Z |
if (closed) { |
result.push('Z'); |
} |
} |
|
return result; |
}; |
|
SVGRenderer.prototype.toLineSegments = function(points) { |
var result = []; |
|
var m = true; |
each(points, function(point) { |
result.push(m ? 'M' : 'L', point.x, point.y); |
m = !m; |
}); |
|
return result; |
}; |
|
/** |
* A 3-D Face is defined by it's 3D vertexes, and is only |
* visible if it's vertexes are counter-clockwise (Back-face culling). |
* It is used as a polyhedron Element |
*/ |
SVGRenderer.prototype.face3d = function(args) { |
var renderer = this, |
ret = this.createElement('path'); |
ret.vertexes = []; |
ret.insidePlotArea = false; |
ret.enabled = true; |
|
wrap(ret, 'attr', function(proceed, hash) { |
if (typeof hash === 'object' && |
(defined(hash.enabled) || defined(hash.vertexes) || defined(hash.insidePlotArea))) { |
this.enabled = pick(hash.enabled, this.enabled); |
this.vertexes = pick(hash.vertexes, this.vertexes); |
this.insidePlotArea = pick(hash.insidePlotArea, this.insidePlotArea); |
delete hash.enabled; |
delete hash.vertexes; |
delete hash.insidePlotArea; |
|
var chart = charts[renderer.chartIndex], |
vertexes2d = perspective(this.vertexes, chart, this.insidePlotArea), |
path = renderer.toLinePath(vertexes2d, true), |
area = H.shapeArea(vertexes2d), |
visibility = (this.enabled && area > 0) ? 'visible' : 'hidden'; |
|
hash.d = path; |
hash.visibility = visibility; |
} |
return proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
wrap(ret, 'animate', function(proceed, params) { |
if (typeof params === 'object' && |
(defined(params.enabled) || defined(params.vertexes) || defined(params.insidePlotArea))) { |
this.enabled = pick(params.enabled, this.enabled); |
this.vertexes = pick(params.vertexes, this.vertexes); |
this.insidePlotArea = pick(params.insidePlotArea, this.insidePlotArea); |
delete params.enabled; |
delete params.vertexes; |
delete params.insidePlotArea; |
|
var chart = charts[renderer.chartIndex], |
vertexes2d = perspective(this.vertexes, chart, this.insidePlotArea), |
path = renderer.toLinePath(vertexes2d, true), |
area = H.shapeArea(vertexes2d), |
visibility = (this.enabled && area > 0) ? 'visible' : 'hidden'; |
|
params.d = path; |
this.attr('visibility', visibility); |
} |
|
return proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
return ret.attr(args); |
}; |
|
/** |
* A Polyhedron is a handy way of defining a group of 3-D faces. |
* It's only attribute is `faces`, an array of attributes of each one of it's Face3D instances. |
*/ |
SVGRenderer.prototype.polyhedron = function(args) { |
var renderer = this, |
result = this.g(), |
destroy = result.destroy; |
|
|
|
result.faces = []; |
|
|
// destroy all children |
result.destroy = function() { |
for (var i = 0; i < result.faces.length; i++) { |
result.faces[i].destroy(); |
} |
return destroy.call(this); |
}; |
|
wrap(result, 'attr', function(proceed, hash, val, complete, continueAnimation) { |
if (typeof hash === 'object' && defined(hash.faces)) { |
while (result.faces.length > hash.faces.length) { |
result.faces.pop().destroy(); |
} |
while (result.faces.length < hash.faces.length) { |
result.faces.push(renderer.face3d().add(result)); |
} |
for (var i = 0; i < hash.faces.length; i++) { |
result.faces[i].attr(hash.faces[i], null, complete, continueAnimation); |
} |
delete hash.faces; |
} |
return proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
wrap(result, 'animate', function(proceed, params, duration, complete) { |
if (params && params.faces) { |
while (result.faces.length > params.faces.length) { |
result.faces.pop().destroy(); |
} |
while (result.faces.length < params.faces.length) { |
result.faces.push(renderer.face3d().add(result)); |
} |
for (var i = 0; i < params.faces.length; i++) { |
result.faces[i].animate(params.faces[i], duration, complete); |
} |
delete params.faces; |
} |
return proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
return result.attr(args); |
}; |
|
////// CUBOIDS ////// |
SVGRenderer.prototype.cuboid = function(shapeArgs) { |
|
var result = this.g(), |
destroy = result.destroy, |
paths = this.cuboidPath(shapeArgs); |
|
|
|
// create the 3 sides |
result.front = this.path(paths[0]).attr({ |
'class': 'highcharts-3d-front' |
}).add(result); // Front, top and side are never overlapping in our case so it is redundant to set zIndex of every element. |
result.top = this.path(paths[1]).attr({ |
'class': 'highcharts-3d-top' |
}).add(result); |
result.side = this.path(paths[2]).attr({ |
'class': 'highcharts-3d-side' |
}).add(result); |
|
// apply the fill everywhere, the top a bit brighter, the side a bit darker |
result.fillSetter = function(fill) { |
this.front.attr({ |
fill: fill |
}); |
this.top.attr({ |
fill: color(fill).brighten(0.1).get() |
}); |
this.side.attr({ |
fill: color(fill).brighten(-0.1).get() |
}); |
this.color = fill; |
|
// for animation getter (#6776) |
result.fill = fill; |
|
return this; |
}; |
|
// apply opacaity everywhere |
result.opacitySetter = function(opacity) { |
this.front.attr({ |
opacity: opacity |
}); |
this.top.attr({ |
opacity: opacity |
}); |
this.side.attr({ |
opacity: opacity |
}); |
return this; |
}; |
|
result.attr = function(args, val, complete, continueAnimation) { |
|
// Resolve setting attributes by string name |
if (typeof args === 'string' && typeof val !== 'undefined') { |
var key = args; |
args = {}; |
args[key] = val; |
} |
|
if (args.shapeArgs || defined(args.x)) { |
var shapeArgs = args.shapeArgs || args; |
var paths = this.renderer.cuboidPath(shapeArgs); |
this.front.attr({ |
d: paths[0] |
}); |
this.top.attr({ |
d: paths[1] |
}); |
this.side.attr({ |
d: paths[2] |
}); |
} else { |
// getter returns value |
return SVGElement.prototype.attr.call( |
this, args, undefined, complete, continueAnimation |
); |
} |
|
return this; |
}; |
|
result.animate = function(args, duration, complete) { |
if (defined(args.x) && defined(args.y)) { |
var paths = this.renderer.cuboidPath(args); |
this.front.animate({ |
d: paths[0] |
}, duration, complete); |
this.top.animate({ |
d: paths[1] |
}, duration, complete); |
this.side.animate({ |
d: paths[2] |
}, duration, complete); |
this.attr({ |
zIndex: -paths[3] // #4774 |
}); |
} else if (args.opacity) { |
this.front.animate(args, duration, complete); |
this.top.animate(args, duration, complete); |
this.side.animate(args, duration, complete); |
} else { |
SVGElement.prototype.animate.call(this, args, duration, complete); |
} |
return this; |
}; |
|
// destroy all children |
result.destroy = function() { |
this.front.destroy(); |
this.top.destroy(); |
this.side.destroy(); |
|
return destroy.call(this); |
}; |
|
// Apply the Z index to the cuboid group |
result.attr({ |
zIndex: -paths[3] |
}); |
|
return result; |
}; |
|
/** |
* Generates a cuboid |
*/ |
H.SVGRenderer.prototype.cuboidPath = function(shapeArgs) { |
var x = shapeArgs.x, |
y = shapeArgs.y, |
z = shapeArgs.z, |
h = shapeArgs.height, |
w = shapeArgs.width, |
d = shapeArgs.depth, |
chart = charts[this.chartIndex], |
front, |
back, |
top, |
bottom, |
left, |
right, |
shape, |
path1, |
path2, |
path3, |
isFront, |
isTop, |
isRight, |
options3d = chart.options.chart.options3d, |
alpha = options3d.alpha, |
// Priority for x axis is the biggest, |
// because of x direction has biggest influence on zIndex |
incrementX = 10000, |
// y axis has the smallest priority in case of our charts |
// (needs to be set because of stacking) |
incrementY = 10, |
incrementZ = 100, |
zIndex = 0; |
|
// The 8 corners of the cube |
var pArr = [{ |
x: x, |
y: y, |
z: z |
}, { |
x: x + w, |
y: y, |
z: z |
}, { |
x: x + w, |
y: y + h, |
z: z |
}, { |
x: x, |
y: y + h, |
z: z |
}, { |
x: x, |
y: y + h, |
z: z + d |
}, { |
x: x + w, |
y: y + h, |
z: z + d |
}, { |
x: x + w, |
y: y, |
z: z + d |
}, { |
x: x, |
y: y, |
z: z + d |
}]; |
|
// apply perspective |
pArr = perspective(pArr, chart, shapeArgs.insidePlotArea); |
|
// helper method to decide which side is visible |
function mapPath(i) { |
return pArr[i]; |
} |
|
/* |
* First value - path with specific side |
* Second value - added information about side for later calculations. |
* Possible second values are 0 for path1, 1 for path2 and -1 for no path choosed. |
*/ |
var pickShape = function(path1, path2) { |
var ret = [ |
[], -1 |
]; |
path1 = map(path1, mapPath); |
path2 = map(path2, mapPath); |
if (H.shapeArea(path1) < 0) { |
ret = [path1, 0]; |
} else if (H.shapeArea(path2) < 0) { |
ret = [path2, 1]; |
} |
return ret; |
}; |
|
// front or back |
front = [3, 2, 1, 0]; |
back = [7, 6, 5, 4]; |
shape = pickShape(front, back); |
path1 = shape[0]; |
isFront = shape[1]; |
|
|
// top or bottom |
top = [1, 6, 7, 0]; |
bottom = [4, 5, 2, 3]; |
shape = pickShape(top, bottom); |
path2 = shape[0]; |
isTop = shape[1]; |
|
// side |
right = [1, 2, 5, 6]; |
left = [0, 7, 4, 3]; |
shape = pickShape(right, left); |
path3 = shape[0]; |
isRight = shape[1]; |
|
/* |
* New block used for calculating zIndex. It is basing on X, Y and Z position of specific columns. |
* All zIndexes (for X, Y and Z values) are added to the final zIndex, where every value has different priority. |
* The biggest priority is in X and Z directions, the lowest index is for stacked columns (Y direction and the same X and Z positions). |
* Big differents between priorities is made because we need to ensure that even for big changes in Y and Z parameters |
* all columns will be drawn correctly. |
*/ |
|
if (isRight === 1) { |
zIndex += incrementX * (1000 - x); |
} else if (!isRight) { |
zIndex += incrementX * x; |
} |
|
zIndex += incrementY * (!isTop || |
(alpha >= 0 && alpha <= 180 || alpha < 360 && alpha > 357.5) ? // Numbers checked empirically |
chart.plotHeight - y : 10 + y |
); |
|
if (isFront === 1) { |
zIndex += incrementZ * (z); |
} else if (!isFront) { |
zIndex += incrementZ * (1000 - z); |
} |
|
zIndex = -Math.round(zIndex); |
|
return [ |
this.toLinePath(path1, true), |
this.toLinePath(path2, true), |
this.toLinePath(path3, true), |
zIndex |
]; // #4774 |
}; |
|
////// SECTORS ////// |
H.SVGRenderer.prototype.arc3d = function(attribs) { |
|
var wrapper = this.g(), |
renderer = wrapper.renderer, |
customAttribs = ['x', 'y', 'r', 'innerR', 'start', 'end']; |
|
/** |
* Get custom attributes. Don't mutate the original object and return an object with only custom attr. |
*/ |
function suckOutCustom(params) { |
var hasCA = false, |
ca = {}; |
|
params = merge(params); // Don't mutate the original object |
|
for (var key in params) { |
if (inArray(key, customAttribs) !== -1) { |
ca[key] = params[key]; |
delete params[key]; |
hasCA = true; |
} |
} |
return hasCA ? ca : false; |
} |
|
attribs = merge(attribs); |
|
attribs.alpha *= deg2rad; |
attribs.beta *= deg2rad; |
|
// Create the different sub sections of the shape |
wrapper.top = renderer.path(); |
wrapper.side1 = renderer.path(); |
wrapper.side2 = renderer.path(); |
wrapper.inn = renderer.path(); |
wrapper.out = renderer.path(); |
|
/** |
* Add all faces |
*/ |
wrapper.onAdd = function() { |
var parent = wrapper.parentGroup, |
className = wrapper.attr('class'); |
wrapper.top.add(wrapper); |
|
// These faces are added outside the wrapper group because the z index |
// relates to neighbour elements as well |
each(['out', 'inn', 'side1', 'side2'], function(face) { |
wrapper[face] |
.attr({ |
'class': className + ' highcharts-3d-side' |
}) |
.add(parent); |
}); |
}; |
|
// Cascade to faces |
each(['addClass', 'removeClass'], function(fn) { |
wrapper[fn] = function() { |
var args = arguments; |
each(['top', 'out', 'inn', 'side1', 'side2'], function(face) { |
wrapper[face][fn].apply(wrapper[face], args); |
}); |
}; |
}); |
|
/** |
* Compute the transformed paths and set them to the composite shapes |
*/ |
wrapper.setPaths = function(attribs) { |
|
var paths = wrapper.renderer.arc3dPath(attribs), |
zIndex = paths.zTop * 100; |
|
wrapper.attribs = attribs; |
|
wrapper.top.attr({ |
d: paths.top, |
zIndex: paths.zTop |
}); |
wrapper.inn.attr({ |
d: paths.inn, |
zIndex: paths.zInn |
}); |
wrapper.out.attr({ |
d: paths.out, |
zIndex: paths.zOut |
}); |
wrapper.side1.attr({ |
d: paths.side1, |
zIndex: paths.zSide1 |
}); |
wrapper.side2.attr({ |
d: paths.side2, |
zIndex: paths.zSide2 |
}); |
|
|
// show all children |
wrapper.zIndex = zIndex; |
wrapper.attr({ |
zIndex: zIndex |
}); |
|
// Set the radial gradient center the first time |
if (attribs.center) { |
wrapper.top.setRadialReference(attribs.center); |
delete attribs.center; |
} |
}; |
wrapper.setPaths(attribs); |
|
// Apply the fill to the top and a darker shade to the sides |
wrapper.fillSetter = function(value) { |
var darker = color(value).brighten(-0.1).get(); |
|
this.fill = value; |
|
this.side1.attr({ |
fill: darker |
}); |
this.side2.attr({ |
fill: darker |
}); |
this.inn.attr({ |
fill: darker |
}); |
this.out.attr({ |
fill: darker |
}); |
this.top.attr({ |
fill: value |
}); |
return this; |
}; |
|
// Apply the same value to all. These properties cascade down to the children |
// when set to the composite arc3d. |
each(['opacity', 'translateX', 'translateY', 'visibility'], function(setter) { |
wrapper[setter + 'Setter'] = function(value, key) { |
wrapper[key] = value; |
each(['out', 'inn', 'side1', 'side2', 'top'], function(el) { |
wrapper[el].attr(key, value); |
}); |
}; |
}); |
|
/** |
* Override attr to remove shape attributes and use those to set child paths |
*/ |
wrap(wrapper, 'attr', function(proceed, params) { |
var ca; |
if (typeof params === 'object') { |
ca = suckOutCustom(params); |
if (ca) { |
extend(wrapper.attribs, ca); |
wrapper.setPaths(wrapper.attribs); |
} |
} |
return proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
/** |
* Override the animate function by sucking out custom parameters related to the shapes directly, |
* and update the shapes from the animation step. |
*/ |
wrap(wrapper, 'animate', function(proceed, params, animation, complete) { |
var ca, |
from = this.attribs, |
to, |
anim; |
|
// Attribute-line properties connected to 3D. These shouldn't have been in the |
// attribs collection in the first place. |
delete params.center; |
delete params.z; |
delete params.depth; |
delete params.alpha; |
delete params.beta; |
|
anim = animObject(pick(animation, this.renderer.globalAnimation)); |
|
if (anim.duration) { |
ca = suckOutCustom(params); |
params.dummy = 1; // Params need to have a property in order for the step to run (#5765) |
|
if (ca) { |
to = ca; |
anim.step = function(a, fx) { |
function interpolate(key) { |
return from[key] + (pick(to[key], from[key]) - from[key]) * fx.pos; |
} |
|
if (fx.prop === 'dummy') { |
fx.elem.setPaths(merge(from, { |
x: interpolate('x'), |
y: interpolate('y'), |
r: interpolate('r'), |
innerR: interpolate('innerR'), |
start: interpolate('start'), |
end: interpolate('end') |
})); |
} |
}; |
} |
animation = anim; // Only when duration (#5572) |
} |
return proceed.call(this, params, animation, complete); |
}); |
|
// destroy all children |
wrapper.destroy = function() { |
this.top.destroy(); |
this.out.destroy(); |
this.inn.destroy(); |
this.side1.destroy(); |
this.side2.destroy(); |
|
SVGElement.prototype.destroy.call(this); |
}; |
// hide all children |
wrapper.hide = function() { |
this.top.hide(); |
this.out.hide(); |
this.inn.hide(); |
this.side1.hide(); |
this.side2.hide(); |
}; |
wrapper.show = function() { |
this.top.show(); |
this.out.show(); |
this.inn.show(); |
this.side1.show(); |
this.side2.show(); |
}; |
return wrapper; |
}; |
|
/** |
* Generate the paths required to draw a 3D arc |
*/ |
SVGRenderer.prototype.arc3dPath = function(shapeArgs) { |
var cx = shapeArgs.x, // x coordinate of the center |
cy = shapeArgs.y, // y coordinate of the center |
start = shapeArgs.start, // start angle |
end = shapeArgs.end - 0.00001, // end angle |
r = shapeArgs.r, // radius |
ir = shapeArgs.innerR, // inner radius |
d = shapeArgs.depth, // depth |
alpha = shapeArgs.alpha, // alpha rotation of the chart |
beta = shapeArgs.beta; // beta rotation of the chart |
|
// Derived Variables |
var cs = Math.cos(start), // cosinus of the start angle |
ss = Math.sin(start), // sinus of the start angle |
ce = Math.cos(end), // cosinus of the end angle |
se = Math.sin(end), // sinus of the end angle |
rx = r * Math.cos(beta), // x-radius |
ry = r * Math.cos(alpha), // y-radius |
irx = ir * Math.cos(beta), // x-radius (inner) |
iry = ir * Math.cos(alpha), // y-radius (inner) |
dx = d * Math.sin(beta), // distance between top and bottom in x |
dy = d * Math.sin(alpha); // distance between top and bottom in y |
|
// TOP |
var top = ['M', cx + (rx * cs), cy + (ry * ss)]; |
top = top.concat(curveTo(cx, cy, rx, ry, start, end, 0, 0)); |
top = top.concat([ |
'L', cx + (irx * ce), cy + (iry * se) |
]); |
top = top.concat(curveTo(cx, cy, irx, iry, end, start, 0, 0)); |
top = top.concat(['Z']); |
// OUTSIDE |
var b = (beta > 0 ? Math.PI / 2 : 0), |
a = (alpha > 0 ? 0 : Math.PI / 2); |
|
var start2 = start > -b ? start : (end > -b ? -b : start), |
end2 = end < PI - a ? end : (start < PI - a ? PI - a : end), |
midEnd = 2 * PI - a; |
|
// When slice goes over bottom middle, need to add both, left and right outer side. |
// Additionally, when we cross right hand edge, create sharp edge. Outer shape/wall: |
// |
// ------- |
// / ^ \ |
// 4) / / \ \ 1) |
// / / \ \ |
// / / \ \ |
// (c)=> ==== ==== <=(d) |
// \ \ / / |
// \ \<=(a)/ / |
// \ \ / / <=(b) |
// 3) \ v / 2) |
// ------- |
// |
// (a) - inner side |
// (b) - outer side |
// (c) - left edge (sharp) |
// (d) - right edge (sharp) |
// 1..n - rendering order for startAngle = 0, when set to e.g 90, order changes clockwise (1->2, 2->3, n->1) and counterclockwise for negative startAngle |
|
var out = ['M', cx + (rx * cos(start2)), cy + (ry * sin(start2))]; |
out = out.concat(curveTo(cx, cy, rx, ry, start2, end2, 0, 0)); |
|
if (end > midEnd && start < midEnd) { // When shape is wide, it can cross both, (c) and (d) edges, when using startAngle |
// Go to outer side |
out = out.concat([ |
'L', cx + (rx * cos(end2)) + dx, cy + (ry * sin(end2)) + dy |
]); |
// Curve to the right edge of the slice (d) |
out = out.concat(curveTo(cx, cy, rx, ry, end2, midEnd, dx, dy)); |
// Go to the inner side |
out = out.concat([ |
'L', cx + (rx * cos(midEnd)), cy + (ry * sin(midEnd)) |
]); |
// Curve to the true end of the slice |
out = out.concat(curveTo(cx, cy, rx, ry, midEnd, end, 0, 0)); |
// Go to the outer side |
out = out.concat([ |
'L', cx + (rx * cos(end)) + dx, cy + (ry * sin(end)) + dy |
]); |
// Go back to middle (d) |
out = out.concat(curveTo(cx, cy, rx, ry, end, midEnd, dx, dy)); |
out = out.concat([ |
'L', cx + (rx * cos(midEnd)), cy + (ry * sin(midEnd)) |
]); |
// Go back to the left edge |
out = out.concat(curveTo(cx, cy, rx, ry, midEnd, end2, 0, 0)); |
} else if (end > PI - a && start < PI - a) { // But shape can cross also only (c) edge: |
// Go to outer side |
out = out.concat([ |
'L', cx + (rx * Math.cos(end2)) + dx, cy + (ry * Math.sin(end2)) + dy |
]); |
// Curve to the true end of the slice |
out = out.concat(curveTo(cx, cy, rx, ry, end2, end, dx, dy)); |
// Go to the inner side |
out = out.concat([ |
'L', cx + (rx * Math.cos(end)), cy + (ry * Math.sin(end)) |
]); |
// Go back to the artifical end2 |
out = out.concat(curveTo(cx, cy, rx, ry, end, end2, 0, 0)); |
} |
|
out = out.concat([ |
'L', cx + (rx * Math.cos(end2)) + dx, cy + (ry * Math.sin(end2)) + dy |
]); |
out = out.concat(curveTo(cx, cy, rx, ry, end2, start2, dx, dy)); |
out = out.concat(['Z']); |
|
// INSIDE |
var inn = ['M', cx + (irx * cs), cy + (iry * ss)]; |
inn = inn.concat(curveTo(cx, cy, irx, iry, start, end, 0, 0)); |
inn = inn.concat([ |
'L', cx + (irx * Math.cos(end)) + dx, cy + (iry * Math.sin(end)) + dy |
]); |
inn = inn.concat(curveTo(cx, cy, irx, iry, end, start, dx, dy)); |
inn = inn.concat(['Z']); |
|
// SIDES |
var side1 = [ |
'M', cx + (rx * cs), cy + (ry * ss), |
'L', cx + (rx * cs) + dx, cy + (ry * ss) + dy, |
'L', cx + (irx * cs) + dx, cy + (iry * ss) + dy, |
'L', cx + (irx * cs), cy + (iry * ss), |
'Z' |
]; |
var side2 = [ |
'M', cx + (rx * ce), cy + (ry * se), |
'L', cx + (rx * ce) + dx, cy + (ry * se) + dy, |
'L', cx + (irx * ce) + dx, cy + (iry * se) + dy, |
'L', cx + (irx * ce), cy + (iry * se), |
'Z' |
]; |
|
// correction for changed position of vanishing point caused by alpha and beta rotations |
var angleCorr = Math.atan2(dy, -dx), |
angleEnd = Math.abs(end + angleCorr), |
angleStart = Math.abs(start + angleCorr), |
angleMid = Math.abs((start + end) / 2 + angleCorr); |
|
// set to 0-PI range |
function toZeroPIRange(angle) { |
angle = angle % (2 * Math.PI); |
if (angle > Math.PI) { |
angle = 2 * Math.PI - angle; |
} |
return angle; |
} |
angleEnd = toZeroPIRange(angleEnd); |
angleStart = toZeroPIRange(angleStart); |
angleMid = toZeroPIRange(angleMid); |
|
// *1e5 is to compensate pInt in zIndexSetter |
var incPrecision = 1e5, |
a1 = angleMid * incPrecision, |
a2 = angleStart * incPrecision, |
a3 = angleEnd * incPrecision; |
|
return { |
top: top, |
zTop: Math.PI * incPrecision + 1, // max angle is PI, so this is allways higher |
out: out, |
zOut: Math.max(a1, a2, a3), |
inn: inn, |
zInn: Math.max(a1, a2, a3), |
side1: side1, |
zSide1: a3 * 0.99, // to keep below zOut and zInn in case of same values |
side2: side2, |
zSide2: a2 * 0.99 |
}; |
}; |
|
}(Highcharts)); |
(function(H) { |
/** |
* (c) 2010-2017 Torstein Honsi |
* |
* License: www.highcharts.com/license |
*/ |
var Chart = H.Chart, |
each = H.each, |
merge = H.merge, |
perspective = H.perspective, |
pick = H.pick, |
wrap = H.wrap; |
|
/*** |
EXTENSION FOR 3D CHARTS |
***/ |
// Shorthand to check the is3d flag |
Chart.prototype.is3d = function() { |
return this.options.chart.options3d && this.options.chart.options3d.enabled; // #4280 |
}; |
|
Chart.prototype.propsRequireDirtyBox.push('chart.options3d'); |
Chart.prototype.propsRequireUpdateSeries.push('chart.options3d'); |
|
/** |
* Calculate scale of the 3D view. That is required to |
* fit chart's 3D projection into the actual plotting area. Reported as #4933. |
* @notice This function should ideally take the plot values instead of a chart object, |
* but since the chart object is needed for perspective it is not practical. |
* Possible to make both getScale and perspective more logical and also immutable. |
* @param {Object} chart Chart object |
* @param {Number} chart.plotLeft |
* @param {Number} chart.plotWidth |
* @param {Number} chart.plotTop |
* @param {Number} chart.plotHeight |
* @param {Number} depth The depth of the chart |
* @return {Number} The scale to fit the 3D chart into the plotting area. |
*/ |
function getScale(chart, depth) { |
var plotLeft = chart.plotLeft, |
plotRight = chart.plotWidth + plotLeft, |
plotTop = chart.plotTop, |
plotBottom = chart.plotHeight + plotTop, |
originX = plotLeft + chart.plotWidth / 2, |
originY = plotTop + chart.plotHeight / 2, |
bbox3d = { |
minX: Number.MAX_VALUE, |
maxX: -Number.MAX_VALUE, |
minY: Number.MAX_VALUE, |
maxY: -Number.MAX_VALUE |
}, |
corners, |
scale = 1; |
|
// Top left corners: |
corners = [{ |
x: plotLeft, |
y: plotTop, |
z: 0 |
}, { |
x: plotLeft, |
y: plotTop, |
z: depth |
}]; |
|
// Top right corners: |
each([0, 1], function(i) { |
corners.push({ |
x: plotRight, |
y: corners[i].y, |
z: corners[i].z |
}); |
}); |
|
// All bottom corners: |
each([0, 1, 2, 3], function(i) { |
corners.push({ |
x: corners[i].x, |
y: plotBottom, |
z: corners[i].z |
}); |
}); |
|
// Calculate 3D corners: |
corners = perspective(corners, chart, false); |
|
// Get bounding box of 3D element: |
each(corners, function(corner) { |
bbox3d.minX = Math.min(bbox3d.minX, corner.x); |
bbox3d.maxX = Math.max(bbox3d.maxX, corner.x); |
bbox3d.minY = Math.min(bbox3d.minY, corner.y); |
bbox3d.maxY = Math.max(bbox3d.maxY, corner.y); |
}); |
|
// Left edge: |
if (plotLeft > bbox3d.minX) { |
scale = Math.min(scale, 1 - Math.abs((plotLeft + originX) / (bbox3d.minX + originX)) % 1); |
} |
|
// Right edge: |
if (plotRight < bbox3d.maxX) { |
scale = Math.min(scale, (plotRight - originX) / (bbox3d.maxX - originX)); |
} |
|
// Top edge: |
if (plotTop > bbox3d.minY) { |
if (bbox3d.minY < 0) { |
scale = Math.min(scale, (plotTop + originY) / (-bbox3d.minY + plotTop + originY)); |
} else { |
scale = Math.min(scale, 1 - (plotTop + originY) / (bbox3d.minY + originY) % 1); |
} |
} |
|
// Bottom edge: |
if (plotBottom < bbox3d.maxY) { |
scale = Math.min(scale, Math.abs((plotBottom - originY) / (bbox3d.maxY - originY))); |
} |
|
return scale; |
} |
|
|
|
H.wrap(H.Chart.prototype, 'isInsidePlot', function(proceed) { |
return this.is3d() || proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
var defaultOptions = H.getOptions(); |
|
/** |
* Options to render charts in 3 dimensions. |
* This feature requires highcharts-3d.js, found in the download package, |
* or online at code.highcharts.com/highcharts-3d.js. |
* @optionparent |
*/ |
var extendedOptions = { |
|
/** |
* Options regarding the chart area and plot area as well as general |
* chart options. |
* |
* @product highcharts highstock highmaps |
*/ |
chart: { |
|
/** |
* Options to render charts in 3 dimensions. This feature requires |
* `highcharts-3d.js`, found in the download package or online at |
* [code.highcharts.com/highcharts-3d.js](http://code.highcharts.com/highcharts- |
* 3d.js). |
* |
* @since 4.0 |
* @product highcharts |
*/ |
options3d: { |
|
/** |
* Wether to render the chart using the 3D functionality. |
* |
* @type {Boolean} |
* @default false |
* @since 4.0 |
* @product highcharts |
*/ |
enabled: false, |
|
/** |
* One of the two rotation angles for the chart. |
* |
* @type {Number} |
* @default 0 |
* @since 4.0 |
* @product highcharts |
*/ |
alpha: 0, |
|
/** |
* One of the two rotation angles for the chart. |
* |
* @type {Number} |
* @default 0 |
* @since 4.0 |
* @product highcharts |
*/ |
beta: 0, |
|
/** |
* The total depth of the chart. |
* |
* @type {Number} |
* @default 100 |
* @since 4.0 |
* @product highcharts |
*/ |
depth: 100, |
|
/** |
* Whether the 3d box should automatically adjust to the chart plot |
* area. |
* |
* @type {Boolean} |
* @default true |
* @since 4.2.4 |
* @product highcharts |
*/ |
fitToPlot: true, |
|
/** |
* Defines the distance the viewer is standing in front of the chart, |
* this setting is important to calculate the perspective effect |
* in column and scatter charts. It is not used for 3D pie charts. |
* |
* @type {Number} |
* @default 100 |
* @since 4.0 |
* @product highcharts |
*/ |
viewDistance: 25, |
|
/** |
* Set it to `"auto"` to automatically move the labels to the best |
* edge. |
* |
* @validvalue [null, "auto"] |
* @type {String} |
* @default null |
* @since 5.0.12 |
* @product highcharts |
*/ |
axisLabelPosition: 'default', |
|
/** |
* Provides the option to draw a frame around the charts by defining |
* a bottom, front and back panel. |
* |
* @since 4.0 |
* @product highcharts |
*/ |
frame: { |
|
/** |
*/ |
visible: 'default', |
|
/** |
*/ |
size: 1, |
|
/** |
* The bottom of the frame around a 3D chart. |
* |
* @since 4.0 |
* @product highcharts |
*/ |
bottom: {}, |
|
/** |
* The top of the frame around a 3D chart. |
* |
* @type {Object} |
* @since 5.0.12 |
* @product highcharts |
*/ |
top: {}, |
|
/** |
*/ |
left: {}, |
|
/** |
*/ |
right: {}, |
|
/** |
* Defines the back panel of the frame around 3D charts. |
* |
* @since 4.0 |
* @product highcharts |
*/ |
back: {}, |
|
/** |
*/ |
front: {} |
} |
} |
} |
}; |
|
merge(true, defaultOptions, extendedOptions); |
|
|
/** |
* Override the getContainer by adding the required CSS classes for column |
* sides (#6018) |
*/ |
wrap(Chart.prototype, 'getContainer', function(proceed) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
|
this.renderer.definition({ |
tagName: 'style', |
textContent: '.highcharts-3d-top{' + |
'filter: url(#highcharts-brighter)' + |
'}\n' + |
'.highcharts-3d-side{' + |
'filter: url(#highcharts-darker)' + |
'}\n' |
}); |
}); |
|
|
wrap(Chart.prototype, 'setClassName', function(proceed) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
|
if (this.is3d()) { |
this.container.className += ' highcharts-3d-chart'; |
} |
}); |
|
H.wrap(H.Chart.prototype, 'setChartSize', function(proceed) { |
var chart = this, |
options3d = chart.options.chart.options3d; |
|
proceed.apply(chart, [].slice.call(arguments, 1)); |
|
if (chart.is3d()) { |
var inverted = chart.inverted, |
clipBox = chart.clipBox, |
margin = chart.margin, |
x = inverted ? 'y' : 'x', |
y = inverted ? 'x' : 'y', |
w = inverted ? 'height' : 'width', |
h = inverted ? 'width' : 'height'; |
|
clipBox[x] = -(margin[3] || 0); |
clipBox[y] = -(margin[0] || 0); |
clipBox[w] = chart.chartWidth + (margin[3] || 0) + (margin[1] || 0); |
clipBox[h] = chart.chartHeight + (margin[0] || 0) + (margin[2] || 0); |
|
// Set scale, used later in perspective method(): |
chart.scale3d = 1; // @notice getScale uses perspective, so scale3d has to be reset. |
if (options3d.fitToPlot === true) { |
chart.scale3d = getScale(chart, options3d.depth); |
} |
} |
}); |
|
wrap(Chart.prototype, 'redraw', function(proceed) { |
if (this.is3d()) { |
// Set to force a redraw of all elements |
this.isDirtyBox = true; |
this.frame3d = this.get3dFrame(); |
} |
proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
wrap(Chart.prototype, 'render', function(proceed) { |
if (this.is3d()) { |
this.frame3d = this.get3dFrame(); |
} |
proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
// Draw the series in the reverse order (#3803, #3917) |
wrap(Chart.prototype, 'renderSeries', function(proceed) { |
var series, |
i = this.series.length; |
|
if (this.is3d()) { |
while (i--) { |
series = this.series[i]; |
series.translate(); |
series.render(); |
} |
} else { |
proceed.call(this); |
} |
}); |
|
wrap(Chart.prototype, 'drawChartBox', function(proceed) { |
if (this.is3d()) { |
var chart = this, |
renderer = chart.renderer, |
options3d = this.options.chart.options3d, |
frame = chart.get3dFrame(), |
xm = this.plotLeft, |
xp = this.plotLeft + this.plotWidth, |
ym = this.plotTop, |
yp = this.plotTop + this.plotHeight, |
zm = 0, |
zp = options3d.depth, |
xmm = xm - (frame.left.visible ? frame.left.size : 0), |
xpp = xp + (frame.right.visible ? frame.right.size : 0), |
ymm = ym - (frame.top.visible ? frame.top.size : 0), |
ypp = yp + (frame.bottom.visible ? frame.bottom.size : 0), |
zmm = zm - (frame.front.visible ? frame.front.size : 0), |
zpp = zp + (frame.back.visible ? frame.back.size : 0), |
verb = chart.hasRendered ? 'animate' : 'attr'; |
|
this.frame3d = frame; |
|
if (!this.frameShapes) { |
this.frameShapes = { |
bottom: renderer.polyhedron().add(), |
top: renderer.polyhedron().add(), |
left: renderer.polyhedron().add(), |
right: renderer.polyhedron().add(), |
back: renderer.polyhedron().add(), |
front: renderer.polyhedron().add() |
}; |
} |
|
this.frameShapes.bottom[verb]({ |
'class': 'highcharts-3d-frame highcharts-3d-frame-bottom', |
zIndex: frame.bottom.frontFacing ? -1000 : 1000, |
faces: [{ //bottom |
fill: H.color(frame.bottom.color).brighten(0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ypp, |
z: zmm |
}, { |
x: xpp, |
y: ypp, |
z: zmm |
}, { |
x: xpp, |
y: ypp, |
z: zpp |
}, { |
x: xmm, |
y: ypp, |
z: zpp |
}], |
enabled: frame.bottom.visible |
}, |
{ //top |
fill: H.color(frame.bottom.color).brighten(0.1).get(), |
vertexes: [{ |
x: xm, |
y: yp, |
z: zp |
}, { |
x: xp, |
y: yp, |
z: zp |
}, { |
x: xp, |
y: yp, |
z: zm |
}, { |
x: xm, |
y: yp, |
z: zm |
}], |
enabled: frame.bottom.visible |
}, |
{ //left |
fill: H.color(frame.bottom.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ypp, |
z: zmm |
}, { |
x: xmm, |
y: ypp, |
z: zpp |
}, { |
x: xm, |
y: yp, |
z: zp |
}, { |
x: xm, |
y: yp, |
z: zm |
}], |
enabled: frame.bottom.visible && !frame.left.visible |
}, |
{ //right |
fill: H.color(frame.bottom.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xpp, |
y: ypp, |
z: zpp |
}, { |
x: xpp, |
y: ypp, |
z: zmm |
}, { |
x: xp, |
y: yp, |
z: zm |
}, { |
x: xp, |
y: yp, |
z: zp |
}], |
enabled: frame.bottom.visible && !frame.right.visible |
}, |
{ //front |
fill: H.color(frame.bottom.color).get(), |
vertexes: [{ |
x: xpp, |
y: ypp, |
z: zmm |
}, { |
x: xmm, |
y: ypp, |
z: zmm |
}, { |
x: xm, |
y: yp, |
z: zm |
}, { |
x: xp, |
y: yp, |
z: zm |
}], |
enabled: frame.bottom.visible && !frame.front.visible |
}, |
{ //back |
fill: H.color(frame.bottom.color).get(), |
vertexes: [{ |
x: xmm, |
y: ypp, |
z: zpp |
}, { |
x: xpp, |
y: ypp, |
z: zpp |
}, { |
x: xp, |
y: yp, |
z: zp |
}, { |
x: xm, |
y: yp, |
z: zp |
}], |
enabled: frame.bottom.visible && !frame.back.visible |
} |
] |
}); |
this.frameShapes.top[verb]({ |
'class': 'highcharts-3d-frame highcharts-3d-frame-top', |
zIndex: frame.top.frontFacing ? -1000 : 1000, |
faces: [{ //bottom |
fill: H.color(frame.top.color).brighten(0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ymm, |
z: zpp |
}, { |
x: xpp, |
y: ymm, |
z: zpp |
}, { |
x: xpp, |
y: ymm, |
z: zmm |
}, { |
x: xmm, |
y: ymm, |
z: zmm |
}], |
enabled: frame.top.visible |
}, |
{ //top |
fill: H.color(frame.top.color).brighten(0.1).get(), |
vertexes: [{ |
x: xm, |
y: ym, |
z: zm |
}, { |
x: xp, |
y: ym, |
z: zm |
}, { |
x: xp, |
y: ym, |
z: zp |
}, { |
x: xm, |
y: ym, |
z: zp |
}], |
enabled: frame.top.visible |
}, |
{ //left |
fill: H.color(frame.top.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ymm, |
z: zpp |
}, { |
x: xmm, |
y: ymm, |
z: zmm |
}, { |
x: xm, |
y: ym, |
z: zm |
}, { |
x: xm, |
y: ym, |
z: zp |
}], |
enabled: frame.top.visible && !frame.left.visible |
}, |
{ //right |
fill: H.color(frame.top.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xpp, |
y: ymm, |
z: zmm |
}, { |
x: xpp, |
y: ymm, |
z: zpp |
}, { |
x: xp, |
y: ym, |
z: zp |
}, { |
x: xp, |
y: ym, |
z: zm |
}], |
enabled: frame.top.visible && !frame.right.visible |
}, |
{ //front |
fill: H.color(frame.top.color).get(), |
vertexes: [{ |
x: xmm, |
y: ymm, |
z: zmm |
}, { |
x: xpp, |
y: ymm, |
z: zmm |
}, { |
x: xp, |
y: ym, |
z: zm |
}, { |
x: xm, |
y: ym, |
z: zm |
}], |
enabled: frame.top.visible && !frame.front.visible |
}, |
{ //back |
fill: H.color(frame.top.color).get(), |
vertexes: [{ |
x: xpp, |
y: ymm, |
z: zpp |
}, { |
x: xmm, |
y: ymm, |
z: zpp |
}, { |
x: xm, |
y: ym, |
z: zp |
}, { |
x: xp, |
y: ym, |
z: zp |
}], |
enabled: frame.top.visible && !frame.back.visible |
} |
] |
}); |
this.frameShapes.left[verb]({ |
'class': 'highcharts-3d-frame highcharts-3d-frame-left', |
zIndex: frame.left.frontFacing ? -1000 : 1000, |
faces: [{ //bottom |
fill: H.color(frame.left.color).brighten(0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ypp, |
z: zmm |
}, { |
x: xm, |
y: yp, |
z: zm |
}, { |
x: xm, |
y: yp, |
z: zp |
}, { |
x: xmm, |
y: ypp, |
z: zpp |
}], |
enabled: frame.left.visible && !frame.bottom.visible |
}, |
{ //top |
fill: H.color(frame.left.color).brighten(0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ymm, |
z: zpp |
}, { |
x: xm, |
y: ym, |
z: zp |
}, { |
x: xm, |
y: ym, |
z: zm |
}, { |
x: xmm, |
y: ymm, |
z: zmm |
}], |
enabled: frame.left.visible && !frame.top.visible |
}, |
{ //left |
fill: H.color(frame.left.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ypp, |
z: zpp |
}, { |
x: xmm, |
y: ymm, |
z: zpp |
}, { |
x: xmm, |
y: ymm, |
z: zmm |
}, { |
x: xmm, |
y: ypp, |
z: zmm |
}], |
enabled: frame.left.visible |
}, |
{ //right |
fill: H.color(frame.left.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xm, |
y: ym, |
z: zp |
}, { |
x: xm, |
y: yp, |
z: zp |
}, { |
x: xm, |
y: yp, |
z: zm |
}, { |
x: xm, |
y: ym, |
z: zm |
}], |
enabled: frame.left.visible |
}, |
{ //front |
fill: H.color(frame.left.color).get(), |
vertexes: [{ |
x: xmm, |
y: ypp, |
z: zmm |
}, { |
x: xmm, |
y: ymm, |
z: zmm |
}, { |
x: xm, |
y: ym, |
z: zm |
}, { |
x: xm, |
y: yp, |
z: zm |
}], |
enabled: frame.left.visible && !frame.front.visible |
}, |
{ //back |
fill: H.color(frame.left.color).get(), |
vertexes: [{ |
x: xmm, |
y: ymm, |
z: zpp |
}, { |
x: xmm, |
y: ypp, |
z: zpp |
}, { |
x: xm, |
y: yp, |
z: zp |
}, { |
x: xm, |
y: ym, |
z: zp |
}], |
enabled: frame.left.visible && !frame.back.visible |
} |
] |
}); |
this.frameShapes.right[verb]({ |
'class': 'highcharts-3d-frame highcharts-3d-frame-right', |
zIndex: frame.right.frontFacing ? -1000 : 1000, |
faces: [{ //bottom |
fill: H.color(frame.right.color).brighten(0.1).get(), |
vertexes: [{ |
x: xpp, |
y: ypp, |
z: zpp |
}, { |
x: xp, |
y: yp, |
z: zp |
}, { |
x: xp, |
y: yp, |
z: zm |
}, { |
x: xpp, |
y: ypp, |
z: zmm |
}], |
enabled: frame.right.visible && !frame.bottom.visible |
}, |
{ //top |
fill: H.color(frame.right.color).brighten(0.1).get(), |
vertexes: [{ |
x: xpp, |
y: ymm, |
z: zmm |
}, { |
x: xp, |
y: ym, |
z: zm |
}, { |
x: xp, |
y: ym, |
z: zp |
}, { |
x: xpp, |
y: ymm, |
z: zpp |
}], |
enabled: frame.right.visible && !frame.top.visible |
}, |
{ //left |
fill: H.color(frame.right.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xp, |
y: ym, |
z: zm |
}, { |
x: xp, |
y: yp, |
z: zm |
}, { |
x: xp, |
y: yp, |
z: zp |
}, { |
x: xp, |
y: ym, |
z: zp |
}], |
enabled: frame.right.visible |
}, |
{ //right |
fill: H.color(frame.right.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xpp, |
y: ypp, |
z: zmm |
}, { |
x: xpp, |
y: ymm, |
z: zmm |
}, { |
x: xpp, |
y: ymm, |
z: zpp |
}, { |
x: xpp, |
y: ypp, |
z: zpp |
}], |
enabled: frame.right.visible |
}, |
{ //front |
fill: H.color(frame.right.color).get(), |
vertexes: [{ |
x: xpp, |
y: ymm, |
z: zmm |
}, { |
x: xpp, |
y: ypp, |
z: zmm |
}, { |
x: xp, |
y: yp, |
z: zm |
}, { |
x: xp, |
y: ym, |
z: zm |
}], |
enabled: frame.right.visible && !frame.front.visible |
}, |
{ //back |
fill: H.color(frame.right.color).get(), |
vertexes: [{ |
x: xpp, |
y: ypp, |
z: zpp |
}, { |
x: xpp, |
y: ymm, |
z: zpp |
}, { |
x: xp, |
y: ym, |
z: zp |
}, { |
x: xp, |
y: yp, |
z: zp |
}], |
enabled: frame.right.visible && !frame.back.visible |
} |
] |
}); |
this.frameShapes.back[verb]({ |
'class': 'highcharts-3d-frame highcharts-3d-frame-back', |
zIndex: frame.back.frontFacing ? -1000 : 1000, |
faces: [{ //bottom |
fill: H.color(frame.back.color).brighten(0.1).get(), |
vertexes: [{ |
x: xpp, |
y: ypp, |
z: zpp |
}, { |
x: xmm, |
y: ypp, |
z: zpp |
}, { |
x: xm, |
y: yp, |
z: zp |
}, { |
x: xp, |
y: yp, |
z: zp |
}], |
enabled: frame.back.visible && !frame.bottom.visible |
}, |
{ //top |
fill: H.color(frame.back.color).brighten(0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ymm, |
z: zpp |
}, { |
x: xpp, |
y: ymm, |
z: zpp |
}, { |
x: xp, |
y: ym, |
z: zp |
}, { |
x: xm, |
y: ym, |
z: zp |
}], |
enabled: frame.back.visible && !frame.top.visible |
}, |
{ //left |
fill: H.color(frame.back.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ypp, |
z: zpp |
}, { |
x: xmm, |
y: ymm, |
z: zpp |
}, { |
x: xm, |
y: ym, |
z: zp |
}, { |
x: xm, |
y: yp, |
z: zp |
}], |
enabled: frame.back.visible && !frame.left.visible |
}, |
{ //right |
fill: H.color(frame.back.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xpp, |
y: ymm, |
z: zpp |
}, { |
x: xpp, |
y: ypp, |
z: zpp |
}, { |
x: xp, |
y: yp, |
z: zp |
}, { |
x: xp, |
y: ym, |
z: zp |
}], |
enabled: frame.back.visible && !frame.right.visible |
}, |
{ //front |
fill: H.color(frame.back.color).get(), |
vertexes: [{ |
x: xm, |
y: ym, |
z: zp |
}, { |
x: xp, |
y: ym, |
z: zp |
}, { |
x: xp, |
y: yp, |
z: zp |
}, { |
x: xm, |
y: yp, |
z: zp |
}], |
enabled: frame.back.visible |
}, |
{ //back |
fill: H.color(frame.back.color).get(), |
vertexes: [{ |
x: xmm, |
y: ypp, |
z: zpp |
}, { |
x: xpp, |
y: ypp, |
z: zpp |
}, { |
x: xpp, |
y: ymm, |
z: zpp |
}, { |
x: xmm, |
y: ymm, |
z: zpp |
}], |
enabled: frame.back.visible |
} |
] |
}); |
this.frameShapes.front[verb]({ |
'class': 'highcharts-3d-frame highcharts-3d-frame-front', |
zIndex: frame.front.frontFacing ? -1000 : 1000, |
faces: [{ //bottom |
fill: H.color(frame.front.color).brighten(0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ypp, |
z: zmm |
}, { |
x: xpp, |
y: ypp, |
z: zmm |
}, { |
x: xp, |
y: yp, |
z: zm |
}, { |
x: xm, |
y: yp, |
z: zm |
}], |
enabled: frame.front.visible && !frame.bottom.visible |
}, |
{ //top |
fill: H.color(frame.front.color).brighten(0.1).get(), |
vertexes: [{ |
x: xpp, |
y: ymm, |
z: zmm |
}, { |
x: xmm, |
y: ymm, |
z: zmm |
}, { |
x: xm, |
y: ym, |
z: zm |
}, { |
x: xp, |
y: ym, |
z: zm |
}], |
enabled: frame.front.visible && !frame.top.visible |
}, |
{ //left |
fill: H.color(frame.front.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xmm, |
y: ymm, |
z: zmm |
}, { |
x: xmm, |
y: ypp, |
z: zmm |
}, { |
x: xm, |
y: yp, |
z: zm |
}, { |
x: xm, |
y: ym, |
z: zm |
}], |
enabled: frame.front.visible && !frame.left.visible |
}, |
{ //right |
fill: H.color(frame.front.color).brighten(-0.1).get(), |
vertexes: [{ |
x: xpp, |
y: ypp, |
z: zmm |
}, { |
x: xpp, |
y: ymm, |
z: zmm |
}, { |
x: xp, |
y: ym, |
z: zm |
}, { |
x: xp, |
y: yp, |
z: zm |
}], |
enabled: frame.front.visible && !frame.right.visible |
}, |
{ //front |
fill: H.color(frame.front.color).get(), |
vertexes: [{ |
x: xp, |
y: ym, |
z: zm |
}, { |
x: xm, |
y: ym, |
z: zm |
}, { |
x: xm, |
y: yp, |
z: zm |
}, { |
x: xp, |
y: yp, |
z: zm |
}], |
enabled: frame.front.visible |
}, |
{ //back |
fill: H.color(frame.front.color).get(), |
vertexes: [{ |
x: xpp, |
y: ypp, |
z: zmm |
}, { |
x: xmm, |
y: ypp, |
z: zmm |
}, { |
x: xmm, |
y: ymm, |
z: zmm |
}, { |
x: xpp, |
y: ymm, |
z: zmm |
}], |
enabled: frame.front.visible |
} |
] |
}); |
} |
|
return proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
Chart.prototype.retrieveStacks = function(stacking) { |
var series = this.series, |
stacks = {}, |
stackNumber, |
i = 1; |
|
each(this.series, function(s) { |
stackNumber = pick(s.options.stack, (stacking ? 0 : series.length - 1 - s.index)); // #3841, #4532 |
if (!stacks[stackNumber]) { |
stacks[stackNumber] = { |
series: [s], |
position: i |
}; |
i++; |
} else { |
stacks[stackNumber].series.push(s); |
} |
}); |
|
stacks.totalStacks = i + 1; |
return stacks; |
}; |
|
Chart.prototype.get3dFrame = function() { |
var chart = this, |
options3d = chart.options.chart.options3d, |
frameOptions = options3d.frame, |
xm = chart.plotLeft, |
xp = chart.plotLeft + chart.plotWidth, |
ym = chart.plotTop, |
yp = chart.plotTop + chart.plotHeight, |
zm = 0, |
zp = options3d.depth, |
bottomOrientation = H.shapeArea3d([{ |
x: xm, |
y: yp, |
z: zp |
}, { |
x: xp, |
y: yp, |
z: zp |
}, { |
x: xp, |
y: yp, |
z: zm |
}, { |
x: xm, |
y: yp, |
z: zm |
}], chart), |
topOrientation = H.shapeArea3d([{ |
x: xm, |
y: ym, |
z: zm |
}, { |
x: xp, |
y: ym, |
z: zm |
}, { |
x: xp, |
y: ym, |
z: zp |
}, { |
x: xm, |
y: ym, |
z: zp |
}], chart), |
leftOrientation = H.shapeArea3d([{ |
x: xm, |
y: ym, |
z: zm |
}, { |
x: xm, |
y: ym, |
z: zp |
}, { |
x: xm, |
y: yp, |
z: zp |
}, { |
x: xm, |
y: yp, |
z: zm |
}], chart), |
rightOrientation = H.shapeArea3d([{ |
x: xp, |
y: ym, |
z: zp |
}, { |
x: xp, |
y: ym, |
z: zm |
}, { |
x: xp, |
y: yp, |
z: zm |
}, { |
x: xp, |
y: yp, |
z: zp |
}], chart), |
frontOrientation = H.shapeArea3d([{ |
x: xm, |
y: yp, |
z: zm |
}, { |
x: xp, |
y: yp, |
z: zm |
}, { |
x: xp, |
y: ym, |
z: zm |
}, { |
x: xm, |
y: ym, |
z: zm |
}], chart), |
backOrientation = H.shapeArea3d([{ |
x: xm, |
y: ym, |
z: zp |
}, { |
x: xp, |
y: ym, |
z: zp |
}, { |
x: xp, |
y: yp, |
z: zp |
}, { |
x: xm, |
y: yp, |
z: zp |
}], chart), |
defaultShowBottom = false, |
defaultShowTop = false, |
defaultShowLeft = false, |
defaultShowRight = false, |
defaultShowFront = false, |
defaultShowBack = true; |
|
// The 'default' criteria to visible faces of the frame is looking up every |
// axis to decide whenever the left/right//top/bottom sides of the frame |
// will be shown |
each([].concat(chart.xAxis, chart.yAxis, chart.zAxis), function(axis) { |
if (axis) { |
if (axis.horiz) { |
if (axis.opposite) { |
defaultShowTop = true; |
} else { |
defaultShowBottom = true; |
} |
} else { |
if (axis.opposite) { |
defaultShowRight = true; |
} else { |
defaultShowLeft = true; |
} |
} |
} |
}); |
|
var getFaceOptions = function(sources, faceOrientation, defaultVisible) { |
var faceAttrs = ['size', 'color', 'visible']; |
var options = {}; |
for (var i = 0; i < faceAttrs.length; i++) { |
var attr = faceAttrs[i]; |
for (var j = 0; j < sources.length; j++) { |
if (typeof sources[j] === 'object') { |
var val = sources[j][attr]; |
if (val !== undefined && val !== null) { |
options[attr] = val; |
break; |
} |
} |
} |
} |
var isVisible = defaultVisible; |
if (options.visible === true || options.visible === false) { |
isVisible = options.visible; |
} else if (options.visible === 'auto') { |
isVisible = faceOrientation >= 0; |
} |
|
return { |
size: pick(options.size, 1), |
color: pick(options.color, 'none'), |
frontFacing: faceOrientation > 0, |
visible: isVisible |
}; |
}; |
|
// docs @TODO: Add all frame options (left, right, top, bottom, front, back) to |
// apioptions JSDoc once the new system is up. |
var ret = { |
// FIXME: Previously, left/right, top/bottom and front/back pairs shared |
// size and color. |
// For compatibility and consistency sake, when one face have |
// size/color/visibility set, the opposite face will default to the same |
// values. Also, left/right used to be called 'side', so that's also |
// added as a fallback |
bottom: getFaceOptions( |
[frameOptions.bottom, frameOptions.top, frameOptions], |
bottomOrientation, |
defaultShowBottom |
), |
top: getFaceOptions( |
[frameOptions.top, frameOptions.bottom, frameOptions], |
topOrientation, |
defaultShowTop |
), |
left: getFaceOptions( |
[ |
frameOptions.left, |
frameOptions.right, |
frameOptions.side, |
frameOptions |
], |
leftOrientation, |
defaultShowLeft |
), |
right: getFaceOptions( |
[ |
frameOptions.right, |
frameOptions.left, |
frameOptions.side, |
frameOptions |
], |
rightOrientation, |
defaultShowRight |
), |
back: getFaceOptions( |
[frameOptions.back, frameOptions.front, frameOptions], |
backOrientation, |
defaultShowBack |
), |
front: getFaceOptions( |
[frameOptions.front, frameOptions.back, frameOptions], |
frontOrientation, |
defaultShowFront |
) |
}; |
|
|
// Decide the bast place to put axis title/labels based on the visible faces. |
// Ideally, The labels can only be on the edge between a visible face and an invisble one. |
// Also, the Y label should be one the left-most edge (right-most if opposite), |
if (options3d.axisLabelPosition === 'auto') { |
var isValidEdge = function(face1, face2) { |
return (face1.visible !== face2.visible) || |
(face1.visible && face2.visible && (face1.frontFacing !== face2.frontFacing)); |
}; |
|
var yEdges = []; |
if (isValidEdge(ret.left, ret.front)) { |
yEdges.push({ |
y: (ym + yp) / 2, |
x: xm, |
z: zm |
}); |
} |
if (isValidEdge(ret.left, ret.back)) { |
yEdges.push({ |
y: (ym + yp) / 2, |
x: xm, |
z: zp |
}); |
} |
if (isValidEdge(ret.right, ret.front)) { |
yEdges.push({ |
y: (ym + yp) / 2, |
x: xp, |
z: zm |
}); |
} |
if (isValidEdge(ret.right, ret.back)) { |
yEdges.push({ |
y: (ym + yp) / 2, |
x: xp, |
z: zp |
}); |
} |
|
var xBottomEdges = []; |
if (isValidEdge(ret.bottom, ret.front)) { |
xBottomEdges.push({ |
x: (xm + xp) / 2, |
y: yp, |
z: zm |
}); |
} |
if (isValidEdge(ret.bottom, ret.back)) { |
xBottomEdges.push({ |
x: (xm + xp) / 2, |
y: yp, |
z: zp |
}); |
} |
|
var xTopEdges = []; |
if (isValidEdge(ret.top, ret.front)) { |
xTopEdges.push({ |
x: (xm + xp) / 2, |
y: ym, |
z: zm |
}); |
} |
if (isValidEdge(ret.top, ret.back)) { |
xTopEdges.push({ |
x: (xm + xp) / 2, |
y: ym, |
z: zp |
}); |
} |
|
var zBottomEdges = []; |
if (isValidEdge(ret.bottom, ret.left)) { |
zBottomEdges.push({ |
z: (zm + zp) / 2, |
y: yp, |
x: xm |
}); |
} |
if (isValidEdge(ret.bottom, ret.right)) { |
zBottomEdges.push({ |
z: (zm + zp) / 2, |
y: yp, |
x: xp |
}); |
} |
|
var zTopEdges = []; |
if (isValidEdge(ret.top, ret.left)) { |
zTopEdges.push({ |
z: (zm + zp) / 2, |
y: ym, |
x: xm |
}); |
} |
if (isValidEdge(ret.top, ret.right)) { |
zTopEdges.push({ |
z: (zm + zp) / 2, |
y: ym, |
x: xp |
}); |
} |
|
var pickEdge = function(edges, axis, mult) { |
if (edges.length === 0) { |
return null; |
} else if (edges.length === 1) { |
return edges[0]; |
} |
var best = 0, |
projections = perspective(edges, chart, false); |
for (var i = 1; i < projections.length; i++) { |
if (mult * projections[i][axis] > mult * projections[best][axis]) { |
best = i; |
} else if ((mult * projections[i][axis] === mult * projections[best][axis]) && (projections[i].z < projections[best].z)) { |
best = i; |
} |
} |
return edges[best]; |
}; |
ret.axes = { |
y: { |
'left': pickEdge(yEdges, 'x', -1), |
'right': pickEdge(yEdges, 'x', +1) |
}, |
x: { |
'top': pickEdge(xTopEdges, 'y', -1), |
'bottom': pickEdge(xBottomEdges, 'y', +1) |
}, |
z: { |
'top': pickEdge(zTopEdges, 'y', -1), |
'bottom': pickEdge(zBottomEdges, 'y', +1) |
} |
}; |
} else { |
ret.axes = { |
y: { |
'left': { |
x: xm, |
z: zm |
}, |
'right': { |
x: xp, |
z: zm |
} |
}, |
x: { |
'top': { |
y: ym, |
z: zm |
}, |
'bottom': { |
y: yp, |
z: zm |
} |
}, |
z: { |
'top': { |
x: defaultShowLeft ? xp : xm, |
y: ym |
}, |
'bottom': { |
x: defaultShowLeft ? xp : xm, |
y: yp |
} |
} |
}; |
} |
|
return ret; |
}; |
|
|
}(Highcharts)); |
(function(H) { |
/** |
* (c) 2010-2017 Torstein Honsi |
* |
* License: www.highcharts.com/license |
*/ |
var ZAxis, |
|
Axis = H.Axis, |
Chart = H.Chart, |
each = H.each, |
extend = H.extend, |
merge = H.merge, |
perspective = H.perspective, |
pick = H.pick, |
splat = H.splat, |
Tick = H.Tick, |
wrap = H.wrap; |
/*** |
EXTENSION TO THE AXIS |
***/ |
wrap(Axis.prototype, 'setOptions', function(proceed, userOptions) { |
var options; |
proceed.call(this, userOptions); |
if (this.chart.is3d() && this.coll !== 'colorAxis') { |
options = this.options; |
options.tickWidth = pick(options.tickWidth, 0); |
options.gridLineWidth = pick(options.gridLineWidth, 1); |
} |
}); |
|
wrap(Axis.prototype, 'getPlotLinePath', function(proceed) { |
var path = proceed.apply(this, [].slice.call(arguments, 1)); |
|
// Do not do this if the chart is not 3D |
if (!this.chart.is3d() || this.coll === 'colorAxis') { |
return path; |
} |
|
if (path === null) { |
return path; |
} |
|
var chart = this.chart, |
options3d = chart.options.chart.options3d, |
d = this.isZAxis ? chart.plotWidth : options3d.depth, |
frame = chart.frame3d; |
|
var pArr = [ |
this.swapZ({ |
x: path[1], |
y: path[2], |
z: 0 |
}), |
this.swapZ({ |
x: path[1], |
y: path[2], |
z: d |
}), |
this.swapZ({ |
x: path[4], |
y: path[5], |
z: 0 |
}), |
this.swapZ({ |
x: path[4], |
y: path[5], |
z: d |
}) |
]; |
|
var pathSegments = []; |
if (!this.horiz) { // Y-Axis |
if (frame.front.visible) { |
pathSegments.push(pArr[0], pArr[2]); |
} |
if (frame.back.visible) { |
pathSegments.push(pArr[1], pArr[3]); |
} |
if (frame.left.visible) { |
pathSegments.push(pArr[0], pArr[1]); |
} |
if (frame.right.visible) { |
pathSegments.push(pArr[2], pArr[3]); |
} |
} else if (this.isZAxis) { // Z-Axis |
if (frame.left.visible) { |
pathSegments.push(pArr[0], pArr[2]); |
} |
if (frame.right.visible) { |
pathSegments.push(pArr[1], pArr[3]); |
} |
if (frame.top.visible) { |
pathSegments.push(pArr[0], pArr[1]); |
} |
if (frame.bottom.visible) { |
pathSegments.push(pArr[2], pArr[3]); |
} |
} else { // X-Axis |
if (frame.front.visible) { |
pathSegments.push(pArr[0], pArr[2]); |
} |
if (frame.back.visible) { |
pathSegments.push(pArr[1], pArr[3]); |
} |
if (frame.top.visible) { |
pathSegments.push(pArr[0], pArr[1]); |
} |
if (frame.bottom.visible) { |
pathSegments.push(pArr[2], pArr[3]); |
} |
} |
|
pathSegments = perspective(pathSegments, this.chart, false); |
|
return this.chart.renderer.toLineSegments(pathSegments); |
}); |
|
// Do not draw axislines in 3D |
wrap(Axis.prototype, 'getLinePath', function(proceed) { |
return this.chart.is3d() ? [] : proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
wrap(Axis.prototype, 'getPlotBandPath', function(proceed) { |
// Do not do this if the chart is not 3D |
if (!this.chart.is3d() || this.coll === 'colorAxis') { |
return proceed.apply(this, [].slice.call(arguments, 1)); |
} |
|
var args = arguments, |
from = args[1], |
to = args[2], |
path = [], |
fromPath = this.getPlotLinePath(from), |
toPath = this.getPlotLinePath(to); |
|
if (fromPath && toPath) { |
for (var i = 0; i < fromPath.length; i += 6) { |
path.push( |
'M', fromPath[i + 1], fromPath[i + 2], |
'L', fromPath[i + 4], fromPath[i + 5], |
'L', toPath[i + 4], toPath[i + 5], |
'L', toPath[i + 1], toPath[i + 2], |
'Z'); |
} |
} |
|
return path; |
}); |
|
|
function fix3dPosition(axis, pos) { |
if (axis.chart.is3d() && axis.coll !== 'colorAxis') { |
var chart = axis.chart, |
frame = chart.frame3d, |
plotLeft = chart.plotLeft, |
plotRight = chart.plotWidth + plotLeft, |
plotTop = chart.plotTop, |
plotBottom = chart.plotHeight + plotTop, |
dx = 0, |
dy = 0; |
|
pos = axis.swapZ({ |
x: pos.x, |
y: pos.y, |
z: 0 |
}); |
|
|
if (axis.isZAxis) { // Z Axis |
if (axis.opposite) { |
if (frame.axes.z.top === null) { |
return {}; |
} |
dy = pos.y - plotTop; |
pos.x = frame.axes.z.top.x; |
pos.y = frame.axes.z.top.y; |
} else { |
if (frame.axes.z.bottom === null) { |
return {}; |
} |
dy = pos.y - plotBottom; |
pos.x = frame.axes.z.bottom.x; |
pos.y = frame.axes.z.bottom.y; |
} |
} else if (axis.horiz) { // X Axis |
if (axis.opposite) { |
if (frame.axes.x.top === null) { |
return {}; |
} |
dy = pos.y - plotTop; |
pos.y = frame.axes.x.top.y; |
pos.z = frame.axes.x.top.z; |
} else { |
if (frame.axes.x.bottom === null) { |
return {}; |
} |
dy = pos.y - plotBottom; |
pos.y = frame.axes.x.bottom.y; |
pos.z = frame.axes.x.bottom.z; |
} |
} else { //Y Axis |
if (axis.opposite) { |
if (frame.axes.y.right === null) { |
return {}; |
} |
dx = pos.x - plotRight; |
pos.x = frame.axes.y.right.x; |
pos.z = frame.axes.y.right.z; |
} else { |
if (frame.axes.y.left === null) { |
return {}; |
} |
dx = pos.x - plotLeft; |
pos.x = frame.axes.y.left.x; |
pos.z = frame.axes.y.left.z; |
} |
} |
pos = perspective([pos], axis.chart)[0]; |
pos.x += dx; |
pos.y += dy; |
} |
return pos; |
} |
|
/*** |
EXTENSION TO THE TICKS |
***/ |
|
wrap(Tick.prototype, 'getMarkPath', function(proceed) { |
var path = proceed.apply(this, [].slice.call(arguments, 1)); |
|
var pArr = [ |
fix3dPosition(this.axis, { |
x: path[1], |
y: path[2], |
z: 0 |
}), |
fix3dPosition(this.axis, { |
x: path[4], |
y: path[5], |
z: 0 |
}) |
]; |
|
return this.axis.chart.renderer.toLineSegments(pArr); |
}); |
|
wrap(Tick.prototype, 'getLabelPosition', function(proceed) { |
var pos = proceed.apply(this, [].slice.call(arguments, 1)); |
return fix3dPosition(this.axis, pos); |
}); |
|
H.wrap(Axis.prototype, 'getTitlePosition', function(proceed) { |
var pos = proceed.apply(this, [].slice.call(arguments, 1)); |
return fix3dPosition(this, pos); |
}); |
|
wrap(Axis.prototype, 'drawCrosshair', function(proceed) { |
var args = arguments; |
if (this.chart.is3d()) { |
if (args[2]) { |
args[2] = { |
plotX: args[2].plotXold || args[2].plotX, |
plotY: args[2].plotYold || args[2].plotY |
}; |
} |
} |
proceed.apply(this, [].slice.call(args, 1)); |
}); |
|
wrap(Axis.prototype, 'destroy', function(proceed) { |
each(['backFrame', 'bottomFrame', 'sideFrame'], function(prop) { |
if (this[prop]) { |
this[prop] = this[prop].destroy(); |
} |
}, this); |
proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
/*** |
Z-AXIS |
***/ |
|
Axis.prototype.swapZ = function(p, insidePlotArea) { |
if (this.isZAxis) { |
var plotLeft = insidePlotArea ? 0 : this.chart.plotLeft; |
return { |
x: plotLeft + p.z, |
y: p.y, |
z: p.x - plotLeft |
}; |
} |
return p; |
}; |
|
ZAxis = H.ZAxis = function() { |
this.init.apply(this, arguments); |
}; |
extend(ZAxis.prototype, Axis.prototype); |
extend(ZAxis.prototype, { |
isZAxis: true, |
setOptions: function(userOptions) { |
userOptions = merge({ |
offset: 0, |
lineWidth: 0 |
}, userOptions); |
Axis.prototype.setOptions.call(this, userOptions); |
this.coll = 'zAxis'; |
}, |
setAxisSize: function() { |
Axis.prototype.setAxisSize.call(this); |
this.width = this.len = this.chart.options.chart.options3d.depth; |
this.right = this.chart.chartWidth - this.width - this.left; |
}, |
getSeriesExtremes: function() { |
var axis = this, |
chart = axis.chart; |
|
axis.hasVisibleSeries = false; |
|
// Reset properties in case we're redrawing (#3353) |
axis.dataMin = axis.dataMax = axis.ignoreMinPadding = axis.ignoreMaxPadding = null; |
|
if (axis.buildStacks) { |
axis.buildStacks(); |
} |
|
// loop through this axis' series |
each(axis.series, function(series) { |
|
if (series.visible || !chart.options.chart.ignoreHiddenSeries) { |
|
var seriesOptions = series.options, |
zData, |
threshold = seriesOptions.threshold; |
|
axis.hasVisibleSeries = true; |
|
// Validate threshold in logarithmic axes |
if (axis.positiveValuesOnly && threshold <= 0) { |
threshold = null; |
} |
|
zData = series.zData; |
if (zData.length) { |
axis.dataMin = Math.min(pick(axis.dataMin, zData[0]), Math.min.apply(null, zData)); |
axis.dataMax = Math.max(pick(axis.dataMax, zData[0]), Math.max.apply(null, zData)); |
} |
} |
}); |
} |
}); |
|
|
/** |
* Extend the chart getAxes method to also get the color axis |
*/ |
wrap(Chart.prototype, 'getAxes', function(proceed) { |
var chart = this, |
options = this.options, |
zAxisOptions = options.zAxis = splat(options.zAxis || {}); |
|
proceed.call(this); |
|
if (!chart.is3d()) { |
return; |
} |
this.zAxis = []; |
each(zAxisOptions, function(axisOptions, i) { |
axisOptions.index = i; |
axisOptions.isX = true; //Z-Axis is shown horizontally, so it's kind of a X-Axis |
var zAxis = new ZAxis(chart, axisOptions); |
zAxis.setScale(); |
}); |
}); |
|
}(Highcharts)); |
(function(H) { |
/** |
* (c) 2010-2017 Torstein Honsi |
* |
* License: www.highcharts.com/license |
*/ |
var each = H.each, |
perspective = H.perspective, |
pick = H.pick, |
Series = H.Series, |
seriesTypes = H.seriesTypes, |
inArray = H.inArray, |
svg = H.svg, |
wrap = H.wrap; |
|
/*** |
EXTENSION FOR 3D COLUMNS |
***/ |
wrap(seriesTypes.column.prototype, 'translate', function(proceed) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
|
// Do not do this if the chart is not 3D |
if (!this.chart.is3d()) { |
return; |
} |
|
var series = this, |
chart = series.chart, |
seriesOptions = series.options, |
depth = seriesOptions.depth || 25, |
stack = seriesOptions.stacking ? |
(seriesOptions.stack || 0) : |
series.index, // #4743 |
z = stack * (depth + (seriesOptions.groupZPadding || 1)), |
borderCrisp = series.borderWidth % 2 ? 0.5 : 0; |
|
if (chart.inverted && !series.yAxis.reversed) { |
borderCrisp *= -1; |
} |
|
if (seriesOptions.grouping !== false) { |
z = 0; |
} |
|
z += (seriesOptions.groupZPadding || 1); |
each(series.data, function(point) { |
if (point.y !== null) { |
var shapeArgs = point.shapeArgs, |
tooltipPos = point.tooltipPos, |
// Array for final shapeArgs calculation. |
// We are checking two dimensions (x and y). |
dimensions = [ |
['x', 'width'], |
['y', 'height'] |
], |
borderlessBase; // Crisped rects can have +/- 0.5 pixels offset. |
|
// #3131 We need to check if column is inside plotArea. |
each(dimensions, function(d) { |
borderlessBase = shapeArgs[d[0]] - borderCrisp; |
if (borderlessBase < 0) { |
// If borderLessBase is smaller than 0, it is needed to set |
// its value to 0 or 0.5 depending on borderWidth |
// borderWidth may be even or odd. |
shapeArgs[d[1]] += shapeArgs[d[0]] + borderCrisp; |
shapeArgs[d[0]] = -borderCrisp; |
borderlessBase = 0; |
} |
if ( |
borderlessBase + shapeArgs[d[1]] > series[d[0] + 'Axis'].len && |
shapeArgs[d[1]] !== 0 // Do not change height/width of column if 0. |
// #6708 |
) { |
shapeArgs[d[1]] = series[d[0] + 'Axis'].len - shapeArgs[d[0]]; |
} |
if ( |
(shapeArgs[d[1]] !== 0) && // Do not remove columns with zero height/width. |
( |
shapeArgs[d[0]] >= series[d[0] + 'Axis'].len || |
shapeArgs[d[0]] + shapeArgs[d[1]] <= borderCrisp |
) |
) { |
for (var key in shapeArgs) { // Set args to 0 if column is outside the chart. |
shapeArgs[key] = 0; |
} |
} |
}); |
|
point.shapeType = 'cuboid'; |
shapeArgs.z = z; |
shapeArgs.depth = depth; |
shapeArgs.insidePlotArea = true; |
|
// Translate the tooltip position in 3d space |
tooltipPos = perspective([{ |
x: tooltipPos[0], |
y: tooltipPos[1], |
z: z |
}], chart, true)[0]; |
point.tooltipPos = [tooltipPos.x, tooltipPos.y]; |
} |
}); |
// store for later use #4067 |
series.z = z; |
}); |
|
wrap(seriesTypes.column.prototype, 'animate', function(proceed) { |
if (!this.chart.is3d()) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
} else { |
var args = arguments, |
init = args[1], |
yAxis = this.yAxis, |
series = this, |
reversed = this.yAxis.reversed; |
|
if (svg) { // VML is too slow anyway |
if (init) { |
each(series.data, function(point) { |
if (point.y !== null) { |
point.height = point.shapeArgs.height; |
point.shapey = point.shapeArgs.y; //#2968 |
point.shapeArgs.height = 1; |
if (!reversed) { |
if (point.stackY) { |
point.shapeArgs.y = point.plotY + yAxis.translate(point.stackY); |
} else { |
point.shapeArgs.y = point.plotY + (point.negative ? -point.height : point.height); |
} |
} |
} |
}); |
|
} else { // run the animation |
each(series.data, function(point) { |
if (point.y !== null) { |
point.shapeArgs.height = point.height; |
point.shapeArgs.y = point.shapey; //#2968 |
// null value do not have a graphic |
if (point.graphic) { |
point.graphic.animate(point.shapeArgs, series.options.animation); |
} |
} |
}); |
|
// redraw datalabels to the correct position |
this.drawDataLabels(); |
|
// delete this function to allow it only once |
series.animate = null; |
} |
} |
} |
}); |
|
/* |
* In case of 3d columns there is no sense to add this columns |
* to a specific series group - if series is added to a group |
* all columns will have the same zIndex in comparison with different series |
*/ |
|
wrap(seriesTypes.column.prototype, 'plotGroup', function(proceed, prop, name, visibility, zIndex, parent) { |
if (this.chart.is3d() && parent && !this[prop]) { |
this[prop] = parent; |
parent.attr(this.getPlotBox()); |
this[prop].survive = true; |
} |
return proceed.apply(this, Array.prototype.slice.call(arguments, 1)); |
}); |
|
/* |
* When series is not added to group it is needed to change |
* setVisible method to allow correct Legend funcionality |
* This wrap is basing on pie chart series |
*/ |
wrap(seriesTypes.column.prototype, 'setVisible', function(proceed, vis) { |
var series = this, |
pointVis; |
if (series.chart.is3d()) { |
each(series.data, function(point) { |
point.visible = point.options.visible = vis = vis === undefined ? !point.visible : vis; |
pointVis = vis ? 'visible' : 'hidden'; |
series.options.data[inArray(point, series.data)] = point.options; |
if (point.graphic) { |
point.graphic.attr({ |
visibility: pointVis |
}); |
} |
}); |
} |
proceed.apply(this, Array.prototype.slice.call(arguments, 1)); |
}); |
|
wrap(seriesTypes.column.prototype, 'init', function(proceed) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
|
if (this.chart.is3d()) { |
var seriesOptions = this.options, |
grouping = seriesOptions.grouping, |
stacking = seriesOptions.stacking, |
reversedStacks = pick(this.yAxis.options.reversedStacks, true), |
z = 0; |
|
if (!(grouping !== undefined && !grouping)) { |
var stacks = this.chart.retrieveStacks(stacking), |
stack = seriesOptions.stack || 0, |
i; // position within the stack |
for (i = 0; i < stacks[stack].series.length; i++) { |
if (stacks[stack].series[i] === this) { |
break; |
} |
} |
z = (10 * (stacks.totalStacks - stacks[stack].position)) + (reversedStacks ? i : -i); // #4369 |
|
// In case when axis is reversed, columns are also reversed inside the group (#3737) |
if (!this.xAxis.reversed) { |
z = (stacks.totalStacks * 10) - z; |
} |
} |
|
seriesOptions.zIndex = z; |
} |
}); |
|
|
|
wrap(Series.prototype, 'alignDataLabel', function(proceed) { |
|
// Only do this for 3D columns and columnranges |
if (this.chart.is3d() && (this.type === 'column' || this.type === 'columnrange')) { |
var series = this, |
chart = series.chart; |
|
var args = arguments, |
alignTo = args[4]; |
|
var pos = ({ |
x: alignTo.x, |
y: alignTo.y, |
z: series.z |
}); |
pos = perspective([pos], chart, true)[0]; |
alignTo.x = pos.x; |
alignTo.y = pos.y; |
} |
|
proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
// Added stackLabels position calculation for 3D charts. |
wrap(H.StackItem.prototype, 'getStackBox', function(proceed, chart) { // #3946 |
var stackBox = proceed.apply(this, [].slice.call(arguments, 1)); |
|
// Only do this for 3D chart. |
if (chart.is3d()) { |
var pos = ({ |
x: stackBox.x, |
y: stackBox.y, |
z: 0 |
}); |
pos = H.perspective([pos], chart, true)[0]; |
stackBox.x = pos.x; |
stackBox.y = pos.y; |
} |
|
return stackBox; |
}); |
|
/*** |
EXTENSION FOR 3D CYLINDRICAL COLUMNS |
Not supported |
***/ |
/* |
var defaultOptions = H.getOptions(); |
defaultOptions.plotOptions.cylinder = H.merge(defaultOptions.plotOptions.column); |
var CylinderSeries = H.extendClass(seriesTypes.column, { |
type: 'cylinder' |
}); |
seriesTypes.cylinder = CylinderSeries; |
|
wrap(seriesTypes.cylinder.prototype, 'translate', function (proceed) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
|
// Do not do this if the chart is not 3D |
if (!this.chart.is3d()) { |
return; |
} |
|
var series = this, |
chart = series.chart, |
options = chart.options, |
cylOptions = options.plotOptions.cylinder, |
options3d = options.chart.options3d, |
depth = cylOptions.depth || 0, |
alpha = chart.alpha3d; |
|
var z = cylOptions.stacking ? (this.options.stack || 0) * depth : series._i * depth; |
z += depth / 2; |
|
if (cylOptions.grouping !== false) { z = 0; } |
|
each(series.data, function (point) { |
var shapeArgs = point.shapeArgs, |
deg2rad = H.deg2rad; |
point.shapeType = 'arc3d'; |
shapeArgs.x += depth / 2; |
shapeArgs.z = z; |
shapeArgs.start = 0; |
shapeArgs.end = 2 * PI; |
shapeArgs.r = depth * 0.95; |
shapeArgs.innerR = 0; |
shapeArgs.depth = shapeArgs.height * (1 / sin((90 - alpha) * deg2rad)) - z; |
shapeArgs.alpha = 90 - alpha; |
shapeArgs.beta = 0; |
}); |
}); |
*/ |
|
}(Highcharts)); |
(function(H) { |
/** |
* (c) 2010-2017 Torstein Honsi |
* |
* License: www.highcharts.com/license |
*/ |
var deg2rad = H.deg2rad, |
each = H.each, |
pick = H.pick, |
seriesTypes = H.seriesTypes, |
svg = H.svg, |
wrap = H.wrap; |
|
/*** |
EXTENSION FOR 3D PIES |
***/ |
|
wrap(seriesTypes.pie.prototype, 'translate', function(proceed) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
|
// Do not do this if the chart is not 3D |
if (!this.chart.is3d()) { |
return; |
} |
|
var series = this, |
seriesOptions = series.options, |
depth = seriesOptions.depth || 0, |
options3d = series.chart.options.chart.options3d, |
alpha = options3d.alpha, |
beta = options3d.beta, |
z = seriesOptions.stacking ? (seriesOptions.stack || 0) * depth : series._i * depth; |
|
z += depth / 2; |
|
if (seriesOptions.grouping !== false) { |
z = 0; |
} |
|
each(series.data, function(point) { |
|
var shapeArgs = point.shapeArgs, |
angle; |
|
point.shapeType = 'arc3d'; |
|
shapeArgs.z = z; |
shapeArgs.depth = depth * 0.75; |
shapeArgs.alpha = alpha; |
shapeArgs.beta = beta; |
shapeArgs.center = series.center; |
|
angle = (shapeArgs.end + shapeArgs.start) / 2; |
|
point.slicedTranslation = { |
translateX: Math.round(Math.cos(angle) * seriesOptions.slicedOffset * Math.cos(alpha * deg2rad)), |
translateY: Math.round(Math.sin(angle) * seriesOptions.slicedOffset * Math.cos(alpha * deg2rad)) |
}; |
}); |
}); |
|
wrap(seriesTypes.pie.prototype.pointClass.prototype, 'haloPath', function(proceed) { |
var args = arguments; |
return this.series.chart.is3d() ? [] : proceed.call(this, args[1]); |
}); |
|
|
|
wrap(seriesTypes.pie.prototype, 'drawPoints', function(proceed) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
|
if (this.chart.is3d()) { |
each(this.points, function(point) { |
var graphic = point.graphic; |
|
// #4584 Check if has graphic - null points don't have it |
if (graphic) { |
// Hide null or 0 points (#3006, 3650) |
graphic[point.y && point.visible ? 'show' : 'hide'](); |
} |
}); |
} |
}); |
|
wrap(seriesTypes.pie.prototype, 'drawDataLabels', function(proceed) { |
if (this.chart.is3d()) { |
var series = this, |
chart = series.chart, |
options3d = chart.options.chart.options3d; |
each(series.data, function(point) { |
var shapeArgs = point.shapeArgs, |
r = shapeArgs.r, |
a1 = (shapeArgs.alpha || options3d.alpha) * deg2rad, //#3240 issue with datalabels for 0 and null values |
b1 = (shapeArgs.beta || options3d.beta) * deg2rad, |
a2 = (shapeArgs.start + shapeArgs.end) / 2, |
labelPos = point.labelPos, |
labelIndexes = [0, 2, 4], // [x1, y1, x2, y2, x3, y3] |
yOffset = (-r * (1 - Math.cos(a1)) * Math.sin(a2)), // + (sin(a2) > 0 ? sin(a1) * d : 0) |
xOffset = r * (Math.cos(b1) - 1) * Math.cos(a2); |
|
// Apply perspective on label positions |
each(labelIndexes, function(index) { |
labelPos[index] += xOffset; |
labelPos[index + 1] += yOffset; |
}); |
}); |
} |
|
proceed.apply(this, [].slice.call(arguments, 1)); |
}); |
|
wrap(seriesTypes.pie.prototype, 'addPoint', function(proceed) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
if (this.chart.is3d()) { |
// destroy (and rebuild) everything!!! |
this.update(this.userOptions, true); // #3845 pass the old options |
} |
}); |
|
wrap(seriesTypes.pie.prototype, 'animate', function(proceed) { |
if (!this.chart.is3d()) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
} else { |
var args = arguments, |
init = args[1], |
animation = this.options.animation, |
attribs, |
center = this.center, |
group = this.group, |
markerGroup = this.markerGroup; |
|
if (svg) { // VML is too slow anyway |
|
if (animation === true) { |
animation = {}; |
} |
// Initialize the animation |
if (init) { |
|
// Scale down the group and place it in the center |
group.oldtranslateX = group.translateX; |
group.oldtranslateY = group.translateY; |
attribs = { |
translateX: center[0], |
translateY: center[1], |
scaleX: 0.001, // #1499 |
scaleY: 0.001 |
}; |
|
group.attr(attribs); |
if (markerGroup) { |
markerGroup.attrSetters = group.attrSetters; |
markerGroup.attr(attribs); |
} |
|
// Run the animation |
} else { |
attribs = { |
translateX: group.oldtranslateX, |
translateY: group.oldtranslateY, |
scaleX: 1, |
scaleY: 1 |
}; |
group.animate(attribs, animation); |
|
if (markerGroup) { |
markerGroup.animate(attribs, animation); |
} |
|
// Delete this function to allow it only once |
this.animate = null; |
} |
|
} |
} |
}); |
|
}(Highcharts)); |
(function(H) { |
/** |
* (c) 2010-2017 Torstein Honsi |
* |
* License: www.highcharts.com/license |
*/ |
var perspective = H.perspective, |
pick = H.pick, |
Point = H.Point, |
seriesTypes = H.seriesTypes, |
wrap = H.wrap; |
|
/*** |
EXTENSION FOR 3D SCATTER CHART |
***/ |
|
wrap(seriesTypes.scatter.prototype, 'translate', function(proceed) { |
//function translate3d(proceed) { |
proceed.apply(this, [].slice.call(arguments, 1)); |
|
if (!this.chart.is3d()) { |
return; |
} |
|
var series = this, |
chart = series.chart, |
zAxis = pick(series.zAxis, chart.options.zAxis[0]), |
rawPoints = [], |
rawPoint, |
projectedPoints, |
projectedPoint, |
zValue, |
i; |
|
for (i = 0; i < series.data.length; i++) { |
rawPoint = series.data[i]; |
zValue = zAxis.isLog && zAxis.val2lin ? zAxis.val2lin(rawPoint.z) : rawPoint.z; // #4562 |
rawPoint.plotZ = zAxis.translate(zValue); |
|
rawPoint.isInside = rawPoint.isInside ? (zValue >= zAxis.min && zValue <= zAxis.max) : false; |
|
rawPoints.push({ |
x: rawPoint.plotX, |
y: rawPoint.plotY, |
z: rawPoint.plotZ |
}); |
} |
|
projectedPoints = perspective(rawPoints, chart, true); |
|
for (i = 0; i < series.data.length; i++) { |
rawPoint = series.data[i]; |
projectedPoint = projectedPoints[i]; |
|
rawPoint.plotXold = rawPoint.plotX; |
rawPoint.plotYold = rawPoint.plotY; |
rawPoint.plotZold = rawPoint.plotZ; |
|
rawPoint.plotX = projectedPoint.x; |
rawPoint.plotY = projectedPoint.y; |
rawPoint.plotZ = projectedPoint.z; |
|
} |
|
}); |
|
|
wrap(seriesTypes.scatter.prototype, 'init', function(proceed, chart, options) { |
if (chart.is3d()) { |
// add a third coordinate |
this.axisTypes = ['xAxis', 'yAxis', 'zAxis']; |
this.pointArrayMap = ['x', 'y', 'z']; |
this.parallelArrays = ['x', 'y', 'z']; |
|
// Require direct touch rather than using the k-d-tree, because the k-d-tree currently doesn't |
// take the xyz coordinate system into account (#4552) |
this.directTouch = true; |
} |
|
var result = proceed.apply(this, [chart, options]); |
|
if (this.chart.is3d()) { |
// Set a new default tooltip formatter |
var default3dScatterTooltip = 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>z: <b>{point.z}</b><br/>'; |
if (this.userOptions.tooltip) { |
this.tooltipOptions.pointFormat = this.userOptions.tooltip.pointFormat || default3dScatterTooltip; |
} else { |
this.tooltipOptions.pointFormat = default3dScatterTooltip; |
} |
} |
return result; |
}); |
|
/** |
* Updating zIndex for every point - based on the distance from point to camera |
*/ |
wrap(seriesTypes.scatter.prototype, 'pointAttribs', function(proceed, point) { |
var pointOptions = proceed.apply(this, [].slice.call(arguments, 1)); |
if (this.chart.is3d() && point) { |
pointOptions.zIndex = H.pointCameraDistance(point, this.chart); |
} |
return pointOptions; |
}); |
|
|
wrap(Point.prototype, 'applyOptions', function(proceed) { |
var point = proceed.apply(this, [].slice.call(arguments, 1)); |
|
if (this.series.chart.is3d() && point.z === undefined) { |
point.z = 0; |
} |
return point; |
}); |
|
}(Highcharts)); |
(function(H) { |
/** |
* (c) 2010-2017 Torstein Honsi |
* |
* License: www.highcharts.com/license |
*/ |
|
|
}(Highcharts)); |
})); |