Ejemplo n.º 1
0
        /*      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);
        }
Ejemplo n.º 2
0
	/*      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;
	}