text: define(["dojo/_base/declare", "dojox/gfx", "./ScaleBase", "./_circularUtils"], function(declare, gfx, ScaleBase, _circularUtils){
  	return declare("dojox.dgauges.CircularScale", ScaleBase, {
  		// summary:
  		//		The circular scale. A scaler must be set to use this class.
  
  		// originX: Number
  		//		The origin x-coordinate of the scale in pixels.
  		originX: 50,
  		// originY: Number
  		//		The origin y-coordinate of the scale in pixels.
  		originY: 50,
  		// radius: Number
  		//		The outer radius in pixels of the scale.
  		radius: 50,
  		// startAngle: Number
  		//		The start angle of the scale in degrees.
  		startAngle: 0,
  		// endAngle: Number
  		//		The end angle of the scale in degrees.
  		endAngle: 180,
  		// orientation: String
  		//		The orientation of the scale. Can be "clockwise" or "cclockwise".
  		//		The default value is "clockwise".
  		orientation: "clockwise",
  		
  		constructor: function(){
  
  			this.labelPosition = "inside";
  			
  			this.addInvalidatingProperties(["originX", "originY", "radius", "startAngle", "endAngle", "orientation"]);
  		},
  		
  		_getOrientationNum: function(){
  			// summary:
  			//		Internal method.
  			// tags:
  			//		private
  			return this.orientation == "cclockwise" ? -1 : 1;
  		},
  		
  		positionForValue: function(/* Number */value){
  			// summary:
  			//		Transforms a value into an angle using the associated scaler.
  			// returns: Number
  			//		An angle in degrees.
  			var totalAngle = _circularUtils.computeTotalAngle(this.startAngle, this.endAngle, this.orientation);
  			var relativePos = this.scaler.positionForValue(value);
  			return _circularUtils.modAngle(this.startAngle + this._getOrientationNum() * totalAngle * relativePos, 360);
  		},
  		
  		_positionForTickItem: function(tickItem){
  			// summary:
  			//		Internal method.
  			// tags:
  			//		private
  			var totalAngle = _circularUtils.computeTotalAngle(this.startAngle, this.endAngle, this.orientation);
  			return _circularUtils.modAngle(this.startAngle + this._getOrientationNum() * totalAngle * tickItem.position, 360);
  		},
  		
  		valueForPosition: function(/* Number */angle){
  			// summary:
  			//		Transforms an angle in degrees into a value using the associated scaler.
  			// returns: Number
  			//		The value represented by angle. 
  			if(!this.positionInRange(angle)){
  				var min1 = _circularUtils.modAngle(this.startAngle - angle, 360);
  				var min2 = 360 - min1;
  				var max1 = _circularUtils.modAngle(this.endAngle - angle, 360);
  				var max2 = 360 - max1;
  				var pos;
  				if(Math.min(min1, min2) < Math.min(max1, max2)){
  					pos = 0;
  				}else{
  					pos = 1;
  				}
  			}else{
  				var relativeAngle = _circularUtils.modAngle(this._getOrientationNum() * (angle - this.startAngle), 360);
  				var totalAngle = _circularUtils.computeTotalAngle(this.startAngle, this.endAngle, this.orientation);
  				pos = relativeAngle / totalAngle;
  			}
  			return this.scaler.valueForPosition(pos);
  		},
  		
  		positionInRange: function(/* Number */value){
  			// summary:
  			//		Returns true if the value parameter is between the accepted scale positions.
  			// returns: Boolean
  			//		True if the value parameter is between the accepted scale positions.
  			if(this.startAngle == this.endAngle){
  				return true;
  			}
  			value = _circularUtils.modAngle(value, 360);
  			if(this._getOrientationNum() == 1){
  				if(this.startAngle < this.endAngle){
  					return value >= this.startAngle && value <= this.endAngle;
  				}else{
  					return !(value > this.endAngle && value < this.startAngle);
  				}
  			}else{
  				if(this.startAngle < this.endAngle){
  					return !(value > this.startAngle && value < this.endAngle);
  				}else{
  					return value >= this.endAngle && value <= this.startAngle;
  				}
  			}
  		},
  		
  		_distance: function(x1, y1, x2, y2){
  			// summary:
  			//		Internal method.
  			// tags:
  			//		private
  			return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
  		},
  		
  		_layoutLabel: function(label, txt, ox, oy, lrad, angle, labelPlacement){
  			// summary:
  			//		Internal method.
  			// tags:
  			//		private
  			var font = this._getFont();
  			var box = gfx._base._getTextBox(txt, {
  				font: gfx.makeFontString(gfx.makeParameters(gfx.defaultFont, font))
  			});
  			var tw = box.w;
  			var fz = font.size;
  			var th = gfx.normalizedLength(fz);
  			
  			var tfx = ox + Math.cos(angle) * lrad - tw / 2;
  			var tfy = oy - Math.sin(angle) * lrad - th / 2;
  			var side;
  			
  			var intersections = [];
  			
  			// Intersection with top segment
  			side = tfx;
  			var ipx = side;
  			var ipy = -Math.tan(angle) * side + oy + Math.tan(angle) * ox;
  			// Verify if intersection is on segment
  			if(ipy >= tfy && ipy <= tfy + th){
  				intersections.push({
  					x: ipx,
  					y: ipy
  				});
  			}
  			
  			// Intersection with bottom segment
  			side = tfx + tw;
  			ipx = side;
  			ipy = -Math.tan(angle) * side + oy + Math.tan(angle) * ox;
  			// Verify if intersection is on segment
  			if(ipy >= tfy && ipy <= tfy + th){
  				intersections.push({
  					x: ipx,
  					y: ipy
  				});
  			}
  			// Intersection with left segment
  			side = tfy;
  			ipx = -1 / Math.tan(angle) * side + ox + 1 / Math.tan(angle) * oy;
  			ipy = side;
  			// Verify if intersection is on segment
  			if(ipx >= tfx && ipx <= tfx + tw){
  				intersections.push({
  					x: ipx,
  					y: ipy
  				});
  			}
  			// Intersection with right segment
  			side = tfy + th;
  			ipx = -1 / Math.tan(angle) * side + ox + 1 / Math.tan(angle) * oy;
  			ipy = side;
  			// Verify if intersection is on segment
  			if(ipx >= tfx && ipx <= tfx + tw){
  				intersections.push({
  					x: ipx,
  					y: ipy
  				});
  			}
  			var dif;
  			if(labelPlacement == "inside"){
  				for(var it = 0; it < intersections.length; it++){
  					var ip = intersections[it];
  					dif = this._distance(ip.x, ip.y, ox, oy) - lrad;
  					if(dif >= 0){
  						// Place reference intersection point on reference circle
  						tfx = ox + Math.cos(angle) * (lrad - dif) - tw / 2;
  						tfy = oy - Math.sin(angle) * (lrad - dif) - th / 2;
  						break;
  					}
  				}
  			}else{// "outside" placement
  				for(it = 0; it < intersections.length; it++){
  					ip = intersections[it];
  					dif = this._distance(ip.x, ip.y, ox, oy) - lrad;
  					if(dif <= 0){
  						// Place reference intersection point on reference circle
  						tfx = ox + Math.cos(angle) * (lrad - dif) - tw / 2;
  						tfy = oy - Math.sin(angle) * (lrad - dif) - th / 2;
  						
  						break;
  					}
  				}
  			}
  			if(label){
  				label.setTransform([{
  					dx: tfx + tw / 2,
  					dy: tfy + th
  				}]);
  			}
  		},
  		
  		refreshRendering: function(){
  			this.inherited(arguments);
  			if(!this._gfxGroup || !this.scaler){
  				return;
  			}
  			
  			// Normalize angles
  			this.startAngle = _circularUtils.modAngle(this.startAngle, 360);
  			this.endAngle = _circularUtils.modAngle(this.endAngle, 360);
  			
  			this._ticksGroup.clear();
  			
  			var renderer;
  			var label;
  			var labelText;
  			
  			// Layout ticks
  			var allTicks = this.scaler.computeTicks();
  			
  			var tickBB;
  			for(var i = 0; i < allTicks.length; i++){
  				var tickItem = allTicks[i];
  				renderer = this.tickShapeFunc(this._ticksGroup, this, tickItem);
  				
  				tickBB = this._gauge._computeBoundingBox(renderer);
  				var a;
  				if(tickItem.position){
  					a = this._positionForTickItem(tickItem);
  				}else{
  					a = this.positionForValue(tickItem.value);
  				}
  				if(renderer){
  					renderer.setTransform([{
  						dx: this.originX,
  						dy: this.originY
  					}, gfx.matrix.rotateg(a), {
  						dx: this.radius - tickBB.width - 2 * tickBB.x,
  						dy: 0
  					}]);
  				}
  				labelText = this.tickLabelFunc(tickItem);
  				if(labelText){
  					label = this._ticksGroup.createText({
  						x: 0,
  						y: 0,
  						text: labelText,
  						align: "middle"
  					}).setFont(this._getFont()).setFill(this._getFont().color ? this._getFont().color : "black");
  					var rad = this.radius;
  					if(this.labelPosition == "inside"){
  						rad -= (tickBB.width + this.labelGap);
  					}else{
  						rad += this.labelGap;
  					}
  					this._layoutLabel(label, labelText, this.originX, this.originY, rad, _circularUtils.toRadians(360 - a), this.labelPosition);
  				}
  			}
  			
  			for(var key in this._indicatorsIndex){
  				this._indicatorsRenderers[key] = this._indicatorsIndex[key].invalidateRendering();
  			}
  		}
  	});
  });
  