File indexing completed on 2025-02-02 04:54:39

0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 // SPDX-FileCopyrightText: 2018 Kevin Ottens <ervin@kde.org>
0003 
0004 #include "strokepainter.h"
0005 
0006 #include <QPainter>
0007 #include <QPainterPath>
0008 #include <QVector2D>
0009 
0010 #include <algorithm>
0011 #include <cmath>
0012 using namespace std;
0013 
0014 #include "stroke.h"
0015 
0016 StrokePainter::StrokePainter(QObject *parent)
0017     : QObject(parent)
0018 {
0019 }
0020 
0021 void StrokePainter::render(const Stroke &stroke, QPainter *painter)
0022 {
0023     const auto samples = stroke.samples();
0024     if (samples.size() < 2)
0025         return;
0026 
0027     auto it = samples.cbegin();
0028     auto lastSample = *it;
0029     ++it;
0030 
0031     auto forwardPoints = QVector<QVector2D>{};
0032     auto backwardPoints = QVector<QVector2D>{};
0033 
0034     while (it != samples.cend()) {
0035         const auto currentSample = *it;
0036 
0037         const auto penWidth = currentSample.width;
0038 
0039         const auto currentPos = currentSample.position;
0040         const auto lastPos = lastSample.position;
0041 
0042         const auto direction = currentPos - lastPos;
0043         const auto ortho = QVector2D(direction.y(), -direction.x()).normalized();
0044 
0045         const auto p1 = (lastPos - penWidth * ortho / 2.0f);
0046         const auto p2 = (lastPos + penWidth * ortho / 2.0f);
0047         const auto p3 = (currentPos - penWidth * ortho / 2.0f);
0048         const auto p4 = (currentPos + penWidth * ortho / 2.0f);
0049 
0050         int minX = (int)min({p1.x(), p2.x(), p3.x(), p4.x()});
0051         if (!lowestX || minX < lowestX)
0052             lowestX = minX;
0053 
0054         int maxX = (int)max({p1.x(), p2.x(), p3.x(), p4.x()});
0055         if (!highestX || maxX > highestX)
0056             highestX = maxX;
0057 
0058         int minY = (int)min({p1.y(), p2.y(), p3.y(), p4.y()});
0059         if (!lowestY || minY < lowestY)
0060             lowestY = minY;
0061 
0062         int maxY = (int)max({p1.y(), p2.y(), p3.y(), p4.y()});
0063         if (!highestY || maxY > highestY)
0064             highestY = maxY;
0065 
0066         forwardPoints << p1 << p3;
0067         backwardPoints << p2 << p4;
0068 
0069         lastSample = currentSample;
0070         ++it;
0071     }
0072 
0073     auto path = QPainterPath();
0074     path.moveTo(static_cast<qreal>(forwardPoints[0].x()), static_cast<qreal>(forwardPoints[0].y()));
0075     for (auto it = forwardPoints.cbegin() + 1; it != forwardPoints.cend(); ++it) {
0076         path.lineTo(static_cast<qreal>(it->x()), static_cast<qreal>(it->y()));
0077     }
0078     for (auto it = backwardPoints.crbegin(); it != backwardPoints.crend(); ++it) {
0079         path.lineTo(static_cast<qreal>(it->x()), static_cast<qreal>(it->y()));
0080     }
0081     path.closeSubpath();
0082     path.setFillRule(Qt::WindingFill);
0083 
0084     painter->setPen(Qt::NoPen);
0085     painter->setBrush(stroke.color());
0086     painter->drawPath(path);
0087 }