/* Apply an operation to two regions. * * The idea behind this function is to view the two regions as sets. * Together they cover a rectangle of area that this function divides * into horizontal bands where points are covered only by one region * or by both. For the first case, the nonOverlapFunc is called with * each the band and the band's upper and lower extents. For the * second, the overlapFunc is called to process the entire band. It * is responsible for clipping the rectangles in the band, though * this function provides the boundaries. * At the end of each band, the new region is coalesced, if possible, * to reduce the number of rectangles in the region. */ private static Region RegionOperation(Region reg1, Region reg2, RegionOperationType regionOperationType) { float ybot; /* Bottom of intersection */ float ytop; /* Top of intersection */ int prevBand; /* Index of start of previous band in newReg */ int curBand; /* Index of start of current band in newReg */ int r1BandEnd; /* End of current band in r1 */ int r2BandEnd; /* End of current band in r2 */ float top; /* Top of non-overlapping band */ float bot; /* Bottom of non-overlapping band */ /* Initialization: * set r1, r2, r1End and r2End appropriately */ int r1 = 0; int r1End = reg1.rects.Length; /* End of 1st region */ RectangleF reg1Extent = reg1.extent; int r2 = 0; int r2End = reg2.rects.Length; /* End of 2nd region */ RectangleF reg2Extent = reg2.extent; RectangleF rect1; RectangleF rect2; /* * Allocate a reasonable number of rectangles for the new region. The idea * is to allocate enough so the individual functions don't need to * reallocate and copy the array, which is time consuming, yet we don't * want to use too much memory. */ Region newReg = new Region(Math_Max(reg1.rects.Length, reg2.rects.Length) * 2); // The total number of rectangles we have written to the array int nextRectangle = 0; /* * Initialize ybot and ytop. * In the upcoming loop, ybot and ytop serve different functions depending * on whether the band being handled is an overlapping or non-overlapping * band. * In the case of a non-overlapping band (only one of the regions * has points in the band), ybot is the bottom of the most recent * intersection and thus clips the top of the rectangles in that band. * ytop is the top of the next intersection between the two regions and * serves to clip the bottom of the rectangles in the current band. * For an overlapping band (where the two regions intersect), ytop clips * the top of the rectangles of both regions and ybot clips the bottoms. */ if (reg1Extent.Top < reg2Extent.Top) { ybot = reg1Extent.Top; } else { ybot = reg2Extent.Top; } /* * prevBand serves to mark the start of the previous band so rectangles * can be coalesced into larger rectangles. qv. miCoalesce, above. * In the beginning, there is no previous band, so prevBand == curBand * (curBand is set later on, of course, but the first band will always * start at index 0). prevBand and curBand must be indices because of * the possible expansion, and resultant moving, of the new region's * array of rectangles. */ prevBand = 0; do { rect1 = reg1.rects[r1]; rect2 = reg2.rects[r2]; curBand = nextRectangle; /* * This algorithm proceeds one source-band (as opposed to a * destination band, which is determined by where the two regions * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the * rectangle after the last one in the current band for their * respective regions. */ r1BandEnd = r1; while (r1BandEnd != r1End && reg1.rects[r1BandEnd].Top == rect1.Top) { r1BandEnd++; } r2BandEnd = r2; while (r2BandEnd != r2End && reg2.rects[r2BandEnd].Top == rect2.Top) { r2BandEnd++; } /* * First handle the band that doesn't intersect, if any. * * Note that attention is restricted to one band in the * non-intersecting region at once, so if a region has n * bands between the current position and the next place it overlaps * the other, this entire loop will be passed through n times. */ if (rect1.Top < rect2.Top) { top = Math_Max(rect1.Top, ybot); bot = Math_Min(rect1.Bottom, rect2.Top); if ((top != bot)) { if (regionOperationType == RegionOperationType.Subtract) { SubtractNonOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, top, bot); } else if (regionOperationType == RegionOperationType.Union) { UnionNonOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, top, bot); } } ytop = rect2.Top; } else if (rect2.Top < rect1.Top) { top = Math_Max(rect2.Top, ybot); bot = Math_Min(rect2.Bottom, rect1.Top); if (top != bot) { if (regionOperationType == RegionOperationType.Union) { UnionNonOverlapBands(newReg, ref nextRectangle, reg2, r2, r2BandEnd, top, bot); } } ytop = rect1.Top; } else { ytop = rect1.Top; } /* * If any rectangles got added to the region, try and coalesce them * with rectangles from the previous band. Note we could just do * this test in miCoalesce, but some machines incur a not * inconsiderable cost for function calls, so... */ if (nextRectangle != curBand) { prevBand = CoalesceRegion(newReg, ref nextRectangle, prevBand, curBand); } /* * Now see if we've hit an intersecting band. The two bands only * intersect if ybot > ytop */ ybot = Math_Min(rect1.Bottom, rect2.Bottom); curBand = nextRectangle; if (ybot > ytop) { if (regionOperationType == RegionOperationType.Subtract) { SubtractOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, reg2, r2, r2BandEnd, ytop, ybot); } else if (regionOperationType == RegionOperationType.Union) { UnionOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, reg2, r2, r2BandEnd, ytop, ybot); } else if (regionOperationType == RegionOperationType.Intersect) { IntersectOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, reg2, r2, r2BandEnd, ytop, ybot); } } if (nextRectangle != curBand) { prevBand = CoalesceRegion(newReg, ref nextRectangle, prevBand, curBand); } /* * If we've finished with a band (bottom == ybot) we skip forward * in the region to the next band. */ if (rect1.Bottom == ybot) { r1 = r1BandEnd; } if (rect2.Bottom == ybot) { r2 = r2BandEnd; } }while (r1 != r1End && r2 != r2End); /* * Deal with whichever region still has rectangles left. */ curBand = nextRectangle; if (r1 != r1End) { if (regionOperationType == RegionOperationType.Subtract || regionOperationType == RegionOperationType.Union) { do { rect1 = reg1.rects[r1]; r1BandEnd = r1; while (r1BandEnd < r1End && reg1.rects[r1BandEnd].Top == rect1.Top) { r1BandEnd++; } if (regionOperationType == RegionOperationType.Subtract) { SubtractNonOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, Math_Max(rect1.Top, ybot), rect1.Bottom); } else if (regionOperationType == RegionOperationType.Union) { UnionNonOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, Math_Max(rect1.Top, ybot), rect1.Bottom); } r1 = r1BandEnd; }while (r1 != r1End); } } else if (r2 != r2End && regionOperationType == RegionOperationType.Union) { do { rect2 = reg2.rects[r2]; r2BandEnd = r2; while (r2BandEnd < r2End && reg2.rects[r2BandEnd].Top == rect2.Top) { r2BandEnd++; } UnionNonOverlapBands(newReg, ref nextRectangle, reg2, r2, r2BandEnd, Math_Max(rect2.Top, ybot), rect2.Bottom); r2 = r2BandEnd; } while (r2 != r2End); } if (nextRectangle != curBand) { CoalesceRegion(newReg, ref nextRectangle, prevBand, curBand); } /* * A bit of cleanup. To keep regions from growing without bound, * we shrink the array of rectangles to match the new number of * rectangles in the region. This never goes to 0, however... * * Only do this stuff if the number of rectangles allocated is more than * twice the number of rectangles in the region (a simple optimization...). */ if (nextRectangle < newReg.rects.Length) { RectangleF[] newRects = new RectangleF[nextRectangle]; Array.Copy(newReg.rects, newRects, nextRectangle); newReg.rects = newRects; } return(newReg); }
/* Apply an operation to two regions. * * The idea behind this function is to view the two regions as sets. * Together they cover a rectangle of area that this function divides * into horizontal bands where points are covered only by one region * or by both. For the first case, the nonOverlapFunc is called with * each the band and the band's upper and lower extents. For the * second, the overlapFunc is called to process the entire band. It * is responsible for clipping the rectangles in the band, though * this function provides the boundaries. * At the end of each band, the new region is coalesced, if possible, * to reduce the number of rectangles in the region. */ private static Region RegionOperation( Region reg1, Region reg2, RegionOperationType regionOperationType) { float ybot; /* Bottom of intersection */ float ytop; /* Top of intersection */ int prevBand; /* Index of start of previous band in newReg */ int curBand; /* Index of start of current band in newReg */ int r1BandEnd; /* End of current band in r1 */ int r2BandEnd; /* End of current band in r2 */ float top; /* Top of non-overlapping band */ float bot; /* Bottom of non-overlapping band */ /* Initialization: * set r1, r2, r1End and r2End appropriately */ int r1 = 0; int r1End = reg1.rects.Length; /* End of 1st region */ RectangleF reg1Extent = reg1.extent; int r2 = 0; int r2End = reg2.rects.Length; /* End of 2nd region */ RectangleF reg2Extent = reg2.extent; RectangleF rect1; RectangleF rect2; /* * Allocate a reasonable number of rectangles for the new region. The idea * is to allocate enough so the individual functions don't need to * reallocate and copy the array, which is time consuming, yet we don't * want to use too much memory. */ Region newReg = new Region(Math_Max(reg1.rects.Length, reg2.rects.Length) * 2); // The total number of rectangles we have written to the array int nextRectangle = 0; /* * Initialize ybot and ytop. * In the upcoming loop, ybot and ytop serve different functions depending * on whether the band being handled is an overlapping or non-overlapping * band. * In the case of a non-overlapping band (only one of the regions * has points in the band), ybot is the bottom of the most recent * intersection and thus clips the top of the rectangles in that band. * ytop is the top of the next intersection between the two regions and * serves to clip the bottom of the rectangles in the current band. * For an overlapping band (where the two regions intersect), ytop clips * the top of the rectangles of both regions and ybot clips the bottoms. */ if (reg1Extent.Top < reg2Extent.Top) ybot = reg1Extent.Top; else ybot = reg2Extent.Top; /* * prevBand serves to mark the start of the previous band so rectangles * can be coalesced into larger rectangles. qv. miCoalesce, above. * In the beginning, there is no previous band, so prevBand == curBand * (curBand is set later on, of course, but the first band will always * start at index 0). prevBand and curBand must be indices because of * the possible expansion, and resultant moving, of the new region's * array of rectangles. */ prevBand = 0; do { rect1 = reg1.rects[r1]; rect2 = reg2.rects[r2]; curBand = nextRectangle; /* * This algorithm proceeds one source-band (as opposed to a * destination band, which is determined by where the two regions * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the * rectangle after the last one in the current band for their * respective regions. */ r1BandEnd = r1; while (r1BandEnd != r1End && reg1.rects[r1BandEnd].Top == rect1.Top) r1BandEnd++; r2BandEnd = r2; while (r2BandEnd != r2End && reg2.rects[r2BandEnd].Top == rect2.Top) r2BandEnd++; /* * First handle the band that doesn't intersect, if any. * * Note that attention is restricted to one band in the * non-intersecting region at once, so if a region has n * bands between the current position and the next place it overlaps * the other, this entire loop will be passed through n times. */ if (rect1.Top < rect2.Top) { top = Math_Max(rect1.Top, ybot); bot = Math_Min(rect1.Bottom, rect2.Top); if ((top != bot)) { if (regionOperationType == RegionOperationType.Subtract) SubtractNonOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, top, bot); else if (regionOperationType == RegionOperationType.Union) UnionNonOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, top, bot); } ytop = rect2.Top; } else if (rect2.Top < rect1.Top) { top = Math_Max(rect2.Top, ybot); bot = Math_Min(rect2.Bottom, rect1.Top); if (top != bot) { if (regionOperationType == RegionOperationType.Union) UnionNonOverlapBands(newReg, ref nextRectangle, reg2, r2, r2BandEnd, top, bot); } ytop = rect1.Top; } else ytop = rect1.Top; /* * If any rectangles got added to the region, try and coalesce them * with rectangles from the previous band. Note we could just do * this test in miCoalesce, but some machines incur a not * inconsiderable cost for function calls, so... */ if (nextRectangle != curBand) { prevBand = CoalesceRegion(newReg, ref nextRectangle, prevBand, curBand); } /* * Now see if we've hit an intersecting band. The two bands only * intersect if ybot > ytop */ ybot = Math_Min(rect1.Bottom, rect2.Bottom); curBand = nextRectangle; if (ybot > ytop) { if (regionOperationType == RegionOperationType.Subtract) SubtractOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, reg2, r2, r2BandEnd, ytop, ybot); else if (regionOperationType == RegionOperationType.Union) UnionOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, reg2, r2, r2BandEnd, ytop, ybot); else if (regionOperationType == RegionOperationType.Intersect) IntersectOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, reg2, r2, r2BandEnd, ytop, ybot); } if (nextRectangle != curBand) prevBand = CoalesceRegion (newReg, ref nextRectangle, prevBand, curBand); /* * If we've finished with a band (bottom == ybot) we skip forward * in the region to the next band. */ if (rect1.Bottom == ybot) r1 = r1BandEnd; if (rect2.Bottom == ybot) r2 = r2BandEnd; } while (r1 != r1End && r2 != r2End); /* * Deal with whichever region still has rectangles left. */ curBand = nextRectangle; if (r1 != r1End) { if (regionOperationType == RegionOperationType.Subtract || regionOperationType == RegionOperationType.Union) { do { rect1 = reg1.rects[r1]; r1BandEnd = r1; while (r1BandEnd < r1End && reg1.rects[r1BandEnd].Top == rect1.Top) r1BandEnd++; if (regionOperationType == RegionOperationType.Subtract) SubtractNonOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, Math_Max(rect1.Top, ybot), rect1.Bottom); else if (regionOperationType == RegionOperationType.Union) UnionNonOverlapBands(newReg, ref nextRectangle, reg1, r1, r1BandEnd, Math_Max(rect1.Top, ybot), rect1.Bottom); r1 = r1BandEnd; } while (r1 != r1End); } } else if (r2 != r2End && regionOperationType == RegionOperationType.Union) { do { rect2 = reg2.rects[r2]; r2BandEnd = r2; while (r2BandEnd < r2End && reg2.rects[r2BandEnd].Top == rect2.Top) r2BandEnd++; UnionNonOverlapBands(newReg, ref nextRectangle, reg2, r2, r2BandEnd, Math_Max(rect2.Top, ybot), rect2.Bottom); r2 = r2BandEnd; } while (r2 != r2End); } if (nextRectangle != curBand) CoalesceRegion (newReg, ref nextRectangle, prevBand, curBand); /* * A bit of cleanup. To keep regions from growing without bound, * we shrink the array of rectangles to match the new number of * rectangles in the region. This never goes to 0, however... * * Only do this stuff if the number of rectangles allocated is more than * twice the number of rectangles in the region (a simple optimization...). */ if (nextRectangle < newReg.rects.Length) { RectangleF[] newRects = new RectangleF[nextRectangle]; Array.Copy(newReg.rects, newRects, nextRectangle); newReg.rects = newRects; } return newReg; }