186 lines
5.0 KiB
C++
186 lines
5.0 KiB
C++
#include "blobinvertedrect.hpp"
|
|
#include "blobgroup.hpp"
|
|
#include "blobmaterial.hpp"
|
|
|
|
#include <qsggeometry.h>
|
|
#include <qsgnode.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
BlobInvertedRect::BlobInvertedRect(QQuickItem* parent)
|
|
: BlobShape(parent) {
|
|
}
|
|
|
|
static void setFrameIndices(quint16* idx) {
|
|
// Top strip: 0-1-4, 1-5-4
|
|
idx[0] = 0;
|
|
idx[1] = 1;
|
|
idx[2] = 4;
|
|
idx[3] = 1;
|
|
idx[4] = 5;
|
|
idx[5] = 4;
|
|
// Right strip: 1-2-5, 2-6-5
|
|
idx[6] = 1;
|
|
idx[7] = 2;
|
|
idx[8] = 5;
|
|
idx[9] = 2;
|
|
idx[10] = 6;
|
|
idx[11] = 5;
|
|
// Bottom strip: 2-3-6, 3-7-6
|
|
idx[12] = 2;
|
|
idx[13] = 3;
|
|
idx[14] = 6;
|
|
idx[15] = 3;
|
|
idx[16] = 7;
|
|
idx[17] = 6;
|
|
// Left strip: 3-0-7, 0-4-7
|
|
idx[18] = 3;
|
|
idx[19] = 0;
|
|
idx[20] = 7;
|
|
idx[21] = 0;
|
|
idx[22] = 4;
|
|
idx[23] = 7;
|
|
}
|
|
|
|
QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
|
|
if (!m_group) {
|
|
delete oldNode;
|
|
return nullptr;
|
|
}
|
|
|
|
const float pad = static_cast<float>(m_group->smoothing());
|
|
|
|
// Compute inner hole boundary in local coords
|
|
// Inset past the inner border edge by 2x smoothing to cover the blend zone
|
|
const float inset = pad * 2.0f;
|
|
const float holeLeft = static_cast<float>(m_borderLeft) + inset;
|
|
const float holeTop = static_cast<float>(m_borderTop) + inset;
|
|
const float holeRight = static_cast<float>(width() - m_borderRight) - inset;
|
|
const float holeBot = static_cast<float>(height() - m_borderBottom) - inset;
|
|
|
|
// If the hole is too small or invalid, fall back to full quad
|
|
if (holeLeft >= holeRight || holeTop >= holeBot)
|
|
return BlobShape::updatePaintNode(oldNode, nullptr);
|
|
|
|
auto* node = static_cast<QSGGeometryNode*>(oldNode);
|
|
|
|
const bool needsRebuild = !node || node->geometry()->vertexCount() != 8;
|
|
|
|
if (needsRebuild) {
|
|
delete oldNode;
|
|
node = new QSGGeometryNode;
|
|
|
|
auto* geometry =
|
|
new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 8, 24, QSGGeometry::UnsignedShortType);
|
|
geometry->setDrawingMode(QSGGeometry::DrawTriangles);
|
|
node->setGeometry(geometry);
|
|
node->setFlag(QSGNode::OwnsGeometry);
|
|
|
|
setFrameIndices(geometry->indexDataAsUShort());
|
|
|
|
auto* material = new BlobMaterial;
|
|
material->setFlag(QSGMaterial::Blending);
|
|
node->setMaterial(material);
|
|
node->setFlag(QSGNode::OwnsMaterial);
|
|
}
|
|
|
|
// Outer bounds (local coords)
|
|
const float x0 = static_cast<float>(m_localPaddedRect.x());
|
|
const float y0 = static_cast<float>(m_localPaddedRect.y());
|
|
const float x1 = x0 + static_cast<float>(m_localPaddedRect.width());
|
|
const float y1 = y0 + static_cast<float>(m_localPaddedRect.height());
|
|
const float w = x1 - x0;
|
|
const float h = y1 - y0;
|
|
|
|
// Update vertex positions and texture coordinates
|
|
auto* v = node->geometry()->vertexDataAsTexturedPoint2D();
|
|
|
|
// Outer corners
|
|
v[0].set(x0, y0, 0.0f, 0.0f);
|
|
v[1].set(x1, y0, 1.0f, 0.0f);
|
|
v[2].set(x1, y1, 1.0f, 1.0f);
|
|
v[3].set(x0, y1, 0.0f, 1.0f);
|
|
// Inner corners (hole)
|
|
v[4].set(holeLeft, holeTop, (holeLeft - x0) / w, (holeTop - y0) / h);
|
|
v[5].set(holeRight, holeTop, (holeRight - x0) / w, (holeTop - y0) / h);
|
|
v[6].set(holeRight, holeBot, (holeRight - x0) / w, (holeBot - y0) / h);
|
|
v[7].set(holeLeft, holeBot, (holeLeft - x0) / w, (holeBot - y0) / h);
|
|
|
|
node->markDirty(QSGNode::DirtyGeometry);
|
|
|
|
// Update material uniforms
|
|
auto* material = static_cast<BlobMaterial*>(node->material());
|
|
material->m_paddedX = m_cachedPaddedX;
|
|
material->m_paddedY = m_cachedPaddedY;
|
|
material->m_paddedW = m_cachedPaddedW;
|
|
material->m_paddedH = m_cachedPaddedH;
|
|
material->m_smoothFactor = pad;
|
|
material->m_myIndex = m_cachedMyIndex;
|
|
material->m_color = m_group->color();
|
|
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
|
|
material->m_invertedRadius = m_cachedInvertedRadius;
|
|
memcpy(material->m_invertedOuter, m_cachedInvertedOuter, sizeof(m_cachedInvertedOuter));
|
|
memcpy(material->m_invertedInner, m_cachedInvertedInner, sizeof(m_cachedInvertedInner));
|
|
|
|
const int count = static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
|
|
material->m_rectCount = count;
|
|
for (int i = 0; i < count; ++i)
|
|
material->m_rects[i] = m_cachedRects[i];
|
|
|
|
node->markDirty(QSGNode::DirtyMaterial);
|
|
|
|
return node;
|
|
}
|
|
|
|
BlobInvertedRect::~BlobInvertedRect() {
|
|
if (m_group)
|
|
m_group->clearInvertedRect(this);
|
|
}
|
|
|
|
void BlobInvertedRect::setBorderLeft(qreal v) {
|
|
if (qFuzzyCompare(m_borderLeft, v))
|
|
return;
|
|
m_borderLeft = v;
|
|
emit borderLeftChanged();
|
|
if (m_group)
|
|
m_group->markDirty();
|
|
}
|
|
|
|
void BlobInvertedRect::setBorderRight(qreal v) {
|
|
if (qFuzzyCompare(m_borderRight, v))
|
|
return;
|
|
m_borderRight = v;
|
|
emit borderRightChanged();
|
|
if (m_group)
|
|
m_group->markDirty();
|
|
}
|
|
|
|
void BlobInvertedRect::setBorderTop(qreal v) {
|
|
if (qFuzzyCompare(m_borderTop, v))
|
|
return;
|
|
m_borderTop = v;
|
|
emit borderTopChanged();
|
|
if (m_group)
|
|
m_group->markDirty();
|
|
}
|
|
|
|
void BlobInvertedRect::setBorderBottom(qreal v) {
|
|
if (qFuzzyCompare(m_borderBottom, v))
|
|
return;
|
|
m_borderBottom = v;
|
|
emit borderBottomChanged();
|
|
if (m_group)
|
|
m_group->markDirty();
|
|
}
|
|
|
|
void BlobInvertedRect::registerWithGroup() {
|
|
if (m_group)
|
|
m_group->setInvertedRect(this);
|
|
}
|
|
|
|
void BlobInvertedRect::unregisterFromGroup() {
|
|
if (m_group)
|
|
m_group->clearInvertedRect(this);
|
|
}
|