1 package net.systemeD.halcyon.connection.bboxes {
11 public function get left():Number { return x._min; }
12 public function get right():Number { return x._max; }
13 public function get bottom():Number { return y._min; }
14 public function get top():Number { return y._max; }
16 // Initialise from either a bbox or two Intervals
18 public function fromIntervals(x:Interval,y:Interval):Box {
22 public function fromBbox(minx:Number,miny:Number,maxx:Number,maxy:Number):Box {
23 x=new Interval(minx,maxx);
24 y=new Interval(miny,maxy);
28 // If this box has any area, whether it contains a valid amount of space.
30 public function get valid():Boolean {
31 return (x.valid && y.valid);
34 // Whether this box intersects another.
36 public function intersects(other:Box):Boolean {
37 return (x.intersects(other.x) && y.intersects(other.y));
40 // Intersection. May return a box that isn't valid.
41 public function intersection(other:Box):Box {
42 return (new Box().fromIntervals(x.intersection(other.x), y.intersection(other.y)));
45 // Union. Return a Box covering this Box and the other.
46 public function union(other:Box):Box {
47 return (new Box().fromIntervals(x.union(other.x), y.union(other.y)));
50 // Inverse. Returns an array of 4 Boxes covering all space except for this box.
51 public function get inverse():Array {
53 new Box().fromBbox(-Infinity, y._max, Infinity, Infinity),
54 new Box().fromBbox(-Infinity, y._min, x._min, y._max ),
55 new Box().fromBbox(x._max, y._min, Infinity, y._max ),
56 new Box().fromBbox(-Infinity, -Infinity, Infinity, y._min )
60 // Subtraction. take the inverse of one bbox and intersect it with this one. returns an array of Boxes.
61 public function subtract(other:Box):Array {
62 var inverses:Array=other.inverse;
65 for each (var i:Box in inverses) {
66 candidate=intersection(i);
67 if (candidate.valid) results.push(candidate);
72 // Subtract all Boxes in given array. Resulting set of boxes will be disjoint.
73 public function subtractAll(others:Array):Array {
74 var memo:Array=[this];
75 for each (var other:Box in others) {
76 var subtracted:Array=[];
77 for each (var b:Box in memo) {
78 subtracted=subtracted.concat(b.subtract(other));
82 // do we need to flatten memo here?
86 // Is this box directly adjacent to the other, with no gap in between?
88 public function adjacentTo(other:Box):Boolean {
89 return (((x.equals(other.x)) && ((y._min == other.y._max) || (y._max == other.y._min))) ||
90 ((y.equals(other.y)) && ((x._min == other.x._max) || (x._max == other.x._min))));
93 // Merge as many boxes as possible without increasing the total area of the set of boxes. This is done by
94 // identifying edges along which boxes are adjacent. Note that the input set must be disjoint.
96 // This is an O(n^2) algorithm, so it's going to be very slow on large numbers of boxes. There's
97 // almost certainly a better algorithm out there to do the same thing in better time. but it's nice
100 public static function merge(boxes:Array):Array {
101 if (boxes.length==0) return [];
102 var first:Box=boxes.shift();
104 for each (var box:Box in boxes) {
105 if (first.adjacentTo(box)) { first=first.union(box); }
108 return [first].concat(Box.merge(kept));
111 public function equals(other:Box):Boolean {
112 return (x.equals(other.x) && y.equals(other.y));
115 public function get size():Number {
116 return (x.size*y.size);
119 public function toString():String {
120 return ("Box["+x._min+","+y._min+","+x._max+","+y._max+"]");