#include "blobinvertedrect.hpp" #include "blobgroup.hpp" #include "blobmaterial.hpp" #include #include #include #include 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(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(m_borderLeft) + inset; const float holeTop = static_cast(m_borderTop) + inset; const float holeRight = static_cast(width() - m_borderRight) - inset; const float holeBot = static_cast(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(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(m_localPaddedRect.x()); const float y0 = static_cast(m_localPaddedRect.y()); const float x1 = x0 + static_cast(m_localPaddedRect.width()); const float y1 = y0 + static_cast(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(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(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); }