* Region object if no clipping occurs. */ public Region getIntersection(Region r) { if (this.isInsideQuickCheck(r)) { return this; } if (r.isInsideQuickCheck(this)) { return r; } Region ret = new Region((r.lox < this.lox) ? this.lox : r.lox, (r.loy < this.loy) ? this.loy : r.loy, (r.hix > this.hix) ? this.hix : r.hix, (r.hiy > this.hiy) ? this.hiy : r.hiy); if (!ret.isEmpty()) { ret.filterSpans(this, r, INCLUDE_COMMON); } return ret; } /** * Returns a Region object that represents the union of this * object with the specified Region object. * <p> * If {@code A} and {@code B} are both Region Objects and * <code>C = A.getUnion(B);</code> then a point will * be contained in {@code C} iff it is contained in either * {@code A} or {@code B}. * <p> * The return value may be this same object or the argument * Region object if no augmentation occurs. */ public Region getUnion(Region r) { if (r.isEmpty() || r.isInsideQuickCheck(this)) { return this; } if (this.isEmpty() || this.isInsideQuickCheck(r)) { return r; } Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox, (r.loy > this.loy) ? this.loy : r.loy, (r.hix < this.hix) ? this.hix : r.hix, (r.hiy < this.hiy) ? this.hiy : r.hiy); ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B | INCLUDE_COMMON); return ret; } /** * Returns a Region object that represents the difference of the * specified Region object subtracted from this object. * <p> * If {@code A} and {@code B} are both Region Objects and * <code>C = A.getDifference(B);</code> then a point will * be contained in {@code C} iff it is contained in * {@code A} but not contained in {@code B}. * <p> * The return value may be this same object or the argument * Region object if no clipping occurs. */ public Region getDifference(Region r) { if (!r.intersectsQuickCheck(this)) { return this; } if (this.isInsideQuickCheck(r)) { return EMPTY_REGION; } Region ret = new Region(this.lox, this.loy, this.hix, this.hiy); ret.filterSpans(this, r, INCLUDE_A); return ret; } /** * Returns a Region object that represents the exclusive or of this * object with the specified Region object. * <p> * If {@code A} and {@code B} are both Region Objects and * <code>C = A.getExclusiveOr(B);</code> then a point will * be contained in {@code C} iff it is contained in either * {@code A} or {@code B}, but not if it is contained in both. * <p> * The return value may be this same object or the argument * Region object if either is empty. */ public Region getExclusiveOr(Region r) { if (r.isEmpty()) { return this; } if (this.isEmpty()) { return r; } Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox, (r.loy > this.loy) ? this.loy : r.loy, (r.hix < this.hix) ? this.hix : r.hix, (r.hiy < this.hiy) ? this.hiy : r.hiy); ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B); return ret; } static final int INCLUDE_A = 1; static final int INCLUDE_B = 2; static final int INCLUDE_COMMON = 4; private void filterSpans(Region ra, Region rb, int flags) { int abands[] = ra.bands; int bbands[] = rb.bands; if (abands == null) { abands = new int[] {ra.loy, ra.hiy, 1, ra.lox, ra.hix}; } if (bbands == null) { bbands = new int[] {rb.loy, rb.hiy, 1, rb.lox, rb.hix}; } int box[] = new int[6]; int acolstart = 0; int ay1 = abands[acolstart++]; int ay2 = abands[acolstart++]; int acolend = abands[acolstart++]; acolend = acolstart + 2 * acolend; int bcolstart = 0; int by1 = bbands[bcolstart++]; int by2 = bbands[bcolstart++]; int bcolend = bbands[bcolstart++]; bcolend = bcolstart + 2 * bcolend; int y = loy; while (y < hiy) { if (y >= ay2) { if (acolend < ra.endIndex) { acolstart = acolend; ay1 = abands[acolstart++]; ay2 = abands[acolstart++]; acolend = abands[acolstart++]; acolend = acolstart + 2 * acolend; } else { if ((flags & INCLUDE_B) == 0) break; ay1 = ay2 = hiy; } continue; } if (y >= by2) { if (bcolend < rb.endIndex) { bcolstart = bcolend; by1 = bbands[bcolstart++]; by2 = bbands[bcolstart++]; bcolend = bbands[bcolstart++]; bcolend = bcolstart + 2 * bcolend; } else { if ((flags & INCLUDE_A) == 0) break; by1 = by2 = hiy; } continue; } int yend; if (y < by1) { if (y < ay1) { y = Math.min(ay1, by1); continue; } // We are in a set of rows that belong only to A yend = Math.min(ay2, by1); if ((flags & INCLUDE_A) != 0) { box[1] = y; box[3] = yend; int acol = acolstart; while (acol < acolend) { box[0] = abands[acol++]; box[2] = abands[acol++]; appendSpan(box); } } } else if (y < ay1) { // We are in a set of rows that belong only to B yend = Math.min(by2, ay1); if ((flags & INCLUDE_B) != 0) { box[1] = y; box[3] = yend; int bcol = bcolstart; while (bcol < bcolend) { box[0] = bbands[bcol++]; box[2] = bbands[bcol++]; appendSpan(box); } } } else { // We are in a set of rows that belong to both A and B yend = Math.min(ay2, by2); box[1] = y; box[3] = yend; int acol = acolstart; int bcol = bcolstart; int ax1 = abands[acol++]; int ax2 = abands[acol++]; int bx1 = bbands[bcol++]; int bx2 = bbands[bcol++]; int x = Math.min(ax1, bx1); if (x < lox) x = lox; while (x < hix) { if (x >= ax2) { if (acol < acolend) { ax1 = abands[acol++]; ax2 = abands[acol++]; } else { if ((flags & INCLUDE_B) == 0) break; ax1 = ax2 = hix; } continue; } if (x >= bx2) { if (bcol < bcolend) { bx1 = bbands[bcol++]; bx2 = bbands[bcol++]; } else { if ((flags & INCLUDE_A) == 0) break; bx1 = bx2 = hix; } continue; } int xend; boolean appendit; if (x < bx1) { if (x < ax1) { xend = Math.min(ax1, bx1); appendit = false; } else { xend = Math.min(ax2, bx1); appendit = ((flags & INCLUDE_A) != 0); } } else if (x < ax1) { xend = Math.min(ax1, bx2); appendit = ((flags & INCLUDE_B) != 0); } else { xend = Math.min(ax2, bx2); appendit = ((flags & INCLUDE_COMMON) != 0); } if (appendit) { box[0] = x; box[2] = xend; appendSpan(box); } x = xend; } } y = yend; } endRow(box); calcBBox(); } /** * Returns a Region object that represents the bounds of the * intersection of this object with the bounds of the specified * Region object. * <p> * The return value may be this same object if no clipping occurs * and this Region is rectangular. */ public Region getBoundsIntersection(Rectangle r) { return getBoundsIntersectionXYWH(r.x, r.y, r.width, r.height); } /** * Returns a Region object that represents the bounds of the * intersection of this object with the bounds of the specified * rectangular area in x, y, width, height format. * <p> * The return value may be this same object if no clipping occurs * and this Region is rectangular. */ public Region getBoundsIntersectionXYWH(int x, int y, int w, int h) { return getBoundsIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); } /** * Returns a Region object that represents the bounds of the * intersection of this object with the bounds of the specified * rectangular area in lox, loy, hix, hiy format. * <p> * The return value may be this same object if no clipping occurs * and this Region is rectangular. */ public Region getBoundsIntersectionXYXY(int lox, int loy, int hix, int hiy) { if (this.bands == null && this.lox >= lox && this.loy >= loy && this.hix <= hix && this.hiy <= hiy) { return this; } return new Region((lox < this.lox) ? this.lox : lox, (loy < this.loy) ? this.loy : loy, (hix > this.hix) ? this.hix : hix, (hiy > this.hiy) ? this.hiy : hiy); } /** * Returns a Region object that represents the intersection of * this object with the bounds of the specified Region object. * <p> * The return value may be this same object or the argument * Region object if no clipping occurs and the Regions are * rectangular. */ public Region getBoundsIntersection(Region r) { if (this.encompasses(r)) { return r; } if (r.encompasses(this)) { return this; } return new Region((r.lox < this.lox) ? this.lox : r.lox, (r.loy < this.loy) ? this.loy : r.loy, (r.hix > this.hix) ? this.hix : r.hix, (r.hiy > this.hiy) ? this.hiy : r.hiy); } /** * Appends a single span defined by the 4 parameters * spanlox, spanloy, spanhix, spanhiy. * This span must be at a higher starting Y coordinate than * the previous data or it must have a Y range equal to the * highest Y band in the region and a higher X coordinate * than any of the spans in that band. */ private void appendSpan(int box[]) { int spanlox, spanloy, spanhix, spanhiy; if ((spanlox = box[0]) < lox) spanlox = lox; if ((spanloy = box[1]) < loy) spanloy = loy; if ((spanhix = box[2]) > hix) spanhix = hix; if ((spanhiy = box[3]) > hiy) spanhiy = hiy; if (spanhix <= spanlox || spanhiy <= spanloy) { return; } int curYrow = box[4]; if (endIndex == 0 || spanloy >= bands[curYrow + 1]) { if (bands == null) { bands = new int[INIT_SIZE]; } else { needSpace(5); endRow(box); curYrow = box[4]; } bands[endIndex++] = spanloy; bands[endIndex++] = spanhiy; bands[endIndex++] = 0; } else if (spanloy == bands[curYrow] && spanhiy == bands[curYrow + 1] && spanlox >= bands[endIndex - 1]) { if (spanlox == bands[endIndex - 1]) { bands[endIndex - 1] = spanhix; return; } needSpace(2); } else { throw new InternalError("bad span"); } bands[endIndex++] = spanlox; bands[endIndex++] = spanhix; bands[curYrow + 2]++; } private void needSpace(int num) { if (endIndex + num >= bands.length) { int[] newbands = new int[bands.length + GROW_SIZE]; System.arraycopy(bands, 0, newbands, 0, endIndex); bands = newbands; } } private void endRow(int box[]) { int cur = box[4]; int prev = box[5]; if (cur > prev) { int[] bands = this.bands; if (bands[prev + 1] == bands[cur] && bands[prev + 2] == bands[cur + 2]) { int num = bands[cur + 2] * 2; cur += 3; prev += 3; while (num > 0) { if (bands[cur++] != bands[prev++]) { break; } num--; } if (num == 0) { // prev == box[4] bands[box[5] + 1] = bands[prev + 1]; endIndex = prev; return; } } } box[5] = box[4]; box[4] = endIndex; } private void calcBBox() { int[] bands = this.bands; if (endIndex <= 5) { if (endIndex == 0) { lox = loy = hix = hiy = 0; } else { loy = bands[0]; hiy = bands[1]; lox = bands[3]; hix = bands[4]; endIndex = 0; } this.bands = null; return; } int lox = this.hix; int hix = this.lox; int hiyindex = 0; int i = 0; while (i < endIndex) { hiyindex = i; int numbands = bands[i + 2]; i += 3; if (lox > bands[i]) { lox = bands[i]; } i += numbands * 2; if (hix < bands[i - 1]) { hix = bands[i - 1]; } } this.lox = lox; this.loy = bands[0]; this.hix = hix; this.hiy = bands[hiyindex + 1]; } /** * Returns the lowest X coordinate in the Region. */ public final int getLoX() { return lox; } /** * Returns the lowest Y coordinate in the Region. */ public final int getLoY() { return loy; } /** * Returns the highest X coordinate in the Region. */ public final int getHiX() { return hix; } /** * Returns the highest Y coordinate in the Region. */ public final int getHiY() { return hiy; } /** * Returns the width of this Region clipped to the range (0 - MAX_INT). */ public final int getWidth() { if (hix < lox) return 0; int w; if ((w = hix - lox) < 0) { w = Integer.MAX_VALUE; } return w; } /** * Returns the height of this Region clipped to the range (0 - MAX_INT). */ public final int getHeight() { if (hiy < loy) return 0; int h; if ((h = hiy - loy) < 0) { h = Integer.MAX_VALUE; } return h; } /** * Returns true iff this Region encloses no area. */ public boolean isEmpty() { return (hix <= lox || hiy <= loy); } /** * Returns true iff this Region represents a single simple * rectangular area. */ public boolean isRectangular() { return (bands == null);Lines 501–1000 of 1255. Previous lines Load more
* Region object if no clipping occurs. */ public Region getIntersection(Region r) { if (this.isInsideQuickCheck(r)) { return this; } if (r.isInsideQuickCheck(this)) { return r; } Region ret = new Region((r.lox < this.lox) ? this.lox : r.lox, (r.loy < this.loy) ? this.loy : r.loy, (r.hix > this.hix) ? this.hix : r.hix, (r.hiy > this.hiy) ? this.hiy : r.hiy); if (!ret.isEmpty()) { ret.filterSpans(this, r, INCLUDE_COMMON); } return ret; } /** * Returns a Region object that represents the union of this * object with the specified Region object. * <p> * If {@code A} and {@code B} are both Region Objects and * <code>C = A.getUnion(B);</code> then a point will * be contained in {@code C} iff it is contained in either * {@code A} or {@code B}. * <p> * The return value may be this same object or the argument * Region object if no augmentation occurs. */ public Region getUnion(Region r) { if (r.isEmpty() || r.isInsideQuickCheck(this)) { return this; } if (this.isEmpty() || this.isInsideQuickCheck(r)) { return r; } Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox, (r.loy > this.loy) ? this.loy : r.loy, (r.hix < this.hix) ? this.hix : r.hix, (r.hiy < this.hiy) ? this.hiy : r.hiy); ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B | INCLUDE_COMMON); return ret; } /** * Returns a Region object that represents the difference of the * specified Region object subtracted from this object. * <p> * If {@code A} and {@code B} are both Region Objects and * <code>C = A.getDifference(B);</code> then a point will * be contained in {@code C} iff it is contained in * {@code A} but not contained in {@code B}. * <p> * The return value may be this same object or the argument * Region object if no clipping occurs. */ public Region getDifference(Region r) { if (!r.intersectsQuickCheck(this)) { return this; } if (this.isInsideQuickCheck(r)) { return EMPTY_REGION; } Region ret = new Region(this.lox, this.loy, this.hix, this.hiy); ret.filterSpans(this, r, INCLUDE_A); return ret; } /** * Returns a Region object that represents the exclusive or of this * object with the specified Region object. * <p> * If {@code A} and {@code B} are both Region Objects and * <code>C = A.getExclusiveOr(B);</code> then a point will * be contained in {@code C} iff it is contained in either * {@code A} or {@code B}, but not if it is contained in both. * <p> * The return value may be this same object or the argument * Region object if either is empty. */ public Region getExclusiveOr(Region r) { if (r.isEmpty()) { return this; } if (this.isEmpty()) { return r; } Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox, (r.loy > this.loy) ? this.loy : r.loy, (r.hix < this.hix) ? this.hix : r.hix, (r.hiy < this.hiy) ? this.hiy : r.hiy); ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B); return ret; } static final int INCLUDE_A = 1; static final int INCLUDE_B = 2; static final int INCLUDE_COMMON = 4; private void filterSpans(Region ra, Region rb, int flags) { int abands[] = ra.bands; int bbands[] = rb.bands; if (abands == null) { abands = new int[] {ra.loy, ra.hiy, 1, ra.lox, ra.hix}; } if (bbands == null) { bbands = new int[] {rb.loy, rb.hiy, 1, rb.lox, rb.hix}; } int box[] = new int[6]; int acolstart = 0; int ay1 = abands[acolstart++]; int ay2 = abands[acolstart++]; int acolend = abands[acolstart++]; acolend = acolstart + 2 * acolend; int bcolstart = 0; int by1 = bbands[bcolstart++]; int by2 = bbands[bcolstart++]; int bcolend = bbands[bcolstart++]; bcolend = bcolstart + 2 * bcolend; int y = loy; while (y < hiy) { if (y >= ay2) { if (acolend < ra.endIndex) { acolstart = acolend; ay1 = abands[acolstart++]; ay2 = abands[acolstart++]; acolend = abands[acolstart++]; acolend = acolstart + 2 * acolend; } else { if ((flags & INCLUDE_B) == 0) break; ay1 = ay2 = hiy; } continue; } if (y >= by2) { if (bcolend < rb.endIndex) { bcolstart = bcolend; by1 = bbands[bcolstart++]; by2 = bbands[bcolstart++]; bcolend = bbands[bcolstart++]; bcolend = bcolstart + 2 * bcolend; } else { if ((flags & INCLUDE_A) == 0) break; by1 = by2 = hiy; } continue; } int yend; if (y < by1) { if (y < ay1) { y = Math.min(ay1, by1); continue; } // We are in a set of rows that belong only to A yend = Math.min(ay2, by1); if ((flags & INCLUDE_A) != 0) { box[1] = y; box[3] = yend; int acol = acolstart; while (acol < acolend) { box[0] = abands[acol++]; box[2] = abands[acol++]; appendSpan(box); } } } else if (y < ay1) { // We are in a set of rows that belong only to B yend = Math.min(by2, ay1); if ((flags & INCLUDE_B) != 0) { box[1] = y; box[3] = yend; int bcol = bcolstart; while (bcol < bcolend) { box[0] = bbands[bcol++]; box[2] = bbands[bcol++]; appendSpan(box); } } } else { // We are in a set of rows that belong to both A and B yend = Math.min(ay2, by2); box[1] = y; box[3] = yend; int acol = acolstart; int bcol = bcolstart; int ax1 = abands[acol++]; int ax2 = abands[acol++]; int bx1 = bbands[bcol++]; int bx2 = bbands[bcol++]; int x = Math.min(ax1, bx1); if (x < lox) x = lox; while (x < hix) { if (x >= ax2) { if (acol < acolend) { ax1 = abands[acol++]; ax2 = abands[acol++]; } else { if ((flags & INCLUDE_B) == 0) break; ax1 = ax2 = hix; } continue; } if (x >= bx2) { if (bcol < bcolend) { bx1 = bbands[bcol++]; bx2 = bbands[bcol++]; } else { if ((flags & INCLUDE_A) == 0) break; bx1 = bx2 = hix; } continue; } int xend; boolean appendit; if (x < bx1) { if (x < ax1) { xend = Math.min(ax1, bx1); appendit = false; } else { xend = Math.min(ax2, bx1); appendit = ((flags & INCLUDE_A) != 0); } } else if (x < ax1) { xend = Math.min(ax1, bx2); appendit = ((flags & INCLUDE_B) != 0); } else { xend = Math.min(ax2, bx2); appendit = ((flags & INCLUDE_COMMON) != 0); } if (appendit) { box[0] = x; box[2] = xend; appendSpan(box); } x = xend; } } y = yend; } endRow(box); calcBBox(); } /** * Returns a Region object that represents the bounds of the * intersection of this object with the bounds of the specified * Region object. * <p> * The return value may be this same object if no clipping occurs * and this Region is rectangular. */ public Region getBoundsIntersection(Rectangle r) { return getBoundsIntersectionXYWH(r.x, r.y, r.width, r.height); } /** * Returns a Region object that represents the bounds of the * intersection of this object with the bounds of the specified * rectangular area in x, y, width, height format. * <p> * The return value may be this same object if no clipping occurs * and this Region is rectangular. */ public Region getBoundsIntersectionXYWH(int x, int y, int w, int h) { return getBoundsIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h)); } /** * Returns a Region object that represents the bounds of the * intersection of this object with the bounds of the specified * rectangular area in lox, loy, hix, hiy format. * <p> * The return value may be this same object if no clipping occurs * and this Region is rectangular. */ public Region getBoundsIntersectionXYXY(int lox, int loy, int hix, int hiy) { if (this.bands == null && this.lox >= lox && this.loy >= loy && this.hix <= hix && this.hiy <= hiy) { return this; } return new Region((lox < this.lox) ? this.lox : lox, (loy < this.loy) ? this.loy : loy, (hix > this.hix) ? this.hix : hix, (hiy > this.hiy) ? this.hiy : hiy); } /** * Returns a Region object that represents the intersection of * this object with the bounds of the specified Region object. * <p> * The return value may be this same object or the argument * Region object if no clipping occurs and the Regions are * rectangular. */ public Region getBoundsIntersection(Region r) { if (this.encompasses(r)) { return r; } if (r.encompasses(this)) { return this; } return new Region((r.lox < this.lox) ? this.lox : r.lox, (r.loy < this.loy) ? this.loy : r.loy, (r.hix > this.hix) ? this.hix : r.hix, (r.hiy > this.hiy) ? this.hiy : r.hiy); } /** * Appends a single span defined by the 4 parameters * spanlox, spanloy, spanhix, spanhiy. * This span must be at a higher starting Y coordinate than * the previous data or it must have a Y range equal to the * highest Y band in the region and a higher X coordinate * than any of the spans in that band. */ private void appendSpan(int box[]) { int spanlox, spanloy, spanhix, spanhiy; if ((spanlox = box[0]) < lox) spanlox = lox; if ((spanloy = box[1]) < loy) spanloy = loy; if ((spanhix = box[2]) > hix) spanhix = hix; if ((spanhiy = box[3]) > hiy) spanhiy = hiy; if (spanhix <= spanlox || spanhiy <= spanloy) { return; } int curYrow = box[4]; if (endIndex == 0 || spanloy >= bands[curYrow + 1]) { if (bands == null) { bands = new int[INIT_SIZE]; } else { needSpace(5); endRow(box); curYrow = box[4]; } bands[endIndex++] = spanloy; bands[endIndex++] = spanhiy; bands[endIndex++] = 0; } else if (spanloy == bands[curYrow] && spanhiy == bands[curYrow + 1] && spanlox >= bands[endIndex - 1]) { if (spanlox == bands[endIndex - 1]) { bands[endIndex - 1] = spanhix; return; } needSpace(2); } else { throw new InternalError("bad span"); } bands[endIndex++] = spanlox; bands[endIndex++] = spanhix; bands[curYrow + 2]++; } private void needSpace(int num) { if (endIndex + num >= bands.length) { int[] newbands = new int[bands.length + GROW_SIZE]; System.arraycopy(bands, 0, newbands, 0, endIndex); bands = newbands; } } private void endRow(int box[]) { int cur = box[4]; int prev = box[5]; if (cur > prev) { int[] bands = this.bands; if (bands[prev + 1] == bands[cur] && bands[prev + 2] == bands[cur + 2]) { int num = bands[cur + 2] * 2; cur += 3; prev += 3; while (num > 0) { if (bands[cur++] != bands[prev++]) { break; } num--; } if (num == 0) { // prev == box[4] bands[box[5] + 1] = bands[prev + 1]; endIndex = prev; return; } } } box[5] = box[4]; box[4] = endIndex; } private void calcBBox() { int[] bands = this.bands; if (endIndex <= 5) { if (endIndex == 0) { lox = loy = hix = hiy = 0; } else { loy = bands[0]; hiy = bands[1]; lox = bands[3]; hix = bands[4]; endIndex = 0; } this.bands = null; return; } int lox = this.hix; int hix = this.lox; int hiyindex = 0; int i = 0; while (i < endIndex) { hiyindex = i; int numbands = bands[i + 2]; i += 3; if (lox > bands[i]) { lox = bands[i]; } i += numbands * 2; if (hix < bands[i - 1]) { hix = bands[i - 1]; } } this.lox = lox; this.loy = bands[0]; this.hix = hix; this.hiy = bands[hiyindex + 1]; } /** * Returns the lowest X coordinate in the Region. */ public final int getLoX() { return lox; } /** * Returns the lowest Y coordinate in the Region. */ public final int getLoY() { return loy; } /** * Returns the highest X coordinate in the Region. */ public final int getHiX() { return hix; } /** * Returns the highest Y coordinate in the Region. */ public final int getHiY() { return hiy; } /** * Returns the width of this Region clipped to the range (0 - MAX_INT). */ public final int getWidth() { if (hix < lox) return 0; int w; if ((w = hix - lox) < 0) { w = Integer.MAX_VALUE; } return w; } /** * Returns the height of this Region clipped to the range (0 - MAX_INT). */ public final int getHeight() { if (hiy < loy) return 0; int h; if ((h = hiy - loy) < 0) { h = Integer.MAX_VALUE; } return h; } /** * Returns true iff this Region encloses no area. */ public boolean isEmpty() { return (hix <= lox || hiy <= loy); } /** * Returns true iff this Region represents a single simple * rectangular area. */ public boolean isRectangular() { return (bands == null);