File indexing completed on 2025-02-02 04:22:31
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_tool_line_helper.h" 0008 0009 #include <QtMath> 0010 0011 #include "kis_algebra_2d.h" 0012 #include "kis_painting_information_builder.h" 0013 #include "kis_image.h" 0014 0015 #include "kis_canvas_resource_provider.h" 0016 #include <brushengine/kis_paintop_preset.h> 0017 0018 struct KisToolLineHelper::Private 0019 { 0020 Private(KisPaintingInformationBuilder *_infoBuilder) 0021 : infoBuilder(_infoBuilder), 0022 useSensors(true), 0023 enabled(true) 0024 { 0025 } 0026 0027 QVector<KisPaintInformation> linePoints; 0028 KisPaintingInformationBuilder *infoBuilder; 0029 bool useSensors; 0030 bool enabled; 0031 }; 0032 0033 KisToolLineHelper::KisToolLineHelper(KisPaintingInformationBuilder *infoBuilder, 0034 KoCanvasResourceProvider *resourceManager, 0035 const KUndo2MagicString &transactionText) 0036 : KisToolFreehandHelper(infoBuilder, 0037 resourceManager, 0038 transactionText, 0039 new KisSmoothingOptions(false)), 0040 m_d(new Private(infoBuilder)) 0041 { 0042 } 0043 0044 KisToolLineHelper::~KisToolLineHelper() 0045 { 0046 delete m_d; 0047 } 0048 0049 void KisToolLineHelper::setEnabled(bool value) 0050 { 0051 m_d->enabled = value; 0052 } 0053 0054 void KisToolLineHelper::setUseSensors(bool value) 0055 { 0056 m_d->useSensors = value; 0057 } 0058 0059 void KisToolLineHelper::repaintLine(KisImageWSP image, KisNodeSP node, 0060 KisStrokesFacade *strokesFacade) 0061 { 0062 if (!m_d->enabled) return; 0063 0064 cancelPaint(); 0065 if (m_d->linePoints.isEmpty()) return; 0066 0067 qreal startAngle = 0.0; 0068 if (m_d->linePoints.length() > 1) { 0069 startAngle = KisAlgebra2D::directionBetweenPoints(m_d->linePoints[0].pos(), 0070 m_d->linePoints[1].pos(), 0071 0.0); 0072 } 0073 0074 KisPaintOpPresetSP preset = resourceManager()->resource(KoCanvasResource::CurrentPaintOpPreset) 0075 .value<KisPaintOpPresetSP>(); 0076 0077 if (preset->settings()->paintOpSize() <= 1) { 0078 KisPaintInformation begin = m_d->linePoints.first(); 0079 KisPaintInformation end = m_d->linePoints.last(); 0080 m_d->linePoints.clear(); 0081 m_d->linePoints.append(begin); 0082 m_d->linePoints.append(end); 0083 adjustPointsToDDA(m_d->linePoints); 0084 } 0085 0086 QVector<KisPaintInformation>::const_iterator it = m_d->linePoints.constBegin(); 0087 QVector<KisPaintInformation>::const_iterator end = m_d->linePoints.constEnd(); 0088 0089 initPaintImpl(startAngle, *it, resourceManager(), image, node, strokesFacade); 0090 ++it; 0091 0092 while (it != end) { 0093 paintLine(*(it - 1), *it); 0094 ++it; 0095 } 0096 } 0097 0098 void KisToolLineHelper::start(KoPointerEvent *event, KoCanvasResourceProvider *resourceManager) 0099 { 0100 if (!m_d->enabled) return; 0101 0102 // Ignore the elapsed stroke time, so that the line tool will behave as if the whole stroke is 0103 // drawn at once. This should prevent any possible spurious dabs caused by airbrushing features. 0104 KisPaintInformation pi = 0105 m_d->infoBuilder->startStroke(event, 0, resourceManager); 0106 0107 if (!m_d->useSensors) { 0108 pi = KisPaintInformation(pi.pos()); 0109 } 0110 0111 m_d->linePoints.append(pi); 0112 } 0113 0114 void KisToolLineHelper::addPoint(KoPointerEvent *event, const QPointF &overridePos) 0115 { 0116 if (!m_d->enabled) return; 0117 0118 // Ignore the elapsed stroke time, so that the line tool will behave as if the whole stroke is 0119 // drawn at once. This should prevent any possible spurious dabs caused by airbrushing features. 0120 KisPaintInformation pi = 0121 m_d->infoBuilder->continueStroke(event, 0); 0122 0123 addPoint(pi, overridePos); 0124 } 0125 0126 void KisToolLineHelper::addPoint(KisPaintInformation pi, const QPointF &overridePos) 0127 { 0128 if (!m_d->enabled) return; 0129 if (!m_d->useSensors) { 0130 pi = KisPaintInformation(pi.pos()); 0131 } 0132 0133 if (!overridePos.isNull()) { 0134 pi.setPos(overridePos); 0135 } 0136 0137 if (m_d->linePoints.size() > 1) { 0138 const QPointF startPos = m_d->linePoints.first().pos(); 0139 const QPointF endPos = pi.pos(); 0140 0141 if (!KisAlgebra2D::fuzzyPointCompare(startPos, endPos)) { 0142 const qreal maxDistance = kisDistance(startPos, endPos); 0143 const QPointF unit = (endPos - startPos) / maxDistance; 0144 0145 QVector<KisPaintInformation>::iterator it = m_d->linePoints.begin(); 0146 ++it; 0147 while (it != m_d->linePoints.end()) { 0148 qreal dist = kisDistance(startPos, it->pos()); 0149 if (dist < maxDistance) { 0150 QPointF pos = startPos + unit * dist; 0151 it->setPos(pos); 0152 ++it; 0153 } else { 0154 it = m_d->linePoints.erase(it); 0155 } 0156 } 0157 } else { 0158 m_d->linePoints.clear(); 0159 } 0160 } 0161 0162 m_d->linePoints.append(pi); 0163 0164 } 0165 0166 void KisToolLineHelper::translatePoints(const QPointF &offset) 0167 { 0168 if (!m_d->enabled) return; 0169 0170 QVector<KisPaintInformation>::iterator it = m_d->linePoints.begin(); 0171 while (it != m_d->linePoints.end()) { 0172 it->setPos(it->pos() + offset); 0173 ++it; 0174 } 0175 } 0176 0177 void KisToolLineHelper::movePointsTo(const QPointF &startPoint, const QPointF &endPoint) 0178 { 0179 if (m_d->linePoints.size() <= 1 ) { 0180 return; 0181 } 0182 0183 if (KisAlgebra2D::fuzzyPointCompare(startPoint, endPoint)) { 0184 return; 0185 } 0186 0187 if (m_d->linePoints.size() > 1) { 0188 const qreal maxDistance = kisDistance(startPoint, endPoint); 0189 const QPointF unit = (endPoint - startPoint) / maxDistance; 0190 0191 QVector<KisPaintInformation>::iterator it = m_d->linePoints.begin(); 0192 ++it; 0193 while (it != m_d->linePoints.end()) { 0194 qreal dist = kisDistance(startPoint, it->pos()); 0195 QPointF pos = startPoint + unit * dist; 0196 it->setPos(pos); 0197 ++it; 0198 } 0199 } 0200 } 0201 0202 void KisToolLineHelper::end() 0203 { 0204 if (!m_d->enabled) return; 0205 KIS_ASSERT_RECOVER_RETURN(isRunning()); 0206 0207 endPaint(); 0208 clearPoints(); 0209 } 0210 0211 0212 void KisToolLineHelper::cancel() 0213 { 0214 if (!m_d->enabled) return; 0215 KIS_ASSERT_RECOVER_RETURN(isRunning()); 0216 0217 cancelPaint(); 0218 clearPoints(); 0219 } 0220 0221 0222 void KisToolLineHelper::clearPoints() 0223 { 0224 m_d->linePoints.clear(); 0225 } 0226 0227 0228 void KisToolLineHelper::clearPaint() 0229 { 0230 if (!m_d->enabled) return; 0231 0232 cancelPaint(); 0233 } 0234 0235 void KisToolLineHelper::adjustPointsToDDA(QVector<KisPaintInformation> &points) 0236 { 0237 int x = qFloor(points.first().pos().x()); 0238 int y = qFloor(points.first().pos().y()); 0239 0240 int x2 = qFloor(points.last().pos().x()); 0241 int y2 = qFloor(points.last().pos().y()); 0242 0243 // Width and height of the line 0244 int xd = x2 - x; 0245 int yd = y2 - y; 0246 0247 float m = 0; 0248 bool lockAxis = true; 0249 0250 if (xd == 0) { 0251 m = 2.0; 0252 } else if ( yd != 0) { 0253 lockAxis = false; 0254 m = (float)yd / (float)xd; 0255 } 0256 0257 float fx = x; 0258 float fy = y; 0259 0260 int inc; 0261 int dist; 0262 0263 if (fabs(m) > 1.0f) { 0264 inc = (yd > 0) ? 1 : -1; 0265 m = (lockAxis)? 0 : 1.0f / m; 0266 m *= inc; 0267 0268 for (int i = 0; i < points.size(); i++){ 0269 dist = abs(qFloor(points.at(i).pos().y()) - y); 0270 fy = y + (dist * inc); 0271 fx = qRound(x + (dist * m)); 0272 points[i].setPos(QPointF(fx,fy)); 0273 } 0274 0275 } else { 0276 inc = (xd > 0) ? 1 : -1; 0277 m *= inc; 0278 0279 for (int i = 0; i < points.size(); i++){ 0280 dist = abs(qFloor(points.at(i).pos().x()) - x); 0281 fx = x + (dist * inc); 0282 fy = qRound(y + (dist * m)); 0283 points[i].setPos(QPointF(fx,fy)); 0284 } 0285 } 0286 }