1package com.yahoo.astra.fl.charts.axes
2{
3	import com.yahoo.astra.utils.GeomUtil;
4	import com.yahoo.astra.utils.NumberUtil;
5
6	import fl.core.UIComponent;
7
8	import flash.geom.Point;
9
10	//TODO: Add support for labels.
11	/**
12	 * The default axis renderer for radial axes.
13	 *
14	 * @author Josh Tynjala
15	 */
16	public class RadialAxisRenderer extends UIComponent implements IRadialAxisRenderer
17	{
18
19	//--------------------------------------
20	//  Class Variables
21	//--------------------------------------
22
23		/**
24		 * @private
25		 */
26		private static var defaultStyles:Object =
27		{
28			//axis
29			showAxis: true,
30			axisWeight: 1,
31			axisColor: 0x888a85,
32
33			//ticks
34			showTicks: true,
35			tickWeight: 1,
36			tickColor: 0x888a85,
37			tickLength: 4,
38			tickPosition: TickPosition.INSIDE,
39
40			//minor ticks
41			showMinorTicks: true,
42			minorTickWeight: 1,
43			minorTickColor: 0x888a85,
44			minorTickLength: 3,
45			minorTickPosition: TickPosition.INSIDE
46		};
47
48	//--------------------------------------
49	//  Class Methods
50	//--------------------------------------
51
52		/**
53		 * @copy fl.core.UIComponent#getStyleDefinition()
54		 */
55		public static function getStyleDefinition():Object
56		{
57			return mergeStyles(defaultStyles, UIComponent.getStyleDefinition());
58		}
59
60	//--------------------------------------
61	//  Constructor
62	//--------------------------------------
63
64		/**
65		 * Constructor.
66		 */
67		public function RadialAxisRenderer()
68		{
69			super();
70		}
71
72	//--------------------------------------
73	//  Properties
74	//--------------------------------------
75
76		/**
77		 * @inheritDoc
78		 */
79		public function get length():Number
80		{
81			return Math.min(this.width, this.height) * Math.PI;
82		}
83
84		/**
85		 * @private
86		 * Storage for the ticks property.
87		 */
88		private var _ticks:Array = [];
89
90		/**
91		 * @inheritDoc
92		 */
93		public function get ticks():Array
94		{
95			return this._ticks;
96		}
97
98		/**
99		 * @private
100		 */
101		public function set ticks(value:Array):void
102		{
103			this._ticks = value;
104			this.invalidate();
105		}
106
107		/**
108		 * @private
109		 * Storage for the minorTicks property.
110		 */
111		private var _minorTicks:Array = [];
112
113		/**
114		 * @inheritDoc
115		 */
116		public function get minorTicks():Array
117		{
118			return this._minorTicks;
119		}
120
121		/**
122		 * @private
123		 */
124		public function set minorTicks(value:Array):void
125		{
126			this._minorTicks = value;
127			this.invalidate();
128		}
129
130	//--------------------------------------
131	//  Public Methods
132	//--------------------------------------
133
134		/**
135		 * @inheritDoc
136		 */
137		public function updateBounds():void
138		{
139			//no labels are created at this time, so this function is pretty useless
140		}
141
142	//--------------------------------------
143	//  Protected Methods
144	//--------------------------------------
145
146		/**
147		 * @private
148		 */
149		override protected function draw():void
150		{
151			var showTicks:Boolean = this.getStyleValue("showTicks") as Boolean;
152			var showMinorTicks:Boolean = this.getStyleValue("showMinorTicks") as Boolean;
153			var ticks:Array = this.ticks.concat();
154			var minorTicks:Array = this.minorTicks.concat();
155			if(showMinorTicks && showTicks)
156			{
157				//filter out minor ticks that appear at the same position
158				//as major ticks.
159				minorTicks = minorTicks.filter(function(item:AxisData, index:int, source:Array):Boolean
160				{
161					return !ticks.some(function(item2:AxisData, index2:int, source2:Array):Boolean
162					{
163						//using fuzzyEquals because we may encounter rounding errors
164						return NumberUtil.fuzzyEquals(item.position, item2.position, 10);
165					});
166				});
167			}
168
169			this.graphics.clear();
170
171			this.drawAxis();
172
173			var tickPosition:String = this.getStyleValue("tickPosition") as String;
174			var tickLength:Number = this.getStyleValue("tickLength") as Number;
175			var tickWeight:int = this.getStyleValue("tickWeight") as int;
176			var tickColor:uint = this.getStyleValue("tickColor") as uint;
177			this.drawTicks(ticks, showTicks, tickPosition, tickLength, tickWeight, tickColor);
178
179			var minorTickPosition:String = this.getStyleValue("minorTickPosition") as String;
180			var minorTickLength:Number = this.getStyleValue("minorTickLength") as Number;
181			var minorTickWeight:int = this.getStyleValue("minorTickWeight") as int;
182			var minorTickColor:uint = this.getStyleValue("minorTickColor") as uint;
183			this.drawTicks(minorTicks, showMinorTicks, minorTickPosition, minorTickLength, minorTickWeight, minorTickColor);
184
185			super.draw();
186		}
187
188		/**
189		 * @private
190		 * Draws the main axis line.
191		 */
192		protected function drawAxis():void
193		{
194			var showAxis:Boolean = this.getStyleValue("showAxis") as Boolean;
195			if(!showAxis)
196			{
197				return;
198			}
199
200			var axisWeight:int = this.getStyleValue("axisWeight") as int;
201			var axisColor:uint = this.getStyleValue("axisColor") as uint;
202			this.graphics.lineStyle(axisWeight, axisColor);
203
204			var center:Point = new Point(this.width / 2, this.height / 2);
205			var radius:Number = Math.min(center.x, center.y);
206			this.graphics.drawCircle(center.x, center.y, radius);
207		}
208
209		/**
210		 * @private
211		 * Draws a set of ticks along the main axis line. This function is shared
212		 * by major and minor ticks.
213		 */
214		protected function drawTicks(data:Array, showTicks:Boolean, tickPosition:String,
215			tickLength:Number, tickWeight:Number, tickColor:uint):void
216		{
217			if(!showTicks)
218			{
219				return;
220			}
221
222			this.graphics.lineStyle(tickWeight, tickColor);
223
224			var center:Point = new Point(this.width / 2, this.height / 2);
225			var radius:Number = Math.min(center.x, center.y);
226
227			var dataCount:int = data.length;
228			for(var i:int = 0; i < dataCount; i++)
229			{
230				var axisData:AxisData = AxisData(data[i]);
231				if(isNaN(axisData.position))
232				{
233					//skip bad positions
234					continue;
235				}
236
237				var position:Number = axisData.position;
238				var angle:Number = GeomUtil.degreesToRadians(position * 360 / this.length);
239				var tickCenter:Point = Point.polar(radius, angle);
240				tickCenter = tickCenter.add(center);
241				switch(tickPosition)
242				{
243					case TickPosition.OUTSIDE:
244						var outsideEnd:Point = Point.polar(tickLength, angle);
245						outsideEnd = outsideEnd.add(tickCenter);
246						this.graphics.moveTo(tickCenter.x, tickCenter.y);
247						this.graphics.lineTo(outsideEnd.x, outsideEnd.y);
248						break;
249					case TickPosition.INSIDE:
250						var insideEnd:Point = Point.polar(tickLength, GeomUtil.degreesToRadians(180 + GeomUtil.radiansToDegrees(angle)));
251						insideEnd = insideEnd.add(tickCenter);
252						this.graphics.moveTo(tickCenter.x, tickCenter.y);
253						this.graphics.lineTo(insideEnd.x, insideEnd.y);
254						break;
255					default: //CROSS
256						outsideEnd = Point.polar(tickLength / 2, angle);
257						outsideEnd = outsideEnd.add(tickCenter);
258						insideEnd = Point.polar(tickLength / 2, GeomUtil.degreesToRadians(180 + GeomUtil.radiansToDegrees(angle)));
259						insideEnd = insideEnd.add(tickCenter);
260						this.graphics.moveTo(outsideEnd.x, outsideEnd.y);
261						this.graphics.lineTo(insideEnd.x, insideEnd.y);
262						break;
263				}
264			}
265
266		}
267
268	}
269}