region.cpp

Go to the documentation of this file.
00001 /* $TOG: Region.c /main/31 1998/02/06 17:50:22 kaleb $ */
00002 /************************************************************************
00003  
00004 Copyright 1987, 1988, 1998  The Open Group
00005  
00006 All Rights Reserved.
00007  
00008 The above copyright notice and this permission notice shall be included in
00009 all copies or substantial portions of the Software.
00010  
00011 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00012 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00013 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00014 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00015 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00016 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00017  
00018 Except as contained in this notice, the name of The Open Group shall not be
00019 used in advertising or otherwise to promote the sale, use or other dealings
00020 in this Software without prior written authorization from The Open Group.
00021  
00022  
00023 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
00024  
00025                         All Rights Reserved
00026  
00027 Permission to use, copy, modify, and distribute this software and its 
00028 documentation for any purpose and without fee is hereby granted, 
00029 provided that the above copyright notice appear in all copies and that
00030 both that copyright notice and this permission notice appear in 
00031 supporting documentation, and that the name of Digital not be
00032 used in advertising or publicity pertaining to distribution of the
00033 software without specific, written prior permission.  
00034  
00035 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
00036 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
00037 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
00038 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
00039 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
00040 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
00041 SOFTWARE.
00042  
00043 ************************************************************************/
00044 /* $XFree86: xc/lib/X11/Region.c,v 1.5 1999/05/09 10:50:01 dawes Exp $ */
00045 /*
00046  * The functions in this file implement the Region abstraction, similar to one
00047  * used in the X11 sample server. A Region is simply an area, as the name
00048  * implies, and is implemented as a "y-x-banded" array of rectangles. To
00049  * explain: Each Region is made up of a certain number of rectangles sorted
00050  * by y coordinate first, and then by x coordinate.
00051  *
00052  * Furthermore, the rectangles are banded such that every rectangle with a
00053  * given upper-left y coordinate (y1) will have the same lower-right y
00054  * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
00055  * will span the entire vertical distance of the band. This means that some
00056  * areas that could be merged into a taller rectangle will be represented as
00057  * several shorter rectangles to account for shorter rectangles to its left
00058  * or right but within its "vertical scope".
00059  *
00060  * An added constraint on the rectangles is that they must cover as much
00061  * horizontal area as possible. E.g. no two rectangles in a band are allowed
00062  * to touch.
00063  *
00064  * Whenever possible, bands will be merged together to cover a greater vertical
00065  * distance (and thus reduce the number of rectangles). Two bands can be merged
00066  * only if the bottom of one touches the top of the other and they have
00067  * rectangles in the same places (of the same width, of course). This maintains
00068  * the y-x-banding that's so nice to have...
00069  */
00070 
00071 // Stolen from Gtk+ and C++-ized by Ron Steinke, January 2003
00072 // Heavily C++-ized with copy on write semantics by Vassilis Virvilis March 2006
00073 
00074 #include <iostream>
00075 
00076 #include "wt/region.h"
00077 
00078 namespace Wt {
00079 
00080 Region::Region()
00081         : rects_(new RectArray),
00082 extents(0, 0, 0, 0) {}
00083 
00084 Region::Region(const Rect& rect)
00085         : rects_(new RectArray(1, rect)),
00086 extents(rect) {}
00087 
00088 Region::Region(const Point& p)
00089         : rects_(new RectArray(1, Rect(p.x(), p.y(), 1, 1))),
00090 extents(rects()[0]) {}
00091 
00092 bool Region::contains(const Point& p) const {
00093     const int n = rects().size();
00094     if (!n)
00095         return false;
00096     if (!extents.contains(p))
00097         return false;
00098 
00099     const RectArray& ra = rects();
00100     for (int i=0; i < n; i++) {
00101         if (ra[i].contains(p))
00102             return true;
00103     }
00104     return false;
00105 }
00106 
00107 bool Region::contains(const Rect& rect) const {
00108     const int n = rects().size();
00109 
00110     /* this is (just) a useful optimization */
00111     if (!n || !extents.intersects(rect))
00112         return false;
00113 
00114     bool partIn = false;
00115     bool partOut = false;
00116     int rx = rect.x();
00117     int ry = rect.y();
00118     const RectArray& ra = rects();
00119 
00120     /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
00121     for (int i = 0; i < n; i++) {
00122         const Rect& pbox = ra[i];
00123 
00124         if (pbox.bottom() < ry)
00125             continue;   /* getting up to speed or skipping remainder of band */
00126 
00127         if (pbox.top() > ry) {
00128             partOut = true; /* missed part of rectangle above */
00129             if (partIn || (pbox.top() > rect.bottom()))
00130                 break;
00131             ry = pbox.top();    /* x guaranteed to be == prect->x1 */
00132         }
00133 
00134         if (pbox.right() < rx)
00135             continue;       /* not far enough over yet */
00136 
00137         if (pbox.left() > rx) {
00138             partOut = true; /* missed part of rectangle to left */
00139             if (partIn)
00140                 break;
00141         }
00142 
00143         if (pbox.left() <= rect.right()) {
00144             partIn = true;  /* definitely overlap */
00145             if (partOut)
00146                 break;
00147         }
00148 
00149         if (pbox.right() >= rect.right()) {
00150             ry = pbox.bottom() + 1; /* finished with this band */
00151             if (ry > rect.bottom())
00152                 break;
00153             rx = rect.left();   /* reset x out to left again */
00154         } else {
00155             /*
00156              * Because boxes in a band are maximal width, if the first box
00157              * to overlap the rectangle doesn't completely cover it in that
00158              * band, the rectangle must be partially out, since some of it
00159              * will be uncovered in that band. partIn will have been set true
00160              * by now...
00161              */
00162             break;
00163         }
00164     }
00165 
00166     return (partIn ? ((ry <= rect.bottom()) ? false: true) : false);
00167 }
00168 
00169 /* TranslateRegion(pRegion, x, y)
00170    translates in place
00171    added by raymond
00172 */
00173 void Region::translate(int dx, int dy) {
00174     const int n = rects_->size();
00175     if (!n)
00176         return;
00177 
00178     detach();
00179     RectArray& ra = *rects_;
00180     for (int i = 0; i < n; i++) {
00181         ra[i].moveBy(dx, dy);
00182     }
00183 
00184     extents.moveBy(dx, dy);
00185 }
00186 
00187 /*! unite */
00188 Region Region::unite(const Region& r) const {
00189     const int n = rects().size();
00190     /*  checks all the simple cases */
00191 
00192     /*
00193      * region is empty
00194      */
00195     if (!n) {
00196         return r;
00197     }
00198 
00199     const int rn = r.rects().size();
00200     /*
00201      * region and other are the same or other is empty
00202      */
00203     if ( (this == &r) || !rn )
00204         return *this;
00205 
00206     // region completely subsumes other
00207     if ((n == 1) && extents.contains(r.extents))
00208         return *this;
00209 
00210     // other completely subsumes region
00211     if ((rn == 1) && r.extents.contains(extents))
00212         return r;
00213 
00214     Region r1(*this);
00215     r1.detach();
00216 
00217     r1.regionOp(r, &Region::unionO,
00218                 &Region::unionNonO, &Region::unionNonO);
00219     r1.extents.setCoords(std::min(extents.left(), r.extents.left()),
00220                          std::min(extents.top(), r.extents.top()),
00221                          std::max(extents.right(), r.extents.right()),
00222                          std::max(extents.bottom(), r.extents.bottom()));
00223 
00224     return r1;
00225 }
00226 
00227 Region Region::intersect(const Region& r) const {
00228     const int n = rects().size();
00229     const int rn = r.rects().size();
00230     Region r1(*this);
00231     r1.detach();
00232 
00233     /* check for trivial reject */
00234     if (!n || !rn || !r1.extents.intersects(r.extents))
00235         r1.rects_->clear();
00236     else
00237         r1.regionOp(r, &Region::intersectO, 0, 0);
00238 
00239     /*
00240      * Can't alter region's extents before miRegionOp depends on the
00241      * extents of the regions being unchanged. Besides, this way there's
00242      * no checking against rectangles that will be nuked due to
00243      * coalescing, so we have to examine fewer rectangles.
00244      */
00245     r1.calc_extents();
00246 
00247     return r1;
00248 }
00249 
00250 Region Region::subtract(const Region& r) const {
00251     const int n = rects().size();
00252     const int rn = r.rects().size();
00253 
00254     /* check for trivial reject */
00255     if (!n || !rn || !extents.intersects(r.extents))
00256         return *this;
00257 
00258     Region r1(*this);
00259     r1.detach();
00260 
00261     r1.regionOp(r, &Region::subtractO, &Region::subtractNonO, 0);
00262 
00263     /*
00264      * Can't alter region's extents before we call miRegionOp because miRegionOp
00265      * depends on the extents of those regions being the unaltered. Besides, this
00266      * way there's no checking against rectangles that will be nuked
00267      * due to coalescing, so we have to examine fewer rectangles.
00268      */
00269     r1.calc_extents ();
00270 
00271     return r1;
00272 }
00273 
00274 Region Region::eor(const Region& r) const {
00275     Region r1(*this);
00276     Region temp(r - r1);
00277 
00278     r1 -= r;
00279 
00280     return r1 |= temp;
00281 }
00282 
00283 Region Region::operator|(const Region& r) const {
00284     return this->unite(r);
00285 }
00286 
00287 Region Region::operator+(const Region& r) const {
00288     return this->unite(r);
00289 }
00290 
00291 Region Region::operator&(const Region& r) const {
00292     return this->intersect(r);
00293 }
00294 
00295 Region Region::operator-(const Region& r) const {
00296     return this->subtract(r);
00297 }
00298 
00299 Region Region::operator^(const Region& r) const {
00300     return this->eor(r);
00301 }
00302 
00303 Region& Region::operator|=(const Region& r) {
00304     detach();
00305     *this = *this | r;
00306     return *this;
00307 }
00308 
00309 Region& Region::operator+=(const Region& r) {
00310     detach();
00311     *this = *this + r;
00312     return *this;
00313 }
00314 
00315 Region& Region::operator&=(const Region& r) {
00316     detach();
00317     *this = *this & r;
00318     return *this;
00319 }
00320 
00321 Region& Region::operator-=(const Region& r) {
00322     detach();
00323     *this = *this - r;
00324     return *this;
00325 }
00326 
00327 Region& Region::operator^=(const Region& r) {
00328     detach();
00329     *this = *this ^ r;
00330     return *this;
00331 }
00332 
00333 bool Region::operator==(const Region& other) const {
00334     const int n = rects().size();
00335     const int other_n = other.rects().size();
00336     if (n!= other_n)
00337         return false;
00338     else if (!n)
00339         return true;
00340     else if (extents != other.extents)
00341         return false;
00342     else {
00343         const RectArray& ra = rects();
00344         const RectArray& other_ra = other.rects();
00345         for (int i = 0; i < n; i++) {
00346             if (ra[i] != other_ra[i])
00347                 return false;
00348         }
00349     }
00350     return true;
00351 }
00352 
00353 void Region::detach() {
00354     //avoid detaching if we are the only client for this surface
00355     if (!rects_.unique()) {
00356         rects_.reset(new RectArray(*rects_));
00357     }
00358 }
00359 
00360 /*-
00361  *-----------------------------------------------------------------------
00362  * calc_extents --
00363  *  Reset the extents of a region to what they should be. Called by
00364  *  miSubtract and miIntersect b/c they can't figure it out along the
00365  *  way or do so easily, as miUnion can.
00366  *
00367  * Results:
00368  *  None.
00369  *
00370  * Side Effects:
00371  *  The region's 'extents' structure is overwritten.
00372  *
00373  *-----------------------------------------------------------------------
00374  */
00375 void Region::calc_extents() {
00376     const int n = rects().size();
00377 
00378     if (!n) {
00379         extents.setRect(0, 0, 0, 0);
00380         return;
00381     }
00382 
00383     const RectArray& ra = rects();
00384     /*
00385      * Since pBox is the first rectangle in the region, it must have the
00386      * smallest y1 and since pBoxEnd is the last rectangle in the region,
00387      * it must have the largest y2, because of banding. Initialize x1 and
00388      * x2 from  pBox and pBoxEnd, resp., as good things to initialize them
00389      * to...
00390      */
00391     extents.setCoords(ra[0].x(), ra[0].y(), ra[0].right(), ra[n - 1].bottom());
00392 
00393     assert(extents.isValid());
00394     for (int i = 1; i < n; i++) {
00395         extents.setCoords(std::min(ra[i].left(), extents.left()), extents.top(),
00396                           std::max(ra[i].right(), extents.right()), extents.bottom());
00397     }
00398     assert(extents.isValid());
00399 }
00400 
00401 /*======================================================================
00402  *      Generic Region Operator
00403  *====================================================================*/
00404 
00405 /*-
00406  *-----------------------------------------------------------------------
00407  * miCoalesce --
00408  *  Attempt to merge the boxes in the current band with those in the
00409  *  previous one. Used only by miRegionOp.
00410  *
00411  * Results:
00412  *  The new index for the previous band.
00413  *
00414  * Side Effects:
00415  *  If coalescing takes place:
00416  *      - rectangles in the previous band will have their y2 fields
00417  *        altered.
00418  *      - pReg->numRects will be decreased.
00419  *
00420  *-----------------------------------------------------------------------
00421  */
00422 /* static int*/
00423 int Region::coalesce (
00424     int       prevStart,    /* Index of start of previous band */
00425     int       curStart)     /* Index of start of current band */
00426 {
00427     int curNumRects = 0; /* Number of rectangles in current band */
00428     int prevNumRects = curStart - prevStart;
00429 
00430     RectArray& ra = *rects_;
00431     const int n = ra.size();
00432     int numRects = n;
00433 
00434     int bandY1 = ra[curStart].top(); /* Y1 coordinate for current band */
00435     int current;
00436     int current_prev = prevStart;
00437     int current_end = n;
00438 
00439     /*
00440      * Figure out how many rectangles are in the current band. Have to do
00441      * this because multiple bands could have been added in miRegionOp
00442      * at the end when one region has been exhausted.
00443      */
00444     for (current = curStart; current < n
00445             && ra[current].top() == bandY1; current++) {
00446         curNumRects++;
00447     }
00448 
00449     if (current != current_end) {
00450         /*
00451          * If more than one band was added, we have to find the start
00452          * of the last band added so the next coalescing job can start
00453          * at the right place... (given when multiple bands are added,
00454          * this may be pointless -- see above).
00455          */
00456         current_end--;
00457         while (ra[current_end - 1].top() == ra[current_end].top()) {
00458             current_end--;
00459         }
00460         curStart = current_end;
00461         current_end = n;
00462     }
00463 
00464     if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
00465         current -= curNumRects;
00466 
00467         /*
00468          * The bands may only be coalesced if the bottom of the previous
00469          * matches the top scanline of the current.
00470          */
00471         if (ra[current_prev].bottom() == ra[current].top() - 1) {
00472             //if (pPrevBox->bottom() == pCurBox->top() - 1) {
00473             /*
00474              * Make sure the bands have boxes in the same places. This
00475              * assumes that boxes have been added in such a way that they
00476              * cover the most area possible. I.e. two boxes in a band must
00477              * have some horizontal space between them.
00478              */
00479 
00480             do {
00481                 if ((ra[current_prev].left() != ra[current].left()) ||
00482                         (ra[current_prev].right() != ra[current].right())) {
00483                     /*
00484                      * The bands don't line up so they can't be coalesced.
00485                      */
00486                     return curStart;
00487                 }
00488                 current_prev++;
00489                 current++;
00490                 prevNumRects -= 1;
00491             } while (prevNumRects != 0);
00492 
00493             numRects -= curNumRects;
00494             rects_->resize(numRects);
00495             current -= curNumRects;
00496             current_prev -= curNumRects;
00497 
00498             /*
00499              * The bands may be merged, so set the bottom y of each box
00500              * in the previous band to that of the corresponding box in
00501              * the current band.
00502              */
00503 
00504             do {
00505                 ra[current_prev].setBottom(ra[current].bottom());
00506                 current_prev++;
00507                 current++;
00508                 curNumRects--;
00509             } while (curNumRects != 0);
00510 
00511             /*
00512              * If only one band was added to the region, we have to backup
00513              * curStart to the start of the previous band.
00514              *
00515              * If more than one band was added to the region, copy the
00516              * other bands down. The assumption here is that the other bands
00517              * came from the same region as the current one and no further
00518              * coalescing can be done on them since it's all been done
00519              * already... curStart is already in the right place.
00520              */
00521             if (current == current_end) {
00522                 curStart = prevStart;
00523             } else {
00524                 do {
00525                     ra[current_prev] = ra[current];
00526                     current_prev++;
00527                     current++;
00528                 } while (current != current_end);
00529             }
00530         }
00531     }
00532     return curStart;
00533 }
00534 
00535 /*-
00536  *-----------------------------------------------------------------------
00537  * miRegionOp --
00538  *  Apply an operation to two regions. Called by miUnion, miInverse,
00539  *  miSubtract, miIntersect...
00540  *
00541  * Results:
00542  *  None.
00543  *
00544  * Side Effects:
00545  *  The new region is overwritten.
00546  *
00547  * Notes:
00548  *  The idea behind this function is to view the two regions as sets.
00549  *  Together they cover a rectangle of area that this function divides
00550  *  into horizontal bands where points are covered only by one region
00551  *  or by both. For the first case, the nonOverlapFunc is called with
00552  *  each the band and the band's upper and lower extents. For the
00553  *  second, the overlapFunc is called to process the entire band. It
00554  *  is responsible for clipping the rectangles in the band, though
00555  *  this function provides the boundaries.
00556  *  At the end of each band, the new region is coalesced, if possible,
00557  *  to reduce the number of rectangles in the region.
00558  *
00559  *-----------------------------------------------------------------------
00560  */
00561 void Region::regionOp(
00562     const Region& other,
00563     overlapFunc overlapFn,      /* Function to call for overlapping bands */
00564     nonOverlapFunc nonOverlap1Fn,   /* Function to call for non-overlapping bands in region 1 */
00565     nonOverlapFunc nonOverlap2Fn)   /* Function to call for non-overlapping bands in region 2 */
00566 {
00567     int ybot;           /* Bottom of intersection */
00568     int ytop;           /* Top of intersection */
00569     unsigned int prevBand;  /* Index of start of previous band in newReg */
00570     unsigned int curBand;   /* Index of start of current band in newReg */
00571     int top;            /* Top of non-overlapping band */
00572     int bot;            /* Bottom of non-overlapping band */
00573 
00574     /*
00575      * Initialization:
00576      *  set r1, r2, r1End and r2End appropriately, preserve the important
00577      * parts of the destination region until the end in case it's one of
00578      * the two source regions, then mark the "new" region empty, allocating
00579      * another array of rectangles for it to use.
00580      */
00581     const RectArray& ra1 = rects();
00582     const int n1 = ra1.size();
00583     int i1 = 0;
00584     const RectArray& ra2 = other.rects();
00585     const int n2 = ra2.size();
00586     int i2 = 0;
00587     // keep one copy
00588     RectArrayPtr old_rects_ = rects_;
00589     int band_1_end;
00590     int band_2_end;
00591 
00592     /*
00593      * Allocate a reasonable number of rectangles for the new region. The idea
00594      * is to allocate enough so the individual functions don't need to
00595      * reallocate and copy the array, which is time consuming, yet we don't
00596      * have to worry about using too much memory. I hope to be able to
00597      * nuke the Xrealloc() at the end of this function eventually.
00598      */
00599     rects_.reset(new RectArray);
00600     rects_->reserve(std::max(n1, n2) * 2);
00601 
00602     /*
00603      * Initialize ybot and ytop.
00604      * In the upcoming loop, ybot and ytop serve different functions depending
00605      * on whether the band being handled is an overlapping or non-overlapping
00606      * band.
00607      *  In the case of a non-overlapping band (only one of the regions
00608      * has points in the band), ybot is the bottom of the most recent
00609      * intersection and thus clips the top of the rectangles in that band.
00610      * ytop is the top of the next intersection between the two regions and
00611      * serves to clip the bottom of the rectangles in the current band.
00612      *  For an overlapping band (where the two regions intersect), ytop clips
00613      * the top of the rectangles of both regions and ybot clips the bottoms.
00614      */
00615     //ybot = std::min(reg1->extents.top(), reg2->extents.top()) - 1;
00616     ybot = std::min(extents.top(), other.extents.top()) - 1;
00617 
00618     /*
00619      * prevBand serves to mark the start of the previous band so rectangles
00620      * can be coalesced into larger rectangles. qv. miCoalesce, above.
00621      * In the beginning, there is no previous band, so prevBand == curBand
00622      * (curBand is set later on, of course, but the first band will always
00623      * start at index 0). prevBand and curBand must be indices because of
00624      * the possible expansion, and resultant moving, of the new region's
00625      * array of rectangles.
00626      */
00627     prevBand = 0;
00628 
00629     do {
00630         curBand = rects().size();
00631 
00632         /*
00633          * This algorithm proceeds one source-band (as opposed to a
00634          * destination band, which is determined by where the two regions
00635          * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
00636          * rectangle after the last one in the current band for their
00637          * respective regions.
00638          */
00639         for (band_1_end = i1; band_1_end < n1 &&
00640             ra1[band_1_end].top() == ra1[i1].top(); band_1_end++) {}
00641 
00642         for (band_2_end = i2; band_2_end < n2 &&
00643             ra2[band_2_end].top() == ra2[i2].top(); band_2_end++) {}
00644 
00645         /*
00646          * First handle the band that doesn't intersect, if any.
00647          *
00648          * Note that attention is restricted to one band in the
00649          * non-intersecting region at once, so if a region has n
00650          * bands between the current position and the next place it overlaps
00651          * the other, this entire loop will be passed through n times.
00652          */
00653         if (ra1[i1].top() < ra2[i2].top()) {
00654             top = std::max (ra1[i1].top(), ybot + 1);
00655             bot = std::min (ra1[i1].bottom(), ra2[i2].top() - 1);
00656 
00657             if ((top != bot + 1) && (nonOverlap1Fn != 0)) {
00658                 (this->*nonOverlap1Fn)(ra1, i1, band_1_end, top, bot);
00659             }
00660 
00661             ytop = ra2[i2].top();
00662         } else if (ra2[i2].top() < ra1[i1].top()) {
00663             top = std::max (ra2[i2].top(), ybot + 1);
00664             bot = std::min (ra2[i2].bottom(), ra1[i1].top() - 1);
00665 
00666             if ((top != bot + 1) && (nonOverlap2Fn != 0)) {
00667                 (this->*nonOverlap2Fn)(ra2, i2, band_2_end, top, bot);
00668             }
00669 
00670             ytop = ra1[i1].top();
00671         } else {
00672             ytop = ra1[i1].top();
00673         }
00674 
00675         /*
00676          * If any rectangles got added to the region, try and coalesce them
00677          * with rectangles from the previous band. Note we could just do
00678          * this test in miCoalesce, but some machines incur a not
00679          * inconsiderable cost for function calls, so...
00680          */
00681         if (rects().size() != curBand) {
00682             prevBand = coalesce(prevBand, curBand);
00683         }
00684 
00685         /*
00686          * Now see if we've hit an intersecting band. The two bands only
00687          * intersect if ybot > ytop
00688          */
00689         ybot = std::min(ra1[i1].bottom(), ra2[i2].bottom());
00690         curBand = rects().size();
00691         if (ybot >= ytop) {
00692             (this->*overlapFn)(ra1, i1, band_1_end, ra2,
00693                                i2, band_2_end, ytop, ybot);
00694         }
00695 
00696         if (rects().size() != curBand) {
00697             prevBand = coalesce(prevBand, curBand);
00698         }
00699 
00700         /*
00701          * If we've finished with a band (y2 == ybot) we skip forward
00702          * in the region to the next band.
00703          */
00704         if (ra1[i1].bottom() == ybot) {
00705             i1 = band_1_end;
00706         }
00707         if (ra2[i2].bottom() == ybot) {
00708             i2 = band_2_end;
00709         }
00710     } while ((i1 != n1) && (i2 != n2));
00711 
00712     /*
00713      * Deal with whichever region still has rectangles left.
00714      */
00715     curBand = rects().size();
00716     if (i1 != n1 && nonOverlap1Fn) {
00717         do {
00718             for (band_1_end = i1; band_1_end < n1 &&
00719                 ra1[band_1_end].top() == ra1[i1].top(); band_1_end++) {}
00720 
00721             (this->*nonOverlap1Fn)(ra1, i1, band_1_end,
00722                                    std::max(ra1[i1].top(), ybot + 1),
00723                                    ra1[i1].bottom());
00724             i1 = band_1_end;
00725         } while (i1 != n1);
00726     } else if (i2 != n2 && nonOverlap2Fn) {
00727         do {
00728             for (band_2_end = i2; band_2_end < n2 &&
00729                 ra2[band_2_end].top() == ra2[i2].top(); band_2_end++) {}
00730 
00731             (this->*nonOverlap2Fn)(ra2, i2, band_2_end,
00732                                    std::max(ra2[i2].top(), ybot + 1),
00733                                    ra2[i2].bottom());
00734             i2 = band_2_end;
00735         } while (i2 != n2);
00736     }
00737 
00738     if (rects().size() != curBand) {
00739         coalesce(prevBand, curBand);
00740     }
00741 }
00742 
00743 /*======================================================================
00744  *      Region Union
00745  *====================================================================*/
00746 
00747 /*-
00748  *-----------------------------------------------------------------------
00749  * miUnionNonO --
00750  *  Handle a non-overlapping band for the union operation. Just
00751  *  Adds the rectangles into the region. Doesn't have to check for
00752  *  subsumption or anything.
00753  *
00754  * Results:
00755  *  None.
00756  *
00757  * Side Effects:
00758  *  pReg->numRects is incremented and the final rectangles overwritten
00759  *  with the rectangles we're passed.
00760  *
00761  *-----------------------------------------------------------------------
00762  */
00763 void Region::unionNonO(const RectArray& ra,
00764                        int start, int end, int y1, int y2) {
00765     assert(y1 <= y2);
00766 
00767     for (int i = start; i < end; i++) {
00768         assert(ra[i].left() <= ra[i].right());
00769         rects_->push_back(Rect(ra[i].left(), y1,
00770                                ra[i].width(), y2 - y1 + 1));
00771     }
00772 }
00773 
00774 void Region::merge_rect(const RectArray& ra, int i, int y1, int y2) {
00775     RectArray& lra = *rects_;
00776     const int total = lra.size();
00777 
00778     if ((total != 0) &&
00779             (lra[total - 1].top() == y1) &&
00780             (lra[total - 1].bottom() == y2) &&
00781             (lra[total - 1].right() + 1 >= ra[i].left())) {
00782 
00783         lra[total - 1].setRight(std::max(lra[total - 1].right(), ra[i].right()));
00784         assert(lra[total - 1].left() <= lra[total - 1].right());
00785     } else {
00786         lra.push_back(Rect(ra[i].left(), y1,
00787                            ra[i].width(), y2 - y1 + 1));
00788     }
00789 }
00790 
00791 /*-
00792  *-----------------------------------------------------------------------
00793  * miUnionO --
00794  *  Handle an overlapping band for the union operation. Picks the
00795  *  left-most rectangle each time and merges it into the region.
00796  *
00797  * Results:
00798  *  None.
00799  *
00800  * Side Effects:
00801  *  Rectangles are overwritten in pReg->rects and pReg->numRects will
00802  *  be changed.
00803  *
00804  *-----------------------------------------------------------------------
00805  */
00806 
00807 void Region::unionO(const RectArray& ra1, int i1, int end1,
00808                     const RectArray& ra2, int i2, int end2,
00809                     int y1, int y2) {
00810     assert (y1 <= y2);
00811     while ((i1 != end1) && (i2 != end2)) {
00812         if (ra1[i1].left() < ra2[i2].left()) {
00813             merge_rect(ra1, i1, y1, y2);
00814             i1++;
00815         } else {
00816             merge_rect(ra2, i2, y1, y2);
00817             i2++;
00818         }
00819     }
00820 
00821     while (i1 != end1) {
00822         merge_rect(ra1, i1, y1, y2);
00823         i1++;
00824     }
00825     while (i2 != end2) {
00826         merge_rect(ra2, i2, y1, y2);
00827         i2++;
00828     }
00829 }
00830 
00831 /*======================================================================
00832  *      Region Intersection
00833  *====================================================================*/
00834 /*-
00835  *-----------------------------------------------------------------------
00836  * miIntersectO --
00837  *  Handle an overlapping band for miIntersect.
00838  *
00839  * Results:
00840  *  None.
00841  *
00842  * Side Effects:
00843  *  Rectangles may be added to the region.
00844  *
00845  *-----------------------------------------------------------------------
00846  */
00847 void Region::intersectO(const RectArray& ra1, int i1, int end1,
00848                         const RectArray& ra2, int i2, int end2,
00849                         int y1, int y2) {
00850     while ((i1 != end1) && (i2 != end2)) {
00851         int x1 = std::max (ra1[i1].left(), ra2[i2].left());
00852         int x2 = std::min (ra1[i1].right(), ra2[i2].right());
00853 
00854         /*
00855          * If there's any overlap between the two rectangles, add that
00856          * overlap to the new region.
00857          * There's no need to check for subsumption because the only way
00858          * such a need could arise is if some region has two rectangles
00859          * right next to each other. Since that should never happen...
00860          */
00861         if (x1 <= x2) {
00862             assert (y1 <= y2);
00863             rects_->push_back(Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1));
00864         }
00865 
00866         /*
00867          * Need to advance the pointers. Shift the one that extends
00868          * to the right the least, since the other still has a chance to
00869          * overlap with that region's next rectangle, if you see what I mean.
00870          */
00871         if (ra1[i1].right() < ra2[i2].right()) {
00872             i1++;
00873         } else if (ra2[i2].right() < ra1[i1].right()) {
00874             i2++;
00875         } else {
00876             i1++;
00877             i2++;
00878         }
00879     }
00880 }
00881 
00882 /*-
00883  *-----------------------------------------------------------------------
00884  * miSubtractNonO --
00885  *  Deal with non-overlapping band for subtraction. Any parts from
00886  *  region 2 we discard. Anything from region 1 we add to the region.
00887  *
00888  * Results:
00889  *  None.
00890  *
00891  * Side Effects:
00892  *  pReg may be affected.
00893  *
00894  *-----------------------------------------------------------------------
00895  */
00896 /* static void*/
00897 void
00898 Region::subtractNonO(const RectArray& ra,
00899                      int start, int end, int y1, int y2) {
00900     assert(y1 <= y2);
00901 
00902     for (int i = start; i < end; i++) {
00903         assert(ra[i].left() <= ra[i].right());
00904         rects_->push_back(Rect(ra[i].left(), y1,
00905                                ra[i].width(), y2 - y1 + 1));
00906     }
00907 }
00908 
00909 /*-
00910  *-----------------------------------------------------------------------
00911  * miSubtractO --
00912  *  Overlapping band subtraction. x1 is the left-most point not yet
00913  *  checked.
00914  *
00915  * Results:
00916  *  None.
00917  *
00918  * Side Effects:
00919  *  pReg may have rectangles added to it.
00920  *
00921  *-----------------------------------------------------------------------
00922  */
00923 void Region::subtractO(const RectArray& ra1, int i1, int end1,
00924                        const RectArray& ra2, int i2, int end2,
00925                        int y1, int y2) {
00926     int x1 = ra1[i1].left();
00927 
00928     assert(y1 <= y2);
00929 
00930     while ((i1 != end1) && (i2 != end2)) {
00931         if (ra2[i2].right() < x1) {
00932             /*
00933              * Subtrahend missed the boat: go to next subtrahend.
00934              */
00935             i2++;
00936         } else if (ra2[i2].left() <= x1) {
00937             /*
00938              * Subtrahend preceeds minuend: nuke left edge of minuend.
00939              */
00940             x1 = ra2[i2].right() + 1;
00941             if (x1 > ra1[i1].right()) {
00942                 /*
00943                  * Minuend completely covered: advance to next minuend and
00944                  * reset left fence to edge of new minuend.
00945                  */
00946                 i1++;
00947                 if (i1 != end1)
00948                     x1 = ra1[i1].left();
00949             } else {
00950                 /*
00951                  * Subtrahend now used up since it doesn't extend beyond
00952                  * minuend
00953                  */
00954                 i2++;
00955             }
00956         } else if (ra2[i2].left() <= ra1[i1].right()) {
00957             /*
00958              * Left part of subtrahend covers part of minuend: add uncovered
00959              * part of minuend to region and skip to next subtrahend.
00960              */
00961             assert(x1 < ra2[i2].left());
00962             rects_->push_back(Rect(x1, y1, ra2[i2].left() - x1, y2 - y1 + 1));
00963 
00964             x1 = ra2[i2].right() + 1;
00965             if (x1 > ra1[i1].right()) {
00966                 /*
00967                  * Minuend used up: advance to new...
00968                  */
00969                 i1++;
00970                 if (i1 != end1)
00971                     x1 = ra1[i1].left();
00972             } else {
00973                 /*
00974                  * Subtrahend used up
00975                  */
00976                 i2++;
00977             }
00978         } else {
00979             /*
00980              * Minuend used up: add any remaining piece before advancing.
00981              */
00982             if (ra1[i1].right() >= x1) {
00983                 rects_->push_back(Rect(x1, y1,
00984                                        ra1[i1].right() - x1 + 1, y2 - y1 + 1));
00985             }
00986             i1++;
00987             if (i1 != end1)
00988                 x1 = ra1[i1].left();
00989         }
00990     }
00991 
00992     /*
00993        * Add remaining minuend rectangles to region.
00994        */
00995     while(i1 != end1) {
00996         assert(x1 <= ra1[i1].right());
00997         rects_->push_back(Rect(x1, y1,
00998                                ra1[i1].right() - x1 + 1, y2 - y1 + 1));
00999 
01000         i1++;
01001         if (i1 != end1)
01002             x1 = ra1[i1].left();
01003     }
01004 }
01005 
01006 std::ostream& operator<<(std::ostream& s, const Region& r) {
01007     const RectArray& ra = r.rects();
01008     const int n = ra.size();
01009 
01010     s << "Region extents: " << r.boundingRect() << std::endl;
01011     for (int i = 0; i < n; i++) {
01012         s << "  rect[" << i << "] = " << ra[i] << std::endl;
01013     }
01014     return s;
01015 }
01016 
01017 }  // namespace Wt

Generated Fri Jul 28 19:23:00 2006.
Copyright © 1998-2003 by the respective authors.

This document is licensed under the terms of the GNU Free Documentation License and may be freely distributed under the conditions given by this license.