Implemented positionRect and supporting functions.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4186 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Ray Greenwell
2006-06-09 20:59:32 +00:00
parent 17abf35d05
commit f6dd283b2a
+99 -1
View File
@@ -5,6 +5,26 @@ import flash.geom.Rectangle;
public class DisplayUtil
{
/**
* Returns the most reasonable position for the specified rectangle to
* be placed at so as to maximize its containment by the specified
* bounding rectangle while still placing it as near its original
* coordinates as possible.
*
* @param rect the rectangle to be positioned.
* @param bounds the containing rectangle.
*/
public static function fitRectInRect (
rect :Rectangle , bounds :Rectangle) :Point
{
// Guarantee that the right and bottom edges will be contained
// and do our best for the top and left edges.
var br :Point = bounds.bottomRight;
return new Point(
Math.min(br.x - rect.width, Math.max(rect.x, bounds.x)),
Math.min(br.y - rect.height, Math.max(rect.y, bounds.y)));
}
/**
* Position the specified rectangle within the bounds, avoiding
* any of the Rectangles in the avoid array, which may be destructively
@@ -18,8 +38,86 @@ public class DisplayUtil
r :Rectangle, bounds :Rectangle, avoid :Array) :Boolean
{
var origPos :Point = r.topLeft;
var pointSorter :Function = createPointSorter(origPos);
var possibles :Array = new Array();
// start things off with the passed-in point (adjusted to
// be inside the bounds, if needed)
possibles.push(fitRectInRect(r, bounds));
return true;
// keep track of area that doesn't generate new possibles
var dead :Array = new Array();
while (possibles.length > 0) {
try {
var p :Point = (possibles.shift() as Point);
r.x = p.x;
r.y = p.y;
// make sure the rectangle is in the view
if (!bounds.containsRect(r)) {
continue;
}
// and not over a dead area
for each (var deadRect :Rectangle in dead) {
if (deadRect.intersects(r)) {
throw true; // continue outer loop
}
}
// see if it hits any rects we're trying to avoid
for (var ii :int = 0; ii < avoid.length; ii++) {
var avoidRect :Rectangle = (avoid[ii] as Rectangle);
if (avoidRect.intersects(r)) {
// remove it from the avoid list
avoid.splice(ii, 1);
// but add it to the dead list
dead.push(avoidRect);
// add 4 new possible points, each pushed in
// one direction
possibles.push(
new Point(avoidRect.x - r.width, r.y),
new Point(r.x, avoidRect.y - r.height),
new Point(avoidRect.x + avoidRect.width, r.y),
new Point(r.x, avoidRect.y + avoidRect.height));
// re-sort the list
possibles.sort(pointSorter);
throw true; // continue outer loop
}
}
// hey! if we got here, then it worked!
return true;
} catch (continueWhile :Boolean) {
// simply catch the boolean and use it to continue inner loops
}
}
// we never found a match, move the rectangle back
r.x = origPos.x;
r.y = origPos.y;
return false;
}
/**
* Create a sort Function that can be used to compare Points in an
* Array according to their distance from the specified Point.
*
* Note: The function will always sort according to distance from the
* passed-in point, even if that point's coordinates change after
* the function is created.
*/
public static function createPointSorter (origin :Point) :Function
{
return function (p1 :Point, p2 :Point) :Number {
var dist1 :Number = Point.distance(origin, p1);
var dist2 :Number = Point.distance(origin, p2);
return (dist1 > dist2) ? 1 : ((dist1 < dist2) ? -1 : 0); // signum
};
}
}
}