/**
 * @license Highcharts JS v7.1.3 (2019-08-14)
 *
 * Highcharts funnel module
 *
 * (c) 2010-2019 Kacper Madej
 *
 * License: www.highcharts.com/license
 */
'use strict';
(function (factory) {
    if (typeof module === 'object' && module.exports) {
        factory['default'] = factory;
        module.exports = factory;
    } else if (typeof define === 'function' && define.amd) {
        define('highcharts/modules/funnel3d', ['highcharts', 'highcharts/highcharts-3d', 'highcharts/modules/cylinder'], function (Highcharts) {
            factory(Highcharts);
            factory.Highcharts = Highcharts;
            return factory;
        });
    } else {
        factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
    }
}(function (Highcharts) {
    var _modules = Highcharts ? Highcharts._modules : {};
    function _registerModule(obj, path, args, fn) {
        if (!obj.hasOwnProperty(path)) {
            obj[path] = fn.apply(null, args);
        }
    }
    _registerModule(_modules, 'modules/funnel3d.src.js', [_modules['parts/Globals.js']], function (H) {
        /* *
         * Highcharts funnel3d series module
         *
         * (c) 2010-2019 Highsoft AS
         * Author: Kacper Madej
         *
         * License: www.highcharts.com/license
         */

        var charts = H.charts,
            color = H.color,
            error = H.error,
            extend = H.extend,
            merge = H.merge,
            pick = H.pick,
            seriesType = H.seriesType,
            seriesTypes = H.seriesTypes,
            relativeLength = H.relativeLength,

            // Use H.Renderer instead of H.SVGRenderer for VML support.
            RendererProto = H.Renderer.prototype,

            cuboidPath = RendererProto.cuboidPath,
            funnel3dMethods;

        /**
         * The funnel3d series type.
         *
         * Requires `highcharts-3d.js`, `cylinder.js` and `funnel3d.js` module.
         *
         * @constructor seriesTypes.funnel3d
         * @augments seriesTypes.column
         */
        seriesType('funnel3d', 'column',
            /**
             * A funnel3d is a 3d version of funnel series type. Funnel charts are
             * a type of chart often used to visualize stages in a sales project,
             * where the top are the initial stages with the most clients.
             *
             * It requires that the `highcharts-3d.js`, `cylinder.js` and
             * `funnel3d.js` module are loaded.
             *
             * @sample highcharts/demo/funnel3d/
             *         Funnel3d
             *
             * @extends      plotOptions.column
             * @excluding    allAreas, boostThreshold, colorAxis, compare, compareBase
             * @product      highcharts
             * @since        7.1.0
             * @optionparent plotOptions.funnel3d
             */
            {
                /** @ignore-option */
                center: ['50%', '50%'],

                /**
                 * The max width of the series compared to the width of the plot area,
                 * or the pixel width if it is a number.
                 *
                 * @type    {Number|String}
                 * @sample  {highcharts} highcharts/demo/funnel3d/ Funnel3d demo
                 * @product highcharts
                 */
                width: '90%',

                /**
                 * The width of the neck, the lower part of the funnel. A number defines
                 * pixel width, a percentage string defines a percentage of the plot
                 * area width.
                 *
                 * @type    {Number|String}
                 * @sample  {highcharts} highcharts/demo/funnel3d/ Funnel3d demo
                 * @product highcharts
                 */
                neckWidth: '30%',

                /**
                 * The height of the series. If it is a number it defines
                 * the pixel height, if it is a percentage string it is the percentage
                 * of the plot area height.
                 *
                 * @type    {Number|String}
                 * @sample  {highcharts} highcharts/demo/funnel3d/ Funnel3d demo
                 * @product highcharts
                 */
                height: '100%',

                /**
                 * The height of the neck, the lower part of the funnel. A number
                 * defines pixel width, a percentage string defines a percentage
                 * of the plot area height.
                 *
                 * @type    {Number|String}
                 * @sample  {highcharts} highcharts/demo/funnel3d/ Funnel3d demo
                 * @product highcharts
                 */
                neckHeight: '25%',

                /**
                 * A reversed funnel has the widest area down. A reversed funnel with
                 * no neck width and neck height is a pyramid.
                 *
                 * @product highcharts
                 */
                reversed: false,

                /**
                 * By deafult sides fill is set to a gradient through this option being
                 * set to `true`. Set to `false` to get solid color for the sides.
                 *
                 * @product highcharts
                 */
                gradientForSides: true,

                animation: false,
                edgeWidth: 0,
                colorByPoint: true,
                showInLegend: false,
                /**
                 * @default {"align": "right", "crop": false, "inside": false, "overflow": "allow"}
                 */
                dataLabels: {
                    /** @ignore-option */
                    align: 'right',
                    /** @ignore-option */
                    crop: false,
                    /** @ignore-option */
                    inside: false,
                    /** @ignore-option */
                    overflow: 'allow'
                }
            }, {
                // Override default axis options with series required options for axes
                bindAxes: function () {
                    H.Series.prototype.bindAxes.apply(this, arguments);

                    extend(this.xAxis.options, {
                        gridLineWidth: 0,
                        lineWidth: 0,
                        title: null,
                        tickPositions: []
                    });
                    extend(this.yAxis.options, {
                        gridLineWidth: 0,
                        title: null,
                        labels: {
                            enabled: false
                        }
                    });
                },

                translate3dShapes: H.noop,

                translate: function () {
                    H.Series.prototype.translate.apply(this, arguments);

                    var sum = 0,
                        series = this,
                        chart = series.chart,
                        options = series.options,
                        reversed = options.reversed,
                        ignoreHiddenPoint = options.ignoreHiddenPoint,
                        plotWidth = chart.plotWidth,
                        plotHeight = chart.plotHeight,
                        cumulative = 0, // start at top
                        center = options.center,
                        centerX = relativeLength(center[0], plotWidth),
                        centerY = relativeLength(center[1], plotHeight),
                        width = relativeLength(options.width, plotWidth),
                        tempWidth,
                        getWidthAt,
                        height = relativeLength(options.height, plotHeight),
                        neckWidth = relativeLength(options.neckWidth, plotWidth),
                        neckHeight = relativeLength(options.neckHeight, plotHeight),
                        neckY = (centerY - height / 2) + height - neckHeight,
                        data = series.data,
                        fraction,
                        tooltipPos,

                        y1,
                        y3,
                        y5,

                        h,
                        shapeArgs;

                    // Return the width at a specific y coordinate
                    series.getWidthAt = getWidthAt = function (y) {
                        var top = (centerY - height / 2);

                        return (y > neckY || height === neckHeight) ?
                            neckWidth :
                            neckWidth + (width - neckWidth) *
                                (1 - (y - top) / (height - neckHeight));
                    };

                    // Expose
                    series.center = [centerX, centerY, height];
                    series.centerX = centerX;

                    /*
                     * Individual point coordinate naming:
                     *
                     *  _________centerX,y1________
                     *  \                         /
                     *   \                       /
                     *    \                     /
                     *     \                   /
                     *      \                 /
                     *        ___centerX,y3___
                     *
                     * Additional for the base of the neck:
                     *
                     *       |               |
                     *       |               |
                     *       |               |
                     *        ___centerX,y5___
                     */

                    // get the total sum
                    data.forEach(function (point) {
                        if (!ignoreHiddenPoint || point.visible !== false) {
                            sum += point.y;
                        }
                    });

                    data.forEach(function (point) {
                        // set start and end positions
                        y5 = null;
                        fraction = sum ? point.y / sum : 0;
                        y1 = centerY - height / 2 + cumulative * height;
                        y3 = y1 + fraction * height;
                        tempWidth = getWidthAt(y1);
                        h = y3 - y1;
                        shapeArgs = {
                            // for fill setter
                            gradientForSides: pick(
                                point.options.gradientForSides,
                                options.gradientForSides
                            ),

                            x: centerX,
                            y: y1,
                            height: h,
                            width: tempWidth,
                            z: 1,
                            top: {
                                width: tempWidth
                            }
                        };
                        tempWidth = getWidthAt(y3);
                        shapeArgs.bottom = {
                            fraction: fraction,
                            width: tempWidth
                        };

                        // the entire point is within the neck
                        if (y1 >= neckY) {
                            shapeArgs.isCylinder = true;
                        } else if (y3 > neckY) {
                            // the base of the neck
                            y5 = y3;
                            tempWidth = getWidthAt(neckY);
                            y3 = neckY;

                            shapeArgs.bottom.width = tempWidth;
                            shapeArgs.middle = {
                                fraction: h ? (neckY - y1) / h : 0,
                                width: tempWidth
                            };
                        }

                        if (reversed) {
                            shapeArgs.y = y1 = centerY + height / 2 -
                                (cumulative + fraction) * height;

                            if (shapeArgs.middle) {
                                shapeArgs.middle.fraction = 1 -
                                    (h ? shapeArgs.middle.fraction : 0);
                            }
                            tempWidth = shapeArgs.width;
                            shapeArgs.width = shapeArgs.bottom.width;
                            shapeArgs.bottom.width = tempWidth;
                        }
                        point.shapeArgs = extend(point.shapeArgs, shapeArgs);

                        // for tooltips and data labels context
                        point.percentage = fraction * 100;
                        point.plotX = centerX;

                        if (reversed) {
                            point.plotY = centerY + height / 2 -
                                (cumulative + fraction / 2) * height;
                        } else {
                            point.plotY = (y1 + (y5 || y3)) / 2;
                        }

                        // Placement of tooltips and data labels in 3D
                        tooltipPos = H.perspective([{
                            x: centerX,
                            y: point.plotY,
                            z: reversed ?
                                -(width - getWidthAt(point.plotY)) / 2 :
                                -(getWidthAt(point.plotY)) / 2
                        }], chart, true)[0];
                        point.tooltipPos = [tooltipPos.x, tooltipPos.y];

                        // base to be used when alignment options are known
                        point.dlBoxRaw = {
                            x: centerX,
                            width: getWidthAt(point.plotY),

                            y: y1,
                            bottom: shapeArgs.height,

                            fullWidth: width
                        };

                        if (!ignoreHiddenPoint || point.visible !== false) {
                            cumulative += fraction;
                        }
                    });
                },

                alignDataLabel: function (point, dataLabel, options) {
                    var series = this,
                        dlBoxRaw = point.dlBoxRaw,
                        inverted = series.chart.inverted,
                        below = point.plotY > pick(
                            series.translatedThreshold,
                            series.yAxis.len
                        ),
                        inside = pick(options.inside, !!series.options.stacking),
                        dlBox = {
                            x: dlBoxRaw.x,
                            y: dlBoxRaw.y,
                            height: 0
                        };

                    options.align = pick(
                        options.align,
                        !inverted || inside ? 'center' : below ? 'right' : 'left'
                    );
                    options.verticalAlign = pick(
                        options.verticalAlign,
                        inverted || inside ? 'middle' : below ? 'top' : 'bottom'
                    );

                    if (options.verticalAlign !== 'top') {
                        dlBox.y += dlBoxRaw.bottom /
                            (options.verticalAlign === 'bottom' ? 1 : 2);
                    }

                    dlBox.width = series.getWidthAt(dlBox.y);

                    if (series.options.reversed) {
                        dlBox.width = dlBoxRaw.fullWidth - dlBox.width;
                    }

                    if (inside) {
                        dlBox.x -= dlBox.width / 2;
                    } else {
                        // swap for inside
                        if (options.align === 'left') {
                            options.align = 'right';
                            dlBox.x -= dlBox.width * 1.5;
                        } else if (options.align === 'right') {
                            options.align = 'left';
                            dlBox.x += dlBox.width / 2;
                        } else {
                            dlBox.x -= dlBox.width / 2;
                        }
                    }

                    point.dlBox = dlBox;
                    seriesTypes.column.prototype.alignDataLabel.apply(
                        series,
                        arguments
                    );
                }
            }, /** @lends seriesTypes.funnel3d.prototype.pointClass.prototype */ {
                shapeType: 'funnel3d'
            });

        /**
         * A `funnel3d` series. If the [type](#series.funnel3d.type) option is
         * not specified, it is inherited from [chart.type](#chart.type).
         *
         * @since     7.1.0
         * @extends   series,plotOptions.funnel3d
         * @excluding allAreas,boostThreshold,colorAxis,compare,compareBase
         * @product   highcharts
         * @sample    {highcharts} highcharts/demo/funnel3d/ Funnel3d demo
         * @apioption series.funnel3d
         */

        /**
         * An array of data points for the series. For the `funnel3d` series
         * type, points can be given in the following ways:
         *
         * 1.  An array of numerical values. In this case, the numerical values
         * will be interpreted as `y` options. The `x` values will be automatically
         * calculated, either starting at 0 and incremented by 1, or from `pointStart`
         * and `pointInterval` given in the series options. If the axis has
         * categories, these will be used. Example:
         *
         *  ```js
         *  data: [0, 5, 3, 5]
         *  ```
         *
         * 2.  An array of objects with named values. The following snippet shows only a
         * few settings, see the complete options set below. If the total number of data
         * points exceeds the series' [turboThreshold](#series.funnel3d.turboThreshold),
         * this option is not available.
         *
         *  ```js
         *     data: [{
         *         y: 2,
         *         name: "Point2",
         *         color: "#00FF00"
         *     }, {
         *         y: 4,
         *         name: "Point1",
         *         color: "#FF00FF"
         *     }]
         *  ```
         *
         * @sample {highcharts} highcharts/chart/reflow-true/
         *         Numerical values
         * @sample {highcharts} highcharts/series/data-array-of-arrays/
         *         Arrays of numeric x and y
         * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
         *         Arrays of datetime x and y
         * @sample {highcharts} highcharts/series/data-array-of-name-value/
         *         Arrays of point.name and y
         * @sample {highcharts} highcharts/series/data-array-of-objects/
         *         Config objects
         *
         * @type      {Array<number|Array<number>|*>}
         * @extends   series.column.data
         * @product   highcharts
         * @apioption series.funnel3d.data
         */


        /**
         * By deafult sides fill is set to a gradient through this option being
         * set to `true`. Set to `false` to get solid color for the sides.
         *
         * @type      {boolean|undefined}
         * @product   highcharts
         * @apioption series.funnel3d.data.gradientForSides
         */

        funnel3dMethods = H.merge(RendererProto.elements3d.cuboid, {
            parts: [
                'top', 'bottom',
                'frontUpper', 'backUpper',
                'frontLower', 'backLower',
                'rightUpper', 'rightLower'
            ],
            mainParts: ['top', 'bottom'],
            sideGroups: [
                'upperGroup', 'lowerGroup'
            ],
            sideParts: {
                upperGroup: ['frontUpper', 'backUpper', 'rightUpper'],
                lowerGroup: ['frontLower', 'backLower', 'rightLower']
            },
            pathType: 'funnel3d',

            // override opacity and color setters to control opacity
            opacitySetter: function (opacity) {
                var funnel3d = this,
                    parts = funnel3d.parts,
                    chart = H.charts[funnel3d.renderer.chartIndex],
                    filterId = 'group-opacity-' + opacity + '-' + chart.index;

                // use default for top and bottom
                funnel3d.parts = funnel3d.mainParts;
                funnel3d.singleSetterForParts('opacity', opacity);

                // restore
                funnel3d.parts = parts;

                if (!chart.renderer.filterId) {

                    chart.renderer.definition({
                        tagName: 'filter',
                        id: filterId,

                        children: [{
                            tagName: 'feComponentTransfer',
                            children: [{
                                tagName: 'feFuncA',
                                type: 'table',
                                tableValues: '0 ' + opacity
                            }]
                        }]
                    });
                    funnel3d.sideGroups.forEach(function (groupName) {
                        funnel3d[groupName].attr({
                            filter: 'url(#' + filterId + ')'
                        });
                    });

                    // styled mode
                    if (funnel3d.renderer.styledMode) {
                        chart.renderer.definition({
                            tagName: 'style',
                            textContent: '.highcharts-' + filterId +
                                ' {filter:url(#' + filterId + ')}'
                        });

                        funnel3d.sideGroups.forEach(function (group) {
                            group.addClass('highcharts-' + filterId);
                        });
                    }
                }

                return funnel3d;
            },

            fillSetter: function (fill) {
                // extract alpha channel to use the opacitySetter
                var funnel3d = this,
                    fillColor = color(fill),
                    alpha = fillColor.rgba[3],
                    partsWithColor = {
                        // standard color for top and bottom
                        top: color(fill).brighten(0.1).get(),
                        bottom: color(fill).brighten(-0.2).get()
                    };

                if (alpha < 1) {
                    fillColor.rgba[3] = 1;
                    fillColor = fillColor.get('rgb');

                    // set opacity through the opacitySetter
                    funnel3d.attr({
                        opacity: alpha
                    });
                } else {
                    // use default for full opacity
                    fillColor = fill;
                }

                // add gradient for sides
                if (
                    !fillColor.linearGradient &&
                    !fillColor.radialGradient &&
                    funnel3d.gradientForSides
                ) {
                    fillColor = {
                        linearGradient: { x1: 0, x2: 1, y1: 1, y2: 1 },
                        stops: [
                            [0, color(fill).brighten(-0.2).get()],
                            [0.5, fill],
                            [1, color(fill).brighten(-0.2).get()]
                        ]
                    };
                }

                // gradient support
                if (fillColor.linearGradient) {
                    // color in steps, as each gradient will generate a key
                    funnel3d.sideGroups.forEach(function (sideGroupName) {
                        var box = funnel3d[sideGroupName].gradientBox,
                            gradient = fillColor.linearGradient,
                            alteredGradient = merge(fillColor, {
                                linearGradient: {
                                    x1: box.x + gradient.x1 * box.width,
                                    y1: box.y + gradient.y1 * box.height,
                                    x2: box.x + gradient.x2 * box.width,
                                    y2: box.y + gradient.y2 * box.height
                                }
                            });

                        funnel3d.sideParts[sideGroupName].forEach(function (partName) {
                            partsWithColor[partName] = alteredGradient;
                        });
                    });
                } else {
                    merge(true, partsWithColor, {
                        frontUpper: fillColor,
                        backUpper: fillColor,
                        rightUpper: fillColor,

                        frontLower: fillColor,
                        backLower: fillColor,
                        rightLower: fillColor
                    });

                    if (fillColor.radialGradient) {
                        funnel3d.sideGroups.forEach(function (sideGroupName) {
                            var gradBox = funnel3d[sideGroupName].gradientBox,
                                centerX = gradBox.x + gradBox.width / 2,
                                centerY = gradBox.y + gradBox.height / 2,
                                diameter = Math.min(gradBox.width, gradBox.height);

                            funnel3d.sideParts[sideGroupName].forEach(
                                function (partName) {
                                    funnel3d[partName].setRadialReference([
                                        centerX, centerY, diameter
                                    ]);
                                }
                            );
                        });
                    }
                }

                funnel3d.singleSetterForParts('fill', null, partsWithColor);

                // fill for animation getter (#6776)
                funnel3d.color = funnel3d.fill = fill;

                // change gradientUnits to userSpaceOnUse for linearGradient
                if (fillColor.linearGradient) {
                    [funnel3d.frontLower, funnel3d.frontUpper].forEach(function (part) {
                        var elem = part.element,
                            grad = elem && funnel3d.renderer.gradients[elem.gradient];

                        if (grad && grad.attr('gradientUnits') !== 'userSpaceOnUse') {
                            grad.attr({
                                gradientUnits: 'userSpaceOnUse'
                            });
                        }
                    });
                }

                return funnel3d;
            },

            adjustForGradient: function () {
                var funnel3d = this,
                    bbox;

                funnel3d.sideGroups.forEach(function (sideGroupName) {
                    // use common extremes for groups for matching gradients
                    var topLeftEdge = {
                            x: Number.MAX_VALUE,
                            y: Number.MAX_VALUE
                        },
                        bottomRightEdge = {
                            x: -Number.MAX_VALUE,
                            y: -Number.MAX_VALUE
                        };

                    // get extremes
                    funnel3d.sideParts[sideGroupName].forEach(function (partName) {
                        var part = funnel3d[partName];

                        bbox = part.getBBox(true);
                        topLeftEdge = {
                            x: Math.min(topLeftEdge.x, bbox.x),
                            y: Math.min(topLeftEdge.y, bbox.y)
                        };
                        bottomRightEdge = {
                            x: Math.max(bottomRightEdge.x, bbox.x + bbox.width),
                            y: Math.max(bottomRightEdge.y, bbox.y + bbox.height)
                        };
                    });

                    // store for color fillSetter
                    funnel3d[sideGroupName].gradientBox = {
                        x: topLeftEdge.x,
                        width: bottomRightEdge.x - topLeftEdge.x,
                        y: topLeftEdge.y,
                        height: bottomRightEdge.y - topLeftEdge.y
                    };
                });
            },

            zIndexSetter: function () {
                // this.added won't work, because zIndex is set after the prop is set,
                // but before the graphic is really added
                if (this.finishedOnAdd) {
                    this.adjustForGradient();
                }

                // run default
                return this.renderer.Element.prototype.zIndexSetter.apply(
                    this, arguments
                );
            },

            onAdd: function () {
                this.adjustForGradient();
                this.finishedOnAdd = true;
            }
        });

        RendererProto.elements3d.funnel3d = funnel3dMethods;

        RendererProto.funnel3d = function (shapeArgs) {
            var renderer = this,
                funnel3d = renderer.element3d('funnel3d', shapeArgs),
                // hide stroke for Firefox
                strokeAttrs = {
                    'stroke-width': 1,
                    stroke: 'none'
                };

            // create groups for sides for oppacity setter
            funnel3d.upperGroup = renderer.g('funnel3d-upper-group').attr({
                zIndex: funnel3d.frontUpper.zIndex
            }).add(funnel3d);

            [
                funnel3d.frontUpper,
                funnel3d.backUpper,
                funnel3d.rightUpper
            ].forEach(function (upperElem) {
                upperElem.attr(strokeAttrs);
                upperElem.add(funnel3d.upperGroup);
            });

            funnel3d.lowerGroup = renderer.g('funnel3d-lower-group').attr({
                zIndex: funnel3d.frontLower.zIndex
            }).add(funnel3d);

            [
                funnel3d.frontLower,
                funnel3d.backLower,
                funnel3d.rightLower
            ].forEach(function (lowerElem) {
                lowerElem.attr(strokeAttrs);
                lowerElem.add(funnel3d.lowerGroup);
            });

            funnel3d.gradientForSides = shapeArgs.gradientForSides;

            return funnel3d;
        };

        /**
         * Generates paths and zIndexes.
         */
        RendererProto.funnel3dPath = function (shapeArgs) {
            // Check getCylinderEnd for better error message if
            // the cylinder module is missing
            if (!this.getCylinderEnd) {
                error(
                    'A required Highcharts module is missing: cylinder.js',
                    true,
                    charts[this.chartIndex]
                );
            }

            var renderer = this,
                chart = charts[renderer.chartIndex],

                // adjust angles for visible edges
                // based on alpha, selected through visual tests
                alphaCorrection = shapeArgs.alphaCorrection = 90 -
                    Math.abs((chart.options.chart.options3d.alpha % 180) - 90),

                // set zIndexes of parts based on cubiod logic, for consistency
                cuboidData = cuboidPath.call(renderer, H.merge(shapeArgs, {
                    depth: shapeArgs.width,
                    width: (shapeArgs.width + shapeArgs.bottom.width) / 2
                })),
                isTopFirst = cuboidData.isTop,
                isFrontFirst = !cuboidData.isFront,
                hasMiddle = !!shapeArgs.middle,

                top = renderer.getCylinderEnd(
                    chart,
                    H.merge(shapeArgs, {
                        x: shapeArgs.x - shapeArgs.width / 2,
                        z: shapeArgs.z - shapeArgs.width / 2,
                        alphaCorrection: alphaCorrection
                    })
                ),
                bottomWidth = shapeArgs.bottom.width,
                bottomArgs = H.merge(shapeArgs, {
                    width: bottomWidth,
                    x: shapeArgs.x - bottomWidth / 2,
                    z: shapeArgs.z - bottomWidth / 2,
                    alphaCorrection: alphaCorrection
                }),
                bottom = renderer.getCylinderEnd(chart, bottomArgs, true),

                middleWidth = bottomWidth,
                middleTopArgs = bottomArgs,
                middleTop = bottom,
                middleBottom = bottom,
                ret,

                // masking for cylinders or a missing part of a side shape
                useAlphaCorrection;

            if (hasMiddle) {
                middleWidth = shapeArgs.middle.width;
                middleTopArgs = H.merge(shapeArgs, {
                    y: shapeArgs.y + shapeArgs.middle.fraction * shapeArgs.height,
                    width: middleWidth,
                    x: shapeArgs.x - middleWidth / 2,
                    z: shapeArgs.z - middleWidth / 2
                });
                middleTop = renderer.getCylinderEnd(chart, middleTopArgs, false);

                middleBottom = renderer.getCylinderEnd(
                    chart,
                    middleTopArgs,
                    false
                );
            }

            ret = {
                top: top,
                bottom: bottom,
                frontUpper: renderer.getCylinderFront(top, middleTop),
                zIndexes: {
                    group: cuboidData.zIndexes.group,
                    top: isTopFirst !== 0 ? 0 : 3,
                    bottom: isTopFirst !== 1 ? 0 : 3,
                    frontUpper: isFrontFirst ? 2 : 1,
                    backUpper: isFrontFirst ? 1 : 2,
                    rightUpper: isFrontFirst ? 2 : 1
                }
            };

            ret.backUpper = renderer.getCylinderBack(top, middleTop);
            useAlphaCorrection = (Math.min(middleWidth, shapeArgs.width) /
                Math.max(middleWidth, shapeArgs.width)) !== 1;

            ret.rightUpper = renderer.getCylinderFront(
                renderer.getCylinderEnd(
                    chart,
                    H.merge(shapeArgs, {
                        x: shapeArgs.x - shapeArgs.width / 2,
                        z: shapeArgs.z - shapeArgs.width / 2,
                        alphaCorrection: useAlphaCorrection ? -alphaCorrection : 0
                    }),
                    false
                ),
                renderer.getCylinderEnd(
                    chart,
                    H.merge(middleTopArgs, {
                        alphaCorrection: useAlphaCorrection ? -alphaCorrection : 0
                    }),
                    !hasMiddle
                )
            );

            if (hasMiddle) {
                useAlphaCorrection = (Math.min(middleWidth, bottomWidth) /
                    Math.max(middleWidth, bottomWidth)) !== 1;

                H.merge(true, ret, {
                    frontLower: renderer.getCylinderFront(middleBottom, bottom),
                    backLower: renderer.getCylinderBack(middleBottom, bottom),
                    rightLower: renderer.getCylinderFront(
                        renderer.getCylinderEnd(
                            chart,
                            H.merge(bottomArgs, {
                                alphaCorrection: useAlphaCorrection ?
                                    -alphaCorrection : 0
                            }),
                            true
                        ),
                        renderer.getCylinderEnd(
                            chart,
                            H.merge(middleTopArgs, {
                                alphaCorrection: useAlphaCorrection ?
                                    -alphaCorrection : 0
                            }),
                            false
                        )
                    ),
                    zIndexes: {
                        frontLower: isFrontFirst ? 2 : 1,
                        backLower: isFrontFirst ? 1 : 2,
                        rightLower: isFrontFirst ? 1 : 2
                    }
                });
            }

            return ret;
        };

    });
    _registerModule(_modules, 'masters/modules/funnel3d.src.js', [], function () {


    });
}));