Updated RasterizedOutline2Packer

Added parameters to control the gutter size of the outlines, the
possibility to track space between previously placed polygons when
evaluating new moves (inner horizons), and the possibility to try a
small number of permutations of the packing sequence in order to
improve the overall efficiency.

Cleaned up QtOutline2Rasterizer.

Updated the relevant samples.
This commit is contained in:
Andrea Maggiordomo 2019-01-31 14:28:24 +01:00
parent e09bc0763a
commit 19adc39387
5 changed files with 682 additions and 304 deletions

View File

@ -115,7 +115,6 @@ int main( int /*argc*/, char **/*argv*/ )
packingParam.costFunction = RasterizedOutline2Packer<float, QtOutline2Rasterizer>::Parameters::LowestHorizon; packingParam.costFunction = RasterizedOutline2Packer<float, QtOutline2Rasterizer>::Parameters::LowestHorizon;
packingParam.doubleHorizon = true; packingParam.doubleHorizon = true;
packingParam.cellSize = 4;
packingParam.rotationNum = 16; //number of rasterizations in 90° packingParam.rotationNum = 16; //number of rasterizations in 90°
RasterizedOutline2Packer<float, QtOutline2Rasterizer>::Pack(outline2Vec,containerSize,trVec,packingParam); RasterizedOutline2Packer<float, QtOutline2Rasterizer>::Pack(outline2Vec,containerSize,trVec,packingParam);

View File

