regiong.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 1,957 行 · 第 1/4 页

CPP
1,957
字号
        {
        /*
         * Make sure the bands have boxes in the same places. This
         * assumes that boxes have been added in such a way that they
         * cover the most area possible. I.e. two boxes in a band must
         * have some horizontal space between them.
         */
            do
            {
                if ((pPrevBox->x1 != pCurBox->x1) ||
                    (pPrevBox->x2 != pCurBox->x2))
                {
                    /*
                     * The bands don't line up so they can't be coalesced.
                     */
                    return (curStart);
                }
                pPrevBox++;
                pCurBox++;
                prevNumRects -= 1;
            } while (prevNumRects != 0);

            pReg->numRects -= curNumRects;
            pCurBox -= curNumRects;
            pPrevBox -= curNumRects;

            /*
             * The bands may be merged, so set the bottom y of each box
             * in the previous band to that of the corresponding box in
             * the current band.
             */
            do
            {
                pPrevBox->y2 = pCurBox->y2;
                pPrevBox++;
                pCurBox++;
                curNumRects -= 1;
            } while (curNumRects != 0);

            /*
             * If only one band was added to the region, we have to backup
             * curStart to the start of the previous band.
             *
             * If more than one band was added to the region, copy the
             * other bands down. The assumption here is that the other bands
             * came from the same region as the current one and no further
             * coalescing can be done on them since it's all been done
             * already... curStart is already in the right place.
             */
            if (pCurBox == pRegEnd)
            {
                curStart = prevStart;
            }
            else
            {
                do
                {
                    *pPrevBox++ = *pCurBox++;
                } while (pCurBox != pRegEnd);
            }

        }
    }
    return (curStart);
}

/*-
 *-----------------------------------------------------------------------
 * miRegionOp --
 *        Apply an operation to two regions. Called by miUnion, miInverse,
 *        miSubtract, miIntersect...
 *
 * Results:
 *        None.
 *
 * Side Effects:
 *        The new region is overwritten.
 *
 * Notes:
 *        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.
 *
 *-----------------------------------------------------------------------
 */
