diff --git a/Modules/SettingsNew/Common/BlobPopup.qml b/Modules/SettingsNew/Common/BlobPopup.qml index eeccdda..4cfd011 100644 --- a/Modules/SettingsNew/Common/BlobPopup.qml +++ b/Modules/SettingsNew/Common/BlobPopup.qml @@ -30,6 +30,7 @@ Item { id: blobGroup color: DynamicColors.palette.m3surfaceContainerHighest + cornerFill: false smoothing: Appearance.rounding.medium Behavior on color { diff --git a/Plugins/ZShell/Blobs/blobgroup.cpp b/Plugins/ZShell/Blobs/blobgroup.cpp index e77cf05..339aae2 100644 --- a/Plugins/ZShell/Blobs/blobgroup.cpp +++ b/Plugins/ZShell/Blobs/blobgroup.cpp @@ -29,6 +29,14 @@ void BlobGroup::setColor(const QColor& c) { markDirty(); } +void BlobGroup::setCornerFill(bool e) { + if (m_cornerFill == e) + return; + m_cornerFill = e; + emit cornerFillChanged(); + markDirty(); +} + void BlobGroup::addShape(BlobShape* shape) { if (!shape || m_shapes.contains(shape)) return; diff --git a/Plugins/ZShell/Blobs/blobgroup.hpp b/Plugins/ZShell/Blobs/blobgroup.hpp index e917804..c9244f4 100644 --- a/Plugins/ZShell/Blobs/blobgroup.hpp +++ b/Plugins/ZShell/Blobs/blobgroup.hpp @@ -13,6 +13,7 @@ Q_OBJECT QML_ELEMENT Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) +Q_PROPERTY(bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY cornerFillChanged) public: explicit BlobGroup(QObject* parent = nullptr); @@ -30,6 +31,12 @@ void setSmoothing(qreal s); void setColor(const QColor& c); +[[nodiscard]] bool cornerFill() const { + return m_cornerFill; +} + +void setCornerFill(bool e); + void addShape(BlobShape* shape); void removeShape(BlobShape* shape); @@ -51,10 +58,12 @@ void ensurePhysicsUpdated(); signals: void smoothingChanged(); void colorChanged(); +void cornerFillChanged(); private: qreal m_smoothing = 32.0; QColor m_color{ 0x44, 0x88, 0xff }; +bool m_cornerFill = true; QList m_shapes; BlobInvertedRect* m_invertedRect = nullptr; bool m_physicsUpdated = false; diff --git a/Plugins/ZShell/Blobs/blobrect.cpp b/Plugins/ZShell/Blobs/blobrect.cpp index 5847fa4..9da0a49 100644 --- a/Plugins/ZShell/Blobs/blobrect.cpp +++ b/Plugins/ZShell/Blobs/blobrect.cpp @@ -27,16 +27,6 @@ void BlobRect::updatePolish() { emit rawDeformMatrixChanged(); updateCenteredDeformMatrix(); m_physicsActive = false; - - if (m_group) { - QMetaObject::invokeMethod( - this, - [this]() { - if (m_group) - m_group->markDirty(); - }, - Qt::QueuedConnection); - } } else { QMetaObject::invokeMethod( this, @@ -105,17 +95,15 @@ void BlobRect::updatePhysics() { const float kStiffness = static_cast(m_stiffness); const float kDamping = static_cast(m_damping); + const float invDamp = 1.0f / (1.0f + kDamping * dt); - const float accel00 = -kStiffness * (m_dm00 - target00) - kDamping * m_dmVel00; - m_dmVel00 += accel00 * dt; + m_dmVel00 = (m_dmVel00 - kStiffness * (m_dm00 - target00) * dt) * invDamp; m_dm00 += m_dmVel00 * dt; - const float accel01 = -kStiffness * (m_dm01 - target01) - kDamping * m_dmVel01; - m_dmVel01 += accel01 * dt; + m_dmVel01 = (m_dmVel01 - kStiffness * (m_dm01 - target01) * dt) * invDamp; m_dm01 += m_dmVel01 * dt; - const float accel11 = -kStiffness * (m_dm11 - target11) - kDamping * m_dmVel11; - m_dmVel11 += accel11 * dt; + m_dmVel11 = (m_dmVel11 - kStiffness * (m_dm11 - target11) * dt) * invDamp; m_dm11 += m_dmVel11 * dt; m_deformMatrix = QMatrix4x4(m_dm00, m_dm01, 0, 0, m_dm01, m_dm11, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); @@ -178,11 +166,24 @@ bool BlobRect::isExcluded(const BlobShape* other) const { return false; } +bool BlobRect::isCornerExcluded(const BlobShape* other) const { + for (const auto& ptr : m_excludeCorners) { + if (ptr == other) + return true; + } + return false; +} + QQmlListProperty BlobRect::exclude() { return QQmlListProperty( this, nullptr, &excludeAppend, &excludeCount, &excludeAt, &excludeClear, &excludeReplace, &excludeRemoveLast); } +QQmlListProperty BlobRect::excludeCorners() { + return QQmlListProperty(this, nullptr, &excludeCornersAppend, &excludeCornersCount, &excludeCornersAt, + &excludeCornersClear, &excludeCornersReplace, &excludeCornersRemoveLast); +} + void BlobRect::excludeAppend(QQmlListProperty* prop, BlobRect* rect) { auto* self = static_cast(prop->object); self->m_exclude.append(rect); @@ -229,6 +230,52 @@ void BlobRect::excludeRemoveLast(QQmlListProperty* prop) { emit self->excludeChanged(); } +void BlobRect::excludeCornersAppend(QQmlListProperty* prop, BlobRect* rect) { + auto* self = static_cast(prop->object); + self->m_excludeCorners.append(rect); + if (self->m_group) + self->m_group->markDirty(); + emit self->excludeCornersChanged(); +} + +qsizetype BlobRect::excludeCornersCount(QQmlListProperty* prop) { + auto* self = static_cast(prop->object); + return self->m_excludeCorners.size(); +} + +BlobRect* BlobRect::excludeCornersAt(QQmlListProperty* prop, qsizetype index) { + auto* self = static_cast(prop->object); + return self->m_excludeCorners.at(index); +} + +void BlobRect::excludeCornersClear(QQmlListProperty* prop) { + auto* self = static_cast(prop->object); + if (self->m_excludeCorners.isEmpty()) + return; + self->m_excludeCorners.clear(); + if (self->m_group) + self->m_group->markDirty(); + emit self->excludeCornersChanged(); +} + +void BlobRect::excludeCornersReplace(QQmlListProperty* prop, qsizetype index, BlobRect* rect) { + auto* self = static_cast(prop->object); + self->m_excludeCorners[index] = rect; + if (self->m_group) + self->m_group->markDirty(); + emit self->excludeCornersChanged(); +} + +void BlobRect::excludeCornersRemoveLast(QQmlListProperty* prop) { + auto* self = static_cast(prop->object); + if (self->m_excludeCorners.isEmpty()) + return; + self->m_excludeCorners.removeLast(); + if (self->m_group) + self->m_group->markDirty(); + emit self->excludeCornersChanged(); +} + void BlobRect::checkAtRest(float speed) { constexpr float kEpsilon = 0.002f; const bool atRest = std::abs(m_dm00 - 1.0f) < kEpsilon && std::abs(m_dm01) < kEpsilon && @@ -242,19 +289,9 @@ void BlobRect::checkAtRest(float speed) { m_dmVel00 = 0.0f; m_dmVel01 = 0.0f; m_dmVel11 = 0.0f; - m_deformMatrix = QMatrix4x4(); + m_deformMatrix = QMatrix4x4(); // identity emit rawDeformMatrixChanged(); updateCenteredDeformMatrix(); m_physicsActive = false; - - if (m_group) { - QMetaObject::invokeMethod( - this, - [this]() { - if (m_group) - m_group->markDirty(); - }, - Qt::QueuedConnection); - } } } diff --git a/Plugins/ZShell/Blobs/blobrect.hpp b/Plugins/ZShell/Blobs/blobrect.hpp index 11d9910..06d851b 100644 --- a/Plugins/ZShell/Blobs/blobrect.hpp +++ b/Plugins/ZShell/Blobs/blobrect.hpp @@ -14,6 +14,7 @@ Q_PROPERTY(qreal stiffness READ stiffness WRITE setStiffness NOTIFY stiffnessCha Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged) Q_PROPERTY(qreal deformScale READ deformScale WRITE setDeformScale NOTIFY deformScaleChanged) Q_PROPERTY(QQmlListProperty exclude READ exclude NOTIFY excludeChanged) +Q_PROPERTY(QQmlListProperty excludeCorners READ excludeCorners NOTIFY excludeCornersChanged) Q_PROPERTY(qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged) Q_PROPERTY(qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged) Q_PROPERTY(qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged) @@ -58,8 +59,10 @@ void setDeformScale(qreal s) { } QQmlListProperty exclude(); +QQmlListProperty excludeCorners(); bool isExcluded(const BlobShape* other) const override; +bool isCornerExcluded(const BlobShape* other) const override; void cornerRadii(float out[4]) const override; [[nodiscard]] qreal topLeftRadius() const { @@ -91,6 +94,7 @@ void stiffnessChanged(); void dampingChanged(); void deformScaleChanged(); void excludeChanged(); +void excludeCornersChanged(); void topLeftRadiusChanged(); void topRightRadiusChanged(); void bottomLeftRadiusChanged(); @@ -130,6 +134,7 @@ qreal m_bottomLeftRadius = -1; qreal m_bottomRightRadius = -1; QList > m_exclude; +QList > m_excludeCorners; static void excludeAppend(QQmlListProperty* prop, BlobRect* rect); static qsizetype excludeCount(QQmlListProperty* prop); @@ -137,4 +142,11 @@ static BlobRect* excludeAt(QQmlListProperty* prop, qsizetype index); static void excludeClear(QQmlListProperty* prop); static void excludeReplace(QQmlListProperty* prop, qsizetype index, BlobRect* rect); static void excludeRemoveLast(QQmlListProperty* prop); + +static void excludeCornersAppend(QQmlListProperty* prop, BlobRect* rect); +static qsizetype excludeCornersCount(QQmlListProperty* prop); +static BlobRect* excludeCornersAt(QQmlListProperty* prop, qsizetype index); +static void excludeCornersClear(QQmlListProperty* prop); +static void excludeCornersReplace(QQmlListProperty* prop, qsizetype index, BlobRect* rect); +static void excludeCornersRemoveLast(QQmlListProperty* prop); }; diff --git a/Plugins/ZShell/Blobs/blobshape.cpp b/Plugins/ZShell/Blobs/blobshape.cpp index 68ff191..e1626ca 100644 --- a/Plugins/ZShell/Blobs/blobshape.cpp +++ b/Plugins/ZShell/Blobs/blobshape.cpp @@ -31,6 +31,12 @@ static float cpuSmoothstep(float edge0, float edge1, float x) { return t * t * (3.0f - 2.0f * t); } +static float cornerFillFactor(float sd, float smoothFactor) { + const float outside = cpuSmoothstep(0.0f, smoothFactor, sd); + const float inside = cpuSmoothstep(0.0f, -smoothFactor, sd); + return std::max(outside, inside); +} + BlobShape::BlobShape(QQuickItem* parent) : QQuickItem(parent) { setFlag(ItemHasContents); @@ -70,19 +76,11 @@ void BlobShape::geometryChange(const QRectF& newGeometry, const QRectF& oldGeome if (m_group) { m_pendingDx += static_cast(newGeometry.x() - oldGeometry.x()); m_pendingDy += static_cast(newGeometry.y() - oldGeometry.y()); - m_pendingDw += static_cast(newGeometry.width() - oldGeometry.width()); - m_pendingDh += static_cast(newGeometry.height() - oldGeometry.height()); - - const float deformMag = std::abs(m_deformMatrix(0, 0) - 1.0f) + std::abs(m_deformMatrix(0, 1)) + - std::abs(m_deformMatrix(1, 0)) + std::abs(m_deformMatrix(1, 1) - 1.0f); - const float syncThreshold = deformMag > 0.001f ? 0.05f : 0.5f; - - if (std::abs(m_pendingDx) > syncThreshold || std::abs(m_pendingDy) > syncThreshold || - std::abs(m_pendingDw) > syncThreshold || std::abs(m_pendingDh) > syncThreshold) { + const auto dw = std::abs(newGeometry.width() - oldGeometry.width()); + const auto dh = std::abs(newGeometry.height() - oldGeometry.height()); + if (std::abs(m_pendingDx) > 0.5f || std::abs(m_pendingDy) > 0.5f || dw > 0.5 || dh > 0.5) { m_pendingDx = 0; m_pendingDy = 0; - m_pendingDw = 0; - m_pendingDh = 0; m_group->markShapeDirty(this); } } @@ -281,10 +279,12 @@ void BlobShape::updatePolish() { const float smoothFactor = pad; constexpr float minR = 2.0f; + const bool cornerFill = m_group->cornerFill(); const auto rectCount = m_cachedRects.size(); for (qsizetype i = 0; i < rectCount; ++i) { auto& ri = m_cachedRects[i]; const int riExcludeMask = ri.excludeMask; + BlobShape* const si = rectShapes[i]; float fTr = 1.0f, fBr = 1.0f, fBl = 1.0f, fTl = 1.0f; const float cTrX = ri.cx + ri.hw, cTrY = ri.cy - ri.hh; @@ -292,19 +292,26 @@ void BlobShape::updatePolish() { const float cBlX = ri.cx - ri.hw, cBlY = ri.cy + ri.hh; const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh; - for (qsizetype j = 0; j < rectCount; ++j) { + for (qsizetype j = 0; cornerFill && j < rectCount; ++j) { if (j == i) continue; if (riExcludeMask & (1 << j)) continue; + BlobShape* const sj = rectShapes[j]; + if (si->isCornerExcluded(sj) || sj->isCornerExcluded(si)) + continue; const auto& rj = m_cachedRects[j]; - fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh))); - fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh))); - fBl = std::min(fBl, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBlX, cBlY, rj.cx, rj.cy, rj.hw, rj.hh))); - fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTlX, cTlY, rj.cx, rj.cy, rj.hw, rj.hh))); + const float sdTr = cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh); + const float sdBr = cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh); + const float sdBl = cpuSdBox(cBlX, cBlY, rj.cx, rj.cy, rj.hw, rj.hh); + const float sdTl = cpuSdBox(cTlX, cTlY, rj.cx, rj.cy, rj.hw, rj.hh); + fTr = std::min(fTr, cornerFillFactor(sdTr, smoothFactor)); + fBr = std::min(fBr, cornerFillFactor(sdBr, smoothFactor)); + fBl = std::min(fBl, cornerFillFactor(sdBl, smoothFactor)); + fTl = std::min(fTl, cornerFillFactor(sdTl, smoothFactor)); } - if (m_cachedHasInverted) { + if (cornerFill && m_cachedHasInverted) { const float icx = m_cachedInvertedInner[0]; const float icy = m_cachedInvertedInner[1]; const float ihw = m_cachedInvertedInner[2]; diff --git a/Plugins/ZShell/Blobs/blobshape.hpp b/Plugins/ZShell/Blobs/blobshape.hpp index 12c8747..e9542b7 100644 --- a/Plugins/ZShell/Blobs/blobshape.hpp +++ b/Plugins/ZShell/Blobs/blobshape.hpp @@ -61,6 +61,10 @@ virtual bool isExcluded(const BlobShape* /*other*/) const { return false; } +virtual bool isCornerExcluded(const BlobShape* /*other*/) const { + return false; +} + virtual void cornerRadii(float out[4]) const; virtual void updatePhysics() { @@ -72,9 +76,10 @@ void updateCenteredDeformMatrix(); BlobGroup* m_group = nullptr; qreal m_radius = 0; -QMatrix4x4 m_deformMatrix; +QMatrix4x4 m_deformMatrix; // identity by default QMatrix4x4 m_centeredDeformMatrix; +// Cached data from updatePolish float m_cachedPaddedX = 0; float m_cachedPaddedY = 0; float m_cachedPaddedW = 0; @@ -84,8 +89,6 @@ QVector m_cachedRects; int m_cachedMyIndex = -2; float m_pendingDx = 0; float m_pendingDy = 0; -float m_pendingDw = 0; -float m_pendingDh = 0; bool m_cachedHasInverted = false; float m_cachedInvertedRadius = 0; float m_cachedInvertedOuter[4] = {};