Esempio n. 1
0
QSGNode * QQuickLineItem::updatePaintNode(QSGNode *prev_node,
                                          UpdatePaintNodeData *upd_data)
{
    Q_UNUSED(upd_data);

    QSGGeometryNode * node = static_cast<QSGGeometryNode*>(prev_node);
    QSGGeometry * geometry = NULL;
    QSGFlatColorMaterial * material = NULL;

    if(!node) {
        // http://qt-project.org/doc/qt-5/qsggeometrynode.html
        node = new QSGGeometryNode;
        geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(),4);
        geometry->setDrawingMode(GL_TRIANGLE_STRIP);
        node->setGeometry(geometry);
        node->setFlag(QSGNode::OwnsGeometry);

        material = new QSGFlatColorMaterial;
        material->setColor(m_color);
        node->setMaterial(material);
        node->setFlag(QSGNode::OwnsMaterial);
    }
    else {
        geometry = node->geometry();
        geometry->allocate(4); // we have to call allocate to invalidate
                               // the older vertex buffer
        material = static_cast<QSGFlatColorMaterial*>(node->material());
    }

    // geometry
    std::vector<QPointF> list_vx;
    if(!calcTriStrip(list_vx)) {
        list_vx.clear();
        list_vx.push_back(QPointF(0,0));
        list_vx.push_back(QPointF(0,0));
        list_vx.push_back(QPointF(0,0));
        list_vx.push_back(QPointF(0,0));
    }

    QSGGeometry::Point2D * vertices =
            geometry->vertexDataAsPoint2D();

    for(size_t i=0; i < list_vx.size(); i++) {
        vertices[i].set(list_vx[i].x(),
                        list_vx[i].y());
    }

    node->markDirty(QSGNode::DirtyGeometry);

    // material
    material->setColor(m_color);
    node->markDirty(QSGNode::DirtyMaterial);

    return node;
}
QSGNode *PhosphorRender::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
    if (!m_ybuffer) {
        return 0;
    }

    QSGGeometryNode *node = 0;
    QSGGeometry *geometry = 0;
    Material *material = 0;

    unsigned n_points;

    if (m_xbuffer) {
        n_points = std::min(m_xbuffer->size(), m_ybuffer->size());
    } else {
        n_points = m_ybuffer->countPointsBetween(m_xmin, m_xmax);
    }

    n_points = std::min(n_points,(unsigned) 65767);

    if (!oldNode) {
        node = new QSGGeometryNode;
        geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), n_points);
        geometry->setDrawingMode(GL_POINTS);
        node->setGeometry(geometry);
        node->setFlag(QSGNode::OwnsGeometry);
        material = new Material;
        material->setFlag(QSGMaterial::Blending);
        node->setMaterial(material);
        node->setFlag(QSGNode::OwnsMaterial);
    } else {
        node = static_cast<QSGGeometryNode *>(oldNode);
        geometry = node->geometry();
        geometry->allocate(n_points);
        geometry->setLineWidth(m_pointSize);
        material = static_cast<Material*>(node->material());
    }

    QRectF bounds = boundingRect();

    material->transformation.setToIdentity();
    material->transformation.scale(bounds.width()/(m_xmax - m_xmin), bounds.height()/(m_ymin - m_ymax));
    material->transformation.translate(-m_xmin, -m_ymax);

    material->pointSize = m_pointSize;
    material->color = m_color;

    auto verticies = geometry->vertexDataAsPoint2D();
    if (m_xbuffer) {
        for (unsigned i=0; i<n_points; i++) {
            verticies[i].set(m_xbuffer->get(i), m_ybuffer->get(i));
        }
    } else {
        m_ybuffer->toVertexData(m_xmin, m_xmax, verticies, n_points);
    }
    node->markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial);

    return node;
}
void SceneGraphDeviceContext::DrawComplexBezierPath(vrv::Point bezier1[4], vrv::Point bezier2[4])
{
    // Note: No support for vertex antialiasing. Use a top-level QQuickView with multisample antialiasing.
    // TODO: Add vertex antialiasing, refer to
    // 1) Qt sources for "void QSGBasicInternalRectangleNode::updateGeometry()" in
    // qtdeclarative/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
    // 2) https://stackoverflow.com/questions/28125425/smooth-painting-in-custom-qml-element

    vrv::Pen currentPen = m_penStack.top();

    int segmentCount = 16;

    QSGGeometryNode *node = new QSGGeometryNode;
    QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2 * segmentCount);
    geometry->setDrawingMode(QSGGeometry::DrawTriangleStrip);
    node->setGeometry(geometry);
    node->setFlag(QSGNode::OwnsGeometry);

    QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
    material->setColor(static_cast<QRgb>(currentPen.GetColour()));
    node->setMaterial(material);
    node->setFlag(QSGNode::OwnsMaterial);

    auto calculateCubicBezierPoint = [](vrv::Point p[4], float t) -> std::tuple<float, float> {
        auto invt = 1 - t;
        auto x = invt * invt * invt * p[0].x + 3 * invt * invt * t * p[1].x + 3 * invt * t * t * p[2].x
            + t * t * t * p[3].x;
        auto y = invt * invt * invt * p[0].y + 3 * invt * invt * t * p[1].y + 3 * invt * t * t * p[2].y
            + t * t * t * p[3].y;
        return std::make_tuple(x, y);
    };

    // This loop calculates the bezier points for the inner and the outer line and add them as vertices. The list of
    // vertices is built so that points from the inner and outer line are alternating. This allows to draw a filled area
    // with DrawTriangleStrip.
    QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D();
    for (int i = 0; i < segmentCount; ++i) {
        float bezierPointX = 0;
        float bezierPointY = 0;
        float currentSegment = i / static_cast<float>(segmentCount - 1);

        // Calculate bezier point on bezier1
        std::tie(bezierPointX, bezierPointY) = calculateCubicBezierPoint(bezier1, currentSegment);
        vertices[i * 2].set(translateX(bezierPointX), translateY(bezierPointY));

        // Calculate bezier point on bezier2
        std::tie(bezierPointX, bezierPointY) = calculateCubicBezierPoint(bezier2, currentSegment);
        vertices[i * 2 + 1].set(translateX(bezierPointX), translateY(bezierPointY));
    }

    node->markDirty(QSGNode::DirtyGeometry);
    AddGeometryNode(node);
}
Esempio n. 4
0
QSGNode *QPScrollingCurve::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
{
    QSGGeometryNode *node = 0;
    QSGGeometry *geometry = 0;
    QSGFlatColorMaterial *material = 0;

    if (!oldNode) {
        node = new QSGGeometryNode;
        geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), m_data.size());
        geometry->setLineWidth(2);
        geometry->setDrawingMode(GL_LINE_STRIP);
        node->setGeometry(geometry);
        node->setFlag(QSGNode::OwnsGeometry);
        material = new QSGFlatColorMaterial;
        material->setColor(m_color);
        node->setMaterial(material);
        node->setFlag(QSGNode::OwnsMaterial);
        node->markDirty(QSGNode::DirtyMaterial);
    } else {
        node = static_cast<QSGGeometryNode *>(oldNode);
        geometry = node->geometry();
        geometry->allocate(m_data.size());
        material = static_cast<QSGFlatColorMaterial*>(node->material());
        if (material->color() != m_color) {
            material->setColor(m_color);
            node->markDirty(QSGNode::DirtyMaterial);
        }
    }

    QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D();

    for (uint i = 0; i < m_data.size(); ++i) {
        QPointF p(i, m_data[i]);
        vertices[i].set(p.x(), p.y());
    }

    node->markDirty(QSGNode::DirtyGeometry);

    return node;
}
Esempio n. 5
0
//! [4]
QSGNode *BezierCurve::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
    QSGGeometryNode *node = 0;
    QSGGeometry *geometry = 0;

    if (!oldNode) {
        node = new QSGGeometryNode;
//! [4] //! [5]
        geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), m_segmentCount);
        geometry->setLineWidth(2);
        geometry->setDrawingMode(GL_LINE_STRIP);
        node->setGeometry(geometry);
        node->setFlag(QSGNode::OwnsGeometry);
