import java.util.*;

const int edge = EDGE_0;
const float k = 10;

module Arrow(Node to) ==> Line(new Vector3f(to-this));

static final int ANZ = 25;
static Sphere[] all;
static double min = 99;

protected void init ()
[
	Axiom ==> 
	for (int i:(0:ANZ-1))([Sphere(1)]);
	{
		derive();
		init1();
		[==>> ^Sphere(1);]
	}
]

private void init1()
[
	{
		Random r = new Random((new Date()).getTime());
		
	}
	s:Sphere, (s.getRadius() == 1) ::> {
		int v1 = 1;
		int v2 = 1;
		int v3 = 1;
		if(r.nextBoolean()) {
			v1 = -1;
		}
		if(r.nextBoolean()) {
			v2 = -1;
		}
		if(r.nextBoolean()) {
			v3 = -1;
		}
		s[radius]:=0.1;
		Vector3d v = new Vector3d(v1*(r.nextInt(10000)),v2*(r.nextInt(10000)),v3*(r.nextInt(10000)));
		v.normalize();
		s.setTransform(v);
		s.setColor(0xFFFF00);
		derive();
		all = array((*t:Sphere,(t[radius]!=1)*));
	}
]



private void legalizeEdge() [
	a:Sphere -edge-> b:Sphere -edge-> c:Sphere -edge-> d:Sphere -edge-> a -edge-> c -edge-> d -edge-> b 
	,(c.getId() < d.getId())
	::> {
		
		Point3d ap = new Point3d(a[x],a[y],a[z]);
		Point3d bp = new Point3d(b[x],b[y],b[z]);
		Point3d cp = new Point3d(c[x],c[y],c[z]);
		Point3d dp = new Point3d(d[x],d[y],d[z]);
		
		if (((ap.distance(cp) > ap.distance(bp)) &&
		(ap.distance(cp) > bp.distance(cp)) && (ap.distance(cp) > cp.distance(dp))
		&& (ap.distance(cp) > dp.distance(ap))) &&
		
		((bp.distance(dp) > ap.distance(bp)) &&
		(bp.distance(dp) > bp.distance(cp)) && (bp.distance(dp) > cp.distance(dp))
		&& (bp.distance(dp) > dp.distance(ap)))       ) {
			if((a.getId() < c.getId())&&(b.getId() < d.getId())) {
				disconn(a,c);
			}
		}
	}
]

public void run() {
	
	for(derive()) transform();
}


public void tr() [
	
	a:Sphere,(a[radius]==1) ==>>;
	
	a:Sphere,(a[radius]!=1)
		::> {
			Vector3d av = new Vector3d(a[x],a[y],a[z]);
			min = 4f/Math.sqrt(ANZ);
			for (int i=0; i< all.length; i++) {
				//Point3d bv = new Point3d(all[i][x],all[i][y],all[i][z]);
				Vector3d bv = new Vector3d(all[i][x],all[i][y],all[i][z]);
				if((av.angle(bv)<=1.5*min)&&(av.angle(bv)>=0.5*min)) {
					conn(a,all[i]);
				}
			}		
		}
	
		{derive();legalizeEdge();}
]

private void transform() [
	
	s:Sphere, (s[radius]!=1) ::> {
		Vector3d trans = new Vector3d(s[x],s[y],s[z]);		
		for (int i=0; i< all.length;i++) {
			double dis = angle(s,all[i]);
			if(all[i] == s) continue;
			if(dis < 3.14) {
				Vector3d delta = 
					new Vector3d(s[x]-all[i][x],s[y]-all[i][y],s[z]-all[i][z]);
				delta = (k/(dis*dis))*delta;
				delta.normalize();
				trans += delta;
			}
		}
		trans.normalize();
		s[x] := trans.x;
		s[y] := trans.y;
		s[z] := trans.z;
	}
]

private double angle(Sphere s, Sphere t) {
	Vector3d vs = new Vector3d(s[x],s[y],s[z]);
	Vector3d vt = new Vector3d(t[x],t[y],t[z]);
	return vs.angle(vt);
}

private void conn (Sphere from, Sphere to) [
	==>>
	from -edge-> to,
	to -edge-> from,
	from Arrow(to);
]

private void disconn (Sphere from, Sphere to) [
	
	from -edge-> to ==>> from,to;
	to -edge-> from ==>> to,from;
	from Arrow(to) ==>> from;
	to Arrow(from) ==>> to;
]
