GraphLayout
元ネタ:http://java.sun.com/applets/jdk/1.4/demo/applets/GraphLayout/example3.html
(操作)左ドラッグ:ノードを移動(移動後、固定)
左クリック+シフト:固定状態を解除
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.display.GradientType;
import flash.geom.Matrix;
import flash.text.TextField;
import flash.text.TextFormat;
/*
元ネタ:http://java.sun.com/applets/jdk/1.4/demo/applets/GraphLayout/example3.html
(操作)左ドラッグ:ノードを移動(移動後、固定)
左クリック+シフト:固定状態を解除
*/
[SWF(framerate="30",width="480",height="480",backgroundColor="0xffffff")]
public class Practice07 extends Sprite{
private var nodes:Vector.<Node>;
private var edges:Vector.<Edge>;
private var rect:Rectangle = new Rectangle(0, 0, 480, 480);
private var baseLen:Number = 40;
private var limit:Number = 10;
private var pick:Node = null;
private var text:TextField;
public function Practice07():void {
nodes = new Vector.<Node>();
edges = new Vector.<Edge>();
text = new TextField();
var format:TextFormat = new TextFormat();
format.size = 14;
format.bold = true;
text.defaultTextFormat = format;
addChild(text);
stage.addEventListener(Event.ENTER_FRAME, onFrame);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
var str:String = "zero-one,zero-two,zero-three,zero-four,zero-five,zero-six,zero-seven,zero-eight,zero-nine,one-ten,two-twenty,three-thirty,four-fourty,five-fifty,six-sixty,seven-seventy,eight-eighty,nine-ninety,ten-twenty/80,twenty-thirty/80,thirty-fourty/80,fourty-fifty/80,fifty-sixty/80,sixty-seventy/80,seventy-eighty/80,eighty-ninety/80,ninety-ten/80,one-two/30,two-three/30,three-four/30,four-five/30,five-six/30,six-seven/30,seven-eight/30,eight-nine/30,nine-one/30";
init(str);
}
private function init(str:String):void {
var s1:Array = str.split(",");
for each(var s:String in s1) {
var s2:Array = s.split("-");
var ii:int = (s2[1] as String).indexOf("/");
if (ii < 0) {
addEdge(s2[0] as String,s2[1] as String,baseLen);
}else {
var sx:Array = (s2[1] as String).split("/");
addEdge(s2[0] as String,sx[0] as String,parseFloat(sx[1] as String));
}
}
}
private function onFrame(evt:Event): void {
relax();
graphics.clear();
for each(var e:Edge in edges) {
var x1:Number = nodes[e.from].x;
var y1:Number = nodes[e.from].y;
var x2:Number = nodes[e.to].x;
var y2:Number = nodes[e.to].y;
var len:Number = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) - e.len;
if (len < 20) {
graphics.lineStyle(1, 0xaaaaaa);
}else {
graphics.lineStyle(1, 0xff0000);
}
graphics.moveTo(nodes[e.from].x, nodes[e.from].y);
graphics.lineTo(nodes[e.to].x, nodes[e.to].y);
}
for each(var n:Node in nodes) {
if (n.fixed) {
graphics.lineStyle(2, 0x0000ff);
graphics.drawCircle(n.x, n.y, n.radius + 2);
}
}
if (pick != null) {
graphics.lineStyle(2, 0xFF0000);
graphics.drawCircle(pick.x, pick.y, pick.radius + 2);
text.x = pick.x+pick.radius;
text.y = pick.y - pick.radius;
text.text = pick.name;
}
}
private function onMouseDown(e:MouseEvent):void {
for each(var n:Node in nodes) {
if (n.isContain(e.stageX, e.stageY)) {
if (e.shiftKey) {
if (n.fixed) n.fixed = false;
}else {
pick = n;
pick.fixed = true;
}
return;
}
}
pick = null;
}
private function onMouseUp(e:MouseEvent):void {
pick = null;
text.text = "";
}
private function onMouseMove(e:MouseEvent):void {
if (pick == null) return;
pick.x = e.stageX;
pick.y = e.stageY;
}
private function relax():void {
for (var i:int = 0; i < edges.length; i++) {
var e:Edge = edges[i];
var vx:Number = nodes[e.to].x - nodes[e.from].x;
var vy:Number = nodes[e.to].y - nodes[e.from].y;
var len:Number = Math.sqrt(vx * vx + vy * vy);
len = Math.max(len, 0.001);
var f:Number = (edges[i].len - len) / (len * 3);
var dx:Number = f * vx;
var dy:Number = f * vy;
nodes[e.to].dx += dx;
nodes[e.to].dy += dy;
nodes[e.from].dx += -dx;
nodes[e.from].dy += -dy;
}
for (i = 0; i < nodes.length; i++) {
var n1:Node = nodes[i];
dx = 0;
dy = 0;
for (var j:int = 0; j < nodes.length; j++) {
if(i==j)continue;
var n2:Node = nodes[j];
vx = n1.x - n2.x;
vy = n1.y - n2.y;
len = vx * vx + vy * vy;
if (len == 0) {
dx += Math.random();
dy += Math.random();
} else if (len < 100*100) {
dx += vx / len;
dy += vy / len;
}
}
var dlen:Number = dx * dx + dy * dy;
if (dlen > 0) {
dlen = Math.sqrt(dlen) / 2;
n1.dx += dx / dlen;
n1.dy += dy / dlen;
}
}
for (i=0;i<nodes.length;i++){
var n:Node = nodes[i];
if (!n.fixed) {
var ll:Number = Math.max(1.5, limit / n.neigh);
n.x += Math.max(-ll, Math.min(ll, n.dx));
n.y += Math.max(-ll, Math.min(ll, n.dy));
}
if (n.x < 0) {
n.x = 0;
} else if (n.x > rect.width) {
n.x = rect.width;
}
if (n.y < 0) {
n.y = 0;
} else if (n.y > rect.height) {
n.y = rect.height;
}
n.dx /= 2;
n.dy /= 2;
}
}
private function findNode(name:String):int {
for (var i:int = 0; i < nodes.length; i++) {
if (nodes[i].name == name) return i;
}
return addNode(name,rect.width*Math.random(),rect.height*Math.random());
}
private function addNode(name:String,nx:Number,ny:Number):int {
var n:Node = new Node();
n.name = name;
n.x = nx;
n.y = ny;
nodes.push(n);
addChild(n);
return nodes.length - 1;
}
private function addEdge(name0:String,name1:String,len:Number):void {
var e:Edge = new Edge();
e.from = findNode(name0);
e.to = findNode(name1);
e.len = len;
nodes[e.from].neigh++;
nodes[e.to].neigh++;
edges.push(e);
}
}
}
import flash.display.GradientType;
import flash.display.InterpolationMethod;
import flash.display.MovieClip;
import flash.display.SpreadMethod;
import flash.geom.Matrix;
import flash.text.TextField;
class Node extends MovieClip {
public var dx:Number=0;
public var dy:Number=0;
public var neigh:int = 0;
public var fixed:Boolean = false;
public var radius:Number = 10;
public function Node():void {
var matrix:Matrix = new Matrix();
matrix.createGradientBox(radius * 2, radius, Math.PI / 2, x - radius * 0.5, y - radius * 0.8);
var c:int = Math.floor(Math.random() * 0xffffff);
var c2:int = Math.min(c * 1.2, 0xffffff);
var colors:Array = [c, c2];
var alphas:Array = [0.8, 0.2];
var ratios:Array = [0,255];
graphics.beginFill(c);
graphics.drawCircle(x, y, radius);
graphics.beginGradientFill(GradientType.RADIAL, colors,alphas,ratios,matrix,
SpreadMethod.PAD, InterpolationMethod.LINEAR_RGB, 0);
graphics.drawEllipse(x - radius * 0.5, y - radius * 0.8, radius , radius * 0.5);
graphics.endFill();
}
public function isContain(px:Number, py:Number):Boolean {
var vx:Number = px - x;
var vy:Number = py - y;
return ((radius * radius - (vx * vx + vy * vy)) > 0);
}
}
class Edge extends MovieClip{
public var from:int = 0;
public var to:int = 0;
public var len:Number = 0;
}