/* static void*/
void REGION::
miRegionOp(
    register Region         newReg,                              /* Place to store result */
    Region                  reg1,                                /* First region in operation */
    Region                  reg2,                                /* 2d region in operation */
    int                      (*overlapFunc)(
        register Region     pReg,
        register BoxPtr     r1,
        BoxPtr              r1End,
        register BoxPtr     r2,
        BoxPtr              r2End,
        wxCoord               y1,
        wxCoord               y2),              /* Function to call for over-
                                                 * lapping bands */
    int                      (*nonOverlap1Func)(
        register Region     pReg,
        register BoxPtr     r,
        BoxPtr              rEnd,
        register wxCoord      y1,
        register wxCoord      y2),              /* Function to call for non-
                                                 * overlapping bands in region
                                                 * 1 */
    int                      (*nonOverlap2Func)(
        register Region     pReg,
        register BoxPtr     r,
        BoxPtr              rEnd,
        register wxCoord      y1,
        register wxCoord      y2))              /* Function to call for non-
                                                 * overlapping bands in region
                                                 * 2 */
{
    register BoxPtr        r1; /* Pointer into first region */
    register BoxPtr        r2; /* Pointer into 2d region */
    BoxPtr              r1End; /* End of 1st region */
    BoxPtr              r2End; /* End of 2d region */
    register wxCoord     ybot; /* Bottom of intersection */
    register wxCoord     ytop; /* Top of intersection */
    BoxPtr           oldRects; /* Old rects for newReg */
    int              prevBand; /* Index of start of
                                * previous band in newReg */
    int               curBand; /* Index of start of current
                                * band in newReg */
    register BoxPtr r1BandEnd; /* End of current band in r1 */
    register BoxPtr r2BandEnd; /* End of current band in r2 */
    wxCoord               top; /* Top of non-overlapping
                                * band */
    wxCoord               bot; /* Bottom of non-overlapping
                                * band */

    /*
     * Initialization:
     *        set r1, r2, r1End and r2End appropriately, preserve the important
     * parts of the destination region until the end in case it's one of
     * the two source regions, then mark the "new" region empty, allocating
     * another array of rectangles for it to use.
     */
    r1 = reg1->rects;
    r2 = reg2->rects;
    r1End = r1 + reg1->numRects;
    r2End = r2 + reg2->numRects;

    oldRects = newReg->rects;

    EMPTY_REGION(newReg);

    /*
     * 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
     * have to worry about using too much memory. I hope to be able to
     * nuke the realloc() at the end of this function eventually.
     */
    newReg->size = wxMax(reg1->numRects,reg2->numRects) * 2;

    if (! (newReg->rects = (BoxPtr)
           malloc ((unsigned) (sizeof(BoxRec) * newReg->size)))) {
        newReg->size = 0;
        return;
    }

    /*
     * 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 (reg1->extents.y1 < reg2->extents.y1)
        ybot = reg1->extents.y1;
    else
        ybot = reg2->extents.y1;

    /*
     * 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
    {
        curBand = newReg->numRects;

        /*
         * 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) && (r1BandEnd->y1 == r1->y1))
        {
            r1BandEnd++;
        }

        r2BandEnd = r2;
        while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
        {
            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 (r1->y1 < r2->y1)
        {
            top = wxMax(r1->y1,ybot);
            bot = wxMin(r1->y2,r2->y1);

            if ((top != bot) && (nonOverlap1Func != NULL))
            {
                (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot);
            }

            ytop = r2->y1;
        }
        else if (r2->y1 < r1->y1)
        {
            top = wxMax(r2->y1,ybot);
            bot = wxMin(r2->y2,r1->y1);

            if ((top != bot) && (nonOverlap2Func != NULL))
            {
                (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot);
            }

            ytop = r1->y1;
        }
        else
        {
            ytop = r1->y1;
        }

        /*
         * 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 (newReg->numRects != curBand)
        {
            prevBand = miCoalesce (newReg, prevBand, curBand);
        }

        /*
         * Now see if we've hit an intersecting band. The two bands only
         * intersect if ybot > ytop
         */
        ybot = wxMin(r1->y2, r2->y2);
        curBand = newReg->numRects;
        if (ybot > ytop)
        {
            (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);

        }

        if (newReg->numRects != curBand)
        {
            prevBand = miCoalesce (newReg, prevBand, curBand);
        }

        /*
         * If we've finished with a band (y2 == ybot) we skip forward
         * in the region to the next band.
         */
        if (r1->y2 == ybot)
        {
            r1 = r1BandEnd;
        }
        if (r2->y2 == ybot)
        {
            r2 = r2BandEnd;
        }
    } while ((r1 != r1End) && (r2 != r2End));

    /*
     * Deal with whichever region still has rectangles left.
     */
    curBand = newReg->numRects;
    if (r1 != r1End)
    {
        if (nonOverlap1Func != NULL)
        {
            do
            {
                r1BandEnd = r1;
                while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
                {
                    r1BandEnd++;
                }
                (* nonOverlap1Func) (newReg, r1, r1BandEnd,
                                     wxMax(r1->y1,ybot), r1->y2);
                r1 = r1BandEnd;
            } while (r1 != r1End);
        }
    }
    else if ((r2 != r2End) && (nonOverlap2Func != NULL))
    {
        do
        {
            r2BandEnd = r2;
            while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
            {
                 r2BandEnd++;
            }
            (* nonOverlap2Func) (newReg, r2, r2BandEnd,
                                wxMax(r2->y1,ybot), r2->y2);
            r2 = r2BandEnd;
        } while (r2 != r2End);
    }

    if (newReg->numRects != curBand)
    {
        (void) miCoalesce (newReg, 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 (newReg->numRects < (newReg->size >> 1))
    {
        if (REGION_NOT_EMPTY(newReg))
        {
            BoxPtr prev_rects = newReg->rects;
            newReg->size = newReg->numRects;
            newReg->rects = (BoxPtr) realloc ((char *) newReg->rects,
                                   (unsigned) (sizeof(BoxRec) * newReg->size));
            if (! newReg->rects)
                newReg->rects = prev_rects;
        }
        else
        {
            /*
             * No point in doing the extra work involved in an realloc if
             * the region is empty
             */
            newReg->size = 1;
            free((char *) newReg->rects);
            newReg->rects = (BoxPtr) malloc(sizeof(BoxRec));
        }
    }
    free ((char *) oldRects);
    return;
}

/*======================================================================
 *            Region Union
 *====================================================================*/

/*-
 *-----------------------------------------------------------------------
 * miUnionNonO --
 *        Handle a non-overlapping band for the union operation. Just
 *        Adds the rectangles into the region. Doesn't have to check for
 *        subsumption or anything.
 *
 * Results:
 *        None.
 *
 * Side Effects:
 *        pReg->numRects is incremented and the final rectangles overwritten
 *        with the rectangles we're passed.
 *
 *-----------------------------------------------------------------------
 */
/* static void*/
int REGION::
miUnionNonO (
    register Region        pReg,
    register BoxPtr        r,
    BoxPtr                 rEnd,
    register wxCoord       y1,
    register wxCoord       y2)
{
    register BoxPtr        pNextRect;

    pNextRect = &pReg->rects[pReg->numRects];

    assert(y1 < y2);

    while (r != rEnd)
    {
        assert(r->x1 < r->x2);
        MEMCHECK(pReg, pNextRect, pReg->rects);
        pNextRect->x1 = r->x1;
        pNextRect->y1 = y1;
        pNextRect->x2 = r->x2;
        pNextRect->y2 = y2;
        pReg->numRects += 1;
        pNextRect++;

        assert(pReg->numRects<=pReg->size);
        r++;
    }
    return 0;        /* lint */
}


/*-
 *-----------------------------------------------------------------------
 * miUnionO --
 *        Handle an overlapping band for the union operation. Picks the
 *        left-most rectangle each time and merges it into the region.
 *
 * Results:
 *        None.
 *
 * Side Effects:
 *        Rectangles are overwritten in pReg->rects and pReg->numRects will
 *        be changed.
 *
 *-----------------------------------------------------------------------
 */

/* static void*/
int REGION::
miUnionO (
    register Region        pReg,
    register BoxPtr        r1,
    BoxPtr                 r1End,
    register BoxPtr        r2,
    BoxPtr                 r2End,
    register wxCoord       y1,
    register wxCoord       y2)
{
    register BoxPtr        pNextRect;

    pNextRect = &pReg->rects[pReg->numRects];

#define MERGERECT(r) \
    if ((pReg->numRects != 0) &&  \
        (pNextRect[-1].y1 == y1) &&  \
        (pNextRect[-1].y2 == y2) &&  \
        (pNextRect[-1].x2 >= r->x1))  \
    {  \
        if (pNextRect[-1].x2 < r->x2)  \
        {  \
            pNextRect[-1].x2 = r->x2;  \
            assert(pNextRect[-1].x1<pNextRect[-1].x2); \
        }  \
    }  \
    else  \
    {  \
        MEMCHECK(pReg, pNextRect, pReg->rects);  \
        pNextRect->y1 = y1;  \
        pNextRect->y2 = y2;  \
        pNextRect->x1 = r->x1;  \
        pNextRect->x2 = r->x2;  \
        pReg->numRects += 1;  \
        pNextRect += 1;  \
    }  \
    assert(pReg->numRects<=pReg->size);\
    r++;

    assert (y1<y2);
    while ((r1 != r1End) && (r2 != r2End))

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?