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
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.