Spatial Partitioning Test
Testing internal spacial partitioning object grid class.
Click to add blob.
[+] and [-] to change mouse radius.
/**
* Copyright abeall ( http://wonderfl.net/user/abeall )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/5Eya
*/
package {
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.events.MouseEvent;
import flash.utils.Dictionary;
import flash.geom.Point;
import flash.events.Event;
import flash.display.Graphics;
import flash.display.DisplayObject;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.display.Sprite;
public class SpatialManager extends MovieClip {
private const blobMinRadius:Number = 5;
private const blobMaxRadius:Number = 15;
private const blobMaxSpeed:Number = 1;
private const blobConnectionSensitivity:Number = 4;
private var blobs:Vector.<Shape> = new Vector.<Shape>();
private var blobsInfo:Dictionary = new Dictionary(true);
private var gridWidth:Number;
private var gridHeight:Number;
private var grid:ObjectGrid;
private var gridLayer:Shape;
private var connectorsLayer:Shape;
private var mouseLayer:Shape;
private var blobsLayer:Sprite;
private var mouseRadius:Number = blobMaxRadius;
private var playing:Boolean = true;
public function SpatialManager() {
// init stage
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
// init grid
gridLayer = new Shape();
gridLayer.cacheAsBitmap = true;
addChild(gridLayer);
// create the grid layer
gridLayer = new Shape();
gridLayer.cacheAsBitmap = true;
addChild(gridLayer);
// create the blobs container
blobsLayer = new Sprite();
addChild(blobsLayer);
// create connector canvas
connectorsLayer = new Shape();
addChild(connectorsLayer);
// create mouse canvas
mouseLayer = new Shape();
addChild(mouseLayer);
// draw the grid
updateGrid();
// add random blobs
var i:int = 25;
while(i--)
addBlob(Math.random() * gridWidth, Math.random() * gridHeight);
stage.addEventListener(MouseEvent.CLICK, stageClick);
stage.addEventListener(KeyboardEvent.KEY_DOWN, stageKeyDown);
stage.addEventListener(MouseEvent.MOUSE_MOVE, stageMouseMove);
stage.addEventListener(MouseEvent.MOUSE_WHEEL, stageMouseWheel);
stage.addEventListener(Event.RESIZE, stageResize);
addEventListener(Event.ENTER_FRAME, update);
}
private function updateGrid():void {
// grid props
gridWidth = stage.stageWidth;
gridHeight = stage.stageHeight;
// build grid
var cellSize:Number = blobMaxRadius * blobConnectionSensitivity;
grid = new ObjectGrid(cellSize, gridWidth, gridHeight);
// add existing blobs
var i:int = blobsLayer.numChildren;
while(i--){
grid.add(blobsLayer.getChildAt(i));
}
// draw grid lines
var g:Graphics = gridLayer.graphics;
g.clear();
g.lineStyle(1, 0xeeeeee);
for(var w:int = 0; w < gridWidth; w += cellSize){
g.moveTo(w, 0);
g.lineTo(w, gridHeight);
for(var h:int = 0; h < gridHeight; h += cellSize){
g.moveTo(0, h);
g.lineTo(gridWidth, h);
}
}
}
private function stageClick(e:MouseEvent):void {
addBlob(stage.mouseX, stage.mouseY);
}
private function stageKeyDown(e:KeyboardEvent):void {
switch(e.keyCode){
case Keyboard.SPACE:
playing = !playing;
if(playing)
addEventListener(Event.ENTER_FRAME, update);
else
removeEventListener(Event.ENTER_FRAME, update);
break;
case Keyboard.NUMPAD_ADD:
case Keyboard.EQUAL:
mouseRadius++;
break;
case Keyboard.NUMPAD_SUBTRACT:
case Keyboard.MINUS:
mouseRadius--;
break;
}
}
private function stageMouseMove(e:MouseEvent):void {
var xPos:Number = stage.mouseX, yPos:Number = stage.mouseY;
var g:Graphics = mouseLayer.graphics;
g.clear();
g.beginFill(0xff0000);
g.drawCircle(xPos, yPos, mouseRadius);
g.endFill();
g.lineStyle(1, 0xff0000, .2);
var objs:Vector.<DisplayObject> = grid.getObjectsInCircle(xPos, yPos, mouseRadius * blobConnectionSensitivity, g);
g.lineStyle(3, 0xff0000);
for each(var obj:DisplayObject in objs){
g.moveTo(xPos, yPos);
g.lineTo(obj.x, obj.y);
}
g.lineStyle(1, 0xff0000);
g.drawCircle(xPos, yPos, mouseRadius * blobConnectionSensitivity);
}
private function stageMouseWheel(e:MouseEvent):void {
mouseRadius += e.delta > 0 ? 1 : -1;
mouseRadius = Math.abs(mouseRadius);
}
private function stageResize(e:Event):void {
updateGrid();
}
private function update(e:Event):void {
// clear canvas
var g:Graphics = connectorsLayer.graphics;
g.clear();
// update grid
grid.update();
// update blobs
for each(var blob:Shape in blobs){
var info:Object = blobsInfo[blob];
// move
var vector:Point = info.vector;
blob.x += vector.x;
blob.y += vector.y;
// bounce off edges
if(blob.x < 0 || blob.x > gridWidth)
vector.x *= -1;
if(blob.y < 0 || blob.y > gridHeight)
vector.y *= -1;
// draw connections
var nearbyBlobs:Vector.<DisplayObject> = grid.getObjectsInCircle(blob.x, blob.y, info.radius * blobConnectionSensitivity);
//var nearbyBlobs:Vector.<DisplayObject> = grid.getObjects(blob.x, blob.y);
if(nearbyBlobs.length > 1){
blob.alpha = .5;
g.lineStyle(info.radius * 2, info.color, .33);
for each(var nearbyBlob:Shape in nearbyBlobs){
g.moveTo(blob.x, blob.y);
g.lineTo(nearbyBlob.x, nearbyBlob.y);
}
}else{
blob.alpha = 1.0;
}
}
stageMouseMove(null);
}
private function addBlob(x:Number, y:Number):void {
var color:uint = 0xffffff * Math.random();
var radius:Number = blobMinRadius + (blobMaxRadius - blobMinRadius) * Math.random();
var blob:Shape = new Shape();
blob.graphics.beginFill(color);
blob.graphics.drawCircle(0, 0, radius);
blob.graphics.endFill();
blob.graphics.lineStyle(1, color, .5);
blob.graphics.drawCircle(0, 0, radius * blobConnectionSensitivity);
blob.x = x;
blob.y = y;
blobsLayer.addChild(blob);
blobs.push(blob);
blobsInfo[blob] = {
color: color,
radius: radius,
vector: new Point(
-blobMaxSpeed + Math.random() * (blobMaxSpeed * 2),
-blobMaxSpeed + Math.random() * (blobMaxSpeed * 2)
)
}
grid.add(blob);
}
}
}
import flash.display.DisplayObject;
import flash.utils.Dictionary;
import flash.display.Graphics;
internal class ObjectGrid {
private var cellSize:Number;
private var numColumns:int;
private var numRows:int;
private var numCells:int;
private var grid:Vector.<Vector.<DisplayObject>>;
private var objects:Vector.<DisplayObject> = new Vector.<DisplayObject>();
private var objectsLookup:Dictionary = new Dictionary(true);
public function ObjectGrid(cellSize:Number, width:Number, height:Number, objects:Vector.<DisplayObject> = null) {
this.cellSize = cellSize;
if(objects)
this.objects = objects;
// init grid
numColumns = Math.ceil(width / cellSize) + 1;
numRows = Math.ceil(height / cellSize) + 1;
numCells = numColumns * numRows;
grid = new Vector.<Vector.<DisplayObject>>(numCells, true);
for(var i:uint = 0; i < numCells; i++) {
grid[i] = new Vector.<DisplayObject>();
}
}
public function add(object:DisplayObject):void {
if(objectsLookup[object] == null){
objectsLookup[object] = objects.push(object);
}
}
public function remove(object:DisplayObject):void {
if(objectsLookup[object] != null){
objects.splice(objectsLookup[object], 1);
delete objectsLookup[object];
}
}
public function update():void {
// reset grid
for (var i:uint = 0; i < numCells; ++i) {
grid[i].length = 0;
}
// localize all the objects in the grid
var cellRatio:Number = 1 / cellSize;
i = objects.length;
while(i--){
var obj:DisplayObject = objects[i];
var cellIndex:uint = int(obj.x * cellRatio) * numRows + (obj.y * cellRatio);
grid[cellIndex].push(obj);
}
}
public function getObjects(x:Number, y:Number, cellRadius:uint = 1, debugLayer:Graphics = null):Vector.<DisplayObject> {
var xPos:uint = x / cellSize;
var yPos:uint = y / cellSize;
var xMin:int = xPos - cellRadius;
if (xMin < 0) xMin = 0;
var yMin:int = yPos - cellRadius;
if (yMin < 0) yMin = 0;
var xMax:uint = xPos + cellRadius;
if (xMax >= numColumns) xMax = numColumns - 1;
var yMax:uint = yPos + cellRadius;
if (yMax >= numRows) yMax = numRows - 1;
var results:Vector.<DisplayObject> = new Vector.<DisplayObject>();
for (var currX:uint = xMin; currX <= xMax; currX++) {
var rowIndex:uint = currX * numRows;
for (var currY:uint = yMin; currY <= yMax; currY++) {
var objs:Vector.<DisplayObject> = grid[rowIndex + currY];
for each(var obj:DisplayObject in objs){
results.push(obj);
}
if(debugLayer){
debugLayer.drawRect(currX * cellSize, currY * cellSize, cellSize, cellSize);
}
}
}
return results;
}
public function getObjectsInCircle(x:Number, y:Number, radius:Number, debugLayer:Graphics = null):Vector.<DisplayObject> {
var cellRadius:uint = (radius / cellSize) + 1;
var objs:Vector.<DisplayObject> = getObjects(x, y, cellRadius, debugLayer);
var squaredRadius:Number = radius * radius;
var i:int = objs.length;
while(i--){
var obj:DisplayObject = objs[i];
var dist:Number = (obj.x - x) * (obj.x - x) + (obj.y - y) * (obj.y - y);
if(dist > squaredRadius)
objs.splice(i, 1);
}
return objs;
}
}