fix: sidebar bulge at connection with utilities
Basically make sidebar and utilities exclude each other from merging and manually animate corner radius Also format c++
This commit is contained in:
parent
0f5d2ddc16
commit
e8555061ab
12 changed files with 286 additions and 158 deletions
|
|
@ -166,6 +166,15 @@ Variants {
|
||||||
panel: panels.sidebar
|
panel: panels.sidebar
|
||||||
bar: bar
|
bar: bar
|
||||||
deformAmount: 0
|
deformAmount: 0
|
||||||
|
height: panel.height + 1
|
||||||
|
exclude: [utilsBg]
|
||||||
|
bottomLeftRadius: panels.sidebar.visible ? 0 : radius
|
||||||
|
|
||||||
|
Behavior on bottomLeftRadius {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelBg {
|
PanelBg {
|
||||||
|
|
@ -193,6 +202,14 @@ Variants {
|
||||||
blobGroup: blobGroup
|
blobGroup: blobGroup
|
||||||
panel: panels.utilities
|
panel: panels.utilities
|
||||||
bar: bar
|
bar: bar
|
||||||
|
exclude: [sidebarBg]
|
||||||
|
topLeftRadius: panels.sidebar.visible ? 0 : radius
|
||||||
|
|
||||||
|
Behavior on topLeftRadius {
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelBg {
|
PanelBg {
|
||||||
|
|
|
||||||
|
|
@ -74,20 +74,15 @@ void BlobGroup::markShapeDirty(BlobShape* source) {
|
||||||
|
|
||||||
// Use cached padded rects to find spatial neighbors
|
// Use cached padded rects to find spatial neighbors
|
||||||
const float pad = static_cast<float>(m_smoothing) * 2.0f;
|
const float pad = static_cast<float>(m_smoothing) * 2.0f;
|
||||||
const QRectF srcRect(
|
const QRectF srcRect(static_cast<double>(source->m_cachedPaddedX - pad),
|
||||||
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_cachedPaddedY - pad),
|
|
||||||
static_cast<double>(source->m_cachedPaddedW + pad * 2.0f),
|
|
||||||
static_cast<double>(source->m_cachedPaddedH + pad * 2.0f));
|
static_cast<double>(source->m_cachedPaddedH + pad * 2.0f));
|
||||||
|
|
||||||
for (auto* shape : std::as_const(m_shapes)) {
|
for (auto* shape : std::as_const(m_shapes)) {
|
||||||
if (shape == source)
|
if (shape == source)
|
||||||
continue;
|
continue;
|
||||||
const QRectF otherRect(
|
const QRectF otherRect(static_cast<double>(shape->m_cachedPaddedX), static_cast<double>(shape->m_cachedPaddedY),
|
||||||
static_cast<double>(shape->m_cachedPaddedX),
|
static_cast<double>(shape->m_cachedPaddedW), static_cast<double>(shape->m_cachedPaddedH));
|
||||||
static_cast<double>(shape->m_cachedPaddedY),
|
|
||||||
static_cast<double>(shape->m_cachedPaddedW),
|
|
||||||
static_cast<double>(shape->m_cachedPaddedH));
|
|
||||||
if (srcRect.intersects(otherRect)) {
|
if (srcRect.intersects(otherRect)) {
|
||||||
shape->polish();
|
shape->polish();
|
||||||
shape->update();
|
shape->update();
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,7 @@ class BlobInvertedRect;
|
||||||
class BlobGroup : public QObject {
|
class BlobGroup : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY
|
Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged)
|
||||||
smoothingChanged)
|
|
||||||
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -13,21 +13,36 @@ BlobInvertedRect::BlobInvertedRect(QQuickItem* parent)
|
||||||
|
|
||||||
static void setFrameIndices(quint16* idx) {
|
static void setFrameIndices(quint16* idx) {
|
||||||
// Top strip: 0-1-4, 1-5-4
|
// Top strip: 0-1-4, 1-5-4
|
||||||
idx[0] = 0; idx[1] = 1; idx[2] = 4;
|
idx[0] = 0;
|
||||||
idx[3] = 1; idx[4] = 5; idx[5] = 4;
|
idx[1] = 1;
|
||||||
|
idx[2] = 4;
|
||||||
|
idx[3] = 1;
|
||||||
|
idx[4] = 5;
|
||||||
|
idx[5] = 4;
|
||||||
// Right strip: 1-2-5, 2-6-5
|
// Right strip: 1-2-5, 2-6-5
|
||||||
idx[6] = 1; idx[7] = 2; idx[8] = 5;
|
idx[6] = 1;
|
||||||
idx[9] = 2; idx[10] = 6; idx[11] = 5;
|
idx[7] = 2;
|
||||||
|
idx[8] = 5;
|
||||||
|
idx[9] = 2;
|
||||||
|
idx[10] = 6;
|
||||||
|
idx[11] = 5;
|
||||||
// Bottom strip: 2-3-6, 3-7-6
|
// Bottom strip: 2-3-6, 3-7-6
|
||||||
idx[12] = 2; idx[13] = 3; idx[14] = 6;
|
idx[12] = 2;
|
||||||
idx[15] = 3; idx[16] = 7; idx[17] = 6;
|
idx[13] = 3;
|
||||||
|
idx[14] = 6;
|
||||||
|
idx[15] = 3;
|
||||||
|
idx[16] = 7;
|
||||||
|
idx[17] = 6;
|
||||||
// Left strip: 3-0-7, 0-4-7
|
// Left strip: 3-0-7, 0-4-7
|
||||||
idx[18] = 3; idx[19] = 0; idx[20] = 7;
|
idx[18] = 3;
|
||||||
idx[21] = 0; idx[22] = 4; idx[23] = 7;
|
idx[19] = 0;
|
||||||
|
idx[20] = 7;
|
||||||
|
idx[21] = 0;
|
||||||
|
idx[22] = 4;
|
||||||
|
idx[23] = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSGNode* BlobInvertedRect::updatePaintNode(
|
QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
|
||||||
QSGNode* oldNode, UpdatePaintNodeData*) {
|
|
||||||
if (!m_group) {
|
if (!m_group) {
|
||||||
delete oldNode;
|
delete oldNode;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -55,9 +70,8 @@ QSGNode* BlobInvertedRect::updatePaintNode(
|
||||||
delete oldNode;
|
delete oldNode;
|
||||||
node = new QSGGeometryNode;
|
node = new QSGGeometryNode;
|
||||||
|
|
||||||
auto* geometry = new QSGGeometry(
|
auto* geometry =
|
||||||
QSGGeometry::defaultAttributes_TexturedPoint2D(), 8, 24,
|
new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 8, 24, QSGGeometry::UnsignedShortType);
|
||||||
QSGGeometry::UnsignedShortType);
|
|
||||||
geometry->setDrawingMode(QSGGeometry::DrawTriangles);
|
geometry->setDrawingMode(QSGGeometry::DrawTriangles);
|
||||||
node->setGeometry(geometry);
|
node->setGeometry(geometry);
|
||||||
node->setFlag(QSGNode::OwnsGeometry);
|
node->setFlag(QSGNode::OwnsGeometry);
|
||||||
|
|
@ -105,13 +119,10 @@ QSGNode* BlobInvertedRect::updatePaintNode(
|
||||||
material->m_color = m_group->color();
|
material->m_color = m_group->color();
|
||||||
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
|
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
|
||||||
material->m_invertedRadius = m_cachedInvertedRadius;
|
material->m_invertedRadius = m_cachedInvertedRadius;
|
||||||
memcpy(material->m_invertedOuter, m_cachedInvertedOuter,
|
memcpy(material->m_invertedOuter, m_cachedInvertedOuter, sizeof(m_cachedInvertedOuter));
|
||||||
sizeof(m_cachedInvertedOuter));
|
memcpy(material->m_invertedInner, m_cachedInvertedInner, sizeof(m_cachedInvertedInner));
|
||||||
memcpy(material->m_invertedInner, m_cachedInvertedInner,
|
|
||||||
sizeof(m_cachedInvertedInner));
|
|
||||||
|
|
||||||
const int count =
|
const int count = static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
|
||||||
static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
|
|
||||||
material->m_rectCount = count;
|
material->m_rectCount = count;
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
material->m_rects[i] = m_cachedRects[i];
|
material->m_rects[i] = m_cachedRects[i];
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,10 @@
|
||||||
class BlobInvertedRect : public BlobShape {
|
class BlobInvertedRect : public BlobShape {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal borderLeft READ borderLeft WRITE setBorderLeft NOTIFY
|
Q_PROPERTY(qreal borderLeft READ borderLeft WRITE setBorderLeft NOTIFY borderLeftChanged)
|
||||||
borderLeftChanged)
|
Q_PROPERTY(qreal borderRight READ borderRight WRITE setBorderRight NOTIFY borderRightChanged)
|
||||||
Q_PROPERTY(qreal borderRight READ borderRight WRITE setBorderRight NOTIFY
|
Q_PROPERTY(qreal borderTop READ borderTop WRITE setBorderTop NOTIFY borderTopChanged)
|
||||||
borderRightChanged)
|
Q_PROPERTY(qreal borderBottom READ borderBottom WRITE setBorderBottom NOTIFY borderBottomChanged)
|
||||||
Q_PROPERTY(qreal borderTop READ borderTop WRITE setBorderTop NOTIFY
|
|
||||||
borderTopChanged)
|
|
||||||
Q_PROPERTY(qreal borderBottom READ borderBottom WRITE setBorderBottom NOTIFY
|
|
||||||
borderBottomChanged)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BlobInvertedRect(QQuickItem* parent = nullptr);
|
explicit BlobInvertedRect(QQuickItem* parent = nullptr);
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ QSGMaterialType* BlobMaterial::type() const {
|
||||||
return &s_type;
|
return &s_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSGMaterialShader* BlobMaterial::createShader(
|
QSGMaterialShader* BlobMaterial::createShader(QSGRendererInterface::RenderMode) const {
|
||||||
QSGRendererInterface::RenderMode) const {
|
|
||||||
return new BlobMaterialShader;
|
return new BlobMaterialShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,8 +24,7 @@ BlobMaterialShader::BlobMaterialShader() {
|
||||||
setShaderFileName(FragmentStage, QStringLiteral(":/shaders/blob.frag.qsb"));
|
setShaderFileName(FragmentStage, QStringLiteral(":/shaders/blob.frag.qsb"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlobMaterialShader::updateUniformData(
|
bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) {
|
||||||
RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) {
|
|
||||||
Q_UNUSED(oldMaterial);
|
Q_UNUSED(oldMaterial);
|
||||||
auto* mat = static_cast<BlobMaterial*>(newMaterial);
|
auto* mat = static_cast<BlobMaterial*>(newMaterial);
|
||||||
QByteArray* buf = state.uniformData();
|
QByteArray* buf = state.uniformData();
|
||||||
|
|
@ -85,13 +83,13 @@ bool BlobMaterialShader::updateUniformData(
|
||||||
const auto& r = mat->m_rects[i];
|
const auto& r = mat->m_rects[i];
|
||||||
const int base = 160 + i * 80;
|
const int base = 160 + i * 80;
|
||||||
const float d0[4] = { r.cx, r.cy, r.hw, r.hh };
|
const float d0[4] = { r.cx, r.cy, r.hw, r.hh };
|
||||||
const float d1[4] = { r.radius, r.offsetX, r.offsetY, r.minEig };
|
const float d1[4] = { 0.0f, r.offsetX, r.offsetY, r.minEig };
|
||||||
const float d3[4] = { r.screenHalfX, r.screenHalfY, 0.0f, 0.0f };
|
const float d3[4] = { r.screenHalfX, r.screenHalfY, 0.0f, 0.0f };
|
||||||
memcpy(buf->data() + base, d0, 16);
|
memcpy(buf->data() + base, d0, 16);
|
||||||
memcpy(buf->data() + base + 16, d1, 16);
|
memcpy(buf->data() + base + 16, d1, 16);
|
||||||
memcpy(buf->data() + base + 32, r.invDeform, 16);
|
memcpy(buf->data() + base + 32, r.invDeform, 16);
|
||||||
memcpy(buf->data() + base + 48, d3, 16);
|
memcpy(buf->data() + base + 48, d3, 16);
|
||||||
memcpy(buf->data() + base + 64, r.cornerFill, 16);
|
memcpy(buf->data() + base + 64, r.radius, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -6,22 +6,20 @@
|
||||||
|
|
||||||
struct BlobRectData {
|
struct BlobRectData {
|
||||||
float cx = 0, cy = 0, hw = 0, hh = 0;
|
float cx = 0, cy = 0, hw = 0, hh = 0;
|
||||||
float radius = 0;
|
|
||||||
float offsetX = 0, offsetY = 0;
|
float offsetX = 0, offsetY = 0;
|
||||||
float minEig = 1.0f;
|
float minEig = 1.0f;
|
||||||
// Inverse of 2x2 deformation matrix, column-major for GLSL
|
// Inverse of 2x2 deformation matrix, column-major for GLSL
|
||||||
float invDeform[4] = { 1, 0, 0, 1 };
|
float invDeform[4] = { 1, 0, 0, 1 };
|
||||||
// Screen-space AABB half-extents of the deformed rect
|
// Screen-space AABB half-extents of the deformed rect
|
||||||
float screenHalfX = 0, screenHalfY = 0;
|
float screenHalfX = 0, screenHalfY = 0;
|
||||||
// Pre-computed corner fill factors (tr, br, bl, tl)
|
// Effective per-corner radii (tr, br, bl, tl), pre-computed on CPU
|
||||||
float cornerFill[4] = { 1, 1, 1, 1 };
|
float radius[4] = { 0, 0, 0, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class BlobMaterial : public QSGMaterial {
|
class BlobMaterial : public QSGMaterial {
|
||||||
public:
|
public:
|
||||||
QSGMaterialType* type() const override;
|
QSGMaterialType* type() const override;
|
||||||
QSGMaterialShader* createShader(
|
QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override;
|
||||||
QSGRendererInterface::RenderMode) const override;
|
|
||||||
int compare(const QSGMaterial* other) const override;
|
int compare(const QSGMaterial* other) const override;
|
||||||
|
|
||||||
float m_paddedX = 0;
|
float m_paddedX = 0;
|
||||||
|
|
@ -42,6 +40,5 @@ public:
|
||||||
class BlobMaterialShader : public QSGMaterialShader {
|
class BlobMaterialShader : public QSGMaterialShader {
|
||||||
public:
|
public:
|
||||||
BlobMaterialShader();
|
BlobMaterialShader();
|
||||||
bool updateUniformData(RenderState& state, QSGMaterial* newMaterial,
|
bool updateUniformData(RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override;
|
||||||
QSGMaterial* oldMaterial) override;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,8 @@ void BlobRect::updatePolish() {
|
||||||
|
|
||||||
if (m_physicsActive) {
|
if (m_physicsActive) {
|
||||||
// Check if deformation is visually imperceptible
|
// Check if deformation is visually imperceptible
|
||||||
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01)
|
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01) + std::abs(m_dm11 - 1.0f);
|
||||||
+ std::abs(m_dm11 - 1.0f);
|
float totalVel = std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
|
||||||
float totalVel =
|
|
||||||
std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
|
|
||||||
|
|
||||||
if (totalDelta < 0.004f && totalVel < 0.05f) {
|
if (totalDelta < 0.004f && totalVel < 0.05f) {
|
||||||
// Snap to rest, no visible deformation
|
// Snap to rest, no visible deformation
|
||||||
|
|
@ -62,10 +60,8 @@ void BlobRect::updatePhysics() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float velX =
|
const float velX = static_cast<float>(scenePos.x() - m_prevScenePos.x()) / dt;
|
||||||
static_cast<float>(scenePos.x() - m_prevScenePos.x()) / dt;
|
const float velY = static_cast<float>(scenePos.y() - m_prevScenePos.y()) / dt;
|
||||||
const float velY =
|
|
||||||
static_cast<float>(scenePos.y() - m_prevScenePos.y()) / dt;
|
|
||||||
m_prevScenePos = scenePos;
|
m_prevScenePos = scenePos;
|
||||||
|
|
||||||
const float speed = std::sqrt(velX * velX + velY * velY);
|
const float speed = std::sqrt(velX * velX + velY * velY);
|
||||||
|
|
@ -86,8 +82,7 @@ void BlobRect::updatePhysics() {
|
||||||
float target11 = 1.0f;
|
float target11 = 1.0f;
|
||||||
|
|
||||||
if (speed > 5.0f) {
|
if (speed > 5.0f) {
|
||||||
const float targetStretch =
|
const float targetStretch = 1.0f + std::min(speed * kStretchFactor, kMaxStretch);
|
||||||
1.0f + std::min(speed * kStretchFactor, kMaxStretch);
|
|
||||||
const float targetCompress = 1.0f / targetStretch;
|
const float targetCompress = 1.0f / targetStretch;
|
||||||
|
|
||||||
const float cosA = velX / speed;
|
const float cosA = velX / speed;
|
||||||
|
|
@ -105,35 +100,132 @@ void BlobRect::updatePhysics() {
|
||||||
const float kStiffness = static_cast<float>(m_stiffness);
|
const float kStiffness = static_cast<float>(m_stiffness);
|
||||||
const float kDamping = static_cast<float>(m_damping);
|
const float kDamping = static_cast<float>(m_damping);
|
||||||
|
|
||||||
const float accel00 =
|
const float accel00 = -kStiffness * (m_dm00 - target00) - kDamping * m_dmVel00;
|
||||||
-kStiffness * (m_dm00 - target00) - kDamping * m_dmVel00;
|
|
||||||
m_dmVel00 += accel00 * dt;
|
m_dmVel00 += accel00 * dt;
|
||||||
m_dm00 += m_dmVel00 * dt;
|
m_dm00 += m_dmVel00 * dt;
|
||||||
|
|
||||||
const float accel01 =
|
const float accel01 = -kStiffness * (m_dm01 - target01) - kDamping * m_dmVel01;
|
||||||
-kStiffness * (m_dm01 - target01) - kDamping * m_dmVel01;
|
|
||||||
m_dmVel01 += accel01 * dt;
|
m_dmVel01 += accel01 * dt;
|
||||||
m_dm01 += m_dmVel01 * dt;
|
m_dm01 += m_dmVel01 * dt;
|
||||||
|
|
||||||
const float accel11 =
|
const float accel11 = -kStiffness * (m_dm11 - target11) - kDamping * m_dmVel11;
|
||||||
-kStiffness * (m_dm11 - target11) - kDamping * m_dmVel11;
|
|
||||||
m_dmVel11 += accel11 * dt;
|
m_dmVel11 += accel11 * dt;
|
||||||
m_dm11 += m_dmVel11 * dt;
|
m_dm11 += m_dmVel11 * dt;
|
||||||
|
|
||||||
m_deformMatrix = QMatrix4x4(
|
m_deformMatrix = QMatrix4x4(m_dm00, m_dm01, 0, 0, m_dm01, m_dm11, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||||
m_dm00, m_dm01, 0, 0, m_dm01, m_dm11, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
|
||||||
updateCenteredDeformMatrix();
|
updateCenteredDeformMatrix();
|
||||||
|
|
||||||
checkAtRest(speed);
|
checkAtRest(speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlobRect::setTopLeftRadius(qreal r) {
|
||||||
|
if (!qFuzzyCompare(m_topLeftRadius, r)) {
|
||||||
|
m_topLeftRadius = r;
|
||||||
|
emit topLeftRadiusChanged();
|
||||||
|
if (m_group)
|
||||||
|
m_group->markDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobRect::setTopRightRadius(qreal r) {
|
||||||
|
if (!qFuzzyCompare(m_topRightRadius, r)) {
|
||||||
|
m_topRightRadius = r;
|
||||||
|
emit topRightRadiusChanged();
|
||||||
|
if (m_group)
|
||||||
|
m_group->markDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobRect::setBottomLeftRadius(qreal r) {
|
||||||
|
if (!qFuzzyCompare(m_bottomLeftRadius, r)) {
|
||||||
|
m_bottomLeftRadius = r;
|
||||||
|
emit bottomLeftRadiusChanged();
|
||||||
|
if (m_group)
|
||||||
|
m_group->markDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobRect::setBottomRightRadius(qreal r) {
|
||||||
|
if (!qFuzzyCompare(m_bottomRightRadius, r)) {
|
||||||
|
m_bottomRightRadius = r;
|
||||||
|
emit bottomRightRadiusChanged();
|
||||||
|
if (m_group)
|
||||||
|
m_group->markDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobRect::cornerRadii(float out[4]) const {
|
||||||
|
const auto base = static_cast<float>(m_radius);
|
||||||
|
out[0] = m_topRightRadius >= 0 ? static_cast<float>(m_topRightRadius) : base;
|
||||||
|
out[1] = m_bottomRightRadius >= 0 ? static_cast<float>(m_bottomRightRadius) : base;
|
||||||
|
out[2] = m_bottomLeftRadius >= 0 ? static_cast<float>(m_bottomLeftRadius) : base;
|
||||||
|
out[3] = m_topLeftRadius >= 0 ? static_cast<float>(m_topLeftRadius) : base;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlobRect::isExcluded(const BlobShape* other) const {
|
||||||
|
for (const auto& ptr : m_exclude) {
|
||||||
|
if (ptr == other)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QQmlListProperty<BlobRect> BlobRect::exclude() {
|
||||||
|
return QQmlListProperty<BlobRect>(
|
||||||
|
this, nullptr, &excludeAppend, &excludeCount, &excludeAt, &excludeClear, &excludeReplace, &excludeRemoveLast);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
||||||
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
|
self->m_exclude.append(rect);
|
||||||
|
if (self->m_group)
|
||||||
|
self->m_group->markDirty();
|
||||||
|
emit self->excludeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
qsizetype BlobRect::excludeCount(QQmlListProperty<BlobRect>* prop) {
|
||||||
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
|
return self->m_exclude.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
BlobRect* BlobRect::excludeAt(QQmlListProperty<BlobRect>* prop, qsizetype index) {
|
||||||
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
|
return self->m_exclude.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobRect::excludeClear(QQmlListProperty<BlobRect>* prop) {
|
||||||
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
|
if (self->m_exclude.isEmpty())
|
||||||
|
return;
|
||||||
|
self->m_exclude.clear();
|
||||||
|
if (self->m_group)
|
||||||
|
self->m_group->markDirty();
|
||||||
|
emit self->excludeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobRect::excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect) {
|
||||||
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
|
self->m_exclude[index] = rect;
|
||||||
|
if (self->m_group)
|
||||||
|
self->m_group->markDirty();
|
||||||
|
emit self->excludeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobRect::excludeRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
||||||
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
|
if (self->m_exclude.isEmpty())
|
||||||
|
return;
|
||||||
|
self->m_exclude.removeLast();
|
||||||
|
if (self->m_group)
|
||||||
|
self->m_group->markDirty();
|
||||||
|
emit self->excludeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void BlobRect::checkAtRest(float speed) {
|
void BlobRect::checkAtRest(float speed) {
|
||||||
constexpr float kEpsilon = 0.002f;
|
constexpr float kEpsilon = 0.002f;
|
||||||
const bool atRest =
|
const bool atRest = std::abs(m_dm00 - 1.0f) < kEpsilon && std::abs(m_dm01) < kEpsilon &&
|
||||||
std::abs(m_dm00 - 1.0f) < kEpsilon && std::abs(m_dm01) < kEpsilon &&
|
std::abs(m_dm11 - 1.0f) < kEpsilon && std::abs(m_dmVel00) < kEpsilon &&
|
||||||
std::abs(m_dm11 - 1.0f) < kEpsilon && std::abs(m_dmVel00) < kEpsilon &&
|
std::abs(m_dmVel01) < kEpsilon && std::abs(m_dmVel11) < kEpsilon && speed < 5.0f;
|
||||||
std::abs(m_dmVel01) < kEpsilon && std::abs(m_dmVel11) < kEpsilon &&
|
|
||||||
speed < 5.0f;
|
|
||||||
|
|
||||||
if (atRest) {
|
if (atRest) {
|
||||||
m_dm00 = 1.0f;
|
m_dm00 = 1.0f;
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,22 @@
|
||||||
#include "blobshape.hpp"
|
#include "blobshape.hpp"
|
||||||
|
|
||||||
#include <qelapsedtimer.h>
|
#include <qelapsedtimer.h>
|
||||||
|
#include <qpointer.h>
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
|
#include <qqmllist.h>
|
||||||
|
|
||||||
class BlobRect : public BlobShape {
|
class BlobRect : public BlobShape {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal stiffness READ stiffness WRITE setStiffness NOTIFY
|
Q_PROPERTY(qreal stiffness READ stiffness WRITE setStiffness NOTIFY stiffnessChanged)
|
||||||
stiffnessChanged)
|
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
|
||||||
|
Q_PROPERTY(qreal deformScale READ deformScale WRITE setDeformScale NOTIFY deformScaleChanged)
|
||||||
|
Q_PROPERTY(QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
|
||||||
|
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)
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
|
qreal bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius NOTIFY bottomRightRadiusChanged)
|
||||||
Q_PROPERTY(qreal deformScale READ deformScale WRITE setDeformScale NOTIFY
|
|
||||||
deformScaleChanged)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BlobRect(QQuickItem* parent = nullptr);
|
explicit BlobRect(QQuickItem* parent = nullptr);
|
||||||
|
|
@ -46,10 +51,36 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QQmlListProperty<BlobRect> exclude();
|
||||||
|
|
||||||
|
bool isExcluded(const BlobShape* other) const override;
|
||||||
|
void cornerRadii(float out[4]) const override;
|
||||||
|
|
||||||
|
qreal topLeftRadius() const { return m_topLeftRadius; }
|
||||||
|
|
||||||
|
void setTopLeftRadius(qreal r);
|
||||||
|
|
||||||
|
qreal topRightRadius() const { return m_topRightRadius; }
|
||||||
|
|
||||||
|
void setTopRightRadius(qreal r);
|
||||||
|
|
||||||
|
qreal bottomLeftRadius() const { return m_bottomLeftRadius; }
|
||||||
|
|
||||||
|
void setBottomLeftRadius(qreal r);
|
||||||
|
|
||||||
|
qreal bottomRightRadius() const { return m_bottomRightRadius; }
|
||||||
|
|
||||||
|
void setBottomRightRadius(qreal r);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void stiffnessChanged();
|
void stiffnessChanged();
|
||||||
void dampingChanged();
|
void dampingChanged();
|
||||||
void deformScaleChanged();
|
void deformScaleChanged();
|
||||||
|
void excludeChanged();
|
||||||
|
void topLeftRadiusChanged();
|
||||||
|
void topRightRadiusChanged();
|
||||||
|
void bottomLeftRadiusChanged();
|
||||||
|
void bottomRightRadiusChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updatePolish() override;
|
void updatePolish() override;
|
||||||
|
|
@ -78,4 +109,18 @@ private:
|
||||||
qreal m_stiffness = 200.0;
|
qreal m_stiffness = 200.0;
|
||||||
qreal m_damping = 16.0;
|
qreal m_damping = 16.0;
|
||||||
qreal m_deformScale = 0.0005;
|
qreal m_deformScale = 0.0005;
|
||||||
|
|
||||||
|
qreal m_topLeftRadius = -1;
|
||||||
|
qreal m_topRightRadius = -1;
|
||||||
|
qreal m_bottomLeftRadius = -1;
|
||||||
|
qreal m_bottomRightRadius = -1;
|
||||||
|
|
||||||
|
QList<QPointer<BlobRect>> m_exclude;
|
||||||
|
|
||||||
|
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
||||||
|
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
|
||||||
|
static BlobRect* excludeAt(QQmlListProperty<BlobRect>* prop, qsizetype index);
|
||||||
|
static void excludeClear(QQmlListProperty<BlobRect>* prop);
|
||||||
|
static void excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
||||||
|
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,7 @@ static float deformPadding(const QMatrix4x4& dm, float hw, float hh) {
|
||||||
return std::max(extraX, extraY);
|
return std::max(extraX, extraY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static float cpuSdBox(float px, float py, float cx, float cy, float hw,
|
static float cpuSdBox(float px, float py, float cx, float cy, float hw, float hh) {
|
||||||
float hh) {
|
|
||||||
const float dx = std::abs(px - cx) - hw;
|
const float dx = std::abs(px - cx) - hw;
|
||||||
const float dy = std::abs(py - cy) - hh;
|
const float dy = std::abs(py - cy) - hh;
|
||||||
const float mdx = std::max(dx, 0.0f);
|
const float mdx = std::max(dx, 0.0f);
|
||||||
|
|
@ -66,8 +65,7 @@ void BlobShape::componentComplete() {
|
||||||
registerWithGroup();
|
registerWithGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobShape::geometryChange(
|
void BlobShape::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) {
|
||||||
const QRectF& newGeometry, const QRectF& oldGeometry) {
|
|
||||||
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
||||||
updateCenteredDeformMatrix();
|
updateCenteredDeformMatrix();
|
||||||
if (m_group) {
|
if (m_group) {
|
||||||
|
|
@ -92,6 +90,14 @@ void BlobShape::updateCenteredDeformMatrix() {
|
||||||
emit deformMatrixChanged();
|
emit deformMatrixChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlobShape::cornerRadii(float out[4]) const {
|
||||||
|
const auto r = static_cast<float>(m_radius);
|
||||||
|
out[0] = r;
|
||||||
|
out[1] = r;
|
||||||
|
out[2] = r;
|
||||||
|
out[3] = r;
|
||||||
|
}
|
||||||
|
|
||||||
void BlobShape::registerWithGroup() {
|
void BlobShape::registerWithGroup() {
|
||||||
if (m_group)
|
if (m_group)
|
||||||
m_group->addShape(this);
|
m_group->addShape(this);
|
||||||
|
|
@ -127,19 +133,15 @@ void BlobShape::updatePolish() {
|
||||||
m_cachedPaddedY = static_cast<float>(scenePos.y()) - totalPad;
|
m_cachedPaddedY = static_cast<float>(scenePos.y()) - totalPad;
|
||||||
m_cachedPaddedW = static_cast<float>(width()) + 2.0f * totalPad;
|
m_cachedPaddedW = static_cast<float>(width()) + 2.0f * totalPad;
|
||||||
m_cachedPaddedH = static_cast<float>(height()) + 2.0f * totalPad;
|
m_cachedPaddedH = static_cast<float>(height()) + 2.0f * totalPad;
|
||||||
m_localPaddedRect = QRectF(static_cast<double>(-totalPad),
|
m_localPaddedRect = QRectF(static_cast<double>(-totalPad), static_cast<double>(-totalPad),
|
||||||
static_cast<double>(-totalPad),
|
width() + 2.0 * static_cast<double>(totalPad), height() + 2.0 * static_cast<double>(totalPad));
|
||||||
width() + 2.0 * static_cast<double>(totalPad),
|
|
||||||
height() + 2.0 * static_cast<double>(totalPad));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter nearby normal rects
|
// Filter nearby normal rects
|
||||||
m_cachedRects.clear();
|
m_cachedRects.clear();
|
||||||
m_cachedMyIndex = -2;
|
m_cachedMyIndex = -2;
|
||||||
const QRectF myPadded(static_cast<double>(m_cachedPaddedX),
|
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
|
||||||
static_cast<double>(m_cachedPaddedY),
|
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
|
||||||
static_cast<double>(m_cachedPaddedW),
|
|
||||||
static_cast<double>(m_cachedPaddedH));
|
|
||||||
|
|
||||||
for (BlobShape* other : m_group->shapes()) {
|
for (BlobShape* other : m_group->shapes()) {
|
||||||
if (other->isInvertedRect())
|
if (other->isInvertedRect())
|
||||||
|
|
@ -149,6 +151,9 @@ void BlobShape::updatePolish() {
|
||||||
if (other->width() <= 0 || other->height() <= 0)
|
if (other->width() <= 0 || other->height() <= 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (isExcluded(other))
|
||||||
|
continue;
|
||||||
|
|
||||||
const QPointF otherScene = other->mapToScene(QPointF(0, 0));
|
const QPointF otherScene = other->mapToScene(QPointF(0, 0));
|
||||||
|
|
||||||
bool include = false;
|
bool include = false;
|
||||||
|
|
@ -157,12 +162,9 @@ void BlobShape::updatePolish() {
|
||||||
} else {
|
} else {
|
||||||
const float otherHW = static_cast<float>(other->width()) * 0.5f;
|
const float otherHW = static_cast<float>(other->width()) * 0.5f;
|
||||||
const float otherHH = static_cast<float>(other->height()) * 0.5f;
|
const float otherHH = static_cast<float>(other->height()) * 0.5f;
|
||||||
const float otherPad =
|
const float otherPad = pad + deformPadding(other->m_deformMatrix, otherHW, otherHH);
|
||||||
pad + deformPadding(other->m_deformMatrix, otherHW, otherHH);
|
const QRectF otherPadded(otherScene.x() - static_cast<double>(otherPad),
|
||||||
const QRectF otherPadded(
|
otherScene.y() - static_cast<double>(otherPad), other->width() + 2.0 * static_cast<double>(otherPad),
|
||||||
otherScene.x() - static_cast<double>(otherPad),
|
|
||||||
otherScene.y() - static_cast<double>(otherPad),
|
|
||||||
other->width() + 2.0 * static_cast<double>(otherPad),
|
|
||||||
other->height() + 2.0 * static_cast<double>(otherPad));
|
other->height() + 2.0 * static_cast<double>(otherPad));
|
||||||
include = myPadded.intersects(otherPadded);
|
include = myPadded.intersects(otherPadded);
|
||||||
}
|
}
|
||||||
|
|
@ -180,14 +182,13 @@ void BlobShape::updatePolish() {
|
||||||
r.cy = static_cast<float>(otherScene.y() + other->height() / 2.0);
|
r.cy = static_cast<float>(otherScene.y() + other->height() / 2.0);
|
||||||
r.hw = static_cast<float>(other->width() / 2.0);
|
r.hw = static_cast<float>(other->width() / 2.0);
|
||||||
r.hh = static_cast<float>(other->height() / 2.0);
|
r.hh = static_cast<float>(other->height() / 2.0);
|
||||||
r.radius = static_cast<float>(other->radius());
|
other->cornerRadii(r.radius);
|
||||||
r.offsetX = dm(0, 3);
|
r.offsetX = dm(0, 3);
|
||||||
r.offsetY = dm(1, 3);
|
r.offsetY = dm(1, 3);
|
||||||
|
|
||||||
// Pre-compute inverse deformation matrix
|
// Pre-compute inverse deformation matrix
|
||||||
const float det = a * d - c * b;
|
const float det = a * d - c * b;
|
||||||
const float invDet =
|
const float invDet = std::abs(det) > 1e-6f ? 1.0f / det : 1.0f;
|
||||||
std::abs(det) > 1e-6f ? 1.0f / det : 1.0f;
|
|
||||||
r.invDeform[0] = d * invDet;
|
r.invDeform[0] = d * invDet;
|
||||||
r.invDeform[1] = -b * invDet;
|
r.invDeform[1] = -b * invDet;
|
||||||
r.invDeform[2] = -c * invDet;
|
r.invDeform[2] = -c * invDet;
|
||||||
|
|
@ -218,25 +219,15 @@ void BlobShape::updatePolish() {
|
||||||
auto* inv = m_group->invertedRect();
|
auto* inv = m_group->invertedRect();
|
||||||
if (inv) {
|
if (inv) {
|
||||||
const QPointF invScene = inv->mapToScene(QPointF(0, 0));
|
const QPointF invScene = inv->mapToScene(QPointF(0, 0));
|
||||||
const float outerCX =
|
const float outerCX = static_cast<float>(invScene.x() + inv->width() / 2.0);
|
||||||
static_cast<float>(invScene.x() + inv->width() / 2.0);
|
const float outerCY = static_cast<float>(invScene.y() + inv->height() / 2.0);
|
||||||
const float outerCY =
|
|
||||||
static_cast<float>(invScene.y() + inv->height() / 2.0);
|
|
||||||
const float outerHW = static_cast<float>(inv->width() / 2.0);
|
const float outerHW = static_cast<float>(inv->width() / 2.0);
|
||||||
const float outerHH = static_cast<float>(inv->height() / 2.0);
|
const float outerHH = static_cast<float>(inv->height() / 2.0);
|
||||||
|
|
||||||
const float innerCX =
|
const float innerCX = outerCX + static_cast<float>((inv->borderLeft() - inv->borderRight()) / 2.0);
|
||||||
outerCX +
|
const float innerCY = outerCY + static_cast<float>((inv->borderTop() - inv->borderBottom()) / 2.0);
|
||||||
static_cast<float>((inv->borderLeft() - inv->borderRight()) / 2.0);
|
const float innerHW = outerHW - static_cast<float>((inv->borderLeft() + inv->borderRight()) / 2.0);
|
||||||
const float innerCY =
|
const float innerHH = outerHH - static_cast<float>((inv->borderTop() + inv->borderBottom()) / 2.0);
|
||||||
outerCY +
|
|
||||||
static_cast<float>((inv->borderTop() - inv->borderBottom()) / 2.0);
|
|
||||||
const float innerHW =
|
|
||||||
outerHW -
|
|
||||||
static_cast<float>((inv->borderLeft() + inv->borderRight()) / 2.0);
|
|
||||||
const float innerHH =
|
|
||||||
outerHH -
|
|
||||||
static_cast<float>((inv->borderTop() + inv->borderBottom()) / 2.0);
|
|
||||||
|
|
||||||
// Check if this rect is near the border (within 2x smoothing of inner edge)
|
// Check if this rect is near the border (within 2x smoothing of inner edge)
|
||||||
bool nearBorder = isInvertedRect();
|
bool nearBorder = isInvertedRect();
|
||||||
|
|
@ -247,10 +238,8 @@ void BlobShape::updatePolish() {
|
||||||
const float myHW = m_cachedPaddedW * 0.5f;
|
const float myHW = m_cachedPaddedW * 0.5f;
|
||||||
const float myHH = m_cachedPaddedH * 0.5f;
|
const float myHH = m_cachedPaddedH * 0.5f;
|
||||||
// Near border if any edge of padded rect is within margin of inner edge
|
// Near border if any edge of padded rect is within margin of inner edge
|
||||||
nearBorder = (myCX - myHW < innerCX - innerHW + margin) ||
|
nearBorder = (myCX - myHW < innerCX - innerHW + margin) || (myCX + myHW > innerCX + innerHW - margin) ||
|
||||||
(myCX + myHW > innerCX + innerHW - margin) ||
|
(myCY - myHH < innerCY - innerHH + margin) || (myCY + myHH > innerCY + innerHH - margin);
|
||||||
(myCY - myHH < innerCY - innerHH + margin) ||
|
|
||||||
(myCY + myHH > innerCY + innerHH - margin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nearBorder) {
|
if (nearBorder) {
|
||||||
|
|
@ -269,8 +258,9 @@ void BlobShape::updatePolish() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-compute corner fill factors (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;
|
||||||
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];
|
||||||
|
|
@ -285,14 +275,10 @@ void BlobShape::updatePolish() {
|
||||||
if (j == i)
|
if (j == i)
|
||||||
continue;
|
continue;
|
||||||
const auto& rj = m_cachedRects[j];
|
const auto& rj = m_cachedRects[j];
|
||||||
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor,
|
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||||
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)));
|
||||||
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor,
|
fBl = std::min(fBl, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBlX, cBlY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||||
cpuSdBox(cBrX, cBrY, 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)));
|
||||||
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)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cachedHasInverted) {
|
if (m_cachedHasInverted) {
|
||||||
|
|
@ -300,20 +286,17 @@ void BlobShape::updatePolish() {
|
||||||
const float icy = m_cachedInvertedInner[1];
|
const float icy = m_cachedInvertedInner[1];
|
||||||
const float ihw = m_cachedInvertedInner[2];
|
const float ihw = m_cachedInvertedInner[2];
|
||||||
const float ihh = m_cachedInvertedInner[3];
|
const float ihh = m_cachedInvertedInner[3];
|
||||||
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor,
|
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cTrX, cTrY, icx, icy, ihw, ihh)));
|
||||||
-cpuSdBox(cTrX, cTrY, icx, icy, ihw, ihh)));
|
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cBrX, cBrY, icx, icy, ihw, ihh)));
|
||||||
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor,
|
fBl = std::min(fBl, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cBlX, cBlY, icx, icy, ihw, ihh)));
|
||||||
-cpuSdBox(cBrX, cBrY, icx, icy, ihw, ihh)));
|
fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cTlX, cTlY, icx, icy, ihw, ihh)));
|
||||||
fBl = std::min(fBl, cpuSmoothstep(0.0f, smoothFactor,
|
|
||||||
-cpuSdBox(cBlX, cBlY, icx, icy, ihw, ihh)));
|
|
||||||
fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor,
|
|
||||||
-cpuSdBox(cTlX, cTlY, icx, icy, ihw, ihh)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ri.cornerFill[0] = fTr;
|
// Combine base radii with fill factors into effective per-corner radii
|
||||||
ri.cornerFill[1] = fBr;
|
ri.radius[0] = std::max(ri.radius[0] * fTr, minR);
|
||||||
ri.cornerFill[2] = fBl;
|
ri.radius[1] = std::max(ri.radius[1] * fBr, minR);
|
||||||
ri.cornerFill[3] = fTl;
|
ri.radius[2] = std::max(ri.radius[2] * fBl, minR);
|
||||||
|
ri.radius[3] = std::max(ri.radius[3] * fTl, minR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -327,8 +310,7 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
node = new QSGGeometryNode;
|
node = new QSGGeometryNode;
|
||||||
|
|
||||||
auto* geometry = new QSGGeometry(
|
auto* geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
|
||||||
QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
|
|
||||||
geometry->setDrawingMode(QSGGeometry::DrawTriangleStrip);
|
geometry->setDrawingMode(QSGGeometry::DrawTriangleStrip);
|
||||||
node->setGeometry(geometry);
|
node->setGeometry(geometry);
|
||||||
node->setFlag(QSGNode::OwnsGeometry);
|
node->setFlag(QSGNode::OwnsGeometry);
|
||||||
|
|
@ -366,13 +348,10 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
|
||||||
material->m_color = m_group->color();
|
material->m_color = m_group->color();
|
||||||
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
|
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
|
||||||
material->m_invertedRadius = m_cachedInvertedRadius;
|
material->m_invertedRadius = m_cachedInvertedRadius;
|
||||||
memcpy(material->m_invertedOuter, m_cachedInvertedOuter,
|
memcpy(material->m_invertedOuter, m_cachedInvertedOuter, sizeof(m_cachedInvertedOuter));
|
||||||
sizeof(m_cachedInvertedOuter));
|
memcpy(material->m_invertedInner, m_cachedInvertedInner, sizeof(m_cachedInvertedInner));
|
||||||
memcpy(material->m_invertedInner, m_cachedInvertedInner,
|
|
||||||
sizeof(m_cachedInvertedInner));
|
|
||||||
|
|
||||||
const int count =
|
const int count = static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
|
||||||
static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
|
|
||||||
material->m_rectCount = count;
|
material->m_rectCount = count;
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
material->m_rects[i] = m_cachedRects[i];
|
material->m_rects[i] = m_cachedRects[i];
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@ class BlobShape : public QQuickItem {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(BlobGroup* group READ group WRITE setGroup NOTIFY groupChanged)
|
Q_PROPERTY(BlobGroup* group READ group WRITE setGroup NOTIFY groupChanged)
|
||||||
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
|
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(QMatrix4x4 deformMatrix READ deformMatrix NOTIFY deformMatrixChanged)
|
||||||
QMatrix4x4 deformMatrix READ deformMatrix NOTIFY deformMatrixChanged)
|
|
||||||
|
|
||||||
friend class BlobGroup;
|
friend class BlobGroup;
|
||||||
|
|
||||||
|
|
@ -38,13 +37,16 @@ signals:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void componentComplete() override;
|
void componentComplete() override;
|
||||||
void geometryChange(
|
void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override;
|
||||||
const QRectF& newGeometry, const QRectF& oldGeometry) override;
|
|
||||||
void updatePolish() override;
|
void updatePolish() override;
|
||||||
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
|
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
|
||||||
|
|
||||||
virtual bool isInvertedRect() const { return false; }
|
virtual bool isInvertedRect() const { return false; }
|
||||||
|
|
||||||
|
virtual bool isExcluded(const BlobShape* /*other*/) const { return false; }
|
||||||
|
|
||||||
|
virtual void cornerRadii(float out[4]) const;
|
||||||
|
|
||||||
virtual void updatePhysics() {}
|
virtual void updatePhysics() {}
|
||||||
|
|
||||||
virtual void registerWithGroup();
|
virtual void registerWithGroup();
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ void main() {
|
||||||
vec4 props = rectData[i * 5 + 1]; // radius, offsetX, offsetY, minEig
|
vec4 props = rectData[i * 5 + 1]; // radius, offsetX, offsetY, minEig
|
||||||
vec4 invDm = rectData[i * 5 + 2]; // inverse deform matrix
|
vec4 invDm = rectData[i * 5 + 2]; // inverse deform matrix
|
||||||
vec4 sh = rectData[i * 5 + 3]; // screenHalfX, screenHalfY, 0, 0
|
vec4 sh = rectData[i * 5 + 3]; // screenHalfX, screenHalfY, 0, 0
|
||||||
vec4 fills = rectData[i * 5 + 4]; // f_tr, f_br, f_bl, f_tl
|
vec4 radii = rectData[i * 5 + 4]; // effective per-corner radii (tr, br, bl, tl)
|
||||||
|
|
||||||
// Offset center for asymmetric deformation
|
// Offset center for asymmetric deformation
|
||||||
vec2 center = rect.xy + props.yz;
|
vec2 center = rect.xy + props.yz;
|
||||||
|
|
@ -94,10 +94,7 @@ void main() {
|
||||||
mat2 invDeform = mat2(invDm.xy, invDm.zw);
|
mat2 invDeform = mat2(invDm.xy, invDm.zw);
|
||||||
vec2 transformedPixel = center + invDeform * (pixel - center);
|
vec2 transformedPixel = center + invDeform * (pixel - center);
|
||||||
|
|
||||||
// Use pre-computed corner fill factors
|
// Use pre-computed effective per-corner radii
|
||||||
float br = props.x;
|
|
||||||
float minR = 2.0;
|
|
||||||
vec4 radii = max(br * fills, vec4(minR));
|
|
||||||
float d = sdRoundedBox4(transformedPixel, center, rect.zw, radii);
|
float d = sdRoundedBox4(transformedPixel, center, rect.zw, radii);
|
||||||
|
|
||||||
// Use pre-computed minimum eigenvalue for SDF correction
|
// Use pre-computed minimum eigenvalue for SDF correction
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue