⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 circlelayout.as

📁 用于flash/flex的 as3的 2D图形图像图表的动态生成
💻 AS
字号:
package flare.vis.operator.layout
{
	import flare.util.Property;
	import flare.vis.data.Data;
	import flare.vis.data.DataList;
	import flare.vis.data.NodeSprite;
	import flare.vis.data.ScaleBinding;
	
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	/**
	 * Layout that places items in a circular layout. This operator is quite
	 * flexible and offers a number of layout options:
	 * <ul>
	 *  <li>By default, all items are arranged along the circumference of the
	 *      circle using the sort order of the underlying data list.</li>
	 *  <li>If a data field for either the radius or angle is provided, this
	 *      layout will act as a radial scatter plot, using the data fields
	 *      to determine the radius or angle values of the layout.</li>
	 *  <li>If no data field is provided but the <code>treeLayout</code>
	 *      property is set to <code>true</code>, the layout will use an
	 *      underlying tree structure to layout the data. Leaf nodes will be
	 *      placed along the circumference of the circle, but parent nodes will
	 *      be placed in the interior. Also, the layout will add spacing to
	 *      differentiate sibling groups along the circumference.</li>
	 * </ul>
	 * 
	 * <p>The layout also supports mixes of the above modes. For example, if
	 * <code>treeLayout</code> is set to <code>true</code> and a data field for
	 * the radius is set, the angles in the layout will be determined as in
	 * a normal ciruclar tree layout, but the radius values will be derived
	 * using the data field.</p>
	 */
	public class CircleLayout extends Layout
	{	
		/** The padding around the circumference of the circle, in pixels. */
		public var padding:Number = 50;
		/** The starting angle for the layout, in radians. */
		public var startAngle:Number = Math.PI / 2;
		/** The angular width of the layout, in radians (default is 2 pi). */
		public var angleWidth:Number = 2 * Math.PI;
		/** Flag indicating if tree structure should inform the layout. */
		public var treeLayout:Boolean = false;
		
		protected var _inner:Number = 0, _innerFrac:Number = NaN;
		protected var _outer:Number;
		protected var _group:String;
		protected var _rField:Property;
		protected var _aField:Property;
		protected var _rBinding:ScaleBinding;
		protected var _aBinding:ScaleBinding;
		
		/** The starting (inner) radius at which to place items. 
		 *  Setting this value also overrides the
		 *  <code>startRadiusFraction</code> property. */
		public function get startRadius():Number { return _inner; }
		public function set startRadius(r:Number):void {
			_inner = r; _innerFrac = NaN;
		}
		
		/** The starting (inner) radius as a fraction of the outer radius. 
		 *  Setting this value also overrides the 
		 *  <code>startRadius</code> property. When this property is set to
		 *  <code>NaN</code>, the current value of <code>startRadius</code>
		 *  will be used directly. */
		public function get startRadiusFraction():Number { return _innerFrac; }
		public function set startRadiusFraction(f:Number):void {
			_innerFrac = f;
		}
		
		/** The radius source property. */
		public function get radiusField():String { return _rBinding.property; }
		public function set radiusField(f:String):void { _rBinding.property = f; }
		
		/** The angle source property. */
		public function get angleField():String { return _aBinding.property; }
		public function set angleField(f:String):void { _aBinding.property = f; }
		
		/** The scale binding for the radius. */
		public function get radiusScale():ScaleBinding { return _rBinding; }
		public function set radiusScale(b:ScaleBinding):void {
			if (_rBinding) {
				if (!b.property) b.property = _rBinding.property;
				if (!b.group) b.group = _rBinding.group;
				if (!b.data) b.data = _rBinding.data;
			}
			_rBinding = b;
		}
		
		/** The scale binding for the angle. */
		public function get angleScale():ScaleBinding { return _aBinding; }
		public function set angleScale(b:ScaleBinding):void {
			if (_aBinding) {
				if (!b.property) b.property = _aBinding.property;
				if (!b.group) b.group = _aBinding.group;
				if (!b.data) b.data = _aBinding.data;
			}
			_aBinding = b;
		}
		
		// --------------------------------------------------------------------
				
		/**
		 * Creates a new CircleLayout.
		 * @param radiusField optional data field to encode as radius length
		 * @param angleField optional data field to encode as angle
		 * @param treeLayout boolean flag indicating if any tree-structure in
		 *  the data should be used to inform the layout
		 * @param group the data group to process. If tree layout is set to
		 *  true, this value may get ignored.
		 */
		public function CircleLayout(
			radiusField:String=null, angleField:String=null,
			treeLayout:Boolean=false, group:String=Data.NODES)
		{
			layoutType = POLAR;
			_group = group;
			this.treeLayout = treeLayout;
			
			_rBinding = new ScaleBinding();
			_rBinding.group = _group;
			_rBinding.property = radiusField;
			
			_aBinding = new ScaleBinding();
			_aBinding.group = _group;
			_aBinding.property = angleField;
		}
		
		/** @inheritDoc */
		public override function setup():void
		{
			if (visualization==null) return;
			_rBinding.data = visualization.data;
			_aBinding.data = visualization.data;
		}
		
		/** @inheritDoc */
		protected override function layout():void
		{			
			var list:DataList = visualization.data.group(_group);
			var i:int = 0, N:int = list.length, dr:Number;
			var visitor:Function = null;
			
			// determine radius
			var b:Rectangle = layoutBounds;
			_outer = Math.min(b.width, b.height)/2 - padding;
			_inner = isNaN(_innerFrac) ? _inner : _outer * _innerFrac;
			
			// set the anchor point
			var anchor:Point = layoutAnchor;
			list.visit(function(n:NodeSprite):void { n.origin = anchor; });
			
			// compute angles
			if (_aBinding.property) {
				// if angle property, get scale binding and do layout
				_aBinding.updateBinding();
				_aField = Property.$(_aBinding.property);
				visitor = function(n:NodeSprite):void {
					var f:Number = _aBinding.interpolate(_aField.getValue(n));
					_t.$(n).angle = minAngle(n.angle, 
					                         startAngle - f*angleWidth);
				};
			} else if (treeLayout) {
				// if tree mode, use tree order
				setTreeAngles();
			} else {
				// if nothing use total sort order
				i = 0;
				visitor = function(n:NodeSprite):void {
					_t.$(n).angle = minAngle(n.angle,
						                     startAngle - (i/N)*angleWidth);
					i++;
				};
			}
			if (visitor != null) list.visit(visitor);
			
			// compute radii
			visitor = null;
			if (_rBinding.property) {
				// if radius property, get scale binding and do layout
				_rBinding.updateBinding();
				_rField = Property.$(_rBinding.property);
				dr = _outer - _inner;
				visitor = function(n:NodeSprite):void {
					var f:Number = _rBinding.interpolate(_rField.getValue(n));
					_t.$(n).radius = _inner + f * dr;
				};
			} else if (treeLayout) {
				// if tree-mode, use tree depth
				setTreeRadii();
			} else {
				// if nothing, use outer radius
				visitor = function(n:NodeSprite):void {
					_t.$(n).radius = _outer;
				};
			}
			if (visitor != null) list.visit(visitor);
			if (treeLayout) _t.$(visualization.data.tree.root).radius = 0;
			
			// finish up
			updateEdgePoints(_t);
		}
		
		private function setTreeAngles():void
		{
			// first pass, determine the angular spacing
			var root:NodeSprite = visualization.tree.root, p:NodeSprite = null;
			var leafCount:int = 0, parentCount:int = 0;
			root.visitTreeDepthFirst(function(n:NodeSprite):void {
				if (n.childDegree == 0) {
					if (p != n.parentNode) {
						p = n.parentNode;
						++parentCount;
					}
					++leafCount;
				}
			});
			var inc:Number = (-angleWidth) / (leafCount + parentCount);
			var angle:Number = startAngle;
			
			// second pass, set the angles
			root.visitTreeDepthFirst(function(n:NodeSprite):void {
				var a:Number = 0, b:Number;
				if (n.childDegree == 0) {
					if (p != n.parentNode) {
						p = n.parentNode;
						angle += inc;
					}
					a = angle;
					angle += inc;
				} else if (n.parent != null) {
					a = _t.$(n.firstChildNode).angle;
					b = _t.$(n.lastChildNode).angle - a;
					while (b >  Math.PI) b -= 2*Math.PI;
					while (b < -Math.PI) b += 2*Math.PI;
					a += b / 2;
				}
				_t.$(n).angle = minAngle(n.angle, a);
			});
		}
		
		private function setTreeRadii():void
		{
			var n:NodeSprite;
			var depth:Number = 0, dr:Number = _outer - _inner;
			
			for each (n in visualization.tree.nodes) {
				if (n.childDegree == 0) {
					depth = Math.max(n.depth, depth);
					_t.$(n).radius = _outer;
				}
			}
			for each (n in visualization.tree.nodes) {
				if (n.childDegree != 0) {
					_t.$(n).radius = _inner + (n.depth/depth) * dr;
				}
			}
			
			n = visualization.tree.root;
			if (!_t.immediate) {
	        	delete _t._(n).values.radius;
	        	delete _t._(n).values.angle;
	        }
	        _t.$(n).x = n.origin.x;
	        _t.$(n).y = n.origin.y;
		}
		
	} // end of class CircleLayout
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -