Warning, file /graphics/washipad/src/stroke.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // This file is part of Washi Pad
0002 // SPDX-FileCopyrightText: 2018 Kevin Ottens <ervin@kde.org>
0003 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 
0005 #include "stroke.h"
0006 
0007 #include <QVector2D>
0008 
0009 bool StrokeSample::operator==(const StrokeSample &other) const
0010 {
0011     return position == other.position
0012         && qFuzzyCompare(width, other.width);
0013 }
0014 
0015 Stroke::Type Stroke::type() const
0016 {
0017     return m_type;
0018 }
0019 
0020 void Stroke::setType(Stroke::Type type)
0021 {
0022     m_type = type;
0023 }
0024 
0025 QColor Stroke::color() const
0026 {
0027     return m_color;
0028 }
0029 
0030 void Stroke::setColor(const QColor &color)
0031 {
0032     m_color = color;
0033 }
0034 
0035 QVector<StrokeSample> Stroke::samples() const
0036 {
0037     return m_samples;
0038 }
0039 
0040 void Stroke::addSample(const StrokeSample &sample)
0041 {
0042     m_samples << sample;
0043     m_boundingRectCache = QRectF{};
0044 }
0045 
0046 void Stroke::addSamples(const QVector<StrokeSample> &samples)
0047 {
0048     m_samples += samples;
0049     m_boundingRectCache = QRectF{};
0050 }
0051 
0052 QVector<Stroke> Stroke::eraseArea(const QVector2D &center, float radius)
0053 {
0054     const auto areaBoundingRect = QRectF{(center - QVector2D{radius, radius}).toPointF(),
0055                                          (center + QVector2D{radius, radius}).toPointF()};
0056     auto result = QVector<Stroke>{};
0057 
0058     // For sure won't get any hits
0059     if (!boundingRect().intersects(areaBoundingRect)) {
0060         result.append(*this);
0061         return result;
0062     }
0063 
0064     const auto isHit = [=](const StrokeSample &sample) {
0065         return QVector2D{sample.position - center}.length() <= radius;
0066     };
0067 
0068     auto it = std::cbegin(m_samples);
0069     const auto end = std::cend(m_samples);
0070 
0071     while (it != end) {
0072         auto previous = it;
0073         // Find first hit
0074         it = std::find_if(it, end, isHit);
0075 
0076         // Copy [previous, it) in a new stroke if there's more than one sample
0077         const auto distance = static_cast<int>(std::distance(previous, it));
0078         if (distance >= 2) {
0079             auto stroke = *this;
0080             stroke.m_samples.clear();
0081             stroke.m_samples.reserve(distance);
0082             std::copy(previous, it, std::back_inserter(stroke.m_samples));
0083 
0084             result.append(stroke);
0085         }
0086 
0087         // Jump to next non-hit
0088         it = std::find_if_not(it, end, isHit);
0089     }
0090 
0091     return result;
0092 }
0093 
0094 QRectF Stroke::boundingRect() const
0095 {
0096     if (!m_boundingRectCache.isValid()) {
0097         auto minX = 0.0f;
0098         auto minY = 0.0f;
0099         auto maxX = 0.0f;
0100         auto maxY = 0.0f;
0101 
0102         for (const auto &sample : qAsConst(m_samples)) {
0103             const auto position = sample.position;
0104             if (position.x() < minX)
0105                 minX = position.x();
0106             else if (position.x() > maxX)
0107                 maxX = position.x();
0108 
0109             if (position.y() < minY)
0110                 minY = position.y();
0111             else if (position.y() > maxY)
0112                 maxY = position.y();
0113         }
0114 
0115         m_boundingRectCache = {QPointF{static_cast<qreal>(minX), static_cast<qreal>(minY)},
0116                                QPointF{static_cast<qreal>(maxX), static_cast<qreal>(maxY)}};
0117     }
0118 
0119     return m_boundingRectCache;
0120 }