点配置問題(7)

昨日書いた件。いい加減なソースコードだが、修正するのも面倒なのでそのまま載せてみる。
いろいろ試行錯誤した残骸があちこちにありますが見逃してやってください。

class Arrange {

	var BASE_CENTER_X :Number = 250;
	var BASE_CENTER_Y :Number = 250;
	var base :MovieClip;

	var x :Array;
	var y :Array;
	var sx :Array;
	var sy :Array;
	var saved:Number;
	var drawn:Number;
	var i:Number;
	var j:Number;
	var ls:Number = 100;
	var mm:Number = 100;
	var mmm:Number;
	
	var gmin:Number;
	var gmax:Number;
	var ggmin:Number;
	var ggmax:Number;

	var nn:Number = 15;
	var c:Number = 0.2;
	var qc:Number = 1;
	var rc:Number = 1;
	var err:Number = 1.03;
	var count:Number = 0;
	var cc:Number = 0;
	var pl1:Number = 0;
	var pl2:Number = 0;
	var ps1:Number = 0;
	var ps2:Number = 0;
	
    function Arrange (path) {
		
        var ref = this;
        path.createEmptyMovieClip("base", 10);
        path.createEmptyMovieClip("runnable", 100);
        base = path.base;
        var runnable :MovieClip = path.runnable;
		
		x = new Array();
		y = new Array();
		sx = new Array();
		sy = new Array();
		
		x[0] = 0;
		y[0] = 0;
		for (i=1; i<=nn; i ++){
			x[i] = (Math.random()-0.5)*BASE_CENTER_X/2;
			y[i] = (Math.random()-0.5)*BASE_CENTER_Y/2;
		}
		cc = 0;
		saved = 0;
		drawn = 1;
		
        base._x = BASE_CENTER_X;
        base._y = BASE_CENTER_Y;
        runnable.onEnterFrame = function () :Void {
            ref.onEnterFrameHandler();
        };
    }
	
	/***********************/
	/* primitive           */
	/***********************/
    public function Length(i1:Number,i2:Number):Number{
        return Math.sqrt( (x[i1]-x[i2])*(x[i1]-x[i2])+(y[i1]-y[i2])*(y[i1]-y[i2]) );
    }
    public function sLength(i1:Number,i2:Number):Number{
        return Math.sqrt( (sx[i1]-sx[i2])*(sx[i1]-sx[i2])+(sy[i1]-sy[i2])*(sy[i1]-sy[i2]) );
    }
	public function CheckLengthOver(index:Number){
		var len:Number;
		len = Length(0,index)
		if (len > BASE_CENTER_X/2){
			x[index] /= len/BASE_CENTER_X*2;
			y[index] /= len/BASE_CENTER_X*2;
		}
	}
	
	/***********************/
	/* updating algorithms */
	/***********************/
	public function MovePoint1(){
		var max:Number;
		var min:Number;
		var s2:Number;
		var l2:Number;
		var len:Number;
		var vx:Number;
		var vy:Number;
		for (i=1; i<=nn; i ++){
			max = 0;
			min = 1000;
			for (j=1; j<=nn; j ++){
				if (i == j) continue;
				len=Length(i,j);
				if (len < min){
					s2 = j;
					min = len;
				}
				if (len > max){
					l2 = j;
					max = len
				}
			}
			if (gmax < max){
				gmax = max;
			}
			if (gmin > min){
				gmin = min;
			}
			if (min < 1){
				x[i] = (Math.random()-0.5)*BASE_CENTER_X/2;
				y[i] = (Math.random()-0.5)*BASE_CENTER_Y/2;
			}
			
			vx = c * rc * (x[i] - x[l2]);
			vy = c * rc * (y[i] - y[l2]);
			
			x[i] -= vx;
			y[i] -= vy;
			
			len = Length(i,l2)
			if (len < BASE_CENTER_X){
				x[i] += vx;
				y[i] += vy;
			}
			
			vx = c * qc * (x[s2] - x[i]);
			vy = c * qc * (y[s2] - y[i]);
			
			x[i] -= vx;
			y[i] -= vy;
			
			CheckLengthOver(i);
		}
	}
	public function MovePoint2():Void{
		var max:Number;
		var min:Number;
		var s1:Number;
		var l1:Number;
		var s2:Number;
		var l2:Number;
		var len:Number;
		var vx:Number;
		var vy:Number;
		var mean:Number;
		var sc:Number = 0;
		var lc:Number = 0;
		for (i=1; i<=nn; i ++){
			for (j=1; j<=nn; j ++){
				if (i == j) continue;
				len=Length(i,j);
				if (len < gmin){
					s1 = i;
					s2 = j;
					gmin = len;
				}
				if (len > gmax){
					l1 = i;
					l2 = j;
					gmax = len
				}
			}
			
			if (gmin < 1){
				x[i] = (Math.random()-0.5)*BASE_CENTER_X/2;
				y[i] = (Math.random()-0.5)*BASE_CENTER_Y/2;
			}
			
			if (ps1 == s1 && ps2 == s2){
				sc += 1;
			} else {
				sc = 1;
			}
			
			vx = c / sc * qc * (x[s2] - x[s1]);
			vy = c / sc * qc * (y[s2] - y[s1]);
			
						
			x[s1] -= vx;
			y[s1] -= vy;
			x[s2] += vx;
			y[s2] += vy;
			
			CheckLengthOver(s1);
			CheckLengthOver(s2);
			
			if (pl1 == l1 && pl2 == l2){
				lc += 1;
			} else {
				lc = 1;
			}
			
			vx = c / lc * rc * (x[l1] - x[l2]);
			vy = c / lc * rc * (y[l1] - y[l2]);
			len = Length(l1,l2);
			
			x[l1] -= vx;
			y[l1] -= vy;
			x[l2] += vx;
			y[l2] += vy;
			
			if (len < BASE_CENTER_X){
				x[l1] += vx;
				y[l1] += vy;
				x[l2] -= vx;
				y[l2] -= vy;
			}
			
									
			pl1 = l1;
			pl2 = l2;
			ps1 = s1;
			ps2 = s2;
		}
	}
	
