physics throttling, targeted dirty marking, proximity-based border
- Snap deformation to rest when imperceptible instead of pumping frames - Only trigger markDirty on geometry changes exceeding 0.5px - Only include inverted rect data when rect is near the border
This commit is contained in:
parent
6103ce19e8
commit
ebccf801f4
4 changed files with 96 additions and 20 deletions
|
|
@ -66,6 +66,40 @@ void BlobGroup::markDirty() {
|
|||
}
|
||||
}
|
||||
|
||||
void BlobGroup::markShapeDirty(BlobShape* source) {
|
||||
m_physicsUpdated = false;
|
||||
|
||||
source->polish();
|
||||
source->update();
|
||||
|
||||
// Use cached padded rects to find spatial neighbors
|
||||
const float pad = static_cast<float>(m_smoothing) * 2.0f;
|
||||
const QRectF srcRect(
|
||||
static_cast<double>(source->m_cachedPaddedX - pad),
|
||||
static_cast<double>(source->m_cachedPaddedY - pad),
|
||||
static_cast<double>(source->m_cachedPaddedW + pad * 2.0f),
|
||||
static_cast<double>(source->m_cachedPaddedH + pad * 2.0f));
|
||||
|
||||
for (auto* shape : std::as_const(m_shapes)) {
|
||||
if (shape == source)
|
||||
continue;
|
||||
const QRectF otherRect(
|
||||
static_cast<double>(shape->m_cachedPaddedX),
|
||||
static_cast<double>(shape->m_cachedPaddedY),
|
||||
static_cast<double>(shape->m_cachedPaddedW),
|
||||
static_cast<double>(shape->m_cachedPaddedH));
|
||||
if (srcRect.intersects(otherRect)) {
|
||||
shape->polish();
|
||||
shape->update();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_invertedRect && static_cast<BlobShape*>(m_invertedRect) != source) {
|
||||
static_cast<BlobShape*>(m_invertedRect)->polish();
|
||||
static_cast<BlobShape*>(m_invertedRect)->update();
|
||||
}
|
||||
}
|
||||
|
||||
void BlobGroup::ensurePhysicsUpdated() {
|
||||
if (m_physicsUpdated)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ public:
|
|||
BlobInvertedRect* invertedRect() const { return m_invertedRect; }
|
||||
|
||||
void markDirty();
|
||||
void markShapeDirty(BlobShape* source);
|
||||
void ensurePhysicsUpdated();
|
||||
|
||||
signals:
|
||||
|
|
|
|||
|
|
@ -16,13 +16,30 @@ void BlobRect::updatePolish() {
|
|||
BlobShape::updatePolish();
|
||||
|
||||
if (m_physicsActive) {
|
||||
QMetaObject::invokeMethod(
|
||||
this,
|
||||
[this]() {
|
||||
if (m_physicsActive && m_group)
|
||||
m_group->markDirty();
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
// Check if deformation is visually imperceptible
|
||||
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01)
|
||||
+ std::abs(m_dm11 - 1.0f);
|
||||
float totalVel =
|
||||
std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
|
||||
|
||||
if (totalDelta < 0.004f && totalVel < 0.05f) {
|
||||
// Snap to rest, no visible deformation
|
||||
m_dm00 = 1.0f;
|
||||
m_dm01 = 0.0f;
|
||||
m_dm11 = 1.0f;
|
||||
m_dmVel00 = m_dmVel01 = m_dmVel11 = 0.0f;
|
||||
m_deformMatrix = QMatrix4x4();
|
||||
updateCenteredDeformMatrix();
|
||||
m_physicsActive = false;
|
||||
} else {
|
||||
QMetaObject::invokeMethod(
|
||||
this,
|
||||
[this]() {
|
||||
if (m_physicsActive && m_group)
|
||||
m_group->markDirty();
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,8 +70,15 @@ void BlobShape::geometryChange(
|
|||
const QRectF& newGeometry, const QRectF& oldGeometry) {
|
||||
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
||||
updateCenteredDeformMatrix();
|
||||
if (m_group)
|
||||
m_group->markDirty();
|
||||
if (m_group) {
|
||||
// Only trigger redraw if the change is visually meaningful
|
||||
const auto dx = std::abs(newGeometry.x() - oldGeometry.x());
|
||||
const auto dy = std::abs(newGeometry.y() - oldGeometry.y());
|
||||
const auto dw = std::abs(newGeometry.width() - oldGeometry.width());
|
||||
const auto dh = std::abs(newGeometry.height() - oldGeometry.height());
|
||||
if (dx > 0.5 || dy > 0.5 || dw > 0.5 || dh > 0.5)
|
||||
m_group->markShapeDirty(this);
|
||||
}
|
||||
}
|
||||
|
||||
void BlobShape::updateCenteredDeformMatrix() {
|
||||
|
|
@ -210,9 +217,6 @@ void BlobShape::updatePolish() {
|
|||
|
||||
auto* inv = m_group->invertedRect();
|
||||
if (inv) {
|
||||
m_cachedHasInverted = true;
|
||||
m_cachedInvertedRadius = static_cast<float>(inv->radius());
|
||||
|
||||
const QPointF invScene = inv->mapToScene(QPointF(0, 0));
|
||||
const float outerCX =
|
||||
static_cast<float>(invScene.x() + inv->width() / 2.0);
|
||||
|
|
@ -234,15 +238,35 @@ void BlobShape::updatePolish() {
|
|||
outerHH -
|
||||
static_cast<float>((inv->borderTop() + inv->borderBottom()) / 2.0);
|
||||
|
||||
m_cachedInvertedOuter[0] = outerCX;
|
||||
m_cachedInvertedOuter[1] = outerCY;
|
||||
m_cachedInvertedOuter[2] = outerHW;
|
||||
m_cachedInvertedOuter[3] = outerHH;
|
||||
// Check if this rect is near the border (within 2x smoothing of inner edge)
|
||||
bool nearBorder = isInvertedRect();
|
||||
if (!nearBorder) {
|
||||
const float margin = pad * 2.0f;
|
||||
const float myCX = m_cachedPaddedX + m_cachedPaddedW * 0.5f;
|
||||
const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f;
|
||||
const float myHW = m_cachedPaddedW * 0.5f;
|
||||
const float myHH = m_cachedPaddedH * 0.5f;
|
||||
// Near border if any edge of padded rect is within margin of inner edge
|
||||
nearBorder = (myCX - myHW < innerCX - innerHW + margin) ||
|
||||
(myCX + myHW > innerCX + innerHW - margin) ||
|
||||
(myCY - myHH < innerCY - innerHH + margin) ||
|
||||
(myCY + myHH > innerCY + innerHH - margin);
|
||||
}
|
||||
|
||||
m_cachedInvertedInner[0] = innerCX;
|
||||
m_cachedInvertedInner[1] = innerCY;
|
||||
m_cachedInvertedInner[2] = innerHW;
|
||||
m_cachedInvertedInner[3] = innerHH;
|
||||
if (nearBorder) {
|
||||
m_cachedHasInverted = true;
|
||||
m_cachedInvertedRadius = static_cast<float>(inv->radius());
|
||||
|
||||
m_cachedInvertedOuter[0] = outerCX;
|
||||
m_cachedInvertedOuter[1] = outerCY;
|
||||
m_cachedInvertedOuter[2] = outerHW;
|
||||
m_cachedInvertedOuter[3] = outerHH;
|
||||
|
||||
m_cachedInvertedInner[0] = innerCX;
|
||||
m_cachedInvertedInner[1] = innerCY;
|
||||
m_cachedInvertedInner[2] = innerHW;
|
||||
m_cachedInvertedInner[3] = innerHH;
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-compute corner fill factors (moves O(N²) work from GPU to CPU)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue