/****************************************************************************************************** * (C) 2018 markummitchell@github.com. This file is part of Engauge Digitizer, which is released * * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file * * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. * ******************************************************************************************************/ #include #include "GridLog.h" #include "GridTriangleFill.h" #include #include #include #include // Non-member comparison function static bool compareByY (const QPoint &first, const QPoint &second) { if (first.y() < second.y()) { return true; } else if (first.y() > second.y()) { return false; } else { // If y values are the same then sort by x values return (first.x() < second.x()); } } GridTriangleFill::GridTriangleFill () { } void GridTriangleFill::drawLine (GridLog &gridLog, QImage &image, int x0, int x1, int y) { const double RADIUS = 0.1; if (x0 > x1) { int xTemp = x0; x0 = x1; x1 = xTemp; } for (int x = x0; x <= x1; x++) { gridLog.showOutputScanLinePixel (x, y, RADIUS); image.setPixel (QPoint (x, y), Qt::black); } } void GridTriangleFill::fill (GridLog &gridLog, QImage &image, const QPoint &p0In, const QPoint &p1In, const QPoint &p2In) { if (p0In.x() > 0 && p0In.y() > 0 && p1In.x() > 0 && p1In.y() > 0 && p2In.x() > 0 && p2In.y() > 0) { QPoint p0, p1, p2; sortByAscendingY (p0In, p1In, p2In, p0, p1, p2); if (p1.y() == p2.y()) { // Triangle with flat bottom flatBottom (gridLog, image, p0, p1, p2); } else if (p0.y() == p1.y()) { // Triangle with flat top flatTop (gridLog, image, p0, p1, p2); } else { // General case is handled by splitting the triangle into one flat top piece and // one flat bottom piece. Fourth point is at same y value as middle point p1 double s = double (p1.y() - p0.y()) / double (p2.y() - p0.y()); QPoint p3 (qFloor (p0.x() + s * (p2.x() - p0.x())), p1.y()); flatBottom (gridLog, image, p0, p1, p3); flatTop (gridLog, image, p1, p3, p2); } } } void GridTriangleFill::flatBottom (GridLog &gridLog, QImage &image, const QPoint &p0, const QPoint &p1, const QPoint &p2) { // Either neither or both denominators are zero, since p1.y()=p2.y() double denom0 = p1.y() - p0.y(); double denom1 = p2.y() - p0.y(); if (qAbs (denom0) <= 0 || qAbs (denom1) <= 0) { drawLine (gridLog, image, p0.x(), p2.x(), p0.y()); } else { double slopeInverse0 = (p1.x() - p0.x()) / denom0; double slopeInverse1 = (p2.x() - p0.x()) / denom1; // For rounding lower point down and upper point up, thus ensuring thorough coverage // (=no gaps between triangles), we order the inverse slopes if (slopeInverse0 > slopeInverse1) { double temp = slopeInverse0; slopeInverse0 = slopeInverse1; slopeInverse1 = temp; } double x0 = p0.x(); double x1 = p0.x(); for (int scanLineY = p0.y(); scanLineY <= p1.y(); scanLineY++) { drawLine (gridLog, image, qFloor (x0), qFloor (x1), scanLineY); x0 += slopeInverse0; x1 += slopeInverse1; } } } void GridTriangleFill::flatTop (GridLog &gridLog, QImage &image, const QPoint &p0, const QPoint &p1, const QPoint &p2) { // Either neither or both denominators are zero, since p0.y()=p1.y() double denom0 = p2.y() - p0.y(); double denom1 = p2.y() - p1.y(); if (qAbs (denom0) <= 0 || qAbs (denom1) <= 0) { drawLine (gridLog, image, p0.x(), p2.x(), p0.y()); } else { double slopeInverse0 = (p2.x() - p0.x()) / denom0; double slopeInverse1 = (p2.x() - p1.x()) / denom1; // For rounding lower point down and upper point up, thus ensuring thorough coverage // (=no gaps between triangles), we order the inverse slopes if (slopeInverse1 > slopeInverse0) { double temp = slopeInverse0; slopeInverse0 = slopeInverse1; slopeInverse1 = temp; } double x0 = p2.x(); double x1 = p2.x(); for (int scanLineY = p2.y(); scanLineY >= p0.y(); scanLineY--) { drawLine (gridLog, image, qFloor (x0), qFloor (x1), scanLineY); x0 -= slopeInverse0; x1 -= slopeInverse1; } } } void GridTriangleFill::sortByAscendingY (QPoint p0In, QPoint p1In, QPoint p2In, QPoint &p0, QPoint &p1, QPoint &p2) const { // Sort by ascending y value QList list; list << p0In << p1In << p2In; std::sort (list.begin(), list.end(), compareByY); p0 = list.front(); list.pop_front(); p1 = list.front(); list.pop_front(); p2 = list.front(); }