Rectangle.st
changeset 21269 a12e4696432c
parent 20606 12764db7d3a4
child 21292 21faad473411
child 21311 faf1ec6afec6
--- a/Rectangle.st	Fri Jan 20 20:00:45 2017 +0100
+++ b/Rectangle.st	Fri Jan 20 22:58:32 2017 +0100
@@ -1518,31 +1518,128 @@
     "Created: 25.1.1997 / 17:30:21 / cg"
 !
 
-areasOutside: aRectangle
-    "Answer an Array of Rectangles comprising the parts of the receiver not
-    intersecting aRectangle."
+areasOutside:aRectangle
+    "Answer an Array of Rectangles comprising the parts of the receiver 
+     not intersecting aRectangle.
+     That is all areas with no common pixels."
+
+    |areas|
+
+    "Make sure the intersection is non-empty"
+    (self origin <= aRectangle corner and: [aRectangle origin <= self corner]) ifFalse: [
+        ^ Array with: self
+    ].
+    areas := OrderedCollection new.
+    self areasOutside:aRectangle do:[:r | areas add:r].
+    ^ areas asArray
+
+    "/ cg: the old code below was wrong ...
 
-    | areas yOrigin yCorner origin corner|
+"/    "----------------------------------------------------------------
+"/    | added for GNU-ST compatibility
+"/    |
+"/    | author: Doug McCallum <uunet!!ico.isc.com!!dougm>
+"/    |
+"/    |areasOutside: aRectangle
+"/    | most complicated of the Rectangle primitives
+"/    | The basic methodology is to first determine that there is an
+"/    | intersection by finding the overlapping rectangle.  From the
+"/    | overlapping rectangle, first determine if it runs along an edge.
+"/    | If it doesn't, extend the rectangle up to the top edge and add
+"/    | the new rectangle to the collection and start the rest of the
+"/    | process.  If the left edge does not touch the left edge of self,
+"/    | extend it to the edge saving the new rectangle.  Then do the
+"/    | same to the right edge.  Then check top and bottom edges.  Most
+"/    | of the time only 2 or 3 rectangles get formed, occasionally 4.
+"/    | It should be possible to never get more than 3 but requires more
+"/    | work.
+"/     ----------------------------------------------------------------"
+"/
+"/    | collect iRect tmp |
+"/
+"/    iRect := self intersect: aRectangle.
+"/    iRect isNil ifTrue: [^nil]. "case of no intersection"
+"/                                "the collect collection gathers Rectangles"
+"/    collect := OrderedCollection new: 4.
+"/                                "is it floating or on the edge?"
+"/    (((((iRect top) ~= self top)
+"/         and: [ (iRect bottom) ~= self bottom ])
+"/         and: [ (iRect left) ~= self left ])
+"/         and: [ (iRect right) ~= self right ] )
+"/        ifTrue: "entirely in the center."
+"/            [tmp := Rectangle origin: (Point x: iRect left y: self top)
+"/                              corner: iRect bottomRight.
+"/             collect add: tmp.
+"/             iRect := iRect merge: tmp].
+"/    ((iRect left) ~= self left)
+"/        ifTrue:                 "doesn't touch left edge so make it touch"
+"/            [tmp := Rectangle origin: (Point x: self left y: iRect top)
+"/                              corner: iRect bottomLeft.
+"/                 collect add: tmp.
+"/                                "merge new (tmp) with overlap to keep track"
+"/                 iRect := iRect merge: tmp].
+"/    ((iRect right) ~= self right)
+"/        ifTrue:                 "doesn't touch right edge so extend it"
+"/            [tmp := Rectangle origin: iRect topRight
+"/                              corner: (Point x: self right y: iRect bottom).
+"/                 collect add: tmp.
+"/                 iRect := iRect merge: tmp].
+"/    (((iRect left) ~= self left) or: [(iRect top) ~= self top])
+"/        ifTrue:                 "whole top part can be taken now"
+"/            [tmp := Rectangle origin: self origin corner: iRect topRight.
+"/                 collect add: tmp].
+"/    (((iRect right) ~= self right) or: [(iRect bottom) ~= self bottom])
+"/        ifTrue:                 "whole bottom open and can be taken"
+"/            [tmp := Rectangle origin: iRect bottomLeft corner: self corner.
+"/                 collect add: tmp].
+"/    ^collect
+!
+
+areasOutside:aRectangle do:aBlock
+    "evaluate aBlock for Rectangles comprising the parts of the receiver 
+     not intersecting aRectangle.
+     That is all areas with no common pixels."
+
+    |yOrigin yCorner origin corner|
 
     origin := self origin.
     corner := self corner.
 
     "Make sure the intersection is non-empty"
     (origin <= aRectangle corner and: [aRectangle origin <= corner])
-	    ifFalse: [^ Array with: self].
-    areas := OrderedCollection new.
-    aRectangle origin y > origin y
-	    ifTrue: [areas addLast: (origin corner: corner x @ (yOrigin := aRectangle origin y))]
-	    ifFalse: [yOrigin := origin y].
-    aRectangle corner y < corner y
-	    ifTrue: [areas addLast: (origin x @ (yCorner := aRectangle corner y) corner: corner)]
-	    ifFalse: [yCorner := corner y].
-    aRectangle origin x > origin x
-	    ifTrue: [areas addLast: (origin x @ yOrigin corner: aRectangle origin x @ yCorner)].
-    aRectangle corner x < corner x
-	    ifTrue: [areas addLast: (aRectangle corner x @ yOrigin corner: corner x @ yCorner)].
-    ^areas
+            ifFalse: [ aBlock value:self. ^ self].
 
+    aRectangle origin y > origin y ifTrue: [ 
+        aBlock value: (origin corner: corner x @ (yOrigin := aRectangle origin y)) 
+    ] ifFalse: [ 
+        yOrigin := origin y 
+    ].
+    aRectangle corner y < corner y ifTrue: [ 
+        aBlock value: (origin x @ (yCorner := aRectangle corner y) corner: corner) 
+    ] ifFalse: [
+        yCorner := corner y
+    ].
+    aRectangle origin x > origin x ifTrue: [ 
+        aBlock value: (origin x @ yOrigin corner: aRectangle origin x @ yCorner) 
+    ].
+    aRectangle corner x < corner x ifTrue: [ 
+        aBlock value: (aRectangle corner x @ yOrigin corner: corner x @ yCorner) 
+    ].
+
+    "
+     self assert:
+            ((0@0 corner:100@100) areasOutside:(0@0 corner:50@50)) asArray
+            = { (Rectangle origin:0@50 extent:100@50) . (Rectangle origin:50@0 extent:50@50) }
+
+     self assert:
+            ((0@0 corner:150@100) areasOutside:(0@0 corner:100@100)) asArray
+            = { (Rectangle origin:100@0 extent:50@100) }
+
+     self assert:
+            ((0@0 corner:100@150) areasOutside:(0@0 corner:100@100)) asArray
+            = { (Rectangle origin:0@100 extent:100@50) }
+    "
+    
     "/ cg: the old code below was wrong ...
 
 "/    "----------------------------------------------------------------