File indexing completed on 2024-05-05 16:16:43
0001 /* 0002 * This file is part of KQuickCharts 0003 * SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "PieChartNode.h" 0009 0010 #include <algorithm> 0011 0012 #include <QColor> 0013 #include <QSGGeometry> 0014 #include <cmath> 0015 0016 #include "PieChartMaterial.h" 0017 0018 static const qreal pi = std::acos(-1.0); 0019 static const qreal sectionSize = pi * 0.5; 0020 0021 inline QVector4D colorToVec4(const QColor &color) 0022 { 0023 return QVector4D{float(color.redF()), float(color.greenF()), float(color.blueF()), float(color.alphaF())}; 0024 } 0025 0026 inline qreal degToRad(qreal deg) 0027 { 0028 return (deg / 180.0) * pi; 0029 } 0030 0031 inline QVector2D rotated(const QVector2D vector, qreal angle) 0032 { 0033 auto newX = vector.x() * std::cos(angle) - vector.y() * std::sin(angle); 0034 auto newY = vector.x() * std::sin(angle) + vector.y() * std::cos(angle); 0035 return QVector2D(newX, newY); 0036 } 0037 0038 PieChartNode::PieChartNode() 0039 : PieChartNode(QRectF{}) 0040 { 0041 } 0042 0043 PieChartNode::PieChartNode(const QRectF &rect) 0044 { 0045 m_geometry = new QSGGeometry{QSGGeometry::defaultAttributes_TexturedPoint2D(), 4}; 0046 QSGGeometry::updateTexturedRectGeometry(m_geometry, rect, QRectF{0, 0, 1, 1}); 0047 setGeometry(m_geometry); 0048 0049 m_material = new PieChartMaterial{}; 0050 setMaterial(m_material); 0051 0052 setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial); 0053 } 0054 0055 PieChartNode::~PieChartNode() 0056 { 0057 } 0058 0059 void PieChartNode::setRect(const QRectF &rect) 0060 { 0061 if (rect == m_rect) { 0062 return; 0063 } 0064 0065 m_rect = rect; 0066 QSGGeometry::updateTexturedRectGeometry(m_geometry, m_rect, QRectF{0, 0, 1, 1}); 0067 markDirty(QSGNode::DirtyGeometry); 0068 0069 auto minDimension = qMin(m_rect.width(), m_rect.height()); 0070 0071 QVector2D aspect{1.0, 1.0}; 0072 aspect.setX(rect.width() / minDimension); 0073 aspect.setY(rect.height() / minDimension); 0074 m_material->setAspectRatio(aspect); 0075 0076 m_material->setInnerRadius(m_innerRadius / minDimension); 0077 m_material->setOuterRadius(m_outerRadius / minDimension); 0078 0079 markDirty(QSGNode::DirtyMaterial); 0080 } 0081 0082 void PieChartNode::setInnerRadius(qreal radius) 0083 { 0084 if (qFuzzyCompare(radius, m_innerRadius)) { 0085 return; 0086 } 0087 0088 m_innerRadius = radius; 0089 0090 auto minDimension = qMin(m_rect.width(), m_rect.height()); 0091 m_material->setInnerRadius(m_innerRadius / minDimension); 0092 0093 markDirty(QSGNode::DirtyMaterial); 0094 } 0095 0096 void PieChartNode::setOuterRadius(qreal radius) 0097 { 0098 if (qFuzzyCompare(radius, m_outerRadius)) { 0099 return; 0100 } 0101 0102 m_outerRadius = radius; 0103 0104 auto minDimension = qMin(m_rect.width(), m_rect.height()); 0105 m_material->setOuterRadius(m_outerRadius / minDimension); 0106 0107 markDirty(QSGNode::DirtyMaterial); 0108 } 0109 0110 void PieChartNode::setColors(const QVector<QColor> &colors) 0111 { 0112 m_colors = colors; 0113 updateTriangles(); 0114 } 0115 0116 void PieChartNode::setSections(const QVector<qreal> §ions) 0117 { 0118 m_sections = sections; 0119 updateTriangles(); 0120 } 0121 0122 void PieChartNode::setBackgroundColor(const QColor &color) 0123 { 0124 if (color == m_backgroundColor) { 0125 return; 0126 } 0127 0128 m_backgroundColor = color; 0129 m_material->setBackgroundColor(color); 0130 markDirty(QSGNode::DirtyMaterial); 0131 } 0132 0133 void PieChartNode::setFromAngle(qreal angle) 0134 { 0135 if (qFuzzyCompare(angle, m_fromAngle)) { 0136 return; 0137 } 0138 0139 m_fromAngle = angle; 0140 m_material->setFromAngle(degToRad(angle)); 0141 updateTriangles(); 0142 } 0143 0144 void PieChartNode::setToAngle(qreal angle) 0145 { 0146 if (qFuzzyCompare(angle, m_fromAngle)) { 0147 return; 0148 } 0149 0150 m_toAngle = angle; 0151 m_material->setToAngle(degToRad(angle)); 0152 updateTriangles(); 0153 } 0154 0155 void PieChartNode::setSmoothEnds(bool smooth) 0156 { 0157 if (smooth == m_smoothEnds) { 0158 return; 0159 } 0160 0161 m_smoothEnds = smooth; 0162 m_material->setSmoothEnds(smooth); 0163 markDirty(QSGNode::DirtyMaterial); 0164 } 0165 0166 void PieChartNode::updateTriangles() 0167 { 0168 if (m_sections.isEmpty() || m_sections.size() != m_colors.size()) { 0169 return; 0170 } 0171 0172 qreal startAngle = degToRad(m_fromAngle); 0173 qreal totalAngle = degToRad(m_toAngle - m_fromAngle); 0174 0175 QVector<QVector2D> segments; 0176 QVector<QVector4D> colors; 0177 0178 for (int i = 0; i < m_sections.size(); ++i) { 0179 QVector2D segment{float(startAngle), float(startAngle + m_sections.at(i) * totalAngle)}; 0180 segments << segment; 0181 startAngle = segment.y(); 0182 colors << colorToVec4(m_colors.at(i)); 0183 } 0184 0185 if (m_sections.size() == 1 && qFuzzyCompare(m_sections.at(0), 0.0)) { 0186 segments.clear(); 0187 } 0188 0189 m_material->setSegments(segments); 0190 m_material->setColors(colors); 0191 0192 markDirty(QSGNode::DirtyMaterial); 0193 }