	/***********************/
	/* for Display         */
	/***********************/
	public function DrawPoints(){
		base.createEmptyMovieClip("points", 1);
        var mc1 :MovieClip = base.points;
        mc1.lineStyle(3, 0x000000);
		for (i=1; i<=nn; i ++){
        	mc1.moveTo(x[i]-1, y[i]-1);
			mc1.lineTo(x[i]+1, y[i]-1);
			mc1.lineTo(x[i]+1, y[i]+1);
			mc1.lineTo(x[i]-1, y[i]+1);
			mc1.lineTo(x[i]-1, y[i]-1);
		}
	}
	public function DrawLines(){
		base.createEmptyMovieClip("lines", 2);
        var mc2 :MovieClip = base.lines;
		for (i=1; i< nn; i ++){
			for (j=i+1; j <= nn; j ++){
				if (Length(i,j) < gmin * err){
				    mc2.lineStyle(1, 0xff0000);
					mc2.moveTo(x[i],y[i]);
					mc2.lineTo(x[j],y[j]);
				}
				if (Length(i,j) > gmax / err){
				    mc2.lineStyle(1, 0x0000ff);
					mc2.moveTo(x[i],y[i]);
					mc2.lineTo(x[j],y[j]);
				}
			}
		}
	}
	public function PrintLS(){
		base.createEmptyMovieClip("ls", 3);
        var mc3 :MovieClip = base.ls;
		mc3.createTextField("tf", 1, -100, -200, 200, 20);
        mc3.tf.border = false;
        mc3.tf.text = "L/S : "+ls;
		
		base.createEmptyMovieClip("ls2", 13);
        var mc13 :MovieClip = base.ls2;
		mc13.createTextField("tf", 2, -100, -180, 200, 20);
        mc13.tf.border = false;
        mc13.tf.text = count;
		
		base.createEmptyMovieClip("ls3", 14);
        var mc14 :MovieClip = base.ls3;
		mc14.createTextField("tf", 3, -100, -160, 200, 20);
        mc14.tf.border = false;
        mc14.tf.text = cc;
	}
	public function DrawBestPoints(){
		base.createEmptyMovieClip("bpoints", 4);
        var mc4 :MovieClip = base.bpoints;
        mc4.lineStyle(3, 0x000000);
		for (i=1; i<=nn; i ++){
        	mc4.moveTo(sx[i]-1, sy[i]-1);
			mc4.lineTo(sx[i]+1, sy[i]-1);
			mc4.lineTo(sx[i]+1, sy[i]+1);
			mc4.lineTo(sx[i]-1, sy[i]+1);
			mc4.lineTo(sx[i]-1, sy[i]-1);
		}
	}
	public function DrawBestLines(){
		base.createEmptyMovieClip("blines", 5);
        var mc5 :MovieClip = base.blines;
		for (i=1; i< nn; i ++){
			for (j=i+1; j <= nn; j ++){
				if (sLength(i,j) < ggmin * err){
				    mc5.lineStyle(1, 0xff0000);
					mc5.moveTo(sx[i],sy[i]);
					mc5.lineTo(sx[j],sy[j]);
				}
				if (sLength(i,j) > ggmax / err){
				    mc5.lineStyle(1, 0x0000ff);
					mc5.moveTo(sx[i],sy[i]);
					mc5.lineTo(sx[j],sy[j]);
				}
			}
		}
	}
	public function PrintBestLS(){
		base.createEmptyMovieClip("bls", 6);
        var mc6 :MovieClip = base.bls;
		mc6.createTextField("tf", 1, 200, -200, 200, 20);
        mc6.tf.border = false;
        mc6.tf.text = mmm;
	}
	
	/***********************/
	/* onEnterFrameHandler */
	/***********************/
    public function onEnterFrameHandler () :Void {
		
		count++;
		c = 1/count;
		
		gmax = 0;
		gmin = 1000;
		if (count < 30){
			MovePoint1();
		} else {
			MovePoint2();
		}
		
		if (ls > gmax/gmin){
			ls = gmax/gmin;
			if (mm > ls){
				mm = ls;
				for (i = 1; i <= nn; i ++){
					sx[i] = x[i] + 300;
					sy[i] = y[i];
				}
				saved = 1;
				ggmin = gmin;
				ggmax = gmax;
				mmm = mm;
			}
			cc = 0;
		} else {
			cc ++;
		}
		
		if (cc == 300 || (cc < 300 && count == 3000)){
			ls = 100;
			gmin = 1000;
			gmin = 0;
			count = 0;
			drawn = 0;
			cc = 0;
			for (i=1; i<=nn; i ++){
				x[i] = (Math.random()-0.5)*BASE_CENTER_X/2;
				y[i] = (Math.random()-0.5)*BASE_CENTER_Y/2;
			}
		}
		
		DrawPoints();
		DrawLines();
		PrintLS();
		
		if (saved && !drawn){
			DrawBestPoints();
			DrawBestLines();
			PrintBestLS();
			drawn = 1;
		}
	}

    static function main () :Void {
        var arrange :Arrange = new Arrange(_root);
    }
}

とりあえず試すなら、nn=19とするのがよいだろう。27,37も収束してくれれば綺麗。

やった方がよさそうなことは・・・

  • 点を手動で動かせるようにすること。
  • Nを動的に変えられるようにすること。
  • アルゴリズムを動的に変えられるようにすること。
  • リセットボタンも欲しいか。