123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- /* *
- *
- * (c) 2010-2021 Torstein Honsi
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- 'use strict';
- import H from '../Core/Globals.js';
- import U from '../Core/Utilities.js';
- var pick = U.pick;
- // Mathematical Functionility
- var deg2rad = H.deg2rad;
- /* eslint-disable max-len */
- /**
- * 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 |
- *
- * @private
- * @function rotate3D
- */
- /* eslint-enable max-len */
- /**
- * @private
- * @param {number} x
- * X coordinate
- * @param {number} y
- * Y coordinate
- * @param {number} z
- * Z coordinate
- * @param {Highcharts.Rotation3dObject} angles
- * Rotation angles
- * @return {Highcharts.Rotation3dObject}
- * Rotated position
- */
- 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
- };
- }
- /**
- * Perspective3D function is available in global Highcharts scope because is
- * needed also outside of perspective() function (#8042).
- * @private
- * @function Highcharts.perspective3D
- *
- * @param {Highcharts.Position3DObject} coordinate
- * 3D position
- *
- * @param {Highcharts.Position3DObject} origin
- * 3D root position
- *
- * @param {number} distance
- * Perspective distance
- *
- * @return {Highcharts.PositionObject}
- * Perspective 3D Position
- *
- * @requires highcharts-3d
- */
- 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
- };
- }
- H.perspective3D = perspective3D;
- /**
- * Transforms a given array of points according to the angles in chart.options.
- *
- * @private
- * @function Highcharts.perspective
- *
- * @param {Array<Highcharts.Position3DObject>} points
- * The array of points
- *
- * @param {Highcharts.Chart} chart
- * The chart
- *
- * @param {boolean} [insidePlotArea]
- * Whether to verify that the points are inside the plotArea
- *
- * @param {boolean} [useInvertedPersp]
- * Whether to use inverted perspective in calculations
- *
- * @return {Array<Highcharts.Position3DObject>}
- * An array of transformed points
- *
- * @requires highcharts-3d
- */
- function perspective(points, chart, insidePlotArea, useInvertedPersp) {
- var options3d = chart.options.chart.options3d,
- /* The useInvertedPersp argument is used for
- * inverted charts with already inverted elements,
- * such as dataLabels or tooltip positions.
- */
- inverted = pick(useInvertedPersp, 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 points.map(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),
- // Apply perspective
- coordinate = perspective3D(rotated, origin, origin.vd);
- // 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
- };
- });
- }
- H.perspective = perspective;
- /**
- * Calculate a distance from camera to points - made for calculating zIndex of
- * scatter points.
- *
- * @private
- * @function Highcharts.pointCameraDistance
- *
- * @param {Highcharts.Dictionary<number>} coordinates
- * Coordinates of the specific point
- *
- * @param {Highcharts.Chart} chart
- * Related chart
- *
- * @return {number}
- * Distance from camera to point
- *
- * @requires highcharts-3d
- */
- function pointCameraDistance(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
- },
- // Added support for objects with plotX or x coordinates.
- distance = Math.sqrt(Math.pow(cameraPosition.x - pick(coordinates.plotX, coordinates.x), 2) +
- Math.pow(cameraPosition.y - pick(coordinates.plotY, coordinates.y), 2) +
- Math.pow(cameraPosition.z - pick(coordinates.plotZ, coordinates.z), 2));
- return distance;
- }
- H.pointCameraDistance = pointCameraDistance;
- /**
- * Calculate area of a 2D polygon using Shoelace algorithm
- * https://en.wikipedia.org/wiki/Shoelace_formula
- *
- * @private
- * @function Highcharts.shapeArea
- *
- * @param {Array<Highcharts.PositionObject>} vertexes
- * 2D Polygon
- *
- * @return {number}
- * Calculated area
- *
- * @requires highcharts-3d
- */
- function shapeArea(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;
- }
- H.shapeArea = shapeArea;
- /**
- * Calculate area of a 3D polygon after perspective projection
- *
- * @private
- * @function Highcharts.shapeArea3d
- *
- * @param {Array<Highcharts.Position3DObject>} vertexes
- * 3D Polygon
- *
- * @param {Highcharts.Chart} chart
- * Related chart
- *
- * @param {boolean} [insidePlotArea]
- * Whether to verify that the points are inside the plotArea
- *
- * @return {number}
- * Calculated area
- *
- * @requires highcharts-3d
- */
- function shapeArea3D(vertexes, chart, insidePlotArea) {
- return shapeArea(perspective(vertexes, chart, insidePlotArea));
- }
- H.shapeArea3d = shapeArea3D;
- var mathModule = {
- perspective: perspective,
- perspective3D: perspective3D,
- pointCameraDistance: pointCameraDistance,
- shapeArea: shapeArea,
- shapeArea3D: shapeArea3D
- };
- export default mathModule;
|