//! [5] //! [6]
        QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
        material->setColor(QColor(255, 0, 0));
        node->setMaterial(material);
        node->setFlag(QSGNode::OwnsMaterial);
//! [6] //! [7]
    } else {
        node = static_cast<QSGGeometryNode *>(oldNode);
        geometry = node->geometry();
        geometry->allocate(m_segmentCount);
    }
//! [7]

//! [8]
    QRectF bounds = boundingRect();
    QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D();
    for (int i = 0; i < m_segmentCount; ++i) {
        qreal t = i / qreal(m_segmentCount - 1);
        qreal invt = 1 - t;

        QPointF pos = invt * invt * invt * m_p1
                    + 3 * invt * invt * t * m_p2
                    + 3 * invt * t * t * m_p3
                    + t * t * t * m_p4;

        float x = bounds.x() + pos.x() * bounds.width();
        float y = bounds.y() + pos.y() * bounds.height();

        vertices[i].set(x, y);
    }
    node->markDirty(QSGNode::DirtyGeometry);
//! [8]


//! [9]
    return node;
}
Esempio n. 6
0
QSGNode* drawRect(const QRect& rect, QSGNode* oldNode)
{
    QSGGeometryNode* node;
    if (oldNode == nullptr)
    {
        node = new QSGGeometryNode;

        QSGGeometry* geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 8);
        geometry->setDrawingMode(GL_LINES);
        geometry->setLineWidth(1);

        QSGFlatColorMaterial* material = new QSGFlatColorMaterial;
        material->setColor(QGuiApplication::palette().color(QPalette::Normal, QPalette::WindowText));

        node->setGeometry(geometry);
        node->setFlag(QSGNode::OwnsGeometry);
        node->setMaterial(material);
        node->setFlag(QSGNode::OwnsMaterial);
    }
    else
    {
        node = static_cast<QSGGeometryNode*>(oldNode);
    }

    QSGGeometry* geometry = node->geometry();

    // FIXME: check if these really have to be updated
    geometry->vertexDataAsPoint2D()[0].set(rect.topLeft().x(), rect.topLeft().y());
    geometry->vertexDataAsPoint2D()[1].set(rect.topRight().x(), rect.topRight().y());
    geometry->vertexDataAsPoint2D()[2].set(rect.topRight().x(), rect.topRight().y());
    geometry->vertexDataAsPoint2D()[3].set(rect.bottomRight().x(), rect.bottomRight().y());
    geometry->vertexDataAsPoint2D()[4].set(rect.bottomRight().x(), rect.bottomRight().y());
    geometry->vertexDataAsPoint2D()[5].set(rect.bottomLeft().x(), rect.bottomLeft().y());
    geometry->vertexDataAsPoint2D()[6].set(rect.bottomLeft().x(), rect.bottomLeft().y());
    geometry->vertexDataAsPoint2D()[7].set(rect.topLeft().x(), rect.topLeft().y());

    node->markDirty(QSGNode::DirtyGeometry);

    return node;
}
void SceneGraphDeviceContext::DrawCircle(int x, int y, int radius)
{
    // Note: No support for vertex antialiasing. Use a top-level QQuickView with multisample antialiasing.
    // TODO: Add vertex antialiasing, refer to
    // 1) Qt sources for "void QSGBasicInternalRectangleNode::updateGeometry()" in
    // qtdeclarative/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
    // 2) https://stackoverflow.com/questions/28125425/smooth-painting-in-custom-qml-element

    vrv::Pen currentPen = m_penStack.top();

    int segmentCount = 16;

    QSGGeometryNode *node = new QSGGeometryNode();
    QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), segmentCount);
    geometry->setDrawingMode(QSGGeometry::DrawTriangleFan);
    node->setGeometry(geometry);
    node->setFlag(QSGNode::OwnsGeometry);

    QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
    material->setColor(static_cast<QRgb>(currentPen.GetColour()));
    node->setMaterial(material);
    node->setFlag(QSGNode::OwnsMaterial);

    // This draws individual triangles from the first point (center) to every outer point by using DrawTriangleFan.
    QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D();
    int numPoints = geometry->vertexCount();
    vertices[0].x = translateX(x);
    vertices[0].y = translateY(y);
    for (int i = 1; i < numPoints; ++i) {
        double theta = i * 2 * M_PI / (numPoints - 2);
        vertices[i].x = translateX(x + radius * static_cast<float>(std::cos(theta)));
        vertices[i].y = translateY(y - radius * static_cast<float>(std::sin(theta)));
    }

    node->markDirty(QSGNode::DirtyGeometry);
    AddGeometryNode(node);
}
TimelineRenderPass::State *TimelineSelectionRenderPass::update(
        const TimelineAbstractRenderer *renderer, const TimelineRenderState *parentState,
        State *oldState, int firstIndex, int lastIndex, bool stateChanged, qreal spacing) const
{
    Q_UNUSED(stateChanged);

    const TimelineModel *model = renderer->model();
    if (!model || model->isEmpty())
        return oldState;

    TimelineSelectionRenderPassState *state;

    if (oldState == 0)
        state = new TimelineSelectionRenderPassState;
    else
        state = static_cast<TimelineSelectionRenderPassState *>(oldState);

    int selectedItem = renderer->selectedItem();
    QSGGeometryNode *node = static_cast<QSGGeometryNode *>(model->expanded() ?
                                                               state->expandedOverlay() :
                                                               state->collapsedOverlay());

    if (selectedItem != -1 && selectedItem >= firstIndex && selectedItem < lastIndex) {
        qreal top = 0;
        qreal height = 0;
        if (model->expanded()) {
            int row = model->expandedRow(selectedItem);
            int rowHeight = model->expandedRowHeight(row);
            height = rowHeight * model->relativeHeight(selectedItem);
            top = (model->expandedRowOffset(row) + rowHeight) - height;
        } else {
            int row = model->collapsedRow(selectedItem);
            height = TimelineModel::defaultRowHeight() * model->relativeHeight(selectedItem);
            top = TimelineModel::defaultRowHeight() * (row + 1) - height;
        }

        qint64 startTime = qBound(parentState->start(), model->startTime(selectedItem),
                                  parentState->end());
        qint64 endTime = qBound(parentState->start(), model->endTime(selectedItem),
                                parentState->end());
        qint64 left = startTime - parentState->start();
        qint64 width = endTime - startTime;

        // Construct from upper left and lower right for better precision. When constructing from
        // left and width the error on the left border is inherited by the right border. Like this
        // they're independent.

        QRectF position(left * parentState->scale(), top, width * parentState->scale(),  height);

        QColor itemColor = model->color(selectedItem);
        uchar red = itemColor.red();
        uchar green = itemColor.green();
        uchar blue = itemColor.blue();
        int selectionId = model->selectionId(selectedItem);

        OpaqueColoredPoint2DWithSize *v = OpaqueColoredPoint2DWithSize::fromVertexData(
                    node->geometry());
        v[0].set(position.left(), position.bottom(), -position.width(), -position.height(),
                 selectionId, red, green, blue);
        v[1].set(position.right(), position.bottom(), position.width(), -position.height(),
                 selectionId, red, green, blue);
        v[2].set(position.left(), position.top(), -position.width(), position.height(),
                 selectionId, red, green, blue);
        v[3].set(position.right(), position.top(), position.width(), position.height(),
                 selectionId, red, green, blue);
        state->material()->setSelectionColor(renderer->selectionLocked() ? QColor(96,0,255) :
                                                                           Qt::blue);
        state->material()->setSelectedItem(selectionId);
        state->material()->setScale(QVector2D(spacing / parentState->scale(), 1));
        node->markDirty(QSGNode::DirtyMaterial | QSGNode::DirtyGeometry);
    } else {
        OpaqueColoredPoint2DWithSize *v = OpaqueColoredPoint2DWithSize::fromVertexData(
                    node->geometry());
        for (int i = 0; i < 4; ++i)
            v[i].set(0, 0, 0, 0, 0, 0, 0, 0);
        node->markDirty(QSGNode::DirtyGeometry);
    }
    return state;
}
QSGNode* MDeclarativeStatusBar::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*)
{
    QSGGeometryNode* node = static_cast<QSGGeometryNode*>(oldNode);
    if (!node) {
        node = new QSGGeometryNode;
        node->setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial | QSGNode::OwnsOpaqueMaterial);
        node->setGeometry(new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4));
    }

    if (!sharedPixmapHandle) {
        node->setMaterial(new QSGFlatColorMaterial);
        node->setOpaqueMaterial(new QSGFlatColorMaterial);
        node->markDirty(QSGNode::DirtyMaterial);
        return node;
    }

    if (!sharedTexture || updateSharedTexture) {
        node->setMaterial(new QSGTextureMaterial);
        node->setOpaqueMaterial(new QSGTextureMaterial);

#if defined(HAVE_XLIB)
        MDeclarativeScreen* screen = MDeclarativeScreen::instance();

#if defined(QT_OPENGL_ES_2)
        static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
        static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress("eglCreateImageKHR");
        static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress("eglDestroyImageKHR");
        static PFNEGLQUERYIMAGENOKPROC eglQueryImageNOK = (PFNEGLQUERYIMAGENOKPROC) eglGetProcAddress("eglQueryImageNOK");

        const EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };

        EGLDisplay eglDpy = eglGetDisplay((EGLNativeDisplayType)screen->display());
        EGLImageKHR img = eglCreateImageKHR(eglDpy, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer)sharedPixmapHandle, attribs);

        GLuint textureId;
        glGenTextures(1, &textureId);
        glBindTexture(GL_TEXTURE_2D, textureId);
        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)img);

        GLint width = 0;
        GLint height = 0;
        eglQueryImageNOK(eglDpy, img, EGL_WIDTH, &width);
        eglQueryImageNOK(eglDpy, img, EGL_HEIGHT, &height);

        sharedTexture.reset(canvas()->createTextureFromId(textureId, QSize(width, height), QQuickCanvas::TextureOwnsGLTexture));

        eglDestroyImageKHR(eglDpy, img);
