tweaked rasterized packer

This commit is contained in:
Andrea Maggiordomo 2019-12-10 10:13:38 +01:00
parent acf2331c70
commit 6c02f53fb1
2 changed files with 92 additions and 140 deletions

View File

@ -241,6 +241,10 @@ public:
//the width (in pixels) of the gutter added around the outline
int gutterWidth;
// if false, then do not combine the costs when doubeHorizon is used. This
// can help to keep the packing area in a rectangular region
bool minmax;
///default constructor
Parameters()
{
@ -250,6 +254,7 @@ public:
permutations=false;
rotationNum = 16;
gutterWidth = 0;
minmax = false;
}
};
@ -267,11 +272,7 @@ public:
//the bottomHorizon stores the height of the i-th column in the current solution
std::vector<int> mBottomHorizon;
// secondary horizons, these keep track of the space between the bottom of the
// grid and the already placed polygons
std::vector<int> mSecLeftHorizon;
std::vector<int> mSecBottomHorizon;
// inner horizons base and extent (number of free cells)
std::vector<int> mInnerBottomHorizon;
std::vector<int> mInnerBottomExtent;
@ -290,9 +291,6 @@ public:
mBottomHorizon.resize(size.X(), 0);
mLeftHorizon.resize(size.Y(), 0);
mSecBottomHorizon.resize(size.X(), size.Y() - 1);
mSecLeftHorizon.resize(size.Y(), size.X() - 1);
mInnerBottomHorizon.resize(size.X(), 0);
mInnerBottomExtent.resize(size.X(), 0);
@ -351,35 +349,22 @@ public:
for (size_t i = 0; i < bottom.size(); ++i) {
int y = mInnerBottomHorizon[col + i] - bottom[i];
if (y > y_max) {
if (y + poly.gridHeight(rast_i) >= mSize.Y())
if (y + poly.gridHeight(rast_i) >= mSize.Y()) {
return INVALID_POSITION;
}
y_max = y;
}
}
// check if the placement is feasible
for (size_t i = 0; i < bottom.size(); ++i) {
if (y_max + bottom[i] < mBottomHorizon[col + i]
&& y_max + bottom[i] + deltaY[i] > mInnerBottomHorizon[col + i] + mInnerBottomExtent[col + i])
&& y_max + bottom[i] + deltaY[i] > mInnerBottomHorizon[col + i] + mInnerBottomExtent[col + i]) {
return INVALID_POSITION;
}
}
return y_max;
}
int pushY(RasterizedOutline2& poly, int col, int rast_i) {
std::vector<int>& bottom = poly.getBottom(rast_i);
std::vector<int>& deltaY = poly.getDeltaY(rast_i);
int y_min = INT_MAX;
for (size_t i = 0; i < bottom.size(); ++i) {
int y = mSecBottomHorizon[col + i] - (bottom[i] + deltaY[i]);
if (y < y_min) {
if (y < 0)
return INVALID_POSITION;
y_min = y;
}
}
return y_min;
}
//given a poly and the row at which it is placed,
//this returns the X at which the wasted space is minimum
//i.e. the X at which the polygon touches the left horizon
@ -418,21 +403,6 @@ public:
return x_max;
}
int pushX(RasterizedOutline2& poly, int row, int rast_i) {
std::vector<int>& left = poly.getLeft(rast_i);
std::vector<int>& deltaX = poly.getDeltaX(rast_i);
int x_min = INT_MAX;
for (size_t i = 0; i < left.size(); ++i) {
int x = mSecLeftHorizon[row + i] - (left[i] + deltaX[i]);
if (x < x_min) {
if (x < 0)
return INVALID_POSITION;
x_min = x;
}
}
return x_min;
}
int costYWithPenaltyOnX(RasterizedOutline2& poly, Point2i pos, int rast_i) {
std::vector<int>& left = poly.getLeft(rast_i);
std::vector<int>& deltaX = poly.getDeltaX(rast_i);
@ -508,20 +478,20 @@ public:
{
//return pos.Y() + poly.gridHeight(rast_i);
int cost = -INT_MAX;
int maxY = -INT_MAX;
std::vector<int>& bottom = poly.getBottom(rast_i);
std::vector<int>& deltaY = poly.getDeltaY(rast_i);
for (unsigned i = 0; i < bottom.size(); ++i) {
int currentCost;
int yi = 0;
if (pos.Y() + bottom[i] + deltaY[i] < mBottomHorizon[pos.X() + i]) {
currentCost = -(pos.Y() + bottom[i]);
yi = -(pos.Y() + bottom[i]);
} else {
currentCost = pos.Y() + bottom[i] + deltaY[i];
yi = pos.Y() + bottom[i] + deltaY[i];
}
if (currentCost > cost)
cost = currentCost;
if (yi > maxY)
maxY = yi;
}
return cost;
return maxY;
}
@ -529,20 +499,20 @@ public:
{
//return pos.X() + poly.gridWidth(rast_i);
int cost = -INT_MAX;
int maxX = -INT_MAX;
std::vector<int>& left = poly.getLeft(rast_i);
std::vector<int>& deltaX = poly.getDeltaX(rast_i);
for (unsigned i = 0; i < left.size(); ++i) {
int currentCost = 0;
int xi = 0;
if (pos.X() + left[i] + deltaX[i] < mLeftHorizon[pos.Y() + i]) {
currentCost = -(pos.X() + left[i]);
xi = -(pos.X() + left[i]);
} else {
currentCost = pos.X() + left[i] + deltaX[i];
xi = pos.X() + left[i] + deltaX[i];
}
if (currentCost > cost)
currentCost = cost;
if (xi > maxX)
maxX = xi;
}
return cost;
return maxX;
}
/* Returns the number of empty cells between the poly's left side and the
@ -614,16 +584,8 @@ public:
}
}
int secBtmHor = pos.Y() + bottom[i];
if (secBtmHor < mSecBottomHorizon[pos.X() + i])
mSecBottomHorizon[pos.X() + i] = secBtmHor;
}
/*
if (params.costFunction != Parameters::MixedCost
&& !params.doubleHorizon) return;
*/
//update left horizon
for (int i = 0; i < poly.gridHeight(rast_i); i++) {
int tmpHor = pos.X() + left[i] + deltaX[i];
@ -656,10 +618,6 @@ public:
mInnerLeftExtent[pos.Y() + i] = 0;
}
}
int secLeftHor = pos.X() + left[i];
if (secLeftHor < mSecLeftHorizon[pos.Y() + i])
mSecLeftHorizon[pos.Y() + i] = secLeftHor;
}
}
};
@ -811,51 +769,36 @@ public:
return trials;
}
static bool PackAtFixedScale(std::vector<std::vector<Point2x>> &polyPointsVec,
const std::vector<Point2i> &containerSizes,
std::vector<Similarity2x> &trVec,
std::vector<int> &polyToContainer,
const Parameters &packingPar,
float scale)
{
//create the vector of polys, starting for the poly points we received as parameter
std::vector<RasterizedOutline2> polyVec(polyPointsVec.size());
for(size_t i=0;i<polyVec.size();i++) {
polyVec[i].setPoints(polyPointsVec[i]);
}
std::vector<std::vector<int>> trials = InitializePermutationVectors(polyPointsVec, packingPar);
for (std::size_t i = 0; i < trials.size(); ++i) {
std::vector<Similarity2x> trVecIter;
std::vector<int> polyToContainerIter;
if (PolyPacking(polyPointsVec, containerSizes, trVecIter, polyToContainerIter, packingPar, scale, polyVec, trials[i])) {
trVec = trVecIter;
polyToContainer = polyToContainerIter;
return true;
}
}
return false;
}
/* Function parameters:
/*
* Pack charts using a best effort policy. The idea is that this function
* packs what it can in the given space without scaling the outlines.
*
* Returns the number of charts actually packed.
*
* Function parameters:
* outline2Vec (IN) vector of outlines to pack
* containerSizes (IN) vector of container (grid) sizes
* trVec (OUT) vector of transformations that must be applied to the objects
* polyToContainer (OUT) vector of outline-to-container mappings. If polyToContainer[i] == -1
* then outline i did not fit in the packing grids, and the transformation trVec[i] is meaningless
*
* Returns true if it packed at least one polygon into the container
*
* The idea is that this function packs what it can in the given space without transforming the
* outlines, and returns enough information to the caller in order to decide what to do */
static bool
* */
static int
PackBestEffort(std::vector<std::vector<Point2x>> &outline2Vec,
const std::vector<Point2i> &containerSizes,
std::vector<Similarity2x> &trVec,
std::vector<int> &polyToContainer,
const Parameters &packingPar)
{
return PackBestEffortAtScale(outline2Vec, containerSizes, trVec, polyToContainer, packingPar, 1.0f);
}
/* Same as PackBestEffort() but allows to specify the outlines scaling factor */
static int
PackBestEffortAtScale(std::vector<std::vector<Point2x>> &outline2Vec,
const std::vector<Point2i> &containerSizes,
std::vector<Similarity2x> &trVec,
std::vector<int> &polyToContainer,
const Parameters &packingPar, float scaleFactor)
{
std::vector<RasterizedOutline2> polyVec(outline2Vec.size());
for(size_t i=0;i<polyVec.size();i++) {
@ -867,18 +810,18 @@ public:
std::vector<std::vector<int>> trials = InitializePermutationVectors(outline2Vec, packingPar);
int bestNumPlaced = 0;
for (std::size_t i = 0; i < trials.size(); ++i) {
// TODO this should probably attempt at maximizing the occupancy and/or the number of placed polygons
std::vector<Similarity2x> trVecIter;
std::vector<int> polyToContainerIter;
PolyPacking(outline2Vec, containerSizes, trVecIter, polyToContainerIter, packingPar, 1.0, polyVec, trials[i], true);
PolyPacking(outline2Vec, containerSizes, trVecIter, polyToContainerIter, packingPar, scaleFactor, polyVec, trials[i], true);
int numPlaced = outline2Vec.size() - std::count(polyToContainerIter.begin(), polyToContainerIter.end(), -1);
if (numPlaced > bestNumPlaced) {
trVec = trVecIter;
polyToContainer = polyToContainerIter;
bestNumPlaced = numPlaced;
}
}
return bestNumPlaced > 0;
return bestNumPlaced;
}
//tries to pack polygons using the given gridSize and scaleFactor
@ -942,27 +885,31 @@ public:
//look for the best position, dropping from top
for (int col = 0; col < maxCol; col++) {
int currPolyY;
currPolyY = packingFields[grid_i].dropY(polyVec[i],col, rast_i);
if (currPolyY != INVALID_POSITION) {
assert(currPolyY + polyVec[i].gridHeight(rast_i) < gridSizes[grid_i].Y() && "drop");
int currCost = packingFields[grid_i].getCostX(polyVec[i], Point2i(col, currPolyY), rast_i) +
packingFields[grid_i].getCostY(polyVec[i], Point2i(col, currPolyY), rast_i);
if (currCost < bestCost) {
bestContainer = grid_i;
bestCost = currCost;
bestRastIndex = rast_i;
bestPolyX = col;
bestPolyY = currPolyY;
placedUsingSecondaryHorizon = false;
if (!placedUsingSecondaryHorizon) {
currPolyY = packingFields[grid_i].dropY(polyVec[i],col, rast_i);
if (currPolyY != INVALID_POSITION) {
assert(currPolyY + polyVec[i].gridHeight(rast_i) < gridSizes[grid_i].Y() && "drop");
int currCost = packingFields[grid_i].getCostY(polyVec[i], Point2i(col, currPolyY), rast_i);
if (packingPar.doubleHorizon && (packingPar.minmax == true))
currCost += packingFields[grid_i].getCostX(polyVec[i], Point2i(col, currPolyY), rast_i);
if (currCost < bestCost) {
bestContainer = grid_i;
bestCost = currCost;
bestRastIndex = rast_i;
bestPolyX = col;
bestPolyY = currPolyY;
placedUsingSecondaryHorizon = false;
}
}
}
if (packingPar.innerHorizon) {
currPolyY = packingFields[grid_i].dropYInner(polyVec[i],col, rast_i);
if (currPolyY != INVALID_POSITION) {
assert(currPolyY + polyVec[i].gridHeight(rast_i) < gridSizes[grid_i].Y() && "drop_inner");
int currCost = packingFields[grid_i].getCostX(polyVec[i], Point2i(col, currPolyY), rast_i) +
packingFields[grid_i].getCostY(polyVec[i], Point2i(col, currPolyY), rast_i);
if (currCost < bestCost) {
int currCost = packingFields[grid_i].getCostY(polyVec[i], Point2i(col, currPolyY), rast_i);
if (packingPar.doubleHorizon && (packingPar.minmax == true))
currCost += packingFields[grid_i].getCostX(polyVec[i], Point2i(col, currPolyY), rast_i);
if (!placedUsingSecondaryHorizon || currCost < bestCost) {
bestContainer = grid_i;
bestCost = currCost;
bestRastIndex = rast_i;
@ -979,28 +926,31 @@ public:
for (int row = 0; row < maxRow; row++) {
int currPolyX;
currPolyX = packingFields[grid_i].dropX(polyVec[i],row, rast_i);
if (currPolyX != INVALID_POSITION) {
assert(currPolyX + polyVec[i].gridWidth(rast_i) < gridSizes[grid_i].X() && "drop");
int currCost = packingFields[grid_i].getCostY(polyVec[i], Point2i(currPolyX, row), rast_i) +
packingFields[grid_i].getCostX(polyVec[i], Point2i(currPolyX, row), rast_i);
if (currCost < bestCost) {
bestContainer = grid_i;
bestCost = currCost;
bestRastIndex = rast_i;
bestPolyX = currPolyX;
bestPolyY = row;
placedUsingSecondaryHorizon = false;
if (!placedUsingSecondaryHorizon) {
currPolyX = packingFields[grid_i].dropX(polyVec[i],row, rast_i);
if (currPolyX != INVALID_POSITION) {
assert(currPolyX + polyVec[i].gridWidth(rast_i) < gridSizes[grid_i].X() && "drop");
int currCost = packingFields[grid_i].getCostX(polyVec[i], Point2i(currPolyX, row), rast_i);
if (packingPar.doubleHorizon && (packingPar.minmax == true))
currCost += packingFields[grid_i].getCostY(polyVec[i], Point2i(currPolyX, row), rast_i);
if (currCost < bestCost) {
bestContainer = grid_i;
bestCost = currCost;
bestRastIndex = rast_i;
bestPolyX = currPolyX;
bestPolyY = row;
placedUsingSecondaryHorizon = false;
}
}
}
if (packingPar.innerHorizon) {
currPolyX = packingFields[grid_i].dropXInner(polyVec[i],row, rast_i);
if (currPolyX != INVALID_POSITION) {
assert(currPolyX + polyVec[i].gridWidth(rast_i) < gridSizes[grid_i].X() && "drop_inner");
int currCost = packingFields[grid_i].getCostY(polyVec[i], Point2i(currPolyX, row), rast_i) +
packingFields[grid_i].getCostX(polyVec[i], Point2i(currPolyX, row), rast_i);
if (currCost < bestCost) {
int currCost = packingFields[grid_i].getCostX(polyVec[i], Point2i(currPolyX, row), rast_i);
if (packingPar.doubleHorizon && (packingPar.minmax == true))
currCost += packingFields[grid_i].getCostY(polyVec[i], Point2i(currPolyX, row), rast_i);
if (!placedUsingSecondaryHorizon || currCost < bestCost) {
bestContainer = grid_i;
bestCost = currCost;
bestRastIndex = rast_i;

View File

@ -39,8 +39,9 @@ void QtOutline2Rasterizer::rasterize(RasterizedOutline2 &poly,
// and adding the gutter width.
int sizeX = (int)ceil(bb.DimX()*scale);
int sizeY = (int)ceil(bb.DimY()*scale);
sizeX += gutterWidth;
sizeY += gutterWidth;
int safetyBuffer = 2;
sizeX += (gutterWidth + safetyBuffer);
sizeY += (gutterWidth + safetyBuffer);
QImage img(sizeX,sizeY,QImage::Format_RGB32);
QColor backgroundColor(Qt::transparent);
@ -66,7 +67,7 @@ void QtOutline2Rasterizer::rasterize(RasterizedOutline2 &poly,
painter.setPen(qp);
painter.resetTransform();
painter.translate(QPointF(-(bb.min.X()*scale) + gutterWidth/2.0f, -(bb.min.Y()*scale) + gutterWidth/2.0f));
painter.translate(QPointF(-(bb.min.X()*scale) + (gutterWidth + safetyBuffer)/2.0f, -(bb.min.Y()*scale) + (gutterWidth + safetyBuffer)/2.0f));
painter.rotate(math::ToDeg(rotRad));
painter.scale(scale,scale);
@ -102,7 +103,7 @@ void QtOutline2Rasterizer::rasterize(RasterizedOutline2 &poly,
painter.setPen(qp);
painter.resetTransform();
painter.translate(QPointF(-(bb.min.X()*scale) + gutterWidth/2.0f, -(bb.min.Y()*scale) + gutterWidth/2.0f));
painter.translate(QPointF(-(bb.min.X()*scale) + (gutterWidth + safetyBuffer)/2.0f, -(bb.min.Y()*scale) + (gutterWidth + safetyBuffer)/2.0f));
painter.rotate(math::ToDeg(rotRad));
painter.scale(scale,scale);
@ -195,8 +196,9 @@ void QtOutline2Rasterizer::rasterize(RasterizedOutline2 &poly,
for (int y = 0; y < img.height(); y++) {
const uchar* line = img.scanLine(y);
for(int x = 0; x < img.width(); ++x) {
if (((QRgb*)line)[x] == yellow)
if (((QRgb*)line)[x] == yellow) {
tetrisGrid[y][x] = 1;
}
}
}