C

Qt Cluster: Rendering and Recovery from Main UI Failure

// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

// This file is part of the Qt Safe Renderer module

#include "gaugenode.h"

#include <QtQuick/qsgnode.h>
#include <QtQuick/qsgflatcolormaterial.h>
#include <QtMath>

#define EXTRAVERTICES 3

GaugeNode::GaugeNode(const int &numVertices, const QColor &color = QColor(255, 0, 0),
                     const bool &doNotFill = false)
    : QSGGeometryNode()
    , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), numVertices+EXTRAVERTICES)
    , m_material(nullptr)
    , m_numVertices(numVertices)
    , m_doNotFill(doNotFill)
    , m_color(color)
    , m_cutRad(0.0)
    , m_lefttoright(true)
{
    initGeometry();
}

GaugeNode::~GaugeNode()
{
    if (m_material)
        delete m_material;

}

void GaugeNode::setColor(const QColor &color)
{
    if (m_color == color)
        return;
    m_color = color;
    m_dirtyBits |= QSGNode::DirtyMaterial;
}

void GaugeNode::setCutRad(const float &cutRad)
{
    if (m_cutRad == cutRad)
        return;

    m_cutRad = cutRad;
}

void GaugeNode::setDoNotFill(const bool &doNotFill)
{
    if (m_doNotFill == doNotFill)
        return;

    m_doNotFill = doNotFill;

    if (m_doNotFill)
        m_geometry.setDrawingMode(QSGGeometry::DrawLineStrip);
    else
        m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip);

    m_dirtyBits |= QSGNode::DirtyGeometry;
}

void GaugeNode::setBackCutRad(const double &backCutRad)
{
    if (backCutRad == m_backCutRad)
        return;

    m_backCutRad = backCutRad;
    m_dirtyBits |= QSGNode::DirtyGeometry;
}

void GaugeNode::setRadius(const double &radius)
{
    if (m_radius == radius)
        return;

    m_radius = radius;
    m_dirtyBits |= QSGNode::DirtyGeometry;
}

void GaugeNode::setArcDistPerVert(const double &dist)
{
    if (dist == m_arc_dist_per_vertices)
        return;
    m_arc_dist_per_vertices = dist;
    m_dirtyBits |= QSGNode::DirtyGeometry;
}

void GaugeNode::setNumVertices(const int &numVertices)
{
    if (numVertices == m_numVertices)
        return;

    m_numVertices = numVertices;
    m_geometry.allocate(m_numVertices + 3);
    m_dirtyBits |= QSGNode::DirtyGeometry;
}

void GaugeNode::setFillWidth(const double &fillWidth)
{
    if (m_fillWidth == fillWidth)
        return;

    m_fillWidth = fillWidth;
    m_dirtyBits |= QSGNode::DirtyGeometry;
}

void GaugeNode::setBoundingRect(const QRectF &rect)
{
    if (rect.width() == m_width && rect.height() == m_height)
        return;

    m_height = rect.height();
    m_width = rect.width();
    setCenterPoint(rect.center());
    m_dirtyBits |= QSGNode::DirtyGeometry;
}

void GaugeNode::setCenterPoint(const QPointF &center)
{
    m_center_x = center.x();
    m_center_y = center.y();
}

void GaugeNode::setLeftToRight(const bool &ltr)
{
    if (m_lefttoright == ltr)
        return;

    m_lefttoright = ltr;
    m_dirtyBits |= QSGNode::DirtyMaterial;
}

