feat: add corner fill toggle for blobs
This commit is contained in:
parent
ba2d4f03d6
commit
71386f008d
6 changed files with 95 additions and 2 deletions
|
|
@ -28,6 +28,14 @@ void BlobGroup::setColor(const QColor& c) {
|
||||||
markDirty();
|
markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlobGroup::setCornerFill(bool e) {
|
||||||
|
if (m_cornerFill == e)
|
||||||
|
return;
|
||||||
|
m_cornerFill = e;
|
||||||
|
emit cornerFillChanged();
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
void BlobGroup::addShape(BlobShape* shape) {
|
void BlobGroup::addShape(BlobShape* shape) {
|
||||||
if (!shape || m_shapes.contains(shape))
|
if (!shape || m_shapes.contains(shape))
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ class BlobGroup : public QObject {
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged)
|
Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged)
|
||||||
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
||||||
|
Q_PROPERTY(bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY cornerFillChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BlobGroup(QObject* parent = nullptr);
|
explicit BlobGroup(QObject* parent = nullptr);
|
||||||
|
|
@ -26,6 +27,10 @@ public:
|
||||||
|
|
||||||
void setColor(const QColor& c);
|
void setColor(const QColor& c);
|
||||||
|
|
||||||
|
bool cornerFill() const { return m_cornerFill; }
|
||||||
|
|
||||||
|
void setCornerFill(bool e);
|
||||||
|
|
||||||
void addShape(BlobShape* shape);
|
void addShape(BlobShape* shape);
|
||||||
void removeShape(BlobShape* shape);
|
void removeShape(BlobShape* shape);
|
||||||
|
|
||||||
|
|
@ -43,10 +48,12 @@ public:
|
||||||
signals:
|
signals:
|
||||||
void smoothingChanged();
|
void smoothingChanged();
|
||||||
void colorChanged();
|
void colorChanged();
|
||||||
|
void cornerFillChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
qreal m_smoothing = 32.0;
|
qreal m_smoothing = 32.0;
|
||||||
QColor m_color{ 0x44, 0x88, 0xff };
|
QColor m_color{ 0x44, 0x88, 0xff };
|
||||||
|
bool m_cornerFill = true;
|
||||||
QList<BlobShape*> m_shapes;
|
QList<BlobShape*> m_shapes;
|
||||||
BlobInvertedRect* m_invertedRect = nullptr;
|
BlobInvertedRect* m_invertedRect = nullptr;
|
||||||
bool m_physicsUpdated = false;
|
bool m_physicsUpdated = false;
|
||||||
|
|
|
||||||
|
|
@ -173,11 +173,24 @@ bool BlobRect::isExcluded(const BlobShape* other) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BlobRect::isCornerExcluded(const BlobShape* other) const {
|
||||||
|
for (const auto& ptr : m_excludeCorners) {
|
||||||
|
if (ptr == other)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QQmlListProperty<BlobRect> BlobRect::exclude() {
|
QQmlListProperty<BlobRect> BlobRect::exclude() {
|
||||||
return QQmlListProperty<BlobRect>(
|
return QQmlListProperty<BlobRect>(
|
||||||
this, nullptr, &excludeAppend, &excludeCount, &excludeAt, &excludeClear, &excludeReplace, &excludeRemoveLast);
|
this, nullptr, &excludeAppend, &excludeCount, &excludeAt, &excludeClear, &excludeReplace, &excludeRemoveLast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QQmlListProperty<BlobRect> BlobRect::excludeCorners() {
|
||||||
|
return QQmlListProperty<BlobRect>(this, nullptr, &excludeCornersAppend, &excludeCornersCount, &excludeCornersAt,
|
||||||
|
&excludeCornersClear, &excludeCornersReplace, &excludeCornersRemoveLast);
|
||||||
|
}
|
||||||
|
|
||||||
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
self->m_exclude.append(rect);
|
self->m_exclude.append(rect);
|
||||||
|
|
@ -224,6 +237,52 @@ void BlobRect::excludeRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
||||||
emit self->excludeChanged();
|
emit self->excludeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlobRect::excludeCornersAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
||||||
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
|
self->m_excludeCorners.append(rect);
|
||||||
|
if (self->m_group)
|
||||||
|
self->m_group->markDirty();
|
||||||
|
emit self->excludeCornersChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
qsizetype BlobRect::excludeCornersCount(QQmlListProperty<BlobRect>* prop) {
|
||||||
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
|
return self->m_excludeCorners.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
BlobRect* BlobRect::excludeCornersAt(QQmlListProperty<BlobRect>* prop, qsizetype index) {
|
||||||
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
|
return self->m_excludeCorners.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobRect::excludeCornersClear(QQmlListProperty<BlobRect>* prop) {
|
||||||
|
auto* self = static_cast<BlobRect*>(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<BlobRect>* prop, qsizetype index, BlobRect* rect) {
|
||||||
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
|
self->m_excludeCorners[index] = rect;
|
||||||
|
if (self->m_group)
|
||||||
|
self->m_group->markDirty();
|
||||||
|
emit self->excludeCornersChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobRect::excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
||||||
|
auto* self = static_cast<BlobRect*>(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) {
|
void BlobRect::checkAtRest(float speed) {
|
||||||
constexpr float kEpsilon = 0.002f;
|
constexpr float kEpsilon = 0.002f;
|
||||||
const bool atRest = std::abs(m_dm00 - 1.0f) < kEpsilon && std::abs(m_dm01) < kEpsilon &&
|
const bool atRest = std::abs(m_dm00 - 1.0f) < kEpsilon && std::abs(m_dm01) < kEpsilon &&
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ class BlobRect : public BlobShape {
|
||||||
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
|
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
|
||||||
Q_PROPERTY(qreal deformScale READ deformScale WRITE setDeformScale NOTIFY deformScaleChanged)
|
Q_PROPERTY(qreal deformScale READ deformScale WRITE setDeformScale NOTIFY deformScaleChanged)
|
||||||
Q_PROPERTY(QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
|
Q_PROPERTY(QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
|
||||||
|
Q_PROPERTY(QQmlListProperty<BlobRect> excludeCorners READ excludeCorners NOTIFY excludeCornersChanged)
|
||||||
Q_PROPERTY(qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged)
|
Q_PROPERTY(qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged)
|
||||||
Q_PROPERTY(qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged)
|
Q_PROPERTY(qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged)
|
||||||
Q_PROPERTY(qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged)
|
Q_PROPERTY(qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged)
|
||||||
|
|
@ -52,8 +53,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<BlobRect> exclude();
|
QQmlListProperty<BlobRect> exclude();
|
||||||
|
QQmlListProperty<BlobRect> excludeCorners();
|
||||||
|
|
||||||
bool isExcluded(const BlobShape* other) const override;
|
bool isExcluded(const BlobShape* other) const override;
|
||||||
|
bool isCornerExcluded(const BlobShape* other) const override;
|
||||||
void cornerRadii(float out[4]) const override;
|
void cornerRadii(float out[4]) const override;
|
||||||
|
|
||||||
qreal topLeftRadius() const { return m_topLeftRadius; }
|
qreal topLeftRadius() const { return m_topLeftRadius; }
|
||||||
|
|
@ -77,6 +80,7 @@ signals:
|
||||||
void dampingChanged();
|
void dampingChanged();
|
||||||
void deformScaleChanged();
|
void deformScaleChanged();
|
||||||
void excludeChanged();
|
void excludeChanged();
|
||||||
|
void excludeCornersChanged();
|
||||||
void topLeftRadiusChanged();
|
void topLeftRadiusChanged();
|
||||||
void topRightRadiusChanged();
|
void topRightRadiusChanged();
|
||||||
void bottomLeftRadiusChanged();
|
void bottomLeftRadiusChanged();
|
||||||
|
|
@ -116,6 +120,7 @@ private:
|
||||||
qreal m_bottomRightRadius = -1;
|
qreal m_bottomRightRadius = -1;
|
||||||
|
|
||||||
QList<QPointer<BlobRect>> m_exclude;
|
QList<QPointer<BlobRect>> m_exclude;
|
||||||
|
QList<QPointer<BlobRect>> m_excludeCorners;
|
||||||
|
|
||||||
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
||||||
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
|
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
|
||||||
|
|
@ -123,4 +128,11 @@ private:
|
||||||
static void excludeClear(QQmlListProperty<BlobRect>* prop);
|
static void excludeClear(QQmlListProperty<BlobRect>* prop);
|
||||||
static void excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
static void excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
||||||
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
|
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
|
||||||
|
|
||||||
|
static void excludeCornersAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
||||||
|
static qsizetype excludeCornersCount(QQmlListProperty<BlobRect>* prop);
|
||||||
|
static BlobRect* excludeCornersAt(QQmlListProperty<BlobRect>* prop, qsizetype index);
|
||||||
|
static void excludeCornersClear(QQmlListProperty<BlobRect>* prop);
|
||||||
|
static void excludeCornersReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
||||||
|
static void excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -288,10 +288,12 @@ void BlobShape::updatePolish() {
|
||||||
// Pre-compute effective per-corner radii (moves O(N²) work from GPU to CPU)
|
// Pre-compute effective per-corner radii (moves O(N²) work from GPU to CPU)
|
||||||
const float smoothFactor = pad;
|
const float smoothFactor = pad;
|
||||||
constexpr float minR = 2.0f;
|
constexpr float minR = 2.0f;
|
||||||
|
const bool cornerFill = m_group->cornerFill();
|
||||||
const auto rectCount = m_cachedRects.size();
|
const auto rectCount = m_cachedRects.size();
|
||||||
for (qsizetype i = 0; i < rectCount; ++i) {
|
for (qsizetype i = 0; i < rectCount; ++i) {
|
||||||
auto& ri = m_cachedRects[i];
|
auto& ri = m_cachedRects[i];
|
||||||
const int riExcludeMask = ri.excludeMask;
|
const int riExcludeMask = ri.excludeMask;
|
||||||
|
BlobShape* const si = rectShapes[i];
|
||||||
float fTr = 1.0f, fBr = 1.0f, fBl = 1.0f, fTl = 1.0f;
|
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;
|
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 cBlX = ri.cx - ri.hw, cBlY = ri.cy + ri.hh;
|
||||||
const float cTlX = ri.cx - ri.hw, cTlY = 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)
|
if (j == i)
|
||||||
continue;
|
continue;
|
||||||
if (riExcludeMask & (1 << j))
|
if (riExcludeMask & (1 << j))
|
||||||
continue;
|
continue;
|
||||||
|
BlobShape* const sj = rectShapes[j];
|
||||||
|
if (si->isCornerExcluded(sj) || sj->isCornerExcluded(si))
|
||||||
|
continue;
|
||||||
const auto& rj = m_cachedRects[j];
|
const auto& rj = m_cachedRects[j];
|
||||||
// Skip when the corner is inside rj: it's buried, not on a visible junction,
|
// Skip when the corner is inside rj: it's buried, not on a visible junction,
|
||||||
// so squaring it would only perturb interior SDF gradients.
|
// 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));
|
fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, sdTl));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cachedHasInverted) {
|
if (cornerFill && m_cachedHasInverted) {
|
||||||
const float icx = m_cachedInvertedInner[0];
|
const float icx = m_cachedInvertedInner[0];
|
||||||
const float icy = m_cachedInvertedInner[1];
|
const float icy = m_cachedInvertedInner[1];
|
||||||
const float ihw = m_cachedInvertedInner[2];
|
const float ihw = m_cachedInvertedInner[2];
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ protected:
|
||||||
|
|
||||||
virtual bool isExcluded(const BlobShape* /*other*/) const { return false; }
|
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 cornerRadii(float out[4]) const;
|
||||||
|
|
||||||
virtual void updatePhysics() {}
|
virtual void updatePhysics() {}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue