From 2b9a708ceb1b1e1c68a8fbd0baa04ade0655515b Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Thu, 21 Jul 2022 16:01:11 -0700 Subject: [PATCH] correctly implement placement --- src/cram.c | 196 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 112 insertions(+), 84 deletions(-) diff --git a/src/cram.c b/src/cram.c index 2c150f0..793b847 100644 --- a/src/cram.c +++ b/src/cram.c @@ -118,7 +118,6 @@ typedef struct PackScoreInfo int32_t secondaryScore; int32_t x; int32_t y; - int32_t freeRectIndex; } PackScoreInfo; /* Pixel data functions */ @@ -250,7 +249,8 @@ void Cram_Internal_Score( PackScoreInfo *scoreInfo ) { Rect *freeRect; - int32_t topSideY; + int32_t areaFit; + int32_t shortestSide; int32_t i; scoreInfo->score = INT32_MAX; @@ -262,12 +262,13 @@ void Cram_Internal_Score( if (freeRect->w >= width && freeRect->h >= height) { - topSideY = freeRect->y + height; - if (topSideY < scoreInfo->score || (topSideY == scoreInfo->score && freeRect->x < scoreInfo->secondaryScore)) + areaFit = freeRect->w * freeRect->h - width * height; + shortestSide = Cram_min(freeRect->w - width, freeRect->h - height); + + if (areaFit < scoreInfo->score || (areaFit == scoreInfo->score && shortestSide < scoreInfo->secondaryScore)) { - scoreInfo->score = topSideY; - scoreInfo->secondaryScore = freeRect->x; - scoreInfo->freeRectIndex = i; + scoreInfo->score = areaFit; + scoreInfo->secondaryScore = shortestSide; scoreInfo->x = freeRect->x; scoreInfo->y = freeRect->y; } @@ -275,84 +276,19 @@ void Cram_Internal_Score( } } -static inline void Cram_Internal_AddFreeRect(RectPackContext *context, Rect rect) -{ - if (context->freeRectangleCount == context->freeRectangleCapacity) - { - context->freeRectangleCapacity *= 2; - context->freeRectangles = Cram_realloc(context->freeRectangles, sizeof(Rect) * context->freeRectangleCapacity); - } - - context->freeRectangles[context->freeRectangleCount] = rect; - context->freeRectangleCount += 1; -} - -void Cram_Internal_PlaceRect(RectPackContext *context, Rect *rect, int32_t freeRectIndex) -{ - Rect freeRect = context->freeRectangles[freeRectIndex]; - Rect newRect; - - /* plug the hole */ - context->freeRectangles[freeRectIndex] = context->freeRectangles[context->freeRectangleCount - 1]; - context->freeRectangleCount -= 1; - - /* now we maybe have new free rectangles! */ - /* NOTE: free rectangles can overlap. */ - - if (rect->y < freeRect.y + freeRect.h && rect->y + rect->h > freeRect.y) - { - /* Left side */ - if (rect->x > freeRect.x && rect->x < freeRect.x + freeRect.w) - { - newRect = freeRect; - newRect.w = rect->x - freeRect.x; - Cram_Internal_AddFreeRect(context, newRect); - } - - /* Right side */ - if (rect->x + rect->w < freeRect.x + freeRect.w) - { - newRect = freeRect; - newRect.x = rect->x + rect->w; - newRect.w = freeRect.x + freeRect.w - (rect->x + rect->w); - Cram_Internal_AddFreeRect(context, newRect); - } - } - - if (rect->x < freeRect.x + freeRect.w && rect->x + rect->w > freeRect.x) - { - /* Top side */ - if (rect->y > freeRect.y && rect->y < freeRect.y + freeRect.h) - { - newRect = freeRect; - newRect.h = rect->y - freeRect.y; - Cram_Internal_AddFreeRect(context, newRect); - } - - /* Bottom side */ - if (rect->y + rect->h < freeRect.y + freeRect.h) - { - newRect = freeRect; - newRect.y = rect->y + rect->h; - newRect.h = freeRect.y + freeRect.h - (rect->y + rect->h); - Cram_Internal_AddFreeRect(context, newRect); - } - } -} - -static inline uint8_t Cram_Internal_Contains(Rect *a, Rect *b) +static inline uint8_t Cram_Internal_Contains(Rect* a, Rect* b) { return b->x >= a->x && - b->y >= a->y && - b->x + b->w <= a->x + a->w && - b->y + b->h <= a->y + a->h; + b->y >= a->y && + b->x + b->w <= a->x + a->w && + b->y + b->h <= a->y + a->h; } -void Cram_Internal_PruneRects(RectPackContext *context) +void Cram_Internal_PruneRects(RectPackContext* context) { int32_t i, j; - Rect *a; - Rect *b; + Rect* a; + Rect* b; for (i = context->freeRectangleCount - 1; i >= 0; i -= 1) { @@ -380,6 +316,101 @@ void Cram_Internal_PruneRects(RectPackContext *context) } } +static inline void Cram_Internal_AddFreeRect(RectPackContext *context, Rect rect) +{ + if (context->freeRectangleCount == context->freeRectangleCapacity) + { + context->freeRectangleCapacity *= 2; + context->freeRectangles = Cram_realloc(context->freeRectangles, sizeof(Rect) * context->freeRectangleCapacity); + } + + context->freeRectangles[context->freeRectangleCount] = rect; + context->freeRectangleCount += 1; +} + +uint8_t Cram_Internal_SplitRect(RectPackContext *context, Rect *rect, Rect *freeRect) +{ + Rect newRect; + + /* Test intersection */ + if ( rect->x >= freeRect->x + freeRect->w || + rect->y >= freeRect->y + freeRect->h || + rect->x + rect->w <= freeRect->x || + rect->y + rect->h <= freeRect->y ) + { + return 0; + } + + /* now we have new free rectangles! */ + /* NOTE: free rectangles can overlap. */ + + if (rect->y < freeRect->y + freeRect->h && rect->y + rect->h > freeRect->y) + { + /* Left side */ + if (rect->x > freeRect->x && rect->x < freeRect->x + freeRect->w) + { + newRect = *freeRect; + newRect.w = rect->x - freeRect->x; + Cram_Internal_AddFreeRect(context, newRect); + } + + /* Right side */ + if (rect->x + rect->w < freeRect->x + freeRect->w) + { + newRect = *freeRect; + newRect.x = rect->x + rect->w; + newRect.w = freeRect->x + freeRect->w - (rect->x + rect->w); + Cram_Internal_AddFreeRect(context, newRect); + } + } + + if (rect->x < freeRect->x + freeRect->w && rect->x + rect->w > freeRect->x) + { + /* Top side */ + if (rect->y > freeRect->y && rect->y < freeRect->y + freeRect->h) + { + newRect = *freeRect; + newRect.h = rect->y - freeRect->y; + Cram_Internal_AddFreeRect(context, newRect); + } + + /* Bottom side */ + if (rect->y + rect->h < freeRect->y + freeRect->h) + { + newRect = *freeRect; + newRect.y = rect->y + rect->h; + newRect.h = freeRect->y + freeRect->h - (rect->y + rect->h); + Cram_Internal_AddFreeRect(context, newRect); + } + } + + return 1; +} + +void Cram_Internal_PlaceRect(RectPackContext *context, Rect *rect) +{ + Rect *freeRect; + int32_t i; + + for (i = 0; i < context->freeRectangleCount;) + { + freeRect = &context->freeRectangles[i]; + + if (Cram_Internal_SplitRect(context, rect, freeRect)) + { + /* plug the hole */ + context->freeRectangles[i] = context->freeRectangles[context->freeRectangleCount - 1]; + context->freeRectangleCount -= 1; + } + else + { + i += 1; + } + } + + Cram_Internal_PruneRects(context); +} + /* Given rects with width and height, modifies rects with packed x and y positions. */ int8_t Cram_Internal_PackRects(RectPackContext *context, Rect *rects, uint32_t numRects) { @@ -389,7 +420,7 @@ int8_t Cram_Internal_PackRects(RectPackContext *context, Rect *rects, uint32_t n int32_t bestScore = INT32_MAX; int32_t bestSecondaryScore = INT32_MAX; PackScoreInfo scoreInfo; - int32_t bestRectIndex, bestX, bestY, bestFreeRectIndex; + int32_t bestRectIndex, bestX, bestY; int32_t i, repeat; for (i = 0; i < numRects; i += 1) @@ -412,7 +443,6 @@ int8_t Cram_Internal_PackRects(RectPackContext *context, Rect *rects, uint32_t n bestScore = scoreInfo.score; bestSecondaryScore = scoreInfo.secondaryScore; bestRectIndex = i; - bestFreeRectIndex = scoreInfo.freeRectIndex; bestX = scoreInfo.x; bestY = scoreInfo.y; } @@ -427,13 +457,11 @@ int8_t Cram_Internal_PackRects(RectPackContext *context, Rect *rects, uint32_t n rectPtr = rectsToPack[bestRectIndex]; rectPtr->x = bestX; rectPtr->y = bestY; - Cram_Internal_PlaceRect(context, rectPtr, bestFreeRectIndex); + Cram_Internal_PlaceRect(context, rectPtr); /* plug the hole */ rectsToPack[bestRectIndex] = rectsToPack[rectsToPackCount - 1]; rectsToPackCount -= 1; - - Cram_Internal_PruneRects(context); } Cram_free(rectsToPack);