void GaugeNode::drawGeometryTexturePoint2D()
{
    QSGGeometry::TexturedPoint2D *vertices = m_geometry.vertexDataAsTexturedPoint2D();

    double current_angle_rad = 0.0;
    double currentRadius = m_radius;
    double d_arc_point_x = m_center_x + (currentRadius - m_fillWidth) * qCos(m_backCutRad);
    double d_arc_point_y = m_center_y + (currentRadius - m_fillWidth) * qSin(m_backCutRad);

    vertices[0].set(d_arc_point_x, d_arc_point_y,
                    d_arc_point_x / m_width, d_arc_point_y / m_height);
    d_arc_point_x = m_center_x + currentRadius * qCos(m_backCutRad);
    d_arc_point_y = m_center_y + currentRadius * qSin(m_backCutRad);
    vertices[1].set(d_arc_point_x, d_arc_point_y,
                    d_arc_point_x / m_width, d_arc_point_y / m_height);
    d_arc_point_x = 0;
    d_arc_point_y = 0;

    for (int i = 0; i < m_numVertices; ++i) {
        current_angle_rad = m_backCutRad + i * m_arc_dist_per_vertices + m_arc_dist_per_vertices;

        if (i % 2 == 0)
            currentRadius -= m_fillWidth;
        else
            currentRadius += m_fillWidth;

        d_arc_point_x = m_center_x + currentRadius * qCos(current_angle_rad);
        d_arc_point_y = m_center_y + currentRadius * qSin(current_angle_rad);
        vertices[i + 2].set(d_arc_point_x, d_arc_point_y,
                            d_arc_point_x / m_width, d_arc_point_y / m_height);
    }
    d_arc_point_x = m_center_x + (currentRadius - m_fillWidth) * qCos(current_angle_rad);
    d_arc_point_y = m_center_y + (currentRadius - m_fillWidth) * qSin(current_angle_rad);

    vertices[m_numVertices + 2].set(d_arc_point_x, d_arc_point_y,
                                    d_arc_point_x / m_width, d_arc_point_y / m_height);
}

void GaugeNode::drawMaterial()
{
    static_cast<QSGFlatColorMaterial *>(m_material)->setColor(m_color);
}

void GaugeNode::draw()
{
    if (m_dirtyBits == 0)
        return;

    if (m_dirtyBits.testFlag(QSGNode::DirtyGeometry))
        drawGeometryTexturePoint2D();
    if (m_dirtyBits.testFlag(QSGNode::DirtyMaterial))
        drawMaterial();

    markDirty(m_dirtyBits);
    m_dirtyBits = DirtyState();
}

//Could be used to optimize vertices if only geometry is changed
void GaugeNode::drawGeometry()
{
    QSGGeometry::Point2D *vertices = m_geometry.vertexDataAsPoint2D();

    double d_arc_point_x = 0.0;
    double d_arc_point_y = 0.0;
    double current_angle_rad = 0.0;
    double currentRadius = m_radius;

    vertices[0].set(m_center_x + (currentRadius - m_fillWidth) * qCos(m_backCutRad), m_center_y
                    + (currentRadius - m_fillWidth) * qSin(m_backCutRad));
    vertices[1].set(m_center_x + currentRadius * qCos(m_backCutRad), m_center_y
                    + currentRadius * qSin(m_backCutRad));

    for (int i = 0; i < m_numVertices; ++i) {
        current_angle_rad = m_backCutRad + i * m_arc_dist_per_vertices + m_arc_dist_per_vertices;

        if (i % 2 == 0)
            currentRadius -= m_fillWidth;
        else
            currentRadius += m_fillWidth;

        d_arc_point_x = m_center_x + currentRadius * qCos(current_angle_rad);
        d_arc_point_y = m_center_y + currentRadius * qSin(current_angle_rad);
        vertices[i + 2].set(d_arc_point_x, d_arc_point_y);
    }

    vertices[m_numVertices + 2].set(m_center_x + (currentRadius - m_fillWidth)
                                    * qCos(current_angle_rad), m_center_y
                                    + (currentRadius - m_fillWidth) * qSin(current_angle_rad));

    markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial);
}

void GaugeNode::initGeometry()
{
    m_geometry.setLineWidth(1);
    if (m_doNotFill)
        m_geometry.setDrawingMode(QSGGeometry::DrawLineStrip);
    else
        m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip);

    setGeometry(&m_geometry);

    QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
    material->setColor(m_color);

    m_material = static_cast<QSGMaterial *>(material);
    setMaterial(m_material);
}