    /**
     * @class Ext.chart.series.Radar
     *
     * Creates a Radar Chart. A Radar Chart is a useful visualization technique for comparing different quantitative values for
     * a constrained number of categories.
     *
     * As with all other series, the Radar series must be appended in the *series* Chart array configuration. See the Chart
     * documentation for more information. A typical configuration object for the radar series could be:
     *
     *     @example
     *     var store = Ext.create('Ext.data.JsonStore', {
     *         fields: ['name', 'data1', 'data2', 'data3'],
     *         data: [
     *             { 'name': 'metric one',   'data1': 14, 'data2': 12, 'data3': 13 },
     *             { 'name': 'metric two',   'data1': 16, 'data2':  8, 'data3':  3 },
     *             { 'name': 'metric three', 'data1': 14, 'data2':  2, 'data3':  7 },
     *             { 'name': 'metric four',  'data1':  6, 'data2': 14, 'data3': 23 },
     *             { 'name': 'metric five',  'data1': 36, 'data2': 38, 'data3': 33 }
     *         ]
     *     });
     *
     *     Ext.create('Ext.chart.Chart', {
     *         renderTo: Ext.getBody(),
     *         width: 500,
     *         height: 300,
     *         animate: true,
     *         theme:'Category2',
     *         store: store,
     *         axes: [{
     *             type: 'Radial',
     *             position: 'radial',
     *             label: {
     *                 display: true
     *             }
     *         }],
     *         series: [{
     *             type: 'radar',
     *             xField: 'name',
     *             yField: 'data1',
     *             showInLegend: true,
     *             showMarkers: true,
     *             markerConfig: {
     *                 radius: 5,
     *                 size: 5
     *             },
     *             style: {
     *                 'stroke-width': 2,
     *                 fill: 'none'
     *             }
     *         },{
     *             type: 'radar',
     *             xField: 'name',
     *             yField: 'data2',
     *             showMarkers: true,
     *             showInLegend: true,
     *             markerConfig: {
     *                 radius: 5,
     *                 size: 5
     *             },
     *             style: {
     *                 'stroke-width': 2,
     *                 fill: 'none'
     *             }
     *         },{
     *             type: 'radar',
     *             xField: 'name',
     *             yField: 'data3',
     *             showMarkers: true,
     *             showInLegend: true,
     *             markerConfig: {
     *                 radius: 5,
     *                 size: 5
     *             },
     *             style: {
     *                 'stroke-width': 2,
     *                 fill: 'none'
     *             }
     *         }]
     *     });
     *
     * In this configuration we add three series to the chart. Each of these series is bound to the same
     * categories field, `name` but bound to different properties for each category, `data1`, `data2` and
     * `data3` respectively. All series display markers by having `showMarkers` enabled. The configuration
     * for the markers of each series can be set by adding properties onto the markerConfig object.
     * Finally we override some theme styling properties by adding properties to the `style` object.
     *
     * @xtype radar
     */
    Ext.define('Ext.chart.series.Radar', {

        /* Begin Definitions */

        extend: 'Ext.chart.series.Series',

        requires: ['Ext.chart.Shape', 'Ext.fx.Anim'],

        /* End Definitions */

        type: "radar",
        alias: 'series.radar',

        rad: Math.PI / 180,

        showInLegend: false,

        /**
         * @cfg {Object} style
         * An object containing styles for overriding series styles from Theming.
         */
        style: {},

        constructor: function(config) {
            this.callParent(arguments);
            var me = this,
                surface = me.chart.surface, i, l;
            me.group = surface.getGroup(me.seriesId);
            if (me.showMarkers) {
                me.markerGroup = surface.getGroup(me.seriesId + '-markers');
            }
        },

        /**
         * Draws the series for the current chart.
         */
        drawSeries: function() {
            var me = this,
                store = me.chart.getChartStore(),
                data = store.data.items,
                d, record,
                group = me.group,
                sprite,
                chart = me.chart,
                seriesItems = chart.series.items,
                s, sLen, series,
                animate = chart.animate,
                field = me.field || me.yField,
                surface = chart.surface,
                chartBBox = chart.chartBBox,
                seriesIdx = me.seriesIdx,
                colorArrayStyle = me.colorArrayStyle,
                centerX, centerY,
                items,
                radius,
                maxValue = 0,
                fields = [],
                max = Math.max,
                cos = Math.cos,
                sin = Math.sin,
                pi2 = Math.PI * 2,
                l = store.getCount(),
                startPath, path, x, y, rho,
                i, nfields,
                seriesStyle = me.seriesStyle,
                seriesLabelStyle = me.seriesLabelStyle,
                first = chart.resizing || !me.radar,
                axis = chart.axes && chart.axes.get(0),
                aggregate = !(axis && axis.maximum);

            me.setBBox();

            maxValue = aggregate? 0 : (axis.maximum || 0);

            Ext.apply(seriesStyle, me.style || {});

            //if the store is empty then there's nothing to draw
            if (!store || !store.getCount() || me.seriesIsHidden) {
                me.hide();
                me.items = [];
                if (me.radar) {
                    me.radar.hide(true);
                }
                me.radar = null;
                return;
            }

            if(!seriesStyle['stroke']){
                seriesStyle['stroke'] = colorArrayStyle[seriesIdx % colorArrayStyle.length];
            }

            me.unHighlightItem();
            me.cleanHighlights();

            centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
            centerY = me.centerY = chartBBox.y + (chartBBox.height / 2);
            me.radius = radius = Math.min(chartBBox.width, chartBBox.height) /2;
            me.items = items = [];

            if (aggregate) {
                //get all renderer fields
                for (s = 0, sLen = seriesItems.length; s < sLen; s++) {
                    series = seriesItems[s];
                    fields.push(series.yField);
                }
                //get maxValue to interpolate
                for (d = 0; d < l; d++) {
                    record = data[d];
                    for (i = 0, nfields = fields.length; i < nfields; i++) {
                        maxValue = max(+record.get(fields[i]), maxValue);
                    }
                }
            }
            //ensure non-zero value.
            maxValue = maxValue || 1;
            //create path and items
            startPath = []; path = [];
            for (i = 0; i < l; i++) {
                record = data[i];
                rho = radius * record.get(field) / maxValue;
                x = rho * cos(i / l * pi2);
                y = rho * sin(i / l * pi2);
                if (i == 0) {
                    path.push('M', x + centerX, y + centerY);
                    startPath.push('M', 0.01 * x + centerX, 0.01 * y + centerY);
                } else {
                    path.push('L', x + centerX, y + centerY);
                    startPath.push('L', 0.01 * x + centerX, 0.01 * y + centerY);
                }
                items.push({
                    sprite: false, //TODO(nico): add markers
                    point: [centerX + x, centerY + y],
                    storeItem: record,
                    series: me
                });
            }
            path.push('Z');
            //create path sprite
            if (!me.radar) {
                me.radar = surface.add(Ext.apply({
                    type: 'path',
                    group: group,
                    path: startPath
                }, seriesStyle || {}));
            }
            //reset on resizing
            if (chart.resizing) {
                me.radar.setAttributes({
                    path: startPath
                }, true);
            }
            //render/animate
            if (chart.animate) {
                me.onAnimate(me.radar, {
                    to: Ext.apply({
                        path: path
                    }, seriesStyle || {})
                });
            } else {
                me.radar.setAttributes(Ext.apply({
                    path: path
                }, seriesStyle || {}), true);
            }
            //render markers, labels and callouts
            if (me.showMarkers) {
                me.drawMarkers();
            }
            me.renderLabels();
            me.renderCallouts();
        },

        // @private draws the markers for the lines (if any).
        drawMarkers: function() {
            var me = this,
                chart = me.chart,
                surface = chart.surface,
                markerStyle = Ext.apply({}, me.markerStyle || {}),
                endMarkerStyle = Ext.apply(markerStyle, me.markerConfig, {
                    fill: me.colorArrayStyle[me.seriesIdx % me.colorArrayStyle.length]
                }),
                items = me.items,
                type = endMarkerStyle.type,
                markerGroup = me.markerGroup,
                centerX = me.centerX,
                centerY = me.centerY,
                item, i, l, marker;
            delete endMarkerStyle.type;

            for (i = 0, l = items.length; i < l; i++) {
                item = items[i];
                marker = markerGroup.getAt(i);
                if (!marker) {
                    marker = Ext.chart.Shape[type](surface, Ext.apply({
                        group: markerGroup,
                        x: 0,
                        y: 0,
                        translate: {
                            x: centerX,
                            y: centerY
                        }
                    }, endMarkerStyle));
                }
                else {
                    marker.show();
                }
                item.sprite = marker;
                if (chart.resizing) {
                    marker.setAttributes({
                        x: 0,
                        y: 0,
                        translate: {
                            x: centerX,
                            y: centerY
                        }
                    }, true);
                }
                marker._to = {
                    translate: {
                        x: item.point[0],
                        y: item.point[1]
                    }
                };
                //render/animate
                if (chart.animate) {
                    me.onAnimate(marker, {
                        to: marker._to
                    });
                }
                else {
                    marker.setAttributes(Ext.apply(marker._to, endMarkerStyle || {}), true);
                }
            }
        },

        isItemInPoint: function(x, y, item) {
            var point,
                tolerance = 10,
                abs = Math.abs;
            point = item.point;
            return (abs(point[0] - x) <= tolerance &&
                    abs(point[1] - y) <= tolerance);
        },

        // @private callback for when creating a label sprite.
        onCreateLabel: function(storeItem, item, i, display) {
            var me = this,
                group = me.labelsGroup,
                config = me.label,
                centerX = me.centerX,
                centerY = me.centerY,
                point = item.point,
                endLabelStyle = Ext.apply(me.seriesLabelStyle || {}, config);

            return me.chart.surface.add(Ext.apply({
                'type': 'text',
                'text-anchor': 'middle',
                'group': group,
                'x': centerX,
                'y': centerY
            }, config || {}));
        },

        // @private callback for when placing a label sprite.
        onPlaceLabel: function(label, storeItem, item, i, display, animate) {
            var me = this,
                chart = me.chart,
                resizing = chart.resizing,
                config = me.label,
                format = config.renderer,
                field = config.field,
                centerX = me.centerX,
                centerY = me.centerY,
                opt = {
                    x: item.point[0],
                    y: item.point[1]
                },
                x = opt.x - centerX,
                y = opt.y - centerY;

            label.setAttributes({
                text: format(storeItem.get(field)),
                hidden: true
            },
            true);

            if (resizing) {
                label.setAttributes({
                    x: centerX,
                    y: centerY
                }, true);
            }

            if (animate) {
                label.show(true);
                me.onAnimate(label, {
                    to: opt
                });
            } else {
                label.setAttributes(opt, true);
                label.show(true);
            }
        },

        // @private for toggling (show/hide) series.
        toggleAll: function(show) {
            var me = this,
                i, ln, shadow, shadows;
            if (!show) {
                Ext.chart.series.Radar.superclass.hideAll.call(me);
            }
            else {
                Ext.chart.series.Radar.superclass.showAll.call(me);
            }
            if (me.radar) {
                me.radar.setAttributes({
                    hidden: !show
                }, true);
                //hide shadows too
                if (me.radar.shadows) {
                    for (i = 0, shadows = me.radar.shadows, ln = shadows.length; i < ln; i++) {
                        shadow = shadows[i];
                        shadow.setAttributes({
                            hidden: !show
                        }, true);
                    }
                }
            }
        },

        // @private hide all elements in the series.
        hideAll: function() {
            this.toggleAll(false);
            this.hideMarkers(0);
        },

        // @private show all elements in the series.
        showAll: function() {
            this.toggleAll(true);
        },

        // @private hide all markers that belong to `markerGroup`
        hideMarkers: function(index) {
            var me = this,
                count = me.markerGroup && me.markerGroup.getCount() || 0,
                i = index || 0;
            for (; i < count; i++) {
                me.markerGroup.getAt(i).hide(true);
            }
        }
    });