#else
        Display* dpy = screen->display();
        Window dummy1;
        int x, y;
        unsigned int width, height, borderwidth, depth;
        XGetGeometry(dpy, sharedPixmapHandle, &dummy1, &x, &y, &width, &height, &borderwidth, &depth);
        XImage* xi = XGetImage(dpy, sharedPixmapHandle, 0, 0, width, height, ULONG_MAX, ZPixmap);
        QImage img = MX11Wrapper::qimageFromXImage(xi);
        XDestroyImage(xi);
        sharedTexture.reset(canvas()->createTextureFromImage(img));
#endif
#endif // HAVE_XLIB

        static_cast<QSGTextureMaterial*>(node->material())->setTexture(sharedTexture.data());
        static_cast<QSGOpaqueTextureMaterial*>(node->opaqueMaterial())->setTexture(sharedTexture.data());
        node->markDirty(QSGNode::DirtyMaterial);
        updateSharedTexture = false;
    }

    QRectF sourceRect;
    sourceRect = QRectF(0, 0, width(), height());
    if (mOrientation == MDeclarativeScreen::Portrait || mOrientation == MDeclarativeScreen::PortraitInverted)
        sourceRect.moveTop(height());
    sourceRect = sharedTexture.data()->convertToNormalizedSourceRect(sourceRect);

    QRect targetRect(x(), y(), width(), height());

    QSGGeometry::updateTexturedRectGeometry(node->geometry(), targetRect, sourceRect);
    node->markDirty(QSGNode::DirtyGeometry);

    return node;
}
QSGNode* NodeConnectionLines::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
    if (!m_nodeObject) return nullptr;
    const QVector<QPointer<NodeBase>>& connectedNodes = m_nodeObject->getConnectedNodes();
    int connectionCount = connectedNodes.size();

    // -------------------- Prepare QSG Nodes:
    QSGNode* parentNode = oldNode;
    if (!oldNode) {
        parentNode = new QSGNode();
    } else {
        // update color in case it changed:
        // FIXME: is there a more efficient way to do this?
        for (int i=0; i<parentNode->childCount(); ++i) {
            QSGGeometryNode* childNode = static_cast<QSGGeometryNode*>(parentNode->childAtIndex(i));
            if (!childNode) continue;
            QSGFlatColorMaterial* material = static_cast<QSGFlatColorMaterial*>(childNode->material());
            if (!material) continue;
            material->setColor(m_color);
        }
    }
    // adapt child count:
    int childCount = parentNode->childCount();
    if (childCount < connectionCount) {
        for (int i=0; i<(connectionCount - childCount); ++i) {
            QSGGeometryNode* node = new QSGGeometryNode;
            QSGGeometry* geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 3);
            geometry->setDrawingMode(GL_TRIANGLE_STRIP);
            node->setGeometry(geometry);
            node->setFlag(QSGNode::OwnsGeometry);
            QSGFlatColorMaterial* material = new QSGFlatColorMaterial;
            material->setColor(m_color);
            node->setMaterial(material);
            node->setFlag(QSGNode::OwnsMaterial);
            parentNode->appendChildNode(node);
        }
    } else if (childCount > connectionCount) {
        for (int i=0; i<(childCount - connectionCount); ++i) {
            parentNode->removeChildNode(parentNode->childAtIndex(0));
        }
    }
    Q_ASSERT(parentNode->childCount() == connectionCount);

    // calculate common start point:
    const QPointF p0(width(), height() / 2);
    double widthOffset = m_lineWidth / 2;
    //const QPointF posInScene = mapToScene(QPointF(0, 0));


    for (int i=0; i<connectionCount; ++i) {
        NodeBase* otherNode = connectedNodes[i];
        if (!otherNode) continue;
        QQuickItem* otherGuiItem = otherNode->getGuiItem();
        if (!otherGuiItem) continue;
        const QPointF p3 = mapFromItem(otherGuiItem, QPointF(-otherGuiItem->width() / 2, otherGuiItem->height() / 2));
        int handleLength = std::max(50, std::min(int(p3.x() - p0.x()), 80));
        const QPointF p1(p0.x() + handleLength, p0.y());
        const QPointF p2(p3.x() - handleLength, p3.y());

        // calculate reasonable segment count:
        int segmentCount = qMax(16.0, qMin(qAbs(p3.y() - p0.y()) / 25, 50.0));
        int verticesCount = segmentCount * 2;

        QSGGeometryNode* qsgNode = static_cast<QSGGeometryNode*>(parentNode->childAtIndex(i));
        if (!qsgNode) continue;

        QSGGeometry* geometry = qsgNode->geometry();
        if (!geometry) continue;
        geometry->allocate(verticesCount);
        QSGGeometry::Point2D* vertices = geometry->vertexDataAsPoint2D();

        // triangulate cubic bezier curve:
        for (int i = 0; i < segmentCount; ++i) {
            // t is the position on the line:
            const qreal t = i / qreal(segmentCount - 1);

            // pos is the point on the curve at "t":
            const QPointF pos = calculateBezierPoint(t, p0, p1, p2, p3);

            // normal is the normal vector at that point
            const QPointF normal = normalFromTangent(calculateBezierTangent(t, p0, p1, p2, p3));

            // first is a point offsetted in the normal direction by lineWidth / 2 from pos
            const QPointF first = pos - normal * widthOffset;

            // ssecond is a point offsetted in the negative normal direction by lineWidth / 2 from pos
            const QPointF second = pos + normal * widthOffset;

            // add first and second as vertices to this geometry:
            vertices[i*2].set(first.x(), first.y());
            vertices[i*2+1].set(second.x(), second.y());
        }

        // tell Scene Graph that this items needs to be drawn:
        qsgNode->markDirty(QSGNode::DirtyGeometry);
    }

    return parentNode;
}