119 ScreentoneFunction screentoneFunction)
126 const int alignX = config->alignToPixelGrid() ? config->alignToPixelGridX() : 1;
127 const int alignY = config->alignToPixelGrid() ? config->alignToPixelGridY() : 1;
129 const bool constrainSize = config->constrainSize();
130 sizeX = config->sizeX();
133 sizeY = constrainSize ? sizeX : config->sizeY();
135 const qreal resolution = config->resolution();
136 const bool constrainFrequency = config->constrainFrequency();
137 const qreal frequencyX = config->frequencyX();
140 const qreal frequencyY = constrainFrequency ? frequencyX : config->frequencyY();
141 sizeX = qMax(1.0, resolution / frequencyX);
142 sizeY = qMax(1.0, resolution / frequencyY);
144 const qreal positionX = config->alignToPixelGrid() ? qRound(config->positionX()) : config->positionX();
145 const qreal positionY = config->alignToPixelGrid() ? qRound(config->positionY()) : config->positionY();
147 const qreal shearX = config->shearX();
148 const qreal shearY = config->shearY();
149 const qreal rotation = config->rotation();
155 QTransform screenToImage;
156 screenToImage.rotate(-rotation);
157 screenToImage.scale(sizeX, sizeY);
158 screenToImage.shear(-shearX, -shearY);
165 const QPointF u1 = screenToImage.map(QPointF(
static_cast<qreal
>(alignX), 0.0));
166 const QPointF u2 = screenToImage.map(QPointF(0.0,
static_cast<qreal
>(alignY)));
167 QPointF
v1(qRound(u1.x()), qRound(u1.y()));
168 QPointF
v2(qRound(u2.x()), qRound(u2.y()));
178 const QPointF *p_u = dist1 > dist2 ? &u1 : &u2;
179 QPointF *p_v = dist1 > dist2 ? &
v1 : &
v2;
182 QPair<int, qreal> dists[4]{
189 std::begin(dists), std::end(dists),
190 [](
const QPair<int, qreal> &a,
const QPair<int, qreal> &b)
192 return a.second < b.second;
196 if (dists[0].first == 1) {
197 p_v->setY(p_v->y() - 1.0);
198 }
else if (dists[0].first == 2) {
199 p_v->setX(p_v->x() + 1.0);
200 }
else if (dists[0].first == 3) {
201 p_v->setY(p_v->y() + 1.0);
203 p_v->setX(p_v->x() - 1.0);
206 qreal v1Length = std::sqrt(
v1.x() *
v1.x() +
v1.y() *
v1.y());
207 qreal v2Length = std::sqrt(
v2.x() *
v2.x() +
v2.y() *
v2.y());
211 QSize macrocellTiles(1, 1);
212 while (macrocellTiles.width() * v1Length * macrocellTiles.height() * v2Length < 256) {
213 if (macrocellTiles.width() * v1Length > macrocellTiles.height() * v2Length) {
214 macrocellTiles.setHeight(macrocellTiles.height() + 1);
216 macrocellTiles.setWidth(macrocellTiles.width() + 1);
220 m_macrocellSize = QSize(alignX * macrocellTiles.width(), alignY * macrocellTiles.height());
223 v1 *= macrocellTiles.width();
224 v2 *= macrocellTiles.height();
225 v1Length *= macrocellTiles.width();
226 v2Length *= macrocellTiles.height();
228 const QPointF v3 =
v1 +
v2;
229 const QPointF l1 =
v1;
230 const QPointF l2 =
v2;
231 const QPointF l3 = -
v1;
232 const QPointF l4 = -
v2;
233 const qreal v1MicrocellLength = v1Length / macrocellSizeF.width();
234 const qreal v2MicrocellLength = v2Length / macrocellSizeF.height();
238 const QPolygonF quad(
241 v1 / macrocellSizeF.width(),
242 v1 / macrocellSizeF.width() +
v2 / macrocellSizeF.height(),
243 v2 / macrocellSizeF.height()
252 const QPoint topLeft(
253 static_cast<int>(qMin(0.0, qMin(
v1.x(), qMin(
v2.x(),
v1.x() +
v2.x())))),
254 static_cast<int>(qMin(0.0, qMin(
v1.y(), qMin(
v2.y(),
v1.y() +
v2.y()))))
256 const QPoint bottomRight(
257 static_cast<int>(qMax(0.0, qMax(
v1.x(), qMax(
v2.x(),
v1.x() +
v2.x())))),
258 static_cast<int>(qMax(0.0, qMax(
v1.y(), qMax(
v2.y(),
v1.y() +
v2.y()))))
261 m_templateSize = QSize(bottomRight.x() - topLeft.x() + 2, bottomRight.y() - topLeft.y() + 2);
268 struct AuxiliaryPoint
271 int templatePixelIndex;
274 qreal microcellPixelIndex;
281 qreal distanceToCorner;
283 qreal distanceToCenter;
287 struct AuxiliaryMicrocell
303 for (
int i = 0; i < auxiliaryMicrocells.size(); ++i) {
304 auxiliaryMicrocells[i].index = microcellIndices[i];
319 horizontalCellPositions.append(
v1 /
static_cast<qreal
>(
m_macrocellSize.width()) *
static_cast<qreal
>(i));
323 verticalCellPositions.append(
v2 /
static_cast<qreal
>(
m_macrocellSize.height()) *
static_cast<qreal
>(i));
326 int totalNumberOfAuxiliaryPoints = 0;
331 auto pointBelongsToLeftSideOfEdge = [](
const QPointF &point,
const QPointF &edgeStart,
const QPointF &edgeDirection) ->
bool
333 const qreal r = (point.x() - edgeStart.x()) * edgeDirection.y() - (point.y() - edgeStart.y()) * edgeDirection.x();
334 return (r == 0.0) && ((edgeDirection.y() == 0.0 && edgeDirection.x() > 0.0) || (edgeDirection.y() > 0.0));
350 const QPointF pixelCornerPoint(
354 const QPointF pixelCenterPoint(pixelCornerPoint + QPointF(0.5, 0.5));
356 const QPointF macrocellPos(pixelCenterScreenPoint.x() * v1MicrocellLength, pixelCenterScreenPoint.y() * v2MicrocellLength);
358 int microcellX =
static_cast<int>(std::floor(macrocellPos.x() *
static_cast<qreal
>(
m_macrocellSize.width()) / v1Length));
359 int microcellY =
static_cast<int>(std::floor(macrocellPos.y() *
static_cast<qreal
>(
m_macrocellSize.height()) / v2Length));
368 const QPointF topLeftCorner(horizontalCellPositions[microcellX + 1] + verticalCellPositions[microcellY + 1]);
369 const QPointF topRightCorner(horizontalCellPositions[microcellX + 1 + 1] + verticalCellPositions[microcellY + 1]);
370 const QPointF bottomRightCorner(horizontalCellPositions[microcellX + 1 + 1] + verticalCellPositions[microcellY + 1 + 1]);
371 const QPointF bottomLeftCorner(horizontalCellPositions[microcellX + 1] + verticalCellPositions[microcellY + 1 + 1]);
372 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, topLeftCorner, l1)) {
375 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, topRightCorner, l2)) {
378 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, bottomRightCorner, l3)) {
381 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, bottomLeftCorner, l4)) {
391 const int microcellIndex = microcellY *
m_macrocellSize.width() + microcellX;
392 const qreal microcellPixelIndexX = macrocellPos.x() -
static_cast<qreal
>(microcellX) * v1MicrocellLength;
393 const qreal microcellPixelIndexY = macrocellPos.y() -
static_cast<qreal
>(microcellY) * v2MicrocellLength;
394 const qreal microcellPixelIndex = microcellPixelIndexY * v1MicrocellLength + microcellPixelIndexX;
396 auxiliaryMicrocells[microcellIndex].auxiliaryPoints.push_back(
400 screentoneFunction(pixelCornerScreenPoint.x(), pixelCornerScreenPoint.y()),
401 screentoneFunction(pixelCenterScreenPoint.x(), pixelCenterScreenPoint.y()),
402 lightUpOrderingFunction(pixelCornerScreenPoint.x(), pixelCornerScreenPoint.y()),
403 lightUpOrderingFunction(pixelCenterScreenPoint.x(), pixelCenterScreenPoint.y()),
406 ++totalNumberOfAuxiliaryPoints;
411 for (
int i = 0; i < auxiliaryMicrocells.size(); ++i) {
412 std::sort(auxiliaryMicrocells[i].auxiliaryPoints.begin(), auxiliaryMicrocells[i].auxiliaryPoints.end(),
413 [](
const AuxiliaryPoint &a,
const AuxiliaryPoint &b) {
414 if (qFuzzyCompare(a.valueAtCorner, b.valueAtCorner)) {
415 if (qFuzzyCompare(a.valueAtCenter, b.valueAtCenter)) {
416 if (qFuzzyCompare(a.distanceToCenter, b.distanceToCenter)) {
417 if (qFuzzyCompare(a.distanceToCorner, b.distanceToCorner)) {
418 return a.microcellPixelIndex < b.microcellPixelIndex;
420 return a.distanceToCorner < b.distanceToCorner;
422 return a.distanceToCenter < b.distanceToCenter;
424 return a.valueAtCenter < b.valueAtCenter;
426 return a.valueAtCorner < b.valueAtCorner;
432 std::sort(auxiliaryMicrocells.begin(), auxiliaryMicrocells.end(),
433 [](
const AuxiliaryMicrocell &a,
const AuxiliaryMicrocell &b) {
434 return a.index < b.index;
441 int macrocellPixelIndex = 0;
442 QVector<int> microcellPixelIndices(auxiliaryMicrocells.size());
443 while (macrocellPixelIndex < totalNumberOfAuxiliaryPoints) {
444 for (
int i = 0; i < auxiliaryMicrocells.size(); ++i) {
445 if (microcellPixelIndices[i] == auxiliaryMicrocells[i].auxiliaryPoints.size()) {
448 m_templateData[auxiliaryMicrocells[i].auxiliaryPoints[microcellPixelIndices[i]].templatePixelIndex] =
449 totalNumberOfAuxiliaryPoints - 1 == 0 ? 0
450 :
static_cast<qreal
>(macrocellPixelIndex) /
static_cast<qreal
>(totalNumberOfAuxiliaryPoints - 1);
451 ++microcellPixelIndices[i];
452 ++macrocellPixelIndex;
459 for (
int i = 0; i < m_templateData.size(); ++i) {
460 if (m_templateData[i] < 0.0) {
461 int templateY = i / m_templateSize.width();
462 int templateX = i - m_templateSize.width() * templateY;
463 QPointF pixelCenterPoint(
464 static_cast<qreal
>(templateX - m_originOffset.x()) + 0.5,
465 static_cast<qreal
>(templateY - m_originOffset.y()) + 0.5
467 const QPointF pixelCenterScreenPoint = m_templateToScreenTransform.map(pixelCenterPoint);
468 const qreal a = -std::floor(pixelCenterScreenPoint.x() / macrocellSizeF.width());
469 const qreal
b = -std::floor(pixelCenterScreenPoint.y() / macrocellSizeF.height());
470 pixelCenterPoint += QPointF(a * v1.x() + b * v2.x(), a * v1.y() + b * v2.y());
472 int x =
static_cast<int>(std::floor(pixelCenterPoint.x())) + m_originOffset.x();
473 int y =
static_cast<int>(std::floor(pixelCenterPoint.y())) + m_originOffset.y();
474 int macrocellPointIndex =
y * m_templateSize.width() +
x;
478 if (m_templateData[macrocellPointIndex] < 0.0) {
479 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, QPointF(0.0, 0.0), l1)) {
480 pixelCenterPoint += v2;
482 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, v1, l2)) {
483 pixelCenterPoint -= v1;
485 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, v3, l3)) {
486 pixelCenterPoint -= v2;
488 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, v2, l4)) {
489 pixelCenterPoint += v1;
491 templateX =
static_cast<int>(std::floor(pixelCenterPoint.x())) + m_originOffset.x();
492 templateY =
static_cast<int>(std::floor(pixelCenterPoint.y())) + m_originOffset.y();
493 macrocellPointIndex = templateY * m_templateSize.width() + templateX;
496 m_templateData[i] = m_templateData[macrocellPointIndex];
503 if (macrocellColumns == 1 && macrocellRows == 1) {
509 int availableCellIndex;
515 const int numberOfCells = macrocellColumns * macrocellRows;
520 processedCells.push_back(0);
521 for (
int i = 1; i < numberOfCells; ++i) {
522 availableCells.push_back(i);
525 while (availableCells.size() > 0) {
527 for (
int i = 0; i < availableCells.size(); ++i) {
528 const int availableCellY = availableCells[i] / macrocellColumns;
529 const int availableCellX = availableCells[i] - availableCellY * macrocellColumns;
530 int averageDistanceToProcessedCells = 0;
531 int minimumDistanceToProcessedCells = std::numeric_limits<int>::max();
532 for (
int j = 0; j < processedCells.size(); ++j) {
533 const int processedCellY = processedCells[j] / macrocellColumns;
534 const int processedCellX = processedCells[j] - processedCellY * macrocellColumns;
536 int distanceToCurrentProcessedCell = std::numeric_limits<int>::max();
537 for (
int y = -1; y < 2; ++y) {
538 for (
int x = -1; x < 2; ++x) {
539 const int xx = processedCellX + macrocellColumns * x;
540 const int yy = processedCellY + macrocellRows * y;
541 const int deltaX = availableCellX - xx;
542 const int deltaY = availableCellY - yy;
543 const int distanceSquared = deltaX * deltaX + deltaY * deltaY;
544 if (distanceSquared < distanceToCurrentProcessedCell) {
545 distanceToCurrentProcessedCell = distanceSquared;
549 if (distanceToCurrentProcessedCell < minimumDistanceToProcessedCells) {
550 minimumDistanceToProcessedCells = distanceToCurrentProcessedCell;
552 averageDistanceToProcessedCells += distanceToCurrentProcessedCell;
554 candidates.push_back({i, availableCells[i], minimumDistanceToProcessedCells, averageDistanceToProcessedCells});
558 int bestCandidateIndex = 0;
559 for (
int i = 0; i < candidates.size(); ++i) {
560 if (candidates[i].distanceSquared > candidates[bestCandidateIndex].distanceSquared ||
561 (candidates[i].distanceSquared == candidates[bestCandidateIndex].distanceSquared &&
562 candidates[i].averageDistance > candidates[bestCandidateIndex].averageDistance)) {
563 bestCandidateIndex = i;
567 processedCells.push_back(candidates[bestCandidateIndex].index);
568 availableCells.remove(candidates[bestCandidateIndex].availableCellIndex);
572 for (
int i = 1; i < numberOfCells; ++i) {
573 cellOrderList[processedCells[i]] = i;
576 return cellOrderList;