/**
* Use as you will.
*
* 2010 indiemaps.com
*
* */
package com.indiemaps.graphics.adjacency
{
import __AS3__.vec.Vector;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.filters.GlowFilter;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
public class AdjacencyTest
{
public function AdjacencyTest()
{
}
/**
* The default width of the BitmapData instance used to test for adjacency. This is used if the user does not pass in a BitmapData object to use.
* */
public static const TEST_BITMAP_WIDTH : int = 600;
/**
* The default height of the BitmapData instance used to test for adjacency. This is used if the user does not pass in a BitmapData object to use.
* */
public static const TEST_BITMAP_HEIGHT : int = 600;
/**
* Returns a Vector of DisplayObject features determined to be neighbors of the passed-in feature (from a passed-in Vector of all features).
*
* @param feature The DisplayObject feature of which to determine the neighbors.
* @param features A Vector of all features (all potential neighbors of the feature in question).
* @param strokeWidth The width of the external stroke of the passed-in feature and feature. Is used to determine whether a (performance-costly) OuterGlow filter need be applied to the tested DisplayObject features.
* @param commonTestBitmapData An optional BitmapData instance. Can be passed-in to improve performance when calling this method repeatedly.
* @param commonSecondaryTestBitmapData Another optional BitmapData, of the same dimensions as the above commonTestBitmapData, which can be used to improve performance.
*
* @return A Vector of adjacenct DisplayObject features.
*
* @see getPotentialNeighborsForFeature
*
* */
public static function getNeighborsForFeature( feature : DisplayObject, features : Vector.< DisplayObject >,
strokeWidth : int = 1,
commonTestBitmapData : BitmapData = null,
commonSecondaryTestBitmapData : BitmapData = null ) : Vector.< DisplayObject >
{
var neighbors : Vector.< DisplayObject > = new Vector.< DisplayObject >();
var potentialNeighbors : Vector.< DisplayObject > = getPotentialNeighborsForFeature( feature, features );
var tempBounds : Rectangle = feature.getBounds( feature.parent );
var fullBounds : Rectangle = new Rectangle( tempBounds.x, tempBounds.y, tempBounds.width, tempBounds.height );
for each ( var potentialNeighbor : DisplayObject in potentialNeighbors ) {
tempBounds = potentialNeighbor.getBounds( potentialNeighbor.parent );
if ( tempBounds.left < fullBounds.left ) fullBounds.left = tempBounds.left;
if ( tempBounds.right > fullBounds.right ) fullBounds.right = tempBounds.right;
if ( tempBounds.top < fullBounds.top ) fullBounds.top = tempBounds.top;
if ( tempBounds.bottom > fullBounds.bottom ) fullBounds.bottom = tempBounds.bottom;
}
var img : BitmapData;
if ( commonTestBitmapData ) {
img = commonTestBitmapData;
img.fillRect( new Rectangle( 0, 0, img.width, img.height ), 0x00000000 );
} else {
img = new BitmapData( TEST_BITMAP_WIDTH, TEST_BITMAP_HEIGHT, true, 0 );
}
var zoomy : Number = Math.min( img.width / fullBounds.width, img.height / fullBounds.height );
var matrix : Matrix = new Matrix( zoomy, 0, 0, zoomy,
.5 * img.width + ( feature.x - ( fullBounds.x + .5 * fullBounds.width ) ) * zoomy,
.5 * img.height + ( feature.y - ( fullBounds.y + .5 * fullBounds.height ) ) * zoomy );
img.draw( feature, matrix, new ColorTransform( 1, 1, 1, 1, -255, -255, 255 ) );
if ( strokeWidth < 2 ) {
img.applyFilter( img,
new Rectangle( 0, 0, img.width, img.height ),
new Point(),
new GlowFilter( 0xff0000, 1, 2 - strokeWidth, 2 - strokeWidth, 255 )
);
}
for each ( potentialNeighbor in potentialNeighbors ) {
if ( feature == potentialNeighbor ) continue;
matrix = new Matrix( zoomy, 0, 0, zoomy,
.5 * img.width + ( potentialNeighbor.x - ( fullBounds.x + .5 * fullBounds.width ) ) * zoomy,
.5 * img.height + ( potentialNeighbor.y - ( fullBounds.y + .5 * fullBounds.height ) ) * zoomy );
if ( strokeWidth < 2 ) {
var tempBitmapData : BitmapData;
if ( commonSecondaryTestBitmapData ) {
tempBitmapData = commonSecondaryTestBitmapData;
tempBitmapData.fillRect( new Rectangle( 0, 0, img.width, img.height ), 0x00000000 );
} else {
tempBitmapData = new BitmapData( img.width, img.height, true, 0 );
}
tempBitmapData.draw( potentialNeighbor, matrix, new ColorTransform( 1, 1, 1, 1, 255, -255, -255 ) );
tempBitmapData.applyFilter( tempBitmapData,
new Rectangle( 0, 0, tempBitmapData.width, tempBitmapData.height ),
new Point(),
new GlowFilter( 0x0000ff, 1, 2 - strokeWidth, 2 - strokeWidth, 255 )
);
img.draw( tempBitmapData, null, null, "difference" );
} else {
img.draw( potentialNeighbor, matrix, new ColorTransform( 1, 1, 1, 1, 255, -255, -255 ), "difference" );
}
var overlappingPixels : int = img.threshold( img,
new Rectangle( 0, 0, img.width, img.height ),
new Point(),
">",
0xFFFF0000,
0xff00ff00 );
if ( overlappingPixels ) neighbors.push( potentialNeighbor );
}
return neighbors;
}
/**
* Returns a Vector of DisplayObject features with bounding boxes that overlap the bounding box of the passed-in feature (from a passed-in Vector of all features).
*
* @param feature The DisplayObject feature of which to determine the potential neighbors.
* @param features A Vector of all features (all potential neighbors of the feature in question).
*
* @return a Vector of DisplayObject features with bounding boxes that overlap the bounding box of the passed-in feature
*
* */
public static function getPotentialNeighborsForFeature( feature : DisplayObject, features : Vector.< DisplayObject > ) : Vector.< DisplayObject >
{
var potentialNeighbors : Vector.< DisplayObject > = new Vector.< DisplayObject >();
for each ( var otherFeature : DisplayObject in features ) {
if ( feature == otherFeature ) continue;
if ( feature.getBounds( feature.parent ).intersects( otherFeature.getBounds( feature.parent ) ) ) potentialNeighbors.push( otherFeature );
}
return potentialNeighbors;
}
/**
* Used to determine whether two polygonal features (DisplayObject instances) are adjacent or direct neighbors. Utilizes a bitmap-based test and returns the number of overlapping pixels.
*
* @param feature1 The DisplayObject feature to test against feature2
* @param feature2 The DisplayObject feature to test against feature1
* @param strokeWidth The width of the external stroke of the passed-in feature and feature. Is used to determine whether a (performance-costly) OuterGlow filter need be applied to the tested DisplayObject features.
* @param commonTestBitmapData An optional BitmapData instance. Can be passed-in to improve performance when calling this method repeatedly.
* @param commonSecondaryTestBitmapData Another optional BitmapData, of the same dimensions as the above commonTestBitmapData, which can be used to improve performance.
*
* @return The number of overlapping pixels (0 means the features aren't adjacent, though a threshold may be used to discriminate against features that share a corner but no common border).
*
* */
public static function checkFeatureAdjacency( feature1 : DisplayObject, feature2 : DisplayObject,
strokeWidth : int = 1,
commonTestBitmapData : BitmapData = null,
commonSecondaryTestBitmapData : BitmapData = null ) : int
{
var img : BitmapData;
if ( commonTestBitmapData ) {
img = commonTestBitmapData;
img.fillRect( new Rectangle( 0, 0, img.width, img.height ), 0x00000000 );
} else {
img = new BitmapData( TEST_BITMAP_WIDTH, TEST_BITMAP_HEIGHT, true, 0 );
}
var feature1Bounds : Rectangle = feature1.getBounds( feature1.parent );
var feature2Bounds : Rectangle = feature2.getBounds( feature2.parent );
if ( !feature1Bounds.intersects( feature2Bounds ) ) return 0;
var bothBounds : Rectangle = feature1Bounds.clone();
if ( feature2Bounds.left < bothBounds.left ) bothBounds.left = feature2Bounds.left;
if ( feature2Bounds.right > bothBounds.right ) bothBounds.right = feature2Bounds.right;
if ( feature2Bounds.top < bothBounds.top ) bothBounds.top = feature2Bounds.top;
if ( feature2Bounds.bottom > bothBounds.bottom ) bothBounds.bottom = feature2Bounds.bottom;
var zoomy : Number = Math.min( img.width / bothBounds.width, img.height / bothBounds.height );
var matrix : Matrix = new Matrix( zoomy, 0, 0, zoomy,
.5 * img.width + ( feature1.x - ( bothBounds.x + .5 * bothBounds.width ) ) * zoomy,
.5 * img.height + ( feature1.y - ( bothBounds.y + .5 * bothBounds.height ) ) * zoomy );
img.draw( feature1, matrix, new ColorTransform( 1, 1, 1, 1, 255, -255, -255 ) );
if ( strokeWidth < 2 ) {
img.applyFilter( img,
new Rectangle( 0, 0, img.width, img.height ),
new Point(),
new GlowFilter( 0xff0000, 1, 2 - strokeWidth, 2 - strokeWidth, 255 )
);
}
matrix = new Matrix( zoomy, 0, 0, zoomy,
.5 * img.width + ( feature2.x - ( bothBounds.x + .5 * bothBounds.width ) ) * zoomy,
.5 * img.height + ( feature2.y - ( bothBounds.y + .5 * bothBounds.height ) ) * zoomy );
if ( strokeWidth < 2 ) {
var tempBitmapData : BitmapData;
if ( commonSecondaryTestBitmapData ) {
tempBitmapData = commonSecondaryTestBitmapData;
tempBitmapData.fillRect( new Rectangle( 0, 0, img.width, img.height ), 0x00000000 );
} else {
tempBitmapData = new BitmapData( img.width, img.height, true, 0 );
}
tempBitmapData.draw( feature2, matrix, new ColorTransform( 1, 1, 1, 1, -255, -255, 255 ) );
tempBitmapData.applyFilter( tempBitmapData,
new Rectangle( 0, 0, tempBitmapData.width, tempBitmapData.height ),
new Point(),
new GlowFilter( 0x0000ff, 1, 2 - strokeWidth, 2 - strokeWidth, 255 )
);
img.draw( tempBitmapData, null, null, "difference" );
} else {
img.draw( feature2, matrix, new ColorTransform( 1, 1, 1, 1, -255, -255, 255 ), "difference" );
}
var overlappingPixels : int = img.threshold( img,
new Rectangle( 0, 0, img.width, img.height ),
new Point(),
">",
0xFFFF0000,
0xff000000 );
return overlappingPixels;
}
}
}