#include "blobgroup.hpp" #include "blobinvertedrect.hpp" #include "blobshape.hpp" BlobGroup::BlobGroup(QObject* parent) : QObject(parent) { } BlobGroup::~BlobGroup() { for (auto* shape : std::as_const(m_shapes)) shape->m_group = nullptr; if (m_invertedRect) static_cast(m_invertedRect)->m_group = nullptr; } void BlobGroup::setSmoothing(qreal s) { if (qFuzzyCompare(m_smoothing, s)) return; m_smoothing = s; emit smoothingChanged(); markDirty(); } void BlobGroup::setColor(const QColor& c) { if (m_color == c) return; m_color = c; emit colorChanged(); markDirty(); } void BlobGroup::addShape(BlobShape* shape) { if (!shape || m_shapes.contains(shape)) return; m_shapes.append(shape); markDirty(); } void BlobGroup::removeShape(BlobShape* shape) { m_shapes.removeOne(shape); markDirty(); } void BlobGroup::setInvertedRect(BlobInvertedRect* rect) { if (m_invertedRect == rect) return; m_invertedRect = rect; markDirty(); } void BlobGroup::clearInvertedRect(BlobInvertedRect* rect) { if (m_invertedRect != rect) return; m_invertedRect = nullptr; markDirty(); } void BlobGroup::markDirty() { m_physicsUpdated = false; for (auto* shape : std::as_const(m_shapes)) { shape->polish(); shape->update(); } if (m_invertedRect) { static_cast(m_invertedRect)->polish(); static_cast(m_invertedRect)->update(); } } void BlobGroup::markShapeDirty(BlobShape* source) { m_physicsUpdated = false; source->polish(); source->update(); // Use actual current geometry (not cached rects) to find spatial neighbors. // Cached rects can be stale when multiple shapes move simultaneously, // causing the blob background to desync from panels that changed size. const float pad = static_cast(m_smoothing) * 2.0f; const QRectF srcRect(static_cast(source->m_cachedPaddedX - pad), static_cast(source->m_cachedPaddedY - pad), static_cast(source->m_cachedPaddedW + pad * 2.0f), static_cast(source->m_cachedPaddedH + pad * 2.0f)); for (auto* shape : std::as_const(m_shapes)) { if (shape == source) continue; const QPointF shapeScene = shape->mapToScene(QPointF(0, 0)); const QRectF otherRect(shapeScene.x(), shapeScene.y(), static_cast(shape->width()), static_cast(shape->height())); if (srcRect.intersects(otherRect)) { shape->polish(); shape->update(); } } if (m_invertedRect && static_cast(m_invertedRect) != source) { static_cast(m_invertedRect)->polish(); static_cast(m_invertedRect)->update(); } } void BlobGroup::ensurePhysicsUpdated() { if (m_physicsUpdated) return; m_physicsUpdated = true; for (auto* shape : std::as_const(m_shapes)) shape->updatePhysics(); }