package {
import flash.display.*;
import flash.events.Event;
[SWF(width="500", height="500", backgroundColor="#ffffff")];
public class Main extends MovieClip {
public var rdc:RDC;
public var objects:Array;
public function Main() {
stage.scaleMode=StageScaleMode.NO_SCALE;
stage.align=StageAlign.TOP_LEFT;
// create some simple objects
createObjects(50);
// allow a maximum number of 8 objects/group
RDC.SUBDIVISION_THRESHOLD=8;
// create RDC instance with a reference to a function
// that handles the collision
rdc=new RDC(collide);
this.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
}
private function enterFrameHandler(event:Event):void {
for each (var c:Circle in objects) {
c.color=0xff;
c.x+= c.vx;
c.y+= c.vy;
if (c.x - c.radius <= 0) {
c.vx=Math.abs(c.vx);
} else if (c.x + c.radius >= stage.stageWidth) {
c.vx=- Math.abs(c.vx);
} else if (c.y - c.radius <= 0) {
c.vy=Math.abs(c.vy);
} else if (c.y + c.radius >= stage.stageHeight) {
c.vy=- Math.abs(c.vy);
}
}
// run the recursive algorithm
// x-axis is first analyzed (0), then y-axis (1)
rdc.recursiveClustering(objects,0,1);
}
private function collide(a:Circle,b:Circle):void {
// simple bounding sphere distance check
var r2:Number=a.radius + b.radius;
var dx:Number=a.x - b.x;
var dy:Number=a.y - b.y;
var dist:Number=Math.sqrt(dx * dx + dy * dy) - r2;
if (dist < -3) {
for(var i:int = 0 ; i < objects.length ; i++){;
if (objects[i] == a) {
removeChild(objects[i]);
objects.splice(i,1);
newCircle();
return;
}
}
}
if (dist <= 0) {
// do something meaningful
a.color=b.color=0xff0000;
// 衝突軸ベクトル
var cx:Number=a.x - b.x;
var cy:Number=a.y - b.y;
// 正規化
var len:Number=Math.sqrt(cx * cx + cy * cy);
cx/= len;
cy/= len;
var dot:Number=a.vx - b.vx * cx + a.vy - b.vy * cy;
// 定数ベクトル
var constX:Number=dot * cx;
var constY:Number=dot * cy;
a.vx=- constX + a.vx;
a.vy=- constY + a.vy;
b.vx=constX + b.vx;
b.vy=constY + b.vy;
}
}
private function createObjects(count:Number):void {
objects=[];
for (var i:Number=0; i < count; i++) {
newCircle();
}
}
private function newCircle():void {
var radius:Number=rand(1,10);
var x:Number=rand(radius,stage.stageWidth - radius);
var y:Number=rand(radius,stage.stageHeight - radius);
var circle:Circle=new Circle(x,y,radius);
addChild(circle);
circle.vx=Math.random() * 2;
circle.vy=Math.random() * 2;
objects.push(circle);
}
private function rand(min:int,max:int):int {
return min + Math.floor(Math.random() * max - min + 1);
}
}
}
import flash.display.*;
class Circle extends Sprite {
public var radius:uint;
public var vx:Number;
public var vy:Number;
public function Circle(x:uint, y:uint, radius:uint) {
this.x = x;
this.y = y;
this.alpha = 0.6;
this.radius = radius;
this.vx = this.vy = 1;
}
public function set color(c:int):void {
this.graphics.clear();
this.graphics.lineStyle(2, c, 1);
this.graphics.beginFill(c);
this.graphics.drawCircle(0, 0, radius);
this.graphics.endFill();
}
}
import flash.display.*;
class RDC{
public static var SUBDIVISION_THRESHOLD:int = 4;
public static var CONTACT_THRESHOLD:Number = .001;
public var collide:Function;
public function RDC(collide:Function){
this.collide = collide;
}
private function bruteForce(group:Array):void{
var k:uint = group.length;
for (var i:uint = 0; i < k; i++){
for (var j:uint = i + 1; j < k; j++)
collide(group[i], group[j]);
}
}
public function recursiveClustering(group:Array, axis1:int, axis2:int):void{
if (axis1 == -1 || group.length < RDC.SUBDIVISION_THRESHOLD){
bruteForce(group);
} else {
var boundaries:Array = getOpenCloseBounds(group, axis1);
boundaries.sortOn("pos", Array.NUMERIC);
var newAxis1:int = axis2;
var newAxis2:int = -1;
var groupSubdivided:Boolean = false;
var subgroup:Array = [], count:uint = 0;
var k:uint = boundaries.length;
for (var i:uint = 0; i < k; i++) {
var b:Object = boundaries[i];
if (b.type == "open"){
count++;
subgroup.push(b.obj);
} else {
count--;
if (count == 0){
if (i != (k - 1)){
groupSubdivided = true;
}
if (groupSubdivided){
if (axis1 == 0){
newAxis1 = 1;
} else if (axis1 == 1){
newAxis1 = 0;
}
}
recursiveClustering(subgroup, newAxis1, newAxis2);
subgroup = [];
}
}
}
}
}
private function getOpenCloseBounds(group:Array, axis:uint):Array{
var k:uint = group.length;
var boundaries:Array = [], i:uint, o:Circle;
switch(axis){
case 0:
for (i = 0; i < k; i++){
o = group[i];
boundaries.push(new Entity("open",o.x - o.radius + RDC.CONTACT_THRESHOLD, o));
boundaries.push(new Entity("close", o.x + o.radius - RDC.CONTACT_THRESHOLD, o));
}
break;
case 1:
for (i = 0; i < k; i++){
o = group[i];
boundaries.push(new Entity("open",o.y - o.radius + RDC.CONTACT_THRESHOLD, o));
boundaries.push(new Entity("close", o.y + o.radius - RDC.CONTACT_THRESHOLD, o));
}
}
return boundaries;
}
}
class Entity{
public var obj:Object;
public var type:String;
public var pos:Number;
public function Entity(type:String, pos:Number, obj:*){
this.type = type;
this.pos = pos;
this.obj = obj;
}
}