/* *
 *
 *  (c) 2010-2020 Torstein Honsi
 *
 *  Extension for 3D charts
 *
 *  License: www.highcharts.com/license
 *
 *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
 *
 * */
'use strict';
import H from '../parts/Globals.js';
import U from '../parts/Utilities.js';
var addEvent = U.addEvent, isArray = U.isArray, merge = U.merge, pick = U.pick, wrap = U.wrap;
import '../parts/Chart.js';
var Chart = H.Chart, perspective = H.perspective;
/**
 * Shorthand to check the is3d flag.
 * @private
 * @return {boolean}
 *         Whether it is a 3D chart.
 */
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');
/* eslint-disable no-invalid-this */
// Legacy support for HC < 6 to make 'scatter' series in a 3D chart route to the
// real 'scatter3d' series type.
addEvent(Chart, 'afterInit', function () {
    var options = this.options;
    if (this.is3d()) {
        (options.series || []).forEach(function (s) {
            var type = s.type ||
                options.chart.type ||
                options.chart.defaultSeriesType;
            if (type === 'scatter') {
                s.type = 'scatter3d';
            }
        });
    }
});
// And do it on dynamic add (#8407)
addEvent(Chart, 'addSeries', function (e) {
    if (this.is3d()) {
        if (e.options.type === 'scatter') {
            e.options.type = 'scatter3d';
        }
    }
});
/**
 * 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.
 *
 * @private
 * @function getScale
 *
 * @param {Highcharts.Chart} chart
 * Chart object
 *
 * @param {number} depth
 * The depth of the chart
 *
 * @return {number}
 * The scale to fit the 3D chart into the plotting area.
 *
 * @requires highcharts-3d
 */
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:
    [0, 1].forEach(function (i) {
        corners.push({
            x: plotRight,
            y: corners[i].y,
            z: corners[i].z
        });
    });
    // All bottom corners:
    [0, 1, 2, 3].forEach(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:
    corners.forEach(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;
}
wrap(H.Chart.prototype, 'isInsidePlot', function (proceed) {
    return this.is3d() || proceed.apply(this, [].slice.call(arguments, 1));
});
var defaultOptions = H.getOptions();
/**
 * @optionparent
 */
var extendedOptions = {
    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](https://code.highcharts.com/highcharts-3d.js).
         *
         * @since    4.0
         * @product  highcharts
         * @requires highcharts-3d
         */
        options3d: {
            /**
             * Wether to render the chart using the 3D functionality.
             *
             * @since   4.0
             * @product highcharts
             */
            enabled: false,
            /**
             * One of the two rotation angles for the chart.
             *
             * @since   4.0
             * @product highcharts
             */
            alpha: 0,
            /**
             * One of the two rotation angles for the chart.
             *
             * @since   4.0
             * @product highcharts
             */
            beta: 0,
            /**
             * The total depth of the chart.
             *
             * @since   4.0
             * @product highcharts
             */
            depth: 100,
            /**
             * Whether the 3d box should automatically adjust to the chart plot
             * area.
             *
             * @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.
             *
             * @since   4.0
             * @product highcharts
             */
            viewDistance: 25,
            /**
             * Set it to `"auto"` to automatically move the labels to the best
             * edge.
             *
             * @type    {"auto"|null}
             * @since   5.0.12
             * @product highcharts
             */
            axisLabelPosition: null,
            /**
             * Provides the option to draw a frame around the charts by defining
             * a bottom, front and back panel.
             *
             * @since    4.0
             * @product  highcharts
             * @requires highcharts-3d
             */
            frame: {
                /**
                 * Whether the frames are visible.
                 */
                visible: 'default',
                /**
                 * General pixel thickness for the frame faces.
                 */
                size: 1,
                /**
                 * The bottom of the frame around a 3D chart.
                 *
                 * @since    4.0
                 * @product  highcharts
                 * @requires highcharts-3d
                 */
                /**
                 * The color of the panel.
                 *
                 * @type      {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
                 * @default   transparent
                 * @since     4.0
                 * @product   highcharts
                 * @apioption chart.options3d.frame.bottom.color
                 */
                /**
                 * The thickness of the panel.
                 *
                 * @type      {number}
                 * @default   1
                 * @since     4.0
                 * @product   highcharts
                 * @apioption chart.options3d.frame.bottom.size
                 */
                /**
                 * Whether to display the frame. Possible values are `true`,
                 * `false`, `"auto"` to display only the frames behind the data,
                 * and `"default"` to display faces behind the data based on the
                 * axis layout, ignoring the point of view.
                 *
                 * @sample {highcharts} highcharts/3d/scatter-frame/
                 *         Auto frames
                 *
                 * @type      {boolean|"default"|"auto"}
                 * @default   default
                 * @since     5.0.12
                 * @product   highcharts
                 * @apioption chart.options3d.frame.bottom.visible
                 */
                /**
                 * The bottom of the frame around a 3D chart.
                 */
                bottom: {},
                /**
                 * The top of the frame around a 3D chart.
                 *
                 * @extends chart.options3d.frame.bottom
                 */
                top: {},
                /**
                 * The left side of the frame around a 3D chart.
                 *
                 * @extends chart.options3d.frame.bottom
                 */
                left: {},
                /**
                 * The right of the frame around a 3D chart.
                 *
                 * @extends chart.options3d.frame.bottom
                 */
                right: {},
                /**
                 * The back side of the frame around a 3D chart.
                 *
                 * @extends chart.options3d.frame.bottom
                 */
                back: {},
                /**
                 * The front of the frame around a 3D chart.
                 *
                 * @extends chart.options3d.frame.bottom
                 */
                front: {}
            }
        }
    }
};
merge(true, defaultOptions, extendedOptions);
// Add the required CSS classes for column sides (#6018)
addEvent(Chart, 'afterGetContainer', function () {
    if (this.styledMode) {
        this.renderer.definition({
            tagName: 'style',
            textContent: '.highcharts-3d-top{' +
                'filter: url(#highcharts-brighter)' +
                '}\n' +
                '.highcharts-3d-side{' +
                'filter: url(#highcharts-darker)' +
                '}\n'
        });
        // Add add definitions used by brighter and darker faces of the cuboids.
        [{
                name: 'darker',
                slope: 0.6
            }, {
                name: 'brighter',
                slope: 1.4
            }].forEach(function (cfg) {
            this.renderer.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);
    }
});
wrap(Chart.prototype, 'setClassName', function (proceed) {
    proceed.apply(this, [].slice.call(arguments, 1));
    if (this.is3d()) {
        this.container.className += ' highcharts-3d-chart';
    }
});
addEvent(H.Chart, 'afterSetChartSize', function () {
    var chart = this, options3d = chart.options.chart.options3d;
    if (chart.is3d()) {
        // Add a 0-360 normalisation for alfa and beta angles in 3d graph
        if (options3d) {
            options3d.alpha = options3d.alpha % 360 + (options3d.alpha >= 0 ? 0 : 360);
            options3d.beta = options3d.beta % 360 + (options3d.beta >= 0 ? 0 : 360);
        }
        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():
        // getScale uses perspective, so scale3d has to be reset.
        chart.scale3d = 1;
        if (options3d.fitToPlot === true) {
            chart.scale3d = getScale(chart, options3d.depth);
        }
        // Recalculate the 3d frame with every call of setChartSize,
        // instead of doing it after every redraw(). It avoids ticks
        // and axis title outside of chart.
        chart.frame3d = this.get3dFrame(); // #7942
    }
});
addEvent(Chart, 'beforeRedraw', function () {
    if (this.is3d()) {
        // Set to force a redraw of all elements
        this.isDirtyBox = true;
    }
});
addEvent(Chart, 'beforeRender', function () {
    if (this.is3d()) {
        this.frame3d = this.get3dFrame();
    }
});
// 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);
    }
});
addEvent(Chart, 'afterDrawChartBox', function () {
    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: [{
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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: [{
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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: [{
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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: [{
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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: [{
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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: [{
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                },
                {
                    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
                }]
        });
    }
});
Chart.prototype.retrieveStacks = function (stacking) {
    var series = this.series, stacks = {}, stackNumber, i = 1;
    this.series.forEach(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, faceOrientation = function (vertexes) {
        var area = H.shapeArea3d(vertexes, chart);
        // Give it 0.5 squared-pixel as a margin for rounding errors.
        if (area > 0.5) {
            return 1;
        }
        if (area < -0.5) {
            return -1;
        }
        return 0;
    }, bottomOrientation = faceOrientation([
        { x: xm, y: yp, z: zp },
        { x: xp, y: yp, z: zp },
        { x: xp, y: yp, z: zm },
        { x: xm, y: yp, z: zm }
    ]), topOrientation = faceOrientation([
        { x: xm, y: ym, z: zm },
        { x: xp, y: ym, z: zm },
        { x: xp, y: ym, z: zp },
        { x: xm, y: ym, z: zp }
    ]), leftOrientation = faceOrientation([
        { x: xm, y: ym, z: zm },
        { x: xm, y: ym, z: zp },
        { x: xm, y: yp, z: zp },
        { x: xm, y: yp, z: zm }
    ]), rightOrientation = faceOrientation([
        { x: xp, y: ym, z: zp },
        { x: xp, y: ym, z: zm },
        { x: xp, y: yp, z: zm },
        { x: xp, y: yp, z: zp }
    ]), frontOrientation = faceOrientation([
        { x: xm, y: yp, z: zm },
        { x: xp, y: yp, z: zm },
        { x: xp, y: ym, z: zm },
        { x: xm, y: ym, z: zm }
    ]), backOrientation = faceOrientation([
        { x: xm, y: ym, z: zp },
        { x: xp, y: ym, z: zp },
        { x: xp, y: yp, z: zp },
        { x: xm, y: yp, z: zp }
    ]), 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
    []
        .concat(chart.xAxis, chart.yAxis, chart.zAxis)
        .forEach(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 (typeof 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 = {
        axes: {},
        // 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,
                xDir: { x: 1, y: 0, z: 0 }
            });
        }
        if (isValidEdge(ret.left, ret.back)) {
            yEdges.push({
                y: (ym + yp) / 2,
                x: xm,
                z: zp,
                xDir: { x: 0, y: 0, z: -1 }
            });
        }
        if (isValidEdge(ret.right, ret.front)) {
            yEdges.push({
                y: (ym + yp) / 2,
                x: xp,
                z: zm,
                xDir: { x: 0, y: 0, z: 1 }
            });
        }
        if (isValidEdge(ret.right, ret.back)) {
            yEdges.push({
                y: (ym + yp) / 2,
                x: xp,
                z: zp,
                xDir: { x: -1, y: 0, z: 0 }
            });
        }
        var xBottomEdges = [];
        if (isValidEdge(ret.bottom, ret.front)) {
            xBottomEdges.push({
                x: (xm + xp) / 2,
                y: yp,
                z: zm,
                xDir: { x: 1, y: 0, z: 0 }
            });
        }
        if (isValidEdge(ret.bottom, ret.back)) {
            xBottomEdges.push({
                x: (xm + xp) / 2,
                y: yp,
                z: zp,
                xDir: { x: -1, y: 0, z: 0 }
            });
        }
        var xTopEdges = [];
        if (isValidEdge(ret.top, ret.front)) {
            xTopEdges.push({
                x: (xm + xp) / 2,
                y: ym,
                z: zm,
                xDir: { x: 1, y: 0, z: 0 }
            });
        }
        if (isValidEdge(ret.top, ret.back)) {
            xTopEdges.push({
                x: (xm + xp) / 2,
                y: ym,
                z: zp,
                xDir: { x: -1, y: 0, z: 0 }
            });
        }
        var zBottomEdges = [];
        if (isValidEdge(ret.bottom, ret.left)) {
            zBottomEdges.push({
                z: (zm + zp) / 2,
                y: yp,
                x: xm,
                xDir: { x: 0, y: 0, z: -1 }
            });
        }
        if (isValidEdge(ret.bottom, ret.right)) {
            zBottomEdges.push({
                z: (zm + zp) / 2,
                y: yp,
                x: xp,
                xDir: { x: 0, y: 0, z: 1 }
            });
        }
        var zTopEdges = [];
        if (isValidEdge(ret.top, ret.left)) {
            zTopEdges.push({
                z: (zm + zp) / 2,
                y: ym,
                x: xm,
                xDir: { x: 0, y: 0, z: -1 }
            });
        }
        if (isValidEdge(ret.top, ret.right)) {
            zTopEdges.push({
                z: (zm + zp) / 2,
                y: ym,
                x: xp,
                xDir: { x: 0, y: 0, z: 1 }
            });
        }
        var pickEdge = function (edges, axis, mult) {
            if (edges.length === 0) {
                return null;
            }
            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, xDir: { x: 1, y: 0, z: 0 } },
                'right': { x: xp, z: zm, xDir: { x: 0, y: 0, z: 1 } }
            },
            x: {
                'top': { y: ym, z: zm, xDir: { x: 1, y: 0, z: 0 } },
                'bottom': { y: yp, z: zm, xDir: { x: 1, y: 0, z: 0 } }
            },
            z: {
                'top': {
                    x: defaultShowLeft ? xp : xm,
                    y: ym,
                    xDir: defaultShowLeft ?
                        { x: 0, y: 0, z: 1 } :
                        { x: 0, y: 0, z: -1 }
                },
                'bottom': {
                    x: defaultShowLeft ? xp : xm,
                    y: yp,
                    xDir: defaultShowLeft ?
                        { x: 0, y: 0, z: 1 } :
                        { x: 0, y: 0, z: -1 }
                }
            }
        };
    }
    return ret;
};
// Animation setter for matrix property.
H.Fx.prototype.matrixSetter = function () {
    var interpolated;
    if (this.pos < 1 &&
        (isArray(this.start) || isArray(this.end))) {
        var start = this.start || [1, 0, 0, 1, 0, 0];
        var end = this.end || [1, 0, 0, 1, 0, 0];
        interpolated = [];
        for (var i = 0; i < 6; i++) {
            interpolated.push(this.pos * end[i] + (1 - this.pos) * start[i]);
        }
    }
    else {
        interpolated = this.end;
    }
    this.elem.attr(this.prop, interpolated, null, true);
};
/**
 * Note: As of v5.0.12, `frame.left` or `frame.right` should be used instead.
 *
 * The side for the frame around a 3D chart.
 *
 * @deprecated
 * @since     4.0
 * @product   highcharts
 * @requires  highcharts-3d
 * @apioption chart.options3d.frame.side
 */
/**
 * The color of the panel.
 *
 * @deprecated
 * @type      {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
 * @default   transparent
 * @since     4.0
 * @product   highcharts
 * @apioption chart.options3d.frame.side.color
 */
/**
 * The thickness of the panel.
 *
 * @deprecated
 * @type      {number}
 * @default   1
 * @since     4.0
 * @product   highcharts
 * @apioption chart.options3d.frame.side.size
 */
''; // adds doclets above to transpiled file
