From 71386f008dfc94828906baee31f7a874a5338519 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Fri, 8 May 2026 03:13:53 +1000 Subject: [PATCH] feat: add corner fill toggle for blobs --- plugin/src/Caelestia/Blobs/blobgroup.cpp | 8 ++++ plugin/src/Caelestia/Blobs/blobgroup.hpp | 7 +++ plugin/src/Caelestia/Blobs/blobrect.cpp | 59 ++++++++++++++++++++++++ plugin/src/Caelestia/Blobs/blobrect.hpp | 12 +++++ plugin/src/Caelestia/Blobs/blobshape.cpp | 9 +++- plugin/src/Caelestia/Blobs/blobshape.hpp | 2 + 6 files changed, 95 insertions(+), 2 deletions(-) diff --git a/plugin/src/Caelestia/Blobs/blobgroup.cpp b/plugin/src/Caelestia/Blobs/blobgroup.cpp index a4703c86..e1ff8b23 100644 --- a/plugin/src/Caelestia/Blobs/blobgroup.cpp +++ b/plugin/src/Caelestia/Blobs/blobgroup.cpp @@ -28,6 +28,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/plugin/src/Caelestia/Blobs/blobgroup.hpp b/plugin/src/Caelestia/Blobs/blobgroup.hpp index e09125a9..a73ffc99 100644 --- a/plugin/src/Caelestia/Blobs/blobgroup.hpp +++ b/plugin/src/Caelestia/Blobs/blobgroup.hpp @@ -13,6 +13,7 @@ class BlobGroup : public QObject { 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); @@ -26,6 +27,10 @@ public: void setColor(const QColor& c); + bool cornerFill() const { return m_cornerFill; } + + void setCornerFill(bool e); + void addShape(BlobShape* shape); void removeShape(BlobShape* shape); @@ -43,10 +48,12 @@ public: 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/plugin/src/Caelestia/Blobs/blobrect.cpp b/plugin/src/Caelestia/Blobs/blobrect.cpp index 15486d83..f78d346d 100644 --- a/plugin/src/Caelestia/Blobs/blobrect.cpp +++ b/plugin/src/Caelestia/Blobs/blobrect.cpp @@ -173,11 +173,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); @@ -224,6 +237,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 && diff --git a/plugin/src/Caelestia/Blobs/blobrect.hpp b/plugin/src/Caelestia/Blobs/blobrect.hpp index d2d6ad45..354cc265 100644 --- a/plugin/src/Caelestia/Blobs/blobrect.hpp +++ b/plugin/src/Caelestia/Blobs/blobrect.hpp @@ -14,6 +14,7 @@ class BlobRect : public BlobShape { 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) @@ -52,8 +53,10 @@ public: } 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; qreal topLeftRadius() const { return m_topLeftRadius; } @@ -77,6 +80,7 @@ signals: void dampingChanged(); void deformScaleChanged(); void excludeChanged(); + void excludeCornersChanged(); void topLeftRadiusChanged(); void topRightRadiusChanged(); void bottomLeftRadiusChanged(); @@ -116,6 +120,7 @@ private: qreal m_bottomRightRadius = -1; QList> m_exclude; + QList> m_excludeCorners; static void excludeAppend(QQmlListProperty* prop, BlobRect* rect); static qsizetype excludeCount(QQmlListProperty* prop); @@ -123,4 +128,11 @@ private: 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/plugin/src/Caelestia/Blobs/blobshape.cpp b/plugin/src/Caelestia/Blobs/blobshape.cpp index 2af1748d..8eed3653 100644 --- a/plugin/src/Caelestia/Blobs/blobshape.cpp +++ b/plugin/src/Caelestia/Blobs/blobshape.cpp @@ -288,10 +288,12 @@ void BlobShape::updatePolish() { // Pre-compute effective per-corner radii (moves O(N²) work from GPU to CPU) 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; @@ -299,11 +301,14 @@ 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]; // Skip when the corner is inside rj: it's buried, not on a visible junction, // so squaring it would only perturb interior SDF gradients. @@ -321,7 +326,7 @@ void BlobShape::updatePolish() { fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, sdTl)); } - 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/plugin/src/Caelestia/Blobs/blobshape.hpp b/plugin/src/Caelestia/Blobs/blobshape.hpp index c05a40d8..e179531a 100644 --- a/plugin/src/Caelestia/Blobs/blobshape.hpp +++ b/plugin/src/Caelestia/Blobs/blobshape.hpp @@ -49,6 +49,8 @@ protected: 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() {}