@ -123,8 +123,10 @@ int main(int ,char ** )
RasterizedOutline2Packer<float, QtOutline2Rasterizer>::Parameters packingParam; RasterizedOutline2Packer<float, QtOutline2Rasterizer>::Parameters packingParam;
packingParam.costFunction = RasterizedOutline2Packer<float, QtOutline2Rasterizer>::Parameters::LowestHorizon; packingParam.costFunction = RasterizedOutline2Packer<float, QtOutline2Rasterizer>::Parameters::LowestHorizon;
packingParam.doubleHorizon = true; packingParam.doubleHorizon = true;
packingParam.cellSize = 4; packingParam.innerHorizon = true;
packingParam.permutations = false;
packingParam.rotationNum = 16; //number of rasterizations in 90° packingParam.rotationNum = 16; //number of rasterizations in 90°
packingParam.gutterWidth = 2;
RasterizedOutline2Packer<float, QtOutline2Rasterizer>::Pack(outline2Vec,containerSize,trVec,packingParam); RasterizedOutline2Packer<float, QtOutline2Rasterizer>::Pack(outline2Vec,containerSize,trVec,packingParam);
Outline2Dumper::dumpOutline2VecPNG("PostPackRR.png",outline2Vec,trVec,pp); Outline2Dumper::dumpOutline2VecPNG("PostPackRR.png",outline2Vec,trVec,pp);

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,8 @@
#include <vcg/space/color4.h> #include <vcg/space/color4.h>
#include <wrap/qt/col_qt_convert.h> #include <wrap/qt/col_qt_convert.h>
#include <fstream>
using namespace vcg; using namespace vcg;
using namespace std; using namespace std;
@ -10,9 +12,11 @@ void QtOutline2Rasterizer::rasterize(RasterizedOutline2 &poly,
float scale, float scale,
int rast_i, int rast_i,
int rotationNum, int rotationNum,
int cellSize) int gutterWidth)
{ {
gutterWidth *= 2; // since the brush is centered on the outline multiply the given value by 2
float rotRad = M_PI*2.0f*float(rast_i) / float(rotationNum); float rotRad = M_PI*2.0f*float(rast_i) / float(rotationNum);
//get polygon's BB, rotated according to the input parameter //get polygon's BB, rotated according to the input parameter
@ -24,20 +28,19 @@ void QtOutline2Rasterizer::rasterize(RasterizedOutline2 &poly,
bb.Add(pp); bb.Add(pp);
} }
///CREATE ITS GRID. The grid has to be a multiple of CELLSIZE because this grid's cells have size CELLSIZE //create the polygon to print it
//we'll make so that sizeX and sizeY are multiples of CELLSIZE: QVector<QPointF> points;
//1) we round it to the next integer vector<Point2f> newpoints = poly.getPoints();
//2) add the number which makes it a multiple of CELLSIZE (only if it's not multiple already) for (size_t i = 0; i < newpoints.size(); i++) {
points.push_back(QPointF(newpoints[i].X(), newpoints[i].Y()));
}
// Compute the raster space size by rounding up the scaled bounding box size
// and adding the gutter width.
int sizeX = (int)ceil(bb.DimX()*scale); int sizeX = (int)ceil(bb.DimX()*scale);
int sizeY = (int)ceil(bb.DimY()*scale); int sizeY = (int)ceil(bb.DimY()*scale);
if (sizeX % cellSize != 0) sizeX += (cellSize - ((int)ceil(bb.DimX()*scale) % cellSize)); sizeX += gutterWidth;
if (sizeY % cellSize != 0) sizeY += (cellSize - ((int)ceil(bb.DimY()*scale) % cellSize)); sizeY += gutterWidth;
//security measure: add a dummy column/row thus making the image bigger, and crop it afterwards
//(if it hasn't been filled with anything)
//this is due to the fact that if we have a rectangle which has bb 39.xxx wide, then it won't fit in a 40px wide QImage!! The right side will go outside of the image!! :/
sizeX+=cellSize;
sizeY+=cellSize;
QImage img(sizeX,sizeY,QImage::Format_RGB32); QImage img(sizeX,sizeY,QImage::Format_RGB32);
QColor backgroundColor(Qt::transparent); QColor backgroundColor(Qt::transparent);
@ -46,99 +49,154 @@ void QtOutline2Rasterizer::rasterize(RasterizedOutline2 &poly,
///SETUP OF DRAWING PROCEDURE ///SETUP OF DRAWING PROCEDURE
QPainter painter; QPainter painter;
painter.begin(&img); painter.begin(&img);
QBrush br; {
br.setStyle(Qt::SolidPattern); QBrush br;
QPen qp; br.setStyle(Qt::SolidPattern);
qp.setWidthF(0); br.setColor(Qt::yellow);
qp.setColor(Qt::yellow);
painter.setBrush(br);
painter.setPen(qp);
painter.resetTransform(); QPen qp;
painter.translate(QPointF(-(bb.min.X()*scale) , -(bb.min.Y()*scale) )); qp.setWidthF(0);
painter.rotate(math::ToDeg(rotRad)); qp.setWidth(gutterWidth);
painter.scale(scale,scale); qp.setCosmetic(true);
qp.setColor(Qt::yellow);
qp.setJoinStyle(Qt::MiterJoin);
qp.setMiterLimit(0);
//create the polygon to print it painter.setBrush(br);
QVector<QPointF> points; painter.setPen(qp);
vector<Point2f> newpoints = poly.getPoints();
for (size_t i = 0; i < newpoints.size(); i++) { painter.resetTransform();
points.push_back(QPointF(newpoints[i].X(), newpoints[i].Y())); painter.translate(QPointF(-(bb.min.X()*scale) + gutterWidth/2.0f, -(bb.min.Y()*scale) + gutterWidth/2.0f));
painter.rotate(math::ToDeg(rotRad));
painter.scale(scale,scale);
painter.drawPolygon(QPolygonF(points));
} }
painter.drawPolygon(QPolygonF(points)); painter.end();
// workaround/hack to avoid ``disappearing'' primitives: use a cosmetic pen to
// draw the poly boundary.
// The proper way to do this would be to use conservative reasterization, which
// Qt doesn't seem to support
std::vector<QPointF> lines;
for (int i = 1; i < points.size(); ++i) {
lines.push_back(points[i-1]);
lines.push_back(points[i]);
}
lines.push_back(points.back());
lines.push_back(points.front());
//CROPPING: it is enough to check for the (end - cellSize - 1)th row/col of pixels, if they're all black we can eliminate the last 8columns/rows of pixels painter.begin(&img);
bool cropX = true; {
bool cropY = true; QBrush br;
for (int j=0; j<img.height(); j++) { br.setStyle(Qt::SolidPattern);
const uchar* line = img.scanLine(j); br.setColor(Qt::yellow);
if (j == img.height() - (cellSize - 1) - 1 ) {
for (int x=0; x<img.width(); x++) { QPen qp;
if (((QRgb*)line)[x] != backgroundColor.rgb()) { qp.setWidthF(0);
cropY = false; qp.setWidth(std::max(1, gutterWidth));
break; qp.setCosmetic(true);
} qp.setColor(Qt::yellow);
painter.setBrush(br);
painter.setPen(qp);
painter.resetTransform();
painter.translate(QPointF(-(bb.min.X()*scale) + gutterWidth/2.0f, -(bb.min.Y()*scale) + gutterWidth/2.0f));
painter.rotate(math::ToDeg(rotRad));
painter.scale(scale,scale);
//painter.drawPoints(QPolygonF(points));
painter.drawLines(lines.data(), lines.size()/2);
}
painter.end();
// Cropping
/*
// Slower version
int minX = img.width();
int minY = img.height();
int maxX = -1;
int maxY = -1;
for (int i = 0; i < img.height(); ++i) {
const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i));
for (int j = 0; j < img.width(); ++j) {
if (line[j] != backgroundColor.rgb()) {
if (j < minX) minX = j;
if (j > maxX) maxX = j;
if (i < minY) minY = i;
if (i > maxY) maxY = i;
} }
} }
else { }
if (((QRgb*)line)[img.width() - (cellSize - 1) - 1] != backgroundColor.rgb()) { */
cropX = false;
int minX = img.width();
int minY = img.height();
int maxX = 0;
int maxY = 0;
for (int i = 0; i < img.height(); ++i) {
const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i));
for (int j = 0; j < img.width(); ++j) {
if (line[j] != backgroundColor.rgb()) {
minY = i;
break; break;
} }
} }
if (!cropY) break; if (minY < img.height()) break;
} }
for (int i = img.height() - 1; i >= 0; --i) {
if (cropX || cropY) { const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i));
painter.end(); for (int j = 0; j < img.width(); ++j) {
img = img.copy(0, 0, img.width() - cellSize * cropX, img.height() - cellSize * cropY); if (line[j] != backgroundColor.rgb()) {
painter.begin(&img); maxY = i;
painter.setBrush(br); break;
painter.setPen(qp); }
}
if (maxY > 0) break;
} }
for (int i = minY; i <= maxY; ++i) {
//draw the poly for the second time, this time it is centered to the image const QRgb *line = reinterpret_cast<const QRgb*>(img.scanLine(i));
img.fill(backgroundColor); for (int j = 0; j < minX; ++j)
if (line[j] != backgroundColor.rgb() && j < minX) {
painter.resetTransform(); minX = j;
painter.translate(QPointF(-(bb.min.X()*scale) + (img.width() - ceil(bb.DimX()*scale))/2.0, -(bb.min.Y()*scale) + (img.height() - ceil(bb.DimY()*scale))/2.0)); break;
painter.rotate(math::ToDeg(rotRad)); }
painter.scale(scale,scale); for (int j = img.width() - 1; j >= maxX; --j)
//create the polygon to print it if (line[j] != backgroundColor.rgb() && j > maxX) {
QVector<QPointF> points2; maxX = j;
vector<Point2f> newpoints2 = poly.getPoints(); break;
for (size_t i = 0; i < newpoints2.size(); i++) { }
points2.push_back(QPointF(newpoints2[i].X(), newpoints2[i].Y())); }
assert (minX <= maxX && minY <= maxY);
int imgW = (maxX - minX) + 1;
int imgH = (maxY - minY) + 1;
{
QImage imgcp = img.copy(0, 0, img.width(), img.height());
img = imgcp.copy(minX, minY, imgW, imgH);
} }
painter.drawPolygon(QPolygonF(points2));
//create the first grid, which will then be rotated 3 times. //create the first grid, which will then be rotated 3 times.
//we will reuse this grid to create the rasterizations corresponding to this one rotated by 90/180/270° //we will reuse this grid to create the rasterizations corresponding to this one rotated by 90/180/270°
vector<vector<int> > tetrisGrid; vector<vector<int> > tetrisGrid;
QRgb yellow = QColor(Qt::yellow).rgb(); QRgb yellow = QColor(Qt::yellow).rgb();
int gridWidth = img.width() / cellSize; tetrisGrid.resize(img.height());
int gridHeight = img.height() / cellSize; for (int k = 0; k < img.height(); k++) {
int x = 0; tetrisGrid[k].resize(img.width(), 0);
tetrisGrid.resize(gridHeight);
for (int k = 0; k < gridHeight; k++) {
tetrisGrid[k].resize(gridWidth, 0);
} }
for (int y = 0; y < img.height(); y++) { for (int y = 0; y < img.height(); y++) {
int gridY = y / cellSize;
const uchar* line = img.scanLine(y); const uchar* line = img.scanLine(y);
x = 0; for(int x = 0; x < img.width(); ++x) {
int gridX = 0; if (((QRgb*)line)[x] == yellow)
while(x < img.width()) { tetrisGrid[y][x] = 1;
gridX = x/cellSize;
if (tetrisGrid[gridY][gridX] == 1) {
x+= cellSize - (x % cellSize); //align with the next x
continue;
}
if (((QRgb*)line)[x] == yellow) tetrisGrid[gridY][gridX] = 1;
++x;
} }
} }
@ -154,8 +212,6 @@ void QtOutline2Rasterizer::rasterize(RasterizedOutline2 &poly,
//initializes bottom/left/deltaX/deltaY vectors of the poly, for the current rasterization //initializes bottom/left/deltaX/deltaY vectors of the poly, for the current rasterization
poly.initFromGrid(rast_i + rotationOffset*j); poly.initFromGrid(rast_i + rotationOffset*j);
} }
painter.end();
} }
// rotates the grid 90 degree clockwise (by simple swap) // rotates the grid 90 degree clockwise (by simple swap)

View File

@ -16,9 +16,8 @@ class QtOutline2Rasterizer
public: public:
static void rasterize(vcg::RasterizedOutline2 &poly, static void rasterize(vcg::RasterizedOutline2 &poly,
float scaleFactor, float scaleFactor,
int rast_i, int rotationNum, int cellSize); int rast_i, int rotationNum, int gutterWidth);
static std::vector<std::vector<int> > rotateGridCWise(std::vector< std::vector<int> >& inGrid); static std::vector<std::vector<int> > rotateGridCWise(std::vector< std::vector<int> >& inGrid);
}; };
#endif // QTPOLYRASTERIZER_H #endif // QTPOLYRASTERIZER_H