1 /*
2 sgwireframewidget.cpp
3
4 This file is part of GammaRay, the Qt application inspection and
5 manipulation tool.
6
7 Copyright (C) 2014-2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
8 Author: Anton Kreuzkamp <anton.kreuzkamp@kdab.com>
9
10 Licensees holding valid commercial KDAB GammaRay licenses may use this file in
11 accordance with GammaRay Commercial License Agreement provided with the Software.
12
13 Contact info@kdab.com if any conditions of this licensing are not clear to you.
14
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation, either version 2 of the License, or
18 (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 */
28
29 #include "sgwireframewidget.h"
30 #include "sggeometrymodel.h"
31
32 #include <QApplication>
33 #include <QPainter>
34 #include <QMouseEvent>
35 #include <QItemSelectionModel>
36
37 using namespace GammaRay;
38
SGWireframeWidget(QWidget * parent,Qt::WindowFlags f)39 SGWireframeWidget::SGWireframeWidget(QWidget *parent, Qt::WindowFlags f)
40 : QWidget(parent, f)
41 , m_vertexModel(nullptr)
42 , m_adjacencyModel(nullptr)
43 , m_highlightModel(nullptr)
44 , m_positionColumn(-1)
45 , m_drawingMode(0)
46 , m_geometryWidth(0)
47 , m_geometryHeight(0)
48 , m_zoom(1)
49 , m_offset(10, 10)
50 {
51 }
52
53 SGWireframeWidget::~SGWireframeWidget() = default;
54
paintEvent(QPaintEvent *)55 void SGWireframeWidget::paintEvent(QPaintEvent *)
56 {
57 if (!m_vertexModel || m_vertices.isEmpty() || m_positionColumn == -1)
58 return;
59
60 // Prepare painting
61 m_zoom = qMin((width() - 20) / m_geometryWidth, (height() - 20) / m_geometryHeight);
62
63 QPainter painter(this);
64 painter.setRenderHint(QPainter::Antialiasing);
65 painter.setPen(qApp->palette().color(QPalette::WindowText));
66 painter.setBrush(QBrush(Qt::black, Qt::SolidPattern));
67
68 // Paint
69 for (int i = 0; i < m_adjacencyList.size(); i++) {
70 // Calculate the index of the vertex we're supposed to draw a line from
71 const int index = m_adjacencyList[i];
72
73 if (index >= m_vertices.size())
74 continue;
75
76 // Draw highlighted faces
77 if ((m_drawingMode == GL_TRIANGLES && i % 3 == 2)
78 || (m_drawingMode == GL_TRIANGLE_STRIP && i >= 2)) {
79 drawHighlightedFace(&painter, QVector<int>() << index << m_adjacencyList[i - 1] << m_adjacencyList[i - 2]);
80 } else if (m_drawingMode == GL_TRIANGLE_FAN && i >= 2) {
81 drawHighlightedFace(&painter, QVector<int>() << index << m_adjacencyList[i - 1] << m_adjacencyList.first());
82 }
83
84 #ifndef QT_OPENGL_ES_2
85 else if ((m_drawingMode == GL_QUADS || m_drawingMode == GL_QUAD_STRIP) && i % 4 == 3) {
86 drawHighlightedFace(&painter,
87 QVector<int>() << index << m_adjacencyList[i - 1] << m_adjacencyList[i - 2] << m_adjacencyList[i - 3]);
88 } else if (m_drawingMode == GL_POLYGON && i == m_adjacencyList.size() - 1) {
89 QVector<int> vertices;
90 vertices.reserve(m_adjacencyList.size());
91 for (int j = 0; j < m_adjacencyList.size(); j++)
92 vertices << j;
93 drawHighlightedFace(&painter, vertices);
94 }
95 #endif
96
97 // Draw wires
98
99 // Draw a connection to the previous vertex
100 if (((m_drawingMode == GL_LINES && i % 2)
101 || m_drawingMode == GL_LINE_LOOP
102 || m_drawingMode == GL_LINE_STRIP
103 || (m_drawingMode == GL_TRIANGLES && i % 3)
104 || m_drawingMode == GL_TRIANGLE_STRIP
105 || m_drawingMode == GL_TRIANGLE_FAN
106 #ifndef QT_OPENGL_ES_2
107 || (m_drawingMode == GL_QUADS && i % 4 != 0)
108 || (m_drawingMode == GL_QUAD_STRIP && i % 2)
109 || m_drawingMode == GL_POLYGON
110 #endif
111 ) && i > 0) {
112 drawWire(&painter, index, m_adjacencyList[i - 1]);
113 }
114
115 // Draw a connection to the second previous vertex
116 if ((m_drawingMode == GL_TRIANGLE_STRIP
117 || (m_drawingMode == GL_TRIANGLES && i % 3 == 2)
118 #ifndef QT_OPENGL_ES_2
119 || m_drawingMode == GL_QUAD_STRIP
120 #endif
121 ) && i > 1) {
122 drawWire(&painter, index, m_adjacencyList[i - 2]);
123 }
124
125 // draw a connection to the third previous vertex
126 #ifndef QT_OPENGL_ES_2
127 if (m_drawingMode == GL_QUADS && i % 4 == 3) {
128 drawWire(&painter, index, m_adjacencyList[i - 3]);
129 }
130
131 #endif
132
133 // Draw a connection to the very first vertex
134 if ((m_drawingMode == GL_LINE_LOOP && i == m_adjacencyList.size() - 1)
135 #ifndef QT_OPENGL_ES_2
136 || (m_drawingMode == GL_POLYGON && i == m_adjacencyList.size() - 1)
137 #endif
138 || m_drawingMode == GL_TRIANGLE_FAN)
139 drawWire(&painter, index, m_adjacencyList.first());
140 }
141
142 // Paint the vertices
143 for (int i = 0; i < m_vertices.size(); ++i) {
144 if (m_highlightedVertices.contains(i)) {
145 painter.save();
146
147 // Glow
148 QRadialGradient radialGrad(m_vertices.at(i) * m_zoom + m_offset, 6);
149 radialGrad.setColorAt(0, qApp->palette().color(QPalette::Highlight));
150 radialGrad.setColorAt(1, Qt::transparent);
151
152 painter.setBrush(QBrush(radialGrad));
153 painter.setPen(Qt::NoPen);
154 painter.drawEllipse(m_vertices.at(i) * m_zoom + m_offset, 12, 12);
155
156 // Highlighted point
157 painter.setBrush(QBrush(qApp->palette().color(QPalette::Highlight)));
158 painter.drawEllipse(m_vertices.at(i) * m_zoom + m_offset, 3, 3);
159 painter.restore();
160 } else {
161 // Normal unhighlighted point
162 painter.drawEllipse(m_vertices.at(i) * m_zoom + m_offset, 3, 3);
163 }
164 }
165
166 // Paint hint about which draw mode is used
167 QString drawingMode = m_drawingMode == GL_POINTS ? QStringLiteral("GL_POINTS")
168 : m_drawingMode == GL_LINES ? QStringLiteral("GL_LINES")
169 : m_drawingMode == GL_LINE_STRIP ? QStringLiteral("GL_LINE_STRIP")
170 : m_drawingMode == GL_LINE_LOOP ? QStringLiteral("GL_LINE_LOOP") :
171 #ifndef QT_OPENGL_ES_2
172 m_drawingMode == GL_POLYGON ? QStringLiteral("GL_POLYGON")
173 : m_drawingMode == GL_QUADS ? QStringLiteral("GL_QUADS")
174 : m_drawingMode == GL_QUAD_STRIP ? QStringLiteral("GL_QUAD_STRIP") :
175 #endif
176 m_drawingMode == GL_TRIANGLES ? QStringLiteral("GL_TRIANGLES")
177 : m_drawingMode == GL_TRIANGLE_STRIP ? QStringLiteral("GL_TRIANGLE_STRIP")
178 : m_drawingMode
179 == GL_TRIANGLE_FAN ? QStringLiteral("GL_TRIANGLE_FAN") : tr("Unknown");
180 QString text = tr("Drawing mode: %1").arg(drawingMode);
181 painter.drawText(contentsRect().width() - painter.fontMetrics().width(text),
182 contentsRect().height() - painter.fontMetrics().height(), text);
183 }
184
drawWire(QPainter * painter,int vertexIndex1,int vertexIndex2)185 void SGWireframeWidget::drawWire(QPainter *painter, int vertexIndex1, int vertexIndex2)
186 {
187 // Draw wire
188 if (m_highlightedVertices.contains(vertexIndex1)
189 && m_highlightedVertices.contains(vertexIndex2)) {
190 painter->save();
191 painter->setPen(qApp->palette().color(QPalette::Highlight));
192 painter->drawLine(m_vertices.at(vertexIndex1) * m_zoom + m_offset,
193 m_vertices.at(vertexIndex2) * m_zoom + m_offset);
194 painter->restore();
195 } else if (vertexIndex1 != -1 && vertexIndex2 != -1) {
196 painter->drawLine(m_vertices.at(vertexIndex1) * m_zoom + m_offset,
197 m_vertices.at(vertexIndex2) * m_zoom + m_offset);
198 }
199 }
200
drawHighlightedFace(QPainter * painter,const QVector<int> & vertexIndices)201 void SGWireframeWidget::drawHighlightedFace(QPainter *painter, const QVector<int> &vertexIndices)
202 {
203 QVector<QPointF> vertices;
204 for (int index : vertexIndices) {
205 if (!m_highlightedVertices.contains(index))
206 return; // There is one vertex that is not highlighted. Don't highlight the face.
207 vertices << m_vertices.at(index) * m_zoom + m_offset;
208 }
209 painter->save();
210 QColor color = qApp->palette().color(QPalette::Highlight).lighter();
211 color.setAlphaF(0.8);
212 painter->setBrush(QBrush(color));
213 painter->setPen(Qt::NoPen);
214 painter->drawPolygon(QPolygonF(vertices));
215 painter->restore();
216 }
217
model() const218 QAbstractItemModel *SGWireframeWidget::model() const
219 {
220 return m_vertexModel;
221 }
222
setModel(QAbstractItemModel * vertexModel,QAbstractItemModel * adjacencyModel)223 void SGWireframeWidget::setModel(QAbstractItemModel *vertexModel, QAbstractItemModel *adjacencyModel)
224 {
225 if (m_vertexModel) {
226 disconnect(m_vertexModel, nullptr, this, nullptr);
227 }
228 m_vertexModel = vertexModel;
229 m_vertexModel->rowCount();
230 connect(m_vertexModel, &QAbstractItemModel::modelReset,
231 this, &SGWireframeWidget::onVertexModelReset);
232 connect(m_vertexModel, &QAbstractItemModel::dataChanged,
233 this, &SGWireframeWidget::onVertexModelDataChanged);
234 connect(m_vertexModel, &QAbstractItemModel::rowsInserted,
235 this, &SGWireframeWidget::onVertexModelRowsInserted);
236
237 if (m_adjacencyModel) {
238 disconnect(m_adjacencyModel, nullptr, this, nullptr);
239 }
240 m_adjacencyModel = adjacencyModel;
241 m_adjacencyModel->rowCount();
242 connect(m_adjacencyModel, &QAbstractItemModel::modelReset,
243 this, &SGWireframeWidget::onAdjacencyModelReset);
244 connect(m_adjacencyModel, &QAbstractItemModel::dataChanged,
245 this, &SGWireframeWidget::onAdjacencyModelDataChanged);
246 connect(m_adjacencyModel, &QAbstractItemModel::rowsInserted,
247 this, &SGWireframeWidget::onAdjacencyModelRowsInserted);
248 }
249
setHighlightModel(QItemSelectionModel * selectionModel)250 void SGWireframeWidget::setHighlightModel(QItemSelectionModel *selectionModel)
251 {
252 if (m_highlightModel)
253 disconnect(m_highlightModel, nullptr, this, nullptr);
254
255 m_highlightModel = selectionModel;
256
257 connect(m_highlightModel, &QItemSelectionModel::selectionChanged,
258 this, &SGWireframeWidget::onHighlightDataChanged);
259 }
260
onVertexModelReset()261 void SGWireframeWidget::onVertexModelReset()
262 {
263 fetchVertices();
264 update();
265 }
266
onAdjacencyModelReset()267 void SGWireframeWidget::onAdjacencyModelReset()
268 {
269 fetchAdjacencyList();
270 update();
271 }
272
onVertexModelRowsInserted(const QModelIndex & parent,int first,int last)273 void SGWireframeWidget::onVertexModelRowsInserted(const QModelIndex &parent, int first, int last)
274 {
275 Q_UNUSED(first);
276 Q_UNUSED(last);
277 if (!parent.isValid()) {
278 fetchVertices();
279 update();
280 }
281 }
282
onAdjacencyModelRowsInserted(const QModelIndex & parent,int first,int last)283 void SGWireframeWidget::onAdjacencyModelRowsInserted(const QModelIndex &parent, int first, int last)
284 {
285 Q_UNUSED(first);
286 Q_UNUSED(last);
287 if (!parent.isValid()) {
288 fetchAdjacencyList();
289 update();
290 }
291 }
292
onVertexModelDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)293 void SGWireframeWidget::onVertexModelDataChanged(const QModelIndex &topLeft,
294 const QModelIndex &bottomRight)
295 {
296 if (!topLeft.isValid()
297 || !bottomRight.isValid()
298 || m_positionColumn == -1
299 || (topLeft.column() <= m_positionColumn && bottomRight.column() >= m_positionColumn)) {
300 fetchVertices();
301 update();
302 }
303 }
304
onAdjacencyModelDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)305 void SGWireframeWidget::onAdjacencyModelDataChanged(const QModelIndex &topLeft,
306 const QModelIndex &bottomRight)
307 {
308 if (!topLeft.isValid()
309 || !bottomRight.isValid()
310 || m_positionColumn == -1
311 || (topLeft.column() <= m_positionColumn && bottomRight.column() >= m_positionColumn)) {
312 fetchAdjacencyList();
313 update();
314 }
315 }
316
fetchVertices()317 void SGWireframeWidget::fetchVertices()
318 {
319 // Get the column in which the vertex position data is stored in
320 if (m_positionColumn == -1) {
321 for (int j = 0; j < m_vertexModel->columnCount(); j++) {
322 if (m_vertexModel->data(m_vertexModel->index(0, j),
323 SGVertexModel::IsCoordinateRole).toBool()) {
324 m_positionColumn = j;
325 break;
326 }
327 }
328 }
329
330 // Get all the vertices
331 const int verticesCount = m_vertexModel->rowCount();
332 m_vertices.clear();
333 m_vertices.reserve(verticesCount);
334 m_geometryWidth = 0;
335 m_geometryHeight = 0;
336 for (int i = 0; i < verticesCount; i++) {
337 const QModelIndex index = m_vertexModel->index(i, m_positionColumn);
338 const QVariantList data = m_vertexModel->data(index, SGVertexModel::RenderRole).toList();
339 if (data.isEmpty()) {
340 continue; // The rest of the data will be incomplete as well,
341 // but we need to fetch everything.
342 }
343 if (data.size() >= 2) {
344 const qreal x = data[0].toReal();
345 const qreal y = data[1].toReal();
346 m_vertices << QPointF(x, y);
347 if (x > m_geometryWidth)
348 m_geometryWidth = x;
349 if (y > m_geometryHeight)
350 m_geometryHeight = y;
351 }
352 }
353 }
354
fetchAdjacencyList()355 void SGWireframeWidget::fetchAdjacencyList()
356 {
357 m_drawingMode = m_adjacencyModel->index(0, 0).data(SGAdjacencyModel::DrawingModeRole).toUInt();
358
359 // Get all the wires
360 m_adjacencyList.clear();
361 for (int i = 0; i < m_adjacencyModel->rowCount(); i++) {
362 const QModelIndex index = m_adjacencyModel->index(i, 0);
363 const QVariant data = m_adjacencyModel->data(index, SGAdjacencyModel::RenderRole);
364 if (!data.isValid()) {
365 continue; // The rest of the data will be incomplete as well,
366 // but we need to fetch everything.
367 }
368 const quint32 value = data.value<quint32>();
369 m_adjacencyList << value;
370 }
371 }
372
onHighlightDataChanged(const QItemSelection & selected,const QItemSelection & deselected)373 void SGWireframeWidget::onHighlightDataChanged(const QItemSelection &selected,
374 const QItemSelection &deselected)
375 {
376 foreach (const QModelIndex &index, deselected.indexes()) {
377 m_highlightedVertices.remove(index.row());
378 }
379 foreach (const QModelIndex &index, selected.indexes()) {
380 if (!m_highlightedVertices.contains(index.row()))
381 m_highlightedVertices << index.row();
382 }
383
384 update();
385 }
386
mouseReleaseEvent(QMouseEvent * e)387 void SGWireframeWidget::mouseReleaseEvent(QMouseEvent *e)
388 {
389 if (~e->modifiers() & Qt::ControlModifier)
390 m_highlightModel->clear();
391
392 for (int i = 0; i < m_vertices.size(); i++) {
393 int distance = QLineF(e->pos(), m_vertices.at(i) * m_zoom + m_offset).length();
394 if (distance <= 5) {
395 if (e->modifiers() & Qt::ControlModifier) {
396 m_highlightModel->select(m_vertexModel->index(i, m_positionColumn),
397 QItemSelectionModel::Toggle);
398 } else {
399 m_highlightModel->select(m_vertexModel->index(i, m_positionColumn),
400 QItemSelectionModel::Select);
401 }
402 }
403 }
404
405 QWidget::mouseReleaseEvent(e);
406 }
407