2023-03-02 13:45:23 +00:00
|
|
|
// Copyright (C) 2023 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
|
|
|
|
|
2023-11-23 18:02:05 +00:00
|
|
|
#include <QtCore/QMutexLocker>
|
2023-05-17 11:41:57 +00:00
|
|
|
#include "private/qquick3drepeater_p.h"
|
2023-03-02 13:45:23 +00:00
|
|
|
#include "qquickgraphssurface_p.h"
|
|
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
#include "qcategory3daxis_p.h"
|
2023-11-24 07:27:33 +00:00
|
|
|
#include "qgraphsinputhandler_p.h"
|
2023-09-26 22:59:15 +00:00
|
|
|
#include "qquickgraphssurface_p.h"
|
2023-10-13 08:56:18 +00:00
|
|
|
#include "qquickgraphstexturedata_p.h"
|
2023-09-26 22:59:15 +00:00
|
|
|
#include "qsurface3dseries_p.h"
|
|
|
|
|
#include "qsurfacedataproxy_p.h"
|
2023-03-02 13:45:23 +00:00
|
|
|
#include "qvalue3daxis_p.h"
|
2023-10-13 08:56:18 +00:00
|
|
|
#include "surfaceselectioninstancing_p.h"
|
2023-03-02 13:45:23 +00:00
|
|
|
|
2023-03-24 09:57:43 +00:00
|
|
|
#include <QtQuick3D/private/qquick3dcustommaterial_p.h>
|
2023-10-13 08:56:18 +00:00
|
|
|
#include <QtQuick3D/private/qquick3ddefaultmaterial_p.h>
|
|
|
|
|
#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
|
2023-03-02 13:45:23 +00:00
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
|
|
QQuickGraphsSurface::QQuickGraphsSurface(QQuickItem *parent)
|
2023-09-26 22:59:15 +00:00
|
|
|
: QQuickGraphsItem(parent)
|
2023-03-02 13:45:23 +00:00
|
|
|
{
|
2024-02-09 14:23:04 +00:00
|
|
|
m_graphType = QAbstract3DSeries::SeriesType::Surface;
|
2023-09-26 22:59:15 +00:00
|
|
|
setAxisX(0);
|
|
|
|
|
setAxisY(0);
|
|
|
|
|
setAxisZ(0);
|
2023-03-02 13:45:23 +00:00
|
|
|
setAcceptedMouseButtons(Qt::AllButtons);
|
2023-09-26 22:59:15 +00:00
|
|
|
clearSelection();
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QQuickGraphsSurface::~QQuickGraphsSurface()
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker locker(m_nodeMutex.data());
|
|
|
|
|
const QMutexLocker locker2(mutex());
|
|
|
|
|
for (auto model : m_model)
|
|
|
|
|
delete model;
|
2023-04-11 05:10:15 +00:00
|
|
|
delete m_instancing;
|
|
|
|
|
delete m_sliceInstancing;
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::setAxisX(QValue3DAxis *axis)
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
QQuickGraphsItem::setAxisX(axis);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
QValue3DAxis *QQuickGraphsSurface::axisX() const
|
2023-03-02 13:45:23 +00:00
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
return static_cast<QValue3DAxis *>(QQuickGraphsItem::axisX());
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::setAxisY(QValue3DAxis *axis)
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
QQuickGraphsItem::setAxisY(axis);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
QValue3DAxis *QQuickGraphsSurface::axisY() const
|
2023-03-02 13:45:23 +00:00
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
return static_cast<QValue3DAxis *>(QQuickGraphsItem::axisY());
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::setAxisZ(QValue3DAxis *axis)
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
QQuickGraphsItem::setAxisZ(axis);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QValue3DAxis *QQuickGraphsSurface::axisZ() const
|
|
|
|
|
{
|
|
|
|
|
return static_cast<QValue3DAxis *>(QQuickGraphsItem::axisZ());
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleFlatShadingEnabledChanged()
|
|
|
|
|
{
|
|
|
|
|
auto series = static_cast<QSurface3DSeries *>(sender());
|
|
|
|
|
for (auto model : m_model) {
|
|
|
|
|
if (model->series == series) {
|
|
|
|
|
updateModel(model);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleWireframeColorChanged()
|
|
|
|
|
{
|
|
|
|
|
for (auto model : m_model) {
|
|
|
|
|
QQmlListReference gridMaterialRef(model->gridModel, "materials");
|
2023-05-05 10:51:27 +00:00
|
|
|
auto gridMaterial = gridMaterialRef.at(0);
|
2023-03-02 13:45:23 +00:00
|
|
|
QColor gridColor = model->series->wireframeColor();
|
2023-05-05 10:51:27 +00:00
|
|
|
gridMaterial->setProperty("gridColor", gridColor);
|
2023-03-02 13:45:23 +00:00
|
|
|
|
|
|
|
|
if (sliceView()) {
|
|
|
|
|
QQmlListReference gridMaterialRef(model->sliceGridModel, "materials");
|
|
|
|
|
auto gridMaterial = static_cast<QQuick3DPrincipledMaterial *>(gridMaterialRef.at(0));
|
|
|
|
|
gridMaterial->setBaseColor(gridColor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-17 11:41:57 +00:00
|
|
|
void QQuickGraphsSurface::handleFlipHorizontalGridChanged(bool flip)
|
|
|
|
|
{
|
|
|
|
|
float factor = -1.0f;
|
|
|
|
|
if (isGridUpdated())
|
|
|
|
|
factor = flip ? -1.0f : 1.0f;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < repeaterX()->count(); i++) {
|
2023-08-01 13:43:24 +00:00
|
|
|
QQuick3DNode *obj = static_cast<QQuick3DNode *>(repeaterX()->objectAt(i));
|
2023-05-17 11:41:57 +00:00
|
|
|
QVector3D pos = obj->position();
|
|
|
|
|
pos.setY(pos.y() * factor);
|
|
|
|
|
obj->setPosition(pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < repeaterZ()->count(); i++) {
|
2023-08-01 13:43:24 +00:00
|
|
|
QQuick3DNode *obj = static_cast<QQuick3DNode *>(repeaterZ()->objectAt(i));
|
2023-05-17 11:41:57 +00:00
|
|
|
QVector3D pos = obj->position();
|
|
|
|
|
pos.setY(pos.y() * factor);
|
|
|
|
|
obj->setPosition(pos);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-01 13:43:24 +00:00
|
|
|
QVector3D pos = titleLabelX()->position();
|
|
|
|
|
pos.setY(pos.y() * factor);
|
|
|
|
|
titleLabelX()->setPosition(pos);
|
|
|
|
|
|
|
|
|
|
pos = titleLabelZ()->position();
|
|
|
|
|
pos.setY(pos.y() * factor);
|
|
|
|
|
titleLabelZ()->setPosition(pos);
|
|
|
|
|
|
2023-05-17 11:41:57 +00:00
|
|
|
setGridUpdated(false);
|
|
|
|
|
emit flipHorizontalGridChanged(flip);
|
2023-09-26 22:59:15 +00:00
|
|
|
setFlipHorizontalGridChanged(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::adjustAxisRanges()
|
|
|
|
|
{
|
|
|
|
|
QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
|
|
|
|
|
QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY);
|
|
|
|
|
QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ);
|
|
|
|
|
bool adjustX = (valueAxisX && valueAxisX->isAutoAdjustRange());
|
|
|
|
|
bool adjustY = (valueAxisY && valueAxisY->isAutoAdjustRange());
|
|
|
|
|
bool adjustZ = (valueAxisZ && valueAxisZ->isAutoAdjustRange());
|
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
|
|
if (adjustX || adjustY || adjustZ) {
|
|
|
|
|
float minValueX = 0.0f;
|
|
|
|
|
float maxValueX = 0.0f;
|
|
|
|
|
float minValueY = 0.0f;
|
|
|
|
|
float maxValueY = 0.0f;
|
|
|
|
|
float minValueZ = 0.0f;
|
|
|
|
|
float maxValueZ = 0.0f;
|
|
|
|
|
int seriesCount = m_seriesList.size();
|
|
|
|
|
for (int series = 0; series < seriesCount; series++) {
|
2023-10-13 08:56:18 +00:00
|
|
|
const QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(
|
|
|
|
|
m_seriesList.at(series));
|
2023-09-26 22:59:15 +00:00
|
|
|
const QSurfaceDataProxy *proxy = surfaceSeries->dataProxy();
|
|
|
|
|
if (surfaceSeries->isVisible() && proxy) {
|
|
|
|
|
QVector3D minLimits;
|
|
|
|
|
QVector3D maxLimits;
|
2023-10-13 08:56:18 +00:00
|
|
|
proxy->d_func()->limitValues(minLimits,
|
|
|
|
|
maxLimits,
|
|
|
|
|
valueAxisX,
|
|
|
|
|
valueAxisY,
|
|
|
|
|
valueAxisZ);
|
2023-09-26 22:59:15 +00:00
|
|
|
if (adjustX) {
|
|
|
|
|
if (first) {
|
|
|
|
|
// First series initializes the values
|
|
|
|
|
minValueX = minLimits.x();
|
|
|
|
|
maxValueX = maxLimits.x();
|
|
|
|
|
} else {
|
|
|
|
|
minValueX = qMin(minValueX, minLimits.x());
|
|
|
|
|
maxValueX = qMax(maxValueX, maxLimits.x());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (adjustY) {
|
|
|
|
|
if (first) {
|
|
|
|
|
// First series initializes the values
|
|
|
|
|
minValueY = minLimits.y();
|
|
|
|
|
maxValueY = maxLimits.y();
|
|
|
|
|
} else {
|
|
|
|
|
minValueY = qMin(minValueY, minLimits.y());
|
|
|
|
|
maxValueY = qMax(maxValueY, maxLimits.y());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (adjustZ) {
|
|
|
|
|
if (first) {
|
|
|
|
|
// First series initializes the values
|
|
|
|
|
minValueZ = minLimits.z();
|
|
|
|
|
maxValueZ = maxLimits.z();
|
|
|
|
|
} else {
|
|
|
|
|
minValueZ = qMin(minValueZ, minLimits.z());
|
|
|
|
|
maxValueZ = qMax(maxValueZ, maxLimits.z());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const float adjustmentRatio = 20.0f;
|
|
|
|
|
static const float defaultAdjustment = 1.0f;
|
|
|
|
|
|
|
|
|
|
if (adjustX) {
|
|
|
|
|
// If all points at same coordinate, need to default to some valid range
|
|
|
|
|
float adjustment = 0.0f;
|
|
|
|
|
if (minValueX == maxValueX) {
|
|
|
|
|
if (adjustZ) {
|
2023-10-13 08:56:18 +00:00
|
|
|
// X and Z are linked to have similar unit size, so choose the valid
|
|
|
|
|
// range based on it
|
2023-09-26 22:59:15 +00:00
|
|
|
if (minValueZ == maxValueZ)
|
|
|
|
|
adjustment = defaultAdjustment;
|
|
|
|
|
else
|
|
|
|
|
adjustment = qAbs(maxValueZ - minValueZ) / adjustmentRatio;
|
|
|
|
|
} else {
|
|
|
|
|
if (valueAxisZ)
|
|
|
|
|
adjustment = qAbs(valueAxisZ->max() - valueAxisZ->min()) / adjustmentRatio;
|
|
|
|
|
else
|
|
|
|
|
adjustment = defaultAdjustment;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
valueAxisX->d_func()->setRange(minValueX - adjustment, maxValueX + adjustment, true);
|
|
|
|
|
}
|
|
|
|
|
if (adjustY) {
|
|
|
|
|
// If all points at same coordinate, need to default to some valid range
|
|
|
|
|
// Y-axis unit is not dependent on other axes, so simply adjust +-1.0f
|
|
|
|
|
float adjustment = 0.0f;
|
|
|
|
|
if (minValueY == maxValueY)
|
|
|
|
|
adjustment = defaultAdjustment;
|
|
|
|
|
valueAxisY->d_func()->setRange(minValueY - adjustment, maxValueY + adjustment, true);
|
|
|
|
|
}
|
|
|
|
|
if (adjustZ) {
|
|
|
|
|
// If all points at same coordinate, need to default to some valid range
|
|
|
|
|
float adjustment = 0.0f;
|
|
|
|
|
if (minValueZ == maxValueZ) {
|
|
|
|
|
if (adjustX) {
|
2023-10-13 08:56:18 +00:00
|
|
|
// X and Z are linked to have similar unit size, so choose the valid
|
|
|
|
|
// range based on it
|
2023-09-26 22:59:15 +00:00
|
|
|
if (minValueX == maxValueX)
|
|
|
|
|
adjustment = defaultAdjustment;
|
|
|
|
|
else
|
|
|
|
|
adjustment = qAbs(maxValueX - minValueX) / adjustmentRatio;
|
|
|
|
|
} else {
|
|
|
|
|
if (valueAxisX)
|
|
|
|
|
adjustment = qAbs(valueAxisX->max() - valueAxisX->min()) / adjustmentRatio;
|
|
|
|
|
else
|
|
|
|
|
adjustment = defaultAdjustment;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
valueAxisZ->d_func()->setRange(minValueZ - adjustment, maxValueZ + adjustment, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleArrayReset()
|
|
|
|
|
{
|
|
|
|
|
QSurface3DSeries *series;
|
|
|
|
|
if (qobject_cast<QSurfaceDataProxy *>(sender()))
|
|
|
|
|
series = static_cast<QSurfaceDataProxy *>(sender())->series();
|
|
|
|
|
else
|
|
|
|
|
series = static_cast<QSurface3DSeries *>(sender());
|
|
|
|
|
|
|
|
|
|
if (series->isVisible()) {
|
|
|
|
|
adjustAxisRanges();
|
2023-12-04 09:50:55 +00:00
|
|
|
setDataDirty(true);
|
2023-09-26 22:59:15 +00:00
|
|
|
}
|
|
|
|
|
if (!m_changedSeriesList.contains(series))
|
|
|
|
|
m_changedSeriesList.append(series);
|
|
|
|
|
|
|
|
|
|
// Clear selection unless still valid
|
|
|
|
|
setSelectedPoint(m_selectedPoint, m_selectedSeries, false);
|
|
|
|
|
series->d_func()->markItemLabelDirty();
|
|
|
|
|
emitNeedRender();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleFlatShadingSupportedChange(bool supported)
|
|
|
|
|
{
|
2023-10-13 08:56:18 +00:00
|
|
|
// Handle renderer flat surface support indicator signal. This happens exactly
|
|
|
|
|
// once per renderer.
|
2023-09-26 22:59:15 +00:00
|
|
|
if (m_flatShadingSupported != supported) {
|
|
|
|
|
m_flatShadingSupported = supported;
|
|
|
|
|
// Emit the change for all added surfaces
|
|
|
|
|
for (QAbstract3DSeries *series : m_seriesList) {
|
|
|
|
|
QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series);
|
|
|
|
|
emit surfaceSeries->flatShadingSupportedChanged(m_flatShadingSupported);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleRowsChanged(int startIndex, int count)
|
|
|
|
|
{
|
|
|
|
|
QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(QObject::sender())->series();
|
|
|
|
|
int oldChangeCount = m_changedRows.size();
|
|
|
|
|
if (!oldChangeCount)
|
|
|
|
|
m_changedRows.reserve(count);
|
|
|
|
|
|
|
|
|
|
int selectedRow = m_selectedPoint.x();
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
|
bool newItem = true;
|
|
|
|
|
int candidate = startIndex + i;
|
|
|
|
|
for (int j = 0; j < oldChangeCount; j++) {
|
|
|
|
|
const ChangeRow &oldChangeItem = m_changedRows.at(j);
|
|
|
|
|
if (oldChangeItem.row == candidate && series == oldChangeItem.series) {
|
|
|
|
|
newItem = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (newItem) {
|
|
|
|
|
ChangeRow newChangeItem = {series, candidate};
|
|
|
|
|
m_changedRows.append(newChangeItem);
|
|
|
|
|
if (series == m_selectedSeries && selectedRow == candidate)
|
|
|
|
|
series->d_func()->markItemLabelDirty();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (count) {
|
|
|
|
|
m_changeTracker.rowsChanged = true;
|
2023-12-20 12:33:41 +00:00
|
|
|
setDataDirty(true);
|
2023-09-26 22:59:15 +00:00
|
|
|
|
|
|
|
|
if (series->isVisible())
|
|
|
|
|
adjustAxisRanges();
|
|
|
|
|
emitNeedRender();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleItemChanged(int rowIndex, int columnIndex)
|
|
|
|
|
{
|
|
|
|
|
QSurfaceDataProxy *sender = static_cast<QSurfaceDataProxy *>(QObject::sender());
|
|
|
|
|
QSurface3DSeries *series = sender->series();
|
|
|
|
|
|
|
|
|
|
bool newItem = true;
|
|
|
|
|
QPoint candidate(rowIndex, columnIndex);
|
|
|
|
|
for (ChangeItem item : m_changedItems) {
|
|
|
|
|
if (item.point == candidate && item.series == series) {
|
|
|
|
|
newItem = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (newItem) {
|
|
|
|
|
ChangeItem newItem = {series, candidate};
|
|
|
|
|
m_changedItems.append(newItem);
|
|
|
|
|
m_changeTracker.itemChanged = true;
|
2023-12-20 12:33:41 +00:00
|
|
|
setDataDirty(true);
|
2023-09-26 22:59:15 +00:00
|
|
|
|
|
|
|
|
if (series == m_selectedSeries && m_selectedPoint == candidate)
|
|
|
|
|
series->d_func()->markItemLabelDirty();
|
|
|
|
|
|
|
|
|
|
if (series->isVisible())
|
|
|
|
|
adjustAxisRanges();
|
|
|
|
|
emitNeedRender();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleRowsAdded(int startIndex, int count)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(startIndex);
|
|
|
|
|
Q_UNUSED(count);
|
|
|
|
|
QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(sender())->series();
|
|
|
|
|
if (series->isVisible()) {
|
|
|
|
|
adjustAxisRanges();
|
2023-12-04 09:50:55 +00:00
|
|
|
setDataDirty(true);
|
2023-09-26 22:59:15 +00:00
|
|
|
}
|
|
|
|
|
if (!m_changedSeriesList.contains(series))
|
|
|
|
|
m_changedSeriesList.append(series);
|
|
|
|
|
emitNeedRender();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleRowsInserted(int startIndex, int count)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(startIndex);
|
|
|
|
|
Q_UNUSED(count);
|
|
|
|
|
QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(sender())->series();
|
|
|
|
|
if (series == m_selectedSeries) {
|
|
|
|
|
// If rows inserted to selected series before the selection, adjust the selection
|
|
|
|
|
int selectedRow = m_selectedPoint.x();
|
|
|
|
|
if (startIndex <= selectedRow) {
|
|
|
|
|
selectedRow += count;
|
|
|
|
|
setSelectedPoint(QPoint(selectedRow, m_selectedPoint.y()), m_selectedSeries, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (series->isVisible()) {
|
|
|
|
|
adjustAxisRanges();
|
2023-12-04 09:50:55 +00:00
|
|
|
setDataDirty(true);
|
2023-09-26 22:59:15 +00:00
|
|
|
}
|
|
|
|
|
if (!m_changedSeriesList.contains(series))
|
|
|
|
|
m_changedSeriesList.append(series);
|
|
|
|
|
|
|
|
|
|
emitNeedRender();
|
2023-05-17 11:41:57 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
void QQuickGraphsSurface::handleRowsRemoved(int startIndex, int count)
|
2023-03-02 13:45:23 +00:00
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
Q_UNUSED(startIndex);
|
|
|
|
|
Q_UNUSED(count);
|
|
|
|
|
QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(sender())->series();
|
|
|
|
|
if (series == m_selectedSeries) {
|
|
|
|
|
// If rows removed from selected series before the selection, adjust the selection
|
|
|
|
|
int selectedRow = m_selectedPoint.x();
|
|
|
|
|
if (startIndex <= selectedRow) {
|
|
|
|
|
if ((startIndex + count) > selectedRow)
|
|
|
|
|
selectedRow = -1; // Selected row removed
|
|
|
|
|
else
|
|
|
|
|
selectedRow -= count; // Move selected row down by amount of rows removed
|
|
|
|
|
|
|
|
|
|
setSelectedPoint(QPoint(selectedRow, m_selectedPoint.y()), m_selectedSeries, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (series->isVisible()) {
|
|
|
|
|
adjustAxisRanges();
|
2023-12-04 09:50:55 +00:00
|
|
|
setDataDirty(true);
|
2023-09-26 22:59:15 +00:00
|
|
|
}
|
|
|
|
|
if (!m_changedSeriesList.contains(series))
|
|
|
|
|
m_changedSeriesList.append(series);
|
|
|
|
|
|
|
|
|
|
emitNeedRender();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPoint QQuickGraphsSurface::invalidSelectionPosition()
|
|
|
|
|
{
|
|
|
|
|
static QPoint invalidSelectionPoint(-1, -1);
|
|
|
|
|
return invalidSelectionPoint;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
void QQuickGraphsSurface::setSelectedPoint(const QPoint &position,
|
|
|
|
|
QSurface3DSeries *series,
|
2023-09-26 22:59:15 +00:00
|
|
|
bool enterSlice)
|
|
|
|
|
{
|
|
|
|
|
// If the selection targets non-existent point, clear selection instead.
|
|
|
|
|
QPoint pos = position;
|
|
|
|
|
|
|
|
|
|
// Series may already have been removed, so check it before setting the selection.
|
|
|
|
|
if (!m_seriesList.contains(series))
|
|
|
|
|
series = 0;
|
|
|
|
|
|
|
|
|
|
const QSurfaceDataProxy *proxy = 0;
|
|
|
|
|
if (series)
|
|
|
|
|
proxy = series->dataProxy();
|
|
|
|
|
|
|
|
|
|
if (!proxy)
|
|
|
|
|
pos = invalidSelectionPosition();
|
|
|
|
|
|
|
|
|
|
if (pos != invalidSelectionPosition()) {
|
|
|
|
|
int maxRow = proxy->rowCount() - 1;
|
|
|
|
|
int maxCol = proxy->columnCount() - 1;
|
|
|
|
|
|
2023-11-14 07:22:44 +00:00
|
|
|
if (pos.y() < 0 || pos.y() > maxRow || pos.x() < 0 || pos.x() > maxCol)
|
2023-09-26 22:59:15 +00:00
|
|
|
pos = invalidSelectionPosition();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectionMode().testFlag(QAbstract3DGraph::SelectionSlice)) {
|
|
|
|
|
if (pos == invalidSelectionPosition() || !series->isVisible()) {
|
|
|
|
|
scene()->setSlicingActive(false);
|
|
|
|
|
} else {
|
2023-10-13 08:56:18 +00:00
|
|
|
// If the selected point is outside data window, or there is no selected
|
|
|
|
|
// point, disable slicing
|
2023-09-26 22:59:15 +00:00
|
|
|
float axisMinX = m_axisX->min();
|
|
|
|
|
float axisMaxX = m_axisX->max();
|
|
|
|
|
float axisMinZ = m_axisZ->min();
|
|
|
|
|
float axisMaxZ = m_axisZ->max();
|
|
|
|
|
|
2024-01-17 11:41:50 +00:00
|
|
|
QSurfaceDataItem item = series->dataArray().at(pos.y()).at(pos.x());
|
2023-10-13 08:56:18 +00:00
|
|
|
if (item.x() < axisMinX || item.x() > axisMaxX || item.z() < axisMinZ
|
|
|
|
|
|| item.z() > axisMaxZ) {
|
2023-09-26 22:59:15 +00:00
|
|
|
scene()->setSlicingActive(false);
|
|
|
|
|
} else if (enterSlice) {
|
|
|
|
|
scene()->setSlicingActive(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
emitNeedRender();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pos != m_selectedPoint || series != m_selectedSeries) {
|
|
|
|
|
bool seriesChanged = (series != m_selectedSeries);
|
|
|
|
|
m_selectedPoint = pos;
|
|
|
|
|
m_selectedSeries = series;
|
|
|
|
|
m_changeTracker.selectedPointChanged = true;
|
|
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
// Clear selection from other series and finally set new selection to the
|
|
|
|
|
// specified series
|
2023-09-26 22:59:15 +00:00
|
|
|
for (QAbstract3DSeries *otherSeries : m_seriesList) {
|
|
|
|
|
QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(otherSeries);
|
|
|
|
|
if (surfaceSeries != m_selectedSeries)
|
|
|
|
|
surfaceSeries->d_func()->setSelectedPoint(invalidSelectionPosition());
|
|
|
|
|
}
|
|
|
|
|
if (m_selectedSeries)
|
|
|
|
|
m_selectedSeries->d_func()->setSelectedPoint(m_selectedPoint);
|
|
|
|
|
|
|
|
|
|
if (seriesChanged)
|
|
|
|
|
emit selectedSeriesChanged(m_selectedSeries);
|
|
|
|
|
|
|
|
|
|
emitNeedRender();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::setSelectionMode(QAbstract3DGraph::SelectionFlags mode)
|
|
|
|
|
{
|
|
|
|
|
// Currently surface only supports row and column modes when also slicing
|
|
|
|
|
if ((mode.testFlag(QAbstract3DGraph::SelectionRow)
|
|
|
|
|
|| mode.testFlag(QAbstract3DGraph::SelectionColumn))
|
2023-10-13 08:56:18 +00:00
|
|
|
&& !mode.testFlag(QAbstract3DGraph::SelectionSlice)) {
|
2023-09-26 22:59:15 +00:00
|
|
|
qWarning("Unsupported selection mode.");
|
|
|
|
|
return;
|
|
|
|
|
} else if (mode.testFlag(QAbstract3DGraph::SelectionSlice)
|
|
|
|
|
&& (mode.testFlag(QAbstract3DGraph::SelectionRow)
|
|
|
|
|
== mode.testFlag(QAbstract3DGraph::SelectionColumn))) {
|
|
|
|
|
qWarning("Must specify one of either row or column selection mode"
|
|
|
|
|
"in conjunction with slicing mode.");
|
|
|
|
|
} else {
|
|
|
|
|
QAbstract3DGraph::SelectionFlags oldMode = selectionMode();
|
|
|
|
|
|
|
|
|
|
QQuickGraphsItem::setSelectionMode(mode);
|
|
|
|
|
|
|
|
|
|
if (mode != oldMode) {
|
2023-10-13 08:56:18 +00:00
|
|
|
// Refresh selection upon mode change to ensure slicing is correctly
|
|
|
|
|
// updated according to series the visibility.
|
2023-09-26 22:59:15 +00:00
|
|
|
setSelectedPoint(m_selectedPoint, m_selectedSeries, true);
|
|
|
|
|
|
|
|
|
|
// Special case: Always deactivate slicing when changing away from slice
|
|
|
|
|
// automanagement, as this can't be handled in setSelectedBar.
|
|
|
|
|
if (!mode.testFlag(QAbstract3DGraph::SelectionSlice)
|
2023-10-13 08:56:18 +00:00
|
|
|
&& oldMode.testFlag(QAbstract3DGraph::SelectionSlice)) {
|
2023-09-26 22:59:15 +00:00
|
|
|
scene()->setSlicingActive(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleAxisAutoAdjustRangeChangedInOrientation(
|
|
|
|
|
QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(orientation);
|
|
|
|
|
Q_UNUSED(autoAdjust);
|
|
|
|
|
|
|
|
|
|
adjustAxisRanges();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleAxisRangeChangedBySender(QObject *sender)
|
|
|
|
|
{
|
|
|
|
|
QQuickGraphsItem::handleAxisRangeChangedBySender(sender);
|
|
|
|
|
|
|
|
|
|
// Update selected point - may be moved offscreen
|
|
|
|
|
setSelectedPoint(m_selectedPoint, m_selectedSeries, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleSeriesVisibilityChangedBySender(QObject *sender)
|
|
|
|
|
{
|
|
|
|
|
QQuickGraphsItem::handleSeriesVisibilityChangedBySender(sender);
|
|
|
|
|
|
2023-12-04 09:50:55 +00:00
|
|
|
setSeriesVisibilityDirty(true);
|
2023-09-26 22:59:15 +00:00
|
|
|
// Visibility changes may require disabling slicing,
|
|
|
|
|
// so just reset selection to ensure everything is still valid.
|
|
|
|
|
setSelectedPoint(m_selectedPoint, m_selectedSeries, false);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::setFlipHorizontalGrid(bool flip)
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
if (m_flipHorizontalGrid != flip) {
|
|
|
|
|
m_flipHorizontalGrid = flip;
|
|
|
|
|
m_changeTracker.flipHorizontalGridChanged = true;
|
|
|
|
|
emit flipHorizontalGridChanged(flip);
|
|
|
|
|
emitNeedRender();
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool QQuickGraphsSurface::flipHorizontalGrid() const
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
return m_flipHorizontalGrid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool QQuickGraphsSurface::isFlatShadingSupported()
|
|
|
|
|
{
|
|
|
|
|
return m_flatShadingSupported;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QList<QSurface3DSeries *> QQuickGraphsSurface::surfaceSeriesList()
|
|
|
|
|
{
|
|
|
|
|
QList<QSurface3DSeries *> surfaceSeriesList;
|
|
|
|
|
for (QAbstract3DSeries *abstractSeries : m_seriesList) {
|
|
|
|
|
QSurface3DSeries *surfaceSeries = qobject_cast<QSurface3DSeries *>(abstractSeries);
|
|
|
|
|
if (surfaceSeries)
|
|
|
|
|
surfaceSeriesList.append(surfaceSeries);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return surfaceSeriesList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::updateSurfaceTexture(QSurface3DSeries *series)
|
|
|
|
|
{
|
|
|
|
|
m_changeTracker.surfaceTextureChanged = true;
|
|
|
|
|
|
|
|
|
|
if (!m_changedTextures.contains(series))
|
|
|
|
|
m_changedTextures.append(series);
|
|
|
|
|
|
|
|
|
|
emitNeedRender();
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QQmlListProperty<QSurface3DSeries> QQuickGraphsSurface::seriesList()
|
|
|
|
|
{
|
2023-10-13 08:56:18 +00:00
|
|
|
return QQmlListProperty<QSurface3DSeries>(this,
|
|
|
|
|
this,
|
2023-03-02 13:45:23 +00:00
|
|
|
&QQuickGraphsSurface::appendSeriesFunc,
|
|
|
|
|
&QQuickGraphsSurface::countSeriesFunc,
|
|
|
|
|
&QQuickGraphsSurface::atSeriesFunc,
|
|
|
|
|
&QQuickGraphsSurface::clearSeriesFunc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::appendSeriesFunc(QQmlListProperty<QSurface3DSeries> *list,
|
|
|
|
|
QSurface3DSeries *series)
|
|
|
|
|
{
|
|
|
|
|
reinterpret_cast<QQuickGraphsSurface *>(list->data)->addSeries(series);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qsizetype QQuickGraphsSurface::countSeriesFunc(QQmlListProperty<QSurface3DSeries> *list)
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
return reinterpret_cast<QQuickGraphsSurface *>(list->data)->surfaceSeriesList().size();
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QSurface3DSeries *QQuickGraphsSurface::atSeriesFunc(QQmlListProperty<QSurface3DSeries> *list,
|
|
|
|
|
qsizetype index)
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
return reinterpret_cast<QQuickGraphsSurface *>(list->data)->surfaceSeriesList().at(index);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::clearSeriesFunc(QQmlListProperty<QSurface3DSeries> *list)
|
|
|
|
|
{
|
|
|
|
|
QQuickGraphsSurface *declSurface = reinterpret_cast<QQuickGraphsSurface *>(list->data);
|
2023-09-26 22:59:15 +00:00
|
|
|
QList<QSurface3DSeries *> realList = declSurface->surfaceSeriesList();
|
2023-03-02 13:45:23 +00:00
|
|
|
int count = realList.size();
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
declSurface->removeSeries(realList.at(i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::addSeries(QSurface3DSeries *series)
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
Q_ASSERT(series && series->type() == QAbstract3DSeries::SeriesType::Surface);
|
|
|
|
|
|
|
|
|
|
QQuickGraphsItem::addSeriesInternal(series);
|
|
|
|
|
|
|
|
|
|
QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series);
|
|
|
|
|
if (surfaceSeries->selectedPoint() != invalidSelectionPosition())
|
|
|
|
|
setSelectedPoint(surfaceSeries->selectedPoint(), surfaceSeries, false);
|
|
|
|
|
|
|
|
|
|
if (!surfaceSeries->texture().isNull())
|
|
|
|
|
updateSurfaceTexture(surfaceSeries);
|
|
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
if (isReady())
|
|
|
|
|
addModel(series);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::removeSeries(QSurface3DSeries *series)
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
bool wasVisible = (series && series->d_func()->m_graph == this && series->isVisible());
|
|
|
|
|
|
|
|
|
|
QQuickGraphsItem::removeSeriesInternal(series);
|
|
|
|
|
|
|
|
|
|
if (m_selectedSeries == series)
|
|
|
|
|
setSelectedPoint(invalidSelectionPosition(), 0, false);
|
|
|
|
|
|
|
|
|
|
if (wasVisible)
|
|
|
|
|
adjustAxisRanges();
|
|
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
series->setParent(this); // Reparent as removing will leave series parentless
|
|
|
|
|
for (int i = 0; i < m_model.size();) {
|
|
|
|
|
if (m_model[i]->series == series) {
|
|
|
|
|
m_model[i]->model->deleteLater();
|
|
|
|
|
m_model[i]->gridModel->deleteLater();
|
2023-12-20 07:18:31 +00:00
|
|
|
if (const auto &proxy = m_model[i]->proxyModel)
|
|
|
|
|
proxy->deleteLater();
|
2023-04-05 10:07:42 +00:00
|
|
|
if (sliceView()) {
|
|
|
|
|
m_model[i]->sliceModel->deleteLater();
|
|
|
|
|
m_model[i]->sliceGridModel->deleteLater();
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
m_model.removeAt(i);
|
|
|
|
|
} else {
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
void QQuickGraphsSurface::clearSelection()
|
|
|
|
|
{
|
|
|
|
|
setSelectedPoint(invalidSelectionPosition(), 0, false);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
void QQuickGraphsSurface::handleAxisXChanged(QAbstract3DAxis *axis)
|
|
|
|
|
{
|
|
|
|
|
emit axisXChanged(static_cast<QValue3DAxis *>(axis));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleAxisYChanged(QAbstract3DAxis *axis)
|
|
|
|
|
{
|
|
|
|
|
emit axisYChanged(static_cast<QValue3DAxis *>(axis));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::handleAxisZChanged(QAbstract3DAxis *axis)
|
|
|
|
|
{
|
|
|
|
|
emit axisZChanged(static_cast<QValue3DAxis *>(axis));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::componentComplete()
|
|
|
|
|
{
|
|
|
|
|
QQuickGraphsItem::componentComplete();
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
for (auto series : surfaceSeriesList())
|
2023-03-02 13:45:23 +00:00
|
|
|
addModel(series);
|
|
|
|
|
|
|
|
|
|
QQuick3DNode *parent = rootNode();
|
|
|
|
|
|
|
|
|
|
m_selectionPointer = new QQuick3DModel();
|
|
|
|
|
m_selectionPointer->setParent(parent);
|
|
|
|
|
m_selectionPointer->setParentItem(parent);
|
|
|
|
|
m_selectionPointer->setSource(QUrl(QStringLiteral("#Sphere")));
|
|
|
|
|
auto pointerMaterial = new QQuick3DPrincipledMaterial();
|
|
|
|
|
pointerMaterial->setParent(this);
|
2023-09-26 22:59:15 +00:00
|
|
|
pointerMaterial->setBaseColor(theme()->singleHighlightColor());
|
2023-03-02 13:45:23 +00:00
|
|
|
QQmlListReference materialRef(m_selectionPointer, "materials");
|
|
|
|
|
materialRef.append(pointerMaterial);
|
|
|
|
|
m_instancing = new SurfaceSelectionInstancing();
|
|
|
|
|
m_instancing->setScale(QVector3D(0.001f, 0.001f, 0.001f));
|
|
|
|
|
m_selectionPointer->setInstancing(m_instancing);
|
2023-11-24 07:27:33 +00:00
|
|
|
|
|
|
|
|
graphsInputHandler()->setGraphsItem(this);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::synchData()
|
|
|
|
|
{
|
2024-01-12 09:09:31 +00:00
|
|
|
if (isFlipHorizontalGridChanged())
|
|
|
|
|
setHorizontalFlipFactor(flipHorizontalGrid() ? -1 : 1);
|
|
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
QQuickGraphsItem::synchData();
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
if (isSelectedPointChanged()) {
|
|
|
|
|
if (selectionMode().testFlag(QAbstract3DGraph::SelectionItem))
|
2023-03-02 13:45:23 +00:00
|
|
|
updateSelectedPoint();
|
2023-09-26 22:59:15 +00:00
|
|
|
setSelectedPointChanged(false);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
2023-05-17 11:41:57 +00:00
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
if (isGridUpdated() || isFlipHorizontalGridChanged())
|
|
|
|
|
handleFlipHorizontalGridChanged(flipHorizontalGrid());
|
2023-05-23 07:44:55 +00:00
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
if (isSurfaceTextureChanged()) {
|
|
|
|
|
if (!isChangedTexturesEmpty()) {
|
2023-05-23 07:44:55 +00:00
|
|
|
for (auto model : m_model) {
|
2023-09-26 22:59:15 +00:00
|
|
|
if (hasSeriesToChangeTexture(model->series))
|
2023-08-14 08:38:24 +00:00
|
|
|
updateMaterial(model);
|
2023-05-23 07:44:55 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-09-26 22:59:15 +00:00
|
|
|
setSurfaceTextureChanged(false);
|
2023-05-23 07:44:55 +00:00
|
|
|
}
|
2024-01-31 07:49:29 +00:00
|
|
|
|
|
|
|
|
if (theme()->isShaderGridEnabled()) {
|
|
|
|
|
if (!m_topGrid) {
|
|
|
|
|
//add horizontal top grid
|
|
|
|
|
QUrl topGridUrl = QUrl(QStringLiteral(":/defaultMeshes/barMeshFull"));
|
|
|
|
|
m_topGrid = new QQuick3DModel();
|
|
|
|
|
m_topGridScale = new QQuick3DNode();
|
|
|
|
|
m_topGridRotation = new QQuick3DNode();
|
|
|
|
|
|
|
|
|
|
m_topGridScale->setParent(rootNode());
|
|
|
|
|
m_topGridScale->setParentItem(rootNode());
|
|
|
|
|
|
|
|
|
|
m_topGridRotation->setParent(m_topGridScale);
|
|
|
|
|
m_topGridRotation->setParentItem(m_topGridScale);
|
|
|
|
|
|
|
|
|
|
m_topGrid->setObjectName("Top Grid");
|
|
|
|
|
m_topGrid->setParent(m_topGridRotation);
|
|
|
|
|
m_topGrid->setParentItem(m_topGridRotation);
|
|
|
|
|
|
|
|
|
|
m_topGrid->setSource(topGridUrl);
|
|
|
|
|
m_topGrid->setPickable(false);
|
|
|
|
|
}
|
|
|
|
|
auto min = qMin(scaleWithBackground().x() + backgroundScaleMargin().x(),
|
|
|
|
|
scaleWithBackground().z() + backgroundScaleMargin().z());
|
|
|
|
|
m_topGridScale->setScale(QVector3D(scaleWithBackground().x() + backgroundScaleMargin().x(),
|
|
|
|
|
min * gridOffset(),
|
|
|
|
|
scaleWithBackground().z() + backgroundScaleMargin().z()));
|
|
|
|
|
m_topGridScale->setPosition(
|
|
|
|
|
QVector3D(0.0f, scaleWithBackground().y() + backgroundScaleMargin().y(), 0.0f));
|
|
|
|
|
|
|
|
|
|
m_topGrid->setVisible(m_flipHorizontalGrid);
|
|
|
|
|
QQmlListReference materialsRefF(m_topGrid, "materials");
|
|
|
|
|
QQmlListReference bbRef(background(), "materials");
|
|
|
|
|
QQuick3DCustomMaterial *bgMatFloor;
|
|
|
|
|
if (!materialsRefF.size() && bbRef.size()) {
|
|
|
|
|
bgMatFloor = static_cast<QQuick3DCustomMaterial *>(bbRef.at(0));
|
|
|
|
|
materialsRefF.append(bgMatFloor);
|
|
|
|
|
bgMatFloor->setProperty("gridOnTop", m_flipHorizontalGrid);
|
|
|
|
|
} else if (materialsRefF.size()) {
|
|
|
|
|
bgMatFloor = static_cast<QQuick3DCustomMaterial *>(materialsRefF.at(0));
|
|
|
|
|
bgMatFloor->setProperty("gridOnTop", m_flipHorizontalGrid);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-03 14:20:45 +00:00
|
|
|
|
|
|
|
|
if (m_pickThisFrame) {
|
|
|
|
|
doPicking(m_lastPick);
|
|
|
|
|
m_pickThisFrame = false;
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::updateGraph()
|
|
|
|
|
{
|
2023-05-17 11:41:57 +00:00
|
|
|
for (auto model : m_model) {
|
2023-07-24 06:46:49 +00:00
|
|
|
bool seriesVisible = model->series->isVisible();
|
2023-09-26 22:59:15 +00:00
|
|
|
if (isSeriesVisibilityDirty()) {
|
2023-03-30 05:23:03 +00:00
|
|
|
if (!seriesVisible) {
|
|
|
|
|
model->model->setVisible(seriesVisible);
|
|
|
|
|
model->gridModel->setVisible(seriesVisible);
|
2023-03-02 13:45:23 +00:00
|
|
|
if (sliceView()) {
|
2023-03-30 05:23:03 +00:00
|
|
|
model->sliceModel->setVisible(seriesVisible);
|
|
|
|
|
model->sliceGridModel->setVisible(seriesVisible);
|
2023-12-25 11:06:58 +00:00
|
|
|
|
|
|
|
|
if (m_selectedSeries == model->series) {
|
2024-01-03 10:00:34 +00:00
|
|
|
clearSelection();
|
2023-12-25 11:06:58 +00:00
|
|
|
setSliceActivatedChanged(true);
|
|
|
|
|
m_selectionDirty = !seriesVisible;
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-24 06:46:49 +00:00
|
|
|
if (model->model->visible() != seriesVisible)
|
|
|
|
|
model->model->setVisible(seriesVisible);
|
2024-01-05 09:31:15 +00:00
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
model->gridModel->setVisible(
|
2024-01-05 09:31:15 +00:00
|
|
|
model->series->drawMode().testFlag(QSurface3DSeries::DrawWireframe) && seriesVisible);
|
2023-05-17 11:41:57 +00:00
|
|
|
if (model->series->drawMode().testFlag(QSurface3DSeries::DrawSurface))
|
|
|
|
|
model->model->setLocalOpacity(1.f);
|
|
|
|
|
else
|
|
|
|
|
model->model->setLocalOpacity(.0f);
|
2023-05-23 07:44:55 +00:00
|
|
|
|
|
|
|
|
if (sliceView() && sliceView()->isVisible()) {
|
2023-10-13 08:56:18 +00:00
|
|
|
model->sliceGridModel->setVisible(
|
|
|
|
|
model->series->drawMode().testFlag(QSurface3DSeries::DrawWireframe));
|
2023-07-25 06:58:01 +00:00
|
|
|
if (model->series->drawMode().testFlag(QSurface3DSeries::DrawSurface))
|
|
|
|
|
model->sliceModel->setLocalOpacity(1.f);
|
|
|
|
|
else
|
|
|
|
|
model->sliceModel->setLocalOpacity(.0f);
|
2023-05-23 07:44:55 +00:00
|
|
|
}
|
2023-08-14 08:38:24 +00:00
|
|
|
updateMaterial(model);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
setSeriesVisibilityDirty(false);
|
2023-10-13 08:56:18 +00:00
|
|
|
if (isDataDirty() || isSeriesVisualsDirty()) {
|
2023-04-05 10:07:42 +00:00
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
if (hasChangedSeriesList()) {
|
2023-04-28 08:59:41 +00:00
|
|
|
handleChangedSeries();
|
|
|
|
|
} else {
|
|
|
|
|
for (auto model : m_model) {
|
|
|
|
|
bool visible = model->series->isVisible();
|
|
|
|
|
if (visible)
|
|
|
|
|
updateModel(model);
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
2023-04-28 08:59:41 +00:00
|
|
|
|
2023-12-25 11:06:58 +00:00
|
|
|
if (isSliceEnabled()) {
|
|
|
|
|
if (!sliceView())
|
|
|
|
|
createSliceView();
|
|
|
|
|
|
2024-01-03 10:00:34 +00:00
|
|
|
if (sliceView()->isVisible()) {
|
|
|
|
|
if (!m_selectedSeries) {
|
|
|
|
|
m_selectionDirty = true;
|
2023-12-25 11:06:58 +00:00
|
|
|
setSliceActivatedChanged(true);
|
|
|
|
|
}
|
2024-01-03 10:00:34 +00:00
|
|
|
updateSliceGraph();
|
2023-12-25 11:06:58 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
setDataDirty(false);
|
|
|
|
|
setSeriesVisualsDirty(false);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
2023-08-09 06:04:08 +00:00
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
if (selectionMode().testFlag(QAbstract3DGraph::SelectionItem))
|
2023-08-09 06:04:08 +00:00
|
|
|
updateSelectedPoint();
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-07 14:08:28 +00:00
|
|
|
void QQuickGraphsSurface::calculateSceneScalingFactors()
|
|
|
|
|
{
|
|
|
|
|
float scaleX, scaleY, scaleZ;
|
|
|
|
|
float marginH, marginV;
|
|
|
|
|
|
|
|
|
|
if (margin() < 0.0f) {
|
|
|
|
|
marginH = .1f;
|
|
|
|
|
marginV = .1f;
|
|
|
|
|
} else {
|
|
|
|
|
marginH = margin();
|
|
|
|
|
marginV = margin();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isPolar()) {
|
|
|
|
|
float polarMargin = calculatePolarBackgroundMargin();
|
|
|
|
|
marginH = qMax(marginH, polarMargin);
|
|
|
|
|
}
|
|
|
|
|
float hAspectRatio;
|
|
|
|
|
if (isPolar())
|
|
|
|
|
hAspectRatio = 1.0f;
|
|
|
|
|
else
|
|
|
|
|
hAspectRatio = horizontalAspectRatio();
|
|
|
|
|
|
|
|
|
|
QSizeF areaSize;
|
|
|
|
|
if (qFuzzyIsNull(hAspectRatio)) {
|
|
|
|
|
areaSize.setHeight(axisZ()->max() - axisZ()->min());
|
|
|
|
|
areaSize.setWidth(axisX()->max() - axisX()->min());
|
|
|
|
|
} else {
|
|
|
|
|
areaSize.setHeight(1.0);
|
|
|
|
|
areaSize.setWidth(hAspectRatio);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float horizontalMaxDimension;
|
|
|
|
|
if (aspectRatio() > 2.0f) {
|
|
|
|
|
horizontalMaxDimension = 2.0f;
|
|
|
|
|
scaleY = 2.0f / aspectRatio();
|
|
|
|
|
} else {
|
|
|
|
|
horizontalMaxDimension = aspectRatio();
|
|
|
|
|
scaleY = 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isPolar())
|
|
|
|
|
m_polarRadius = horizontalMaxDimension;
|
|
|
|
|
|
|
|
|
|
float scaleFactor = qMax(areaSize.width(), areaSize.height());
|
|
|
|
|
scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor;
|
|
|
|
|
scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor;
|
|
|
|
|
|
|
|
|
|
setScale(QVector3D(scaleX, scaleY, scaleZ));
|
|
|
|
|
setScaleWithBackground(QVector3D(scaleX, scaleY, scaleZ));
|
|
|
|
|
setBackgroundScaleMargin(QVector3D(marginH, marginV, marginH));
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
void QQuickGraphsSurface::handleChangedSeries()
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
auto changedSeries = changedSeriesList();
|
2023-03-02 13:45:23 +00:00
|
|
|
for (auto series : changedSeries) {
|
|
|
|
|
for (auto model : m_model) {
|
|
|
|
|
if (model->series == series) {
|
|
|
|
|
updateModel(model);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-20 08:59:31 +00:00
|
|
|
inline static float getDataValue(const QSurfaceDataArray &array, bool searchRow, int index)
|
|
|
|
|
{
|
|
|
|
|
if (searchRow)
|
2023-09-08 11:39:55 +00:00
|
|
|
return array.at(0).at(index).x();
|
2023-09-20 08:59:31 +00:00
|
|
|
else
|
2023-09-08 11:39:55 +00:00
|
|
|
return array.at(index).at(0).z();
|
2023-09-20 08:59:31 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
inline static int binarySearchArray(const QSurfaceDataArray &array,
|
|
|
|
|
int maxIndex,
|
|
|
|
|
float limitValue,
|
|
|
|
|
bool searchRow,
|
|
|
|
|
bool lowBound,
|
|
|
|
|
bool ascending)
|
2023-09-20 08:59:31 +00:00
|
|
|
{
|
|
|
|
|
int min = 0;
|
|
|
|
|
int max = maxIndex;
|
|
|
|
|
int mid = 0;
|
|
|
|
|
int retVal;
|
|
|
|
|
|
|
|
|
|
while (max >= min) {
|
|
|
|
|
mid = (min + max) / 2;
|
|
|
|
|
float arrayValue = getDataValue(array, searchRow, mid);
|
|
|
|
|
if (arrayValue == limitValue)
|
|
|
|
|
return mid;
|
|
|
|
|
if (ascending) {
|
|
|
|
|
if (arrayValue < limitValue)
|
|
|
|
|
min = mid + 1;
|
|
|
|
|
else
|
2023-10-13 08:56:18 +00:00
|
|
|
max = mid - 1;
|
2023-09-20 08:59:31 +00:00
|
|
|
} else {
|
|
|
|
|
if (arrayValue > limitValue)
|
|
|
|
|
min = mid + 1;
|
|
|
|
|
else
|
|
|
|
|
max = mid - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lowBound == ascending) {
|
|
|
|
|
if (mid > max)
|
|
|
|
|
retVal = mid;
|
|
|
|
|
else
|
|
|
|
|
retVal = min;
|
|
|
|
|
} else {
|
|
|
|
|
if (mid > max)
|
|
|
|
|
retVal = max;
|
|
|
|
|
else
|
|
|
|
|
retVal = mid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (retVal < 0 || retVal > maxIndex) {
|
|
|
|
|
retVal = -1;
|
|
|
|
|
} else if (lowBound) {
|
|
|
|
|
if (getDataValue(array, searchRow, retVal) < limitValue)
|
|
|
|
|
retVal = -1;
|
|
|
|
|
} else {
|
|
|
|
|
if (getDataValue(array, searchRow, retVal) > limitValue)
|
|
|
|
|
retVal = -1;
|
|
|
|
|
}
|
|
|
|
|
return retVal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QRect QQuickGraphsSurface::calculateSampleSpace(const QSurfaceDataArray &array)
|
|
|
|
|
{
|
|
|
|
|
QRect sampleSpace;
|
|
|
|
|
if (array.size() > 0) {
|
2023-09-08 11:39:55 +00:00
|
|
|
if (array.size() >= 2 && array.at(0).size() >= 2) {
|
2023-09-20 08:59:31 +00:00
|
|
|
const int maxRow = array.size() - 1;
|
2023-09-08 11:39:55 +00:00
|
|
|
const int maxColumn = array.at(0).size() - 1;
|
2023-09-20 08:59:31 +00:00
|
|
|
|
2023-09-08 11:39:55 +00:00
|
|
|
const bool ascendingX = array.at(0).at(0).x() < array.at(0).at(maxColumn).x();
|
|
|
|
|
const bool ascendingZ = array.at(0).at(0).z() < array.at(maxRow).at(0).z();
|
2023-09-20 08:59:31 +00:00
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
int idx = binarySearchArray(array, maxColumn, axisX()->min(), true, true, ascendingX);
|
2023-09-20 08:59:31 +00:00
|
|
|
if (idx != -1) {
|
|
|
|
|
if (ascendingX)
|
|
|
|
|
sampleSpace.setLeft(idx);
|
|
|
|
|
else
|
|
|
|
|
sampleSpace.setRight(idx);
|
|
|
|
|
} else {
|
|
|
|
|
sampleSpace.setWidth(-1);
|
2024-01-10 12:57:58 +00:00
|
|
|
return sampleSpace;
|
2023-09-20 08:59:31 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
idx = binarySearchArray(array, maxColumn, axisX()->max(), true, false, ascendingX);
|
2023-09-20 08:59:31 +00:00
|
|
|
if (idx != -1) {
|
|
|
|
|
if (ascendingX)
|
|
|
|
|
sampleSpace.setRight(idx);
|
|
|
|
|
else
|
|
|
|
|
sampleSpace.setLeft(idx);
|
|
|
|
|
} else {
|
|
|
|
|
sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
|
2024-01-10 12:57:58 +00:00
|
|
|
return sampleSpace;
|
2023-09-20 08:59:31 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
idx = binarySearchArray(array, maxRow, axisZ()->min(), false, true, ascendingZ);
|
2023-09-20 08:59:31 +00:00
|
|
|
if (idx != -1) {
|
|
|
|
|
if (ascendingZ)
|
|
|
|
|
sampleSpace.setTop(idx);
|
|
|
|
|
else
|
|
|
|
|
sampleSpace.setBottom(idx);
|
|
|
|
|
} else {
|
|
|
|
|
sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
|
2024-01-10 12:57:58 +00:00
|
|
|
return sampleSpace;
|
2023-09-20 08:59:31 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
idx = binarySearchArray(array, maxRow, axisZ()->max(), false, false, ascendingZ);
|
2023-09-20 08:59:31 +00:00
|
|
|
if (idx != -1) {
|
|
|
|
|
if (ascendingZ)
|
|
|
|
|
sampleSpace.setBottom(idx);
|
|
|
|
|
else
|
|
|
|
|
sampleSpace.setTop(idx);
|
|
|
|
|
} else {
|
|
|
|
|
sampleSpace.setWidth(-1); // to indicate nothing needs to be shown
|
2024-01-10 12:57:58 +00:00
|
|
|
return sampleSpace;
|
2023-09-20 08:59:31 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sampleSpace;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
void QQuickGraphsSurface::updateModel(SurfaceModel *model)
|
|
|
|
|
{
|
2024-01-17 11:41:50 +00:00
|
|
|
const QSurfaceDataArray &array = model->series->dataArray();
|
2023-09-08 11:39:55 +00:00
|
|
|
|
2023-08-14 05:34:44 +00:00
|
|
|
if (!array.isEmpty()) {
|
|
|
|
|
int rowCount = array.size();
|
2023-09-08 11:39:55 +00:00
|
|
|
int columnCount = array.at(0).size();
|
2023-05-05 10:51:27 +00:00
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
const int maxSize = 4096; // maximum texture size
|
2023-09-20 08:59:31 +00:00
|
|
|
columnCount = qMin(maxSize, columnCount);
|
|
|
|
|
rowCount = qMin(maxSize, rowCount);
|
2023-08-12 21:52:50 +00:00
|
|
|
|
2023-05-05 10:51:27 +00:00
|
|
|
if (model->rowCount != rowCount) {
|
|
|
|
|
model->rowCount = rowCount;
|
2023-12-14 14:18:27 +00:00
|
|
|
setIndexDirty(true);
|
2023-05-05 10:51:27 +00:00
|
|
|
}
|
|
|
|
|
if (model->columnCount != columnCount) {
|
|
|
|
|
model->columnCount = columnCount;
|
2023-12-14 14:18:27 +00:00
|
|
|
setIndexDirty(true);
|
2023-05-05 10:51:27 +00:00
|
|
|
}
|
2023-09-04 05:54:27 +00:00
|
|
|
|
2023-09-20 08:59:31 +00:00
|
|
|
bool dimensionsChanged = false;
|
|
|
|
|
QRect sampleSpace = calculateSampleSpace(array);
|
|
|
|
|
if (sampleSpace != model->sampleSpace) {
|
|
|
|
|
dimensionsChanged = true;
|
|
|
|
|
model->sampleSpace = sampleSpace;
|
|
|
|
|
}
|
|
|
|
|
int rowStart = sampleSpace.top();
|
|
|
|
|
int columnStart = sampleSpace.left();
|
|
|
|
|
int rowLimit = sampleSpace.bottom() + 1;
|
|
|
|
|
int columnLimit = sampleSpace.right() + 1;
|
2023-08-25 10:44:02 +00:00
|
|
|
|
2023-09-04 05:54:27 +00:00
|
|
|
QPoint selC = model->selectedVertex.coord;
|
2023-10-09 11:25:01 +00:00
|
|
|
selC.setX(qMin(selC.x(), columnCount - 1));
|
|
|
|
|
selC.setY(qMin(selC.y(), rowCount - 1));
|
|
|
|
|
QVector3D selP = array.at(selC.y()).at(selC.x()).position();
|
2023-08-25 10:44:02 +00:00
|
|
|
|
|
|
|
|
bool pickOutOfRange = false;
|
2023-09-26 22:59:15 +00:00
|
|
|
if (selP.x() < axisX()->min() || selP.x() > axisX()->max() || selP.z() < axisZ()->min()
|
|
|
|
|
|| selP.z() > axisZ()->max()) {
|
2023-08-25 10:44:02 +00:00
|
|
|
pickOutOfRange = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_isIndexDirty || pickOutOfRange) {
|
2023-08-09 06:04:08 +00:00
|
|
|
model->selectedVertex = SurfaceVertex();
|
2024-01-03 10:00:34 +00:00
|
|
|
if (sliceView() && sliceView()->isVisible() && model->series == m_selectedSeries) {
|
2023-09-26 22:59:15 +00:00
|
|
|
setSlicingActive(false);
|
2023-07-19 08:50:05 +00:00
|
|
|
setSliceActivatedChanged(true);
|
2023-11-23 18:02:05 +00:00
|
|
|
m_selectionDirty = true;
|
2023-07-19 08:50:05 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
int totalSize = rowCount * columnCount * 2;
|
|
|
|
|
float uvX = 1.0f / float(columnCount - 1);
|
|
|
|
|
float uvY = 1.0f / float(rowCount - 1);
|
|
|
|
|
|
|
|
|
|
bool isFlatShadingEnabled = model->series->isFlatShadingEnabled();
|
|
|
|
|
|
2023-05-05 10:51:27 +00:00
|
|
|
QVector3D boundsMin = model->boundsMin;
|
|
|
|
|
QVector3D boundsMax = model->boundsMax;
|
2023-03-02 13:45:23 +00:00
|
|
|
|
2023-05-05 10:51:27 +00:00
|
|
|
QVector<QVector4D> heights;
|
|
|
|
|
heights.reserve(totalSize);
|
|
|
|
|
|
|
|
|
|
QQmlListReference materialRef(model->model, "materials");
|
|
|
|
|
auto material = materialRef.at(0);
|
|
|
|
|
QVariant heightInputAsVariant = material->property("height");
|
2023-09-04 05:54:27 +00:00
|
|
|
QQuick3DShaderUtilsTextureInput *heightInput
|
|
|
|
|
= heightInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
|
2023-05-05 10:51:27 +00:00
|
|
|
QQuick3DTexture *heightMap = heightInput->texture();
|
|
|
|
|
QQuick3DTextureData *heightMapData = nullptr;
|
|
|
|
|
if (!heightMap) {
|
|
|
|
|
heightMap = new QQuick3DTexture();
|
|
|
|
|
heightMap->setParent(this);
|
|
|
|
|
heightMap->setHorizontalTiling(QQuick3DTexture::ClampToEdge);
|
|
|
|
|
heightMap->setVerticalTiling(QQuick3DTexture::ClampToEdge);
|
2024-04-03 14:20:45 +00:00
|
|
|
heightMap->setMinFilter(QQuick3DTexture::Nearest);
|
|
|
|
|
heightMap->setMagFilter(QQuick3DTexture::Nearest);
|
2023-05-05 10:51:27 +00:00
|
|
|
heightMapData = new QQuick3DTextureData();
|
2023-09-20 08:59:31 +00:00
|
|
|
heightMapData->setSize(QSize(sampleSpace.width(), sampleSpace.height()));
|
2023-05-05 10:51:27 +00:00
|
|
|
heightMapData->setFormat(QQuick3DTextureData::RGBA32F);
|
|
|
|
|
heightMapData->setParent(heightMap);
|
|
|
|
|
heightMapData->setParentItem(heightMap);
|
|
|
|
|
} else {
|
|
|
|
|
heightMapData = heightMap->textureData();
|
2023-09-20 08:59:31 +00:00
|
|
|
if (dimensionsChanged)
|
|
|
|
|
heightMapData->setSize(QSize(sampleSpace.width(), sampleSpace.height()));
|
2023-05-05 10:51:27 +00:00
|
|
|
}
|
2023-10-12 07:31:54 +00:00
|
|
|
if (heightMapData->size().width() < 1 || heightMapData->size().height() < 1) {
|
2023-09-20 08:59:31 +00:00
|
|
|
heightMapData->setTextureData(QByteArray());
|
|
|
|
|
heightMap->setTextureData(heightMapData);
|
|
|
|
|
heightInput->setTexture(heightMap);
|
|
|
|
|
model->heightTexture = heightMap;
|
|
|
|
|
return;
|
2023-08-14 05:34:44 +00:00
|
|
|
}
|
2023-09-20 08:59:31 +00:00
|
|
|
material->setProperty("xDiff", 1.0f / float(sampleSpace.width() - 1));
|
|
|
|
|
material->setProperty("yDiff", 1.0f / float(sampleSpace.height() - 1));
|
2023-09-04 05:54:27 +00:00
|
|
|
material->setProperty("flatShading", isFlatShadingEnabled);
|
2023-09-20 08:59:31 +00:00
|
|
|
material->setProperty("rangeMin", QVector2D(columnStart, rowStart));
|
|
|
|
|
material->setProperty("range", QVector2D(sampleSpace.width(), sampleSpace.height()));
|
|
|
|
|
material->setProperty("vertices", QVector2D(columnCount, rowCount));
|
2023-08-14 05:34:44 +00:00
|
|
|
|
2023-09-20 08:59:31 +00:00
|
|
|
model->vertices.clear();
|
|
|
|
|
model->vertices.reserve(totalSize);
|
2023-08-14 05:34:44 +00:00
|
|
|
|
2023-09-20 08:59:31 +00:00
|
|
|
for (int i = rowStart; i < rowLimit; i++) {
|
2023-09-08 11:39:55 +00:00
|
|
|
const QSurfaceDataRow &row = array.at(i);
|
2023-09-04 05:54:27 +00:00
|
|
|
for (int j = columnStart; j < columnLimit; j++) {
|
2023-09-26 22:59:15 +00:00
|
|
|
QVector3D pos = getNormalizedVertex(row.at(j), isPolar(), false);
|
2023-05-05 10:51:27 +00:00
|
|
|
heights.push_back(QVector4D(pos, .0f));
|
2023-06-27 13:17:53 +00:00
|
|
|
SurfaceVertex vertex;
|
2023-03-02 13:45:23 +00:00
|
|
|
vertex.position = pos;
|
2023-08-14 05:34:44 +00:00
|
|
|
vertex.uv = QVector2D(j * uvX, i * uvY);
|
2023-10-09 11:25:01 +00:00
|
|
|
vertex.coord = QPoint(j, i);
|
2023-03-02 13:45:23 +00:00
|
|
|
model->vertices.push_back(vertex);
|
|
|
|
|
if (boundsMin.isNull())
|
|
|
|
|
boundsMin = pos;
|
|
|
|
|
else
|
2023-09-04 05:54:27 +00:00
|
|
|
boundsMin = QVector3D(qMin(boundsMin.x(), pos.x()),
|
|
|
|
|
qMin(boundsMin.y(), pos.y()),
|
|
|
|
|
qMin(boundsMin.z(), pos.z()));
|
2023-03-02 13:45:23 +00:00
|
|
|
if (boundsMax.isNull())
|
|
|
|
|
boundsMax = pos;
|
|
|
|
|
else
|
2023-09-04 05:54:27 +00:00
|
|
|
boundsMax = QVector3D(qMax(boundsMax.x(), pos.x()),
|
|
|
|
|
qMax(boundsMax.y(), pos.y()),
|
|
|
|
|
qMax(boundsMax.z(), pos.z()));
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-05-28 08:39:41 +00:00
|
|
|
model->boundsMin = boundsMin;
|
|
|
|
|
model->boundsMax = boundsMax;
|
|
|
|
|
|
2023-09-04 05:54:27 +00:00
|
|
|
QByteArray heightData = QByteArray(reinterpret_cast<char *>(heights.data()),
|
|
|
|
|
heights.size() * sizeof(QVector4D));
|
2023-05-05 10:51:27 +00:00
|
|
|
heightMapData->setTextureData(heightData);
|
|
|
|
|
heightMap->setTextureData(heightMapData);
|
|
|
|
|
heightInput->setTexture(heightMap);
|
|
|
|
|
model->heightTexture = heightMap;
|
2023-03-02 13:45:23 +00:00
|
|
|
|
2023-05-05 10:51:27 +00:00
|
|
|
if (m_isIndexDirty) {
|
2023-09-20 08:59:31 +00:00
|
|
|
QVector<SurfaceVertex> vertices;
|
2023-09-04 05:54:27 +00:00
|
|
|
for (int i = 0; i < rowCount; i++) {
|
2023-09-08 11:39:55 +00:00
|
|
|
const QSurfaceDataRow &row = array.at(i);
|
2023-09-20 08:59:31 +00:00
|
|
|
for (int j = 0; j < columnCount; j++) {
|
|
|
|
|
SurfaceVertex vertex;
|
2023-09-26 22:59:15 +00:00
|
|
|
QVector3D pos = getNormalizedVertex(row.at(j), isPolar(), false);
|
2023-09-20 08:59:31 +00:00
|
|
|
vertex.position = pos;
|
|
|
|
|
vertex.uv = QVector2D(j * uvX, i * uvY);
|
2023-10-09 11:25:01 +00:00
|
|
|
vertex.coord = QPoint(j, i);
|
2023-09-20 08:59:31 +00:00
|
|
|
vertices.push_back(vertex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
createIndices(model, columnCount, rowCount);
|
2023-05-05 10:51:27 +00:00
|
|
|
auto geometry = model->model->geometry();
|
|
|
|
|
geometry->vertexData().clear();
|
2023-09-20 08:59:31 +00:00
|
|
|
QByteArray vertexBuffer(reinterpret_cast<char *>(vertices.data()),
|
|
|
|
|
vertices.size() * sizeof(SurfaceVertex));
|
2023-03-30 08:01:12 +00:00
|
|
|
geometry->setVertexData(vertexBuffer);
|
2023-05-05 10:51:27 +00:00
|
|
|
QByteArray indexBuffer(reinterpret_cast<char *>(model->indices.data()),
|
|
|
|
|
model->indices.size() * sizeof(quint32));
|
|
|
|
|
geometry->setIndexData(indexBuffer);
|
|
|
|
|
geometry->setBounds(boundsMin, boundsMax);
|
|
|
|
|
geometry->update();
|
2023-08-10 23:51:47 +00:00
|
|
|
|
2023-08-14 05:34:44 +00:00
|
|
|
createGridlineIndices(model, 0, 0, columnCount, rowCount);
|
2023-05-05 10:51:27 +00:00
|
|
|
auto gridGeometry = model->gridModel->geometry();
|
|
|
|
|
gridGeometry->vertexData().clear();
|
|
|
|
|
gridGeometry->setVertexData(vertexBuffer);
|
|
|
|
|
QByteArray gridIndexBuffer(reinterpret_cast<char *>(model->gridIndices.data()),
|
|
|
|
|
model->gridIndices.size() * sizeof(quint32));
|
|
|
|
|
gridGeometry->setIndexData(gridIndexBuffer);
|
|
|
|
|
gridGeometry->setBounds(boundsMin, boundsMax);
|
|
|
|
|
gridGeometry->update();
|
|
|
|
|
m_isIndexDirty = false;
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
QQmlListReference gridMaterialRef(model->gridModel, "materials");
|
2023-05-05 10:51:27 +00:00
|
|
|
auto gridMaterial = gridMaterialRef.at(0);
|
|
|
|
|
QVariant gridHeightInputAsVariant = gridMaterial->property("height");
|
2023-09-04 05:54:27 +00:00
|
|
|
QQuick3DShaderUtilsTextureInput *gridHeightInput
|
|
|
|
|
= gridHeightInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
|
2023-05-05 10:51:27 +00:00
|
|
|
gridHeightInput->setTexture(heightMap);
|
2023-03-02 13:45:23 +00:00
|
|
|
QColor gridColor = model->series->wireframeColor();
|
2023-05-05 10:51:27 +00:00
|
|
|
gridMaterial->setProperty("gridColor", gridColor);
|
2023-09-20 08:59:31 +00:00
|
|
|
gridMaterial->setProperty("range", QVector2D(sampleSpace.width(), sampleSpace.height()));
|
|
|
|
|
gridMaterial->setProperty("vertices", QVector2D(columnCount, rowCount));
|
2023-09-04 05:54:27 +00:00
|
|
|
|
2024-04-03 14:20:45 +00:00
|
|
|
m_proxyDirty = true;
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
2023-09-20 08:59:31 +00:00
|
|
|
updateMaterial(model);
|
2023-05-11 23:38:31 +00:00
|
|
|
updateSelectedPoint();
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-04 05:54:27 +00:00
|
|
|
void QQuickGraphsSurface::updateProxyModel(SurfaceModel *model)
|
|
|
|
|
{
|
|
|
|
|
if (!model->proxyModel)
|
|
|
|
|
createProxyModel(model);
|
|
|
|
|
|
2024-01-17 11:41:50 +00:00
|
|
|
const QSurfaceDataArray &array = model->series->dataArray();
|
2023-09-04 05:54:27 +00:00
|
|
|
if (array.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QRect sampleSpace = model->sampleSpace;
|
|
|
|
|
int rowCount = sampleSpace.height();
|
|
|
|
|
int columnCount = sampleSpace.width();
|
|
|
|
|
int rowStart = sampleSpace.top();
|
|
|
|
|
int columnStart = sampleSpace.left();
|
|
|
|
|
int rowLimit = sampleSpace.bottom() + 1;
|
|
|
|
|
int columnLimit = sampleSpace.right() + 1;
|
|
|
|
|
if (rowCount == 0 || columnCount == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
// calculate decimate factor based on the order of magnitude of total vertices
|
2024-04-03 14:20:45 +00:00
|
|
|
|
|
|
|
|
int minBeforeDecimate = 1000;
|
2023-09-04 05:54:27 +00:00
|
|
|
float totalSize = rowCount * columnCount;
|
2024-04-03 14:20:45 +00:00
|
|
|
int decimateFactor = qMax(qFloor(std::log10(qMax(1.0, totalSize - minBeforeDecimate))), 1);
|
2023-09-04 05:54:27 +00:00
|
|
|
|
|
|
|
|
int proxyColumnCount = 0;
|
|
|
|
|
int proxyRowCount = 0;
|
|
|
|
|
QVector<SurfaceVertex> proxyVerts;
|
|
|
|
|
|
|
|
|
|
float uvY = 1.0f / float(rowCount - 1);
|
|
|
|
|
float uvX = 1.0f / float(columnCount - 1);
|
|
|
|
|
|
|
|
|
|
QVector3D boundsMin = model->boundsMin;
|
|
|
|
|
QVector3D boundsMax = model->boundsMax;
|
|
|
|
|
|
|
|
|
|
int i = rowStart;
|
|
|
|
|
while (i < rowLimit) {
|
2023-09-08 11:39:55 +00:00
|
|
|
const QSurfaceDataRow &row = array.at(i);
|
2023-09-04 05:54:27 +00:00
|
|
|
proxyRowCount++;
|
|
|
|
|
int j = columnStart;
|
|
|
|
|
while (j < columnLimit) {
|
|
|
|
|
// getNormalizedVertex
|
|
|
|
|
if (i == rowStart)
|
|
|
|
|
proxyColumnCount++;
|
2023-09-26 22:59:15 +00:00
|
|
|
QVector3D pos = getNormalizedVertex(row.at(j), isPolar(), false);
|
2023-09-04 05:54:27 +00:00
|
|
|
SurfaceVertex vertex;
|
|
|
|
|
vertex.position = pos;
|
|
|
|
|
vertex.uv = QVector2D(j * uvX, i * uvY);
|
|
|
|
|
vertex.coord = QPoint(i, j);
|
|
|
|
|
proxyVerts.push_back(vertex);
|
|
|
|
|
|
|
|
|
|
boundsMin = QVector3D(qMin(boundsMin.x(), pos.x()),
|
|
|
|
|
qMin(boundsMin.y(), pos.y()),
|
|
|
|
|
qMin(boundsMin.z(), pos.z()));
|
|
|
|
|
boundsMax = QVector3D(qMax(boundsMax.x(), pos.x()),
|
|
|
|
|
qMax(boundsMax.y(), pos.y()),
|
|
|
|
|
qMax(boundsMax.z(), pos.z()));
|
|
|
|
|
|
|
|
|
|
if (j == columnLimit - 1)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
j += decimateFactor;
|
|
|
|
|
if (j >= columnLimit)
|
|
|
|
|
j = columnLimit - 1;
|
|
|
|
|
}
|
|
|
|
|
if (i == rowLimit - 1)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
i += decimateFactor;
|
|
|
|
|
if (i >= rowLimit)
|
|
|
|
|
i = rowLimit - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model->boundsMin = boundsMin;
|
|
|
|
|
model->boundsMax = boundsMax;
|
|
|
|
|
int endX = proxyColumnCount - 1;
|
|
|
|
|
int endY = proxyRowCount - 1;
|
|
|
|
|
int indexCount = 6 * endX * endY;
|
|
|
|
|
|
|
|
|
|
QVector<quint32> *proxyIndices = new QVector<quint32>();
|
|
|
|
|
proxyIndices->resize(indexCount);
|
|
|
|
|
|
|
|
|
|
const int maxRow = array.size() - 1;
|
2023-09-08 11:39:55 +00:00
|
|
|
const int maxColumn = array.at(0).size() - 1;
|
|
|
|
|
const bool ascendingX = array.at(0).at(0).x() < array.at(0).at(maxColumn).x();
|
|
|
|
|
const bool ascendingZ = array.at(0).at(0).z() < array.at(maxRow).at(0).z();
|
2023-09-04 05:54:27 +00:00
|
|
|
|
|
|
|
|
int rowEnd = endY * proxyColumnCount;
|
|
|
|
|
for (int row = 0; row < rowEnd; row += proxyColumnCount) {
|
|
|
|
|
for (int j = 0; j < endX; j++) {
|
|
|
|
|
if (ascendingX && ascendingZ) {
|
|
|
|
|
proxyIndices->push_back(row + j + 1);
|
|
|
|
|
proxyIndices->push_back(row + proxyColumnCount + j);
|
|
|
|
|
proxyIndices->push_back(row + j);
|
|
|
|
|
|
|
|
|
|
proxyIndices->push_back(row + proxyColumnCount + j + 1);
|
|
|
|
|
proxyIndices->push_back(row + proxyColumnCount + j);
|
|
|
|
|
proxyIndices->push_back(row + j + 1);
|
|
|
|
|
} else if (!ascendingX) {
|
|
|
|
|
proxyIndices->push_back(row + proxyColumnCount + j);
|
|
|
|
|
proxyIndices->push_back(row + proxyColumnCount + j + 1);
|
|
|
|
|
proxyIndices->push_back(row + j);
|
|
|
|
|
|
|
|
|
|
proxyIndices->push_back(row + j);
|
|
|
|
|
proxyIndices->push_back(row + proxyColumnCount + j + 1);
|
|
|
|
|
proxyIndices->push_back(row + j + 1);
|
|
|
|
|
} else {
|
|
|
|
|
proxyIndices->push_back(row + proxyColumnCount + j);
|
|
|
|
|
proxyIndices->push_back(row + proxyColumnCount + j + 1);
|
|
|
|
|
proxyIndices->push_back(row + j + 1);
|
|
|
|
|
|
|
|
|
|
proxyIndices->push_back(row + j);
|
|
|
|
|
proxyIndices->push_back(row + proxyColumnCount + j);
|
|
|
|
|
proxyIndices->push_back(row + j + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto geometry = model->proxyModel->geometry();
|
|
|
|
|
geometry->vertexData().clear();
|
|
|
|
|
QByteArray vertexBuffer(reinterpret_cast<char *>(proxyVerts.data()),
|
|
|
|
|
proxyVerts.size() * sizeof(SurfaceVertex));
|
|
|
|
|
geometry->setVertexData(vertexBuffer);
|
|
|
|
|
QByteArray indexBuffer(reinterpret_cast<char *>(proxyIndices->data()),
|
|
|
|
|
proxyIndices->size() * sizeof(quint32));
|
|
|
|
|
geometry->setIndexData(indexBuffer);
|
|
|
|
|
geometry->setBounds(boundsMin, boundsMax);
|
|
|
|
|
geometry->update();
|
2024-04-03 14:20:45 +00:00
|
|
|
m_proxyDirty = false;
|
2023-09-04 05:54:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::createProxyModel(SurfaceModel *model)
|
|
|
|
|
{
|
|
|
|
|
auto proxyModel = new QQuick3DModel();
|
|
|
|
|
proxyModel->setParent(graphNode());
|
|
|
|
|
proxyModel->setParentItem(model->model);
|
|
|
|
|
proxyModel->setObjectName(QStringLiteral("ProxyModel"));
|
|
|
|
|
proxyModel->setVisible(true);
|
2023-09-26 22:59:15 +00:00
|
|
|
if (selectionMode().testFlag(QAbstract3DGraph::SelectionNone))
|
2023-09-04 05:54:27 +00:00
|
|
|
proxyModel->setPickable(false);
|
|
|
|
|
else
|
|
|
|
|
proxyModel->setPickable(true);
|
|
|
|
|
|
|
|
|
|
auto geometry = new QQuick3DGeometry();
|
|
|
|
|
geometry->setParent(proxyModel);
|
|
|
|
|
geometry->setStride(sizeof(SurfaceVertex));
|
|
|
|
|
geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
|
|
|
|
|
geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
|
|
|
|
|
0,
|
|
|
|
|
QQuick3DGeometry::Attribute::F32Type);
|
|
|
|
|
geometry->addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
|
|
|
|
|
sizeof(QVector3D),
|
|
|
|
|
QQuick3DGeometry::Attribute::F32Type);
|
|
|
|
|
geometry->addAttribute(QQuick3DGeometry::Attribute::IndexSemantic,
|
|
|
|
|
0,
|
|
|
|
|
QQuick3DGeometry::Attribute::U32Type);
|
|
|
|
|
proxyModel->setGeometry(geometry);
|
|
|
|
|
|
|
|
|
|
QQmlListReference materialRef(proxyModel, "materials");
|
|
|
|
|
QQuick3DPrincipledMaterial *material = new QQuick3DPrincipledMaterial();
|
|
|
|
|
material->setParent(proxyModel);
|
|
|
|
|
material->setBaseColor(Qt::white);
|
|
|
|
|
material->setOpacity(0);
|
|
|
|
|
material->setCullMode(QQuick3DMaterial::NoCulling);
|
|
|
|
|
materialRef.append(material);
|
|
|
|
|
|
|
|
|
|
model->proxyModel = proxyModel;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-14 08:38:24 +00:00
|
|
|
void QQuickGraphsSurface::updateMaterial(SurfaceModel *model)
|
2023-03-02 13:45:23 +00:00
|
|
|
{
|
2023-05-23 07:44:55 +00:00
|
|
|
QQmlListReference materialRef(model->model, "materials");
|
2023-08-14 08:38:24 +00:00
|
|
|
|
|
|
|
|
QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(materialRef.at(0));
|
|
|
|
|
|
|
|
|
|
if (!material) {
|
|
|
|
|
material = createQmlCustomMaterial(QStringLiteral(":/materials/SurfaceMaterial"));
|
|
|
|
|
model->customMaterial = material;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool textured = !(model->series->texture().isNull() && model->series->textureFile().isEmpty());
|
|
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
if (isSeriesVisualsDirty() || !textured) {
|
2023-08-14 08:38:24 +00:00
|
|
|
float minY = model->boundsMin.y();
|
|
|
|
|
float maxY = model->boundsMax.y();
|
|
|
|
|
float range = maxY - minY;
|
|
|
|
|
|
|
|
|
|
switch (model->series->colorStyle()) {
|
2023-10-13 08:56:18 +00:00
|
|
|
case (Q3DTheme::ColorStyle::ObjectGradient):
|
2023-08-14 08:38:24 +00:00
|
|
|
material->setProperty("colorStyle", 0);
|
|
|
|
|
material->setProperty("gradientMin", -(minY / range));
|
|
|
|
|
material->setProperty("gradientHeight", 1.0f / range);
|
|
|
|
|
break;
|
2023-10-13 08:56:18 +00:00
|
|
|
case (Q3DTheme::ColorStyle::RangeGradient):
|
2023-08-14 08:38:24 +00:00
|
|
|
material->setProperty("colorStyle", 1);
|
|
|
|
|
break;
|
2023-10-13 08:56:18 +00:00
|
|
|
case (Q3DTheme::ColorStyle::Uniform):
|
2023-08-14 08:38:24 +00:00
|
|
|
material->setProperty("colorStyle", 2);
|
2023-10-13 08:56:18 +00:00
|
|
|
material->setProperty("uniformColor", model->series->baseColor());
|
2023-08-14 08:38:24 +00:00
|
|
|
break;
|
2023-05-23 07:44:55 +00:00
|
|
|
}
|
2023-08-14 08:38:24 +00:00
|
|
|
|
|
|
|
|
QVariant textureInputAsVariant = material->property("custex");
|
2023-10-13 08:56:18 +00:00
|
|
|
QQuick3DShaderUtilsTextureInput *textureInput
|
|
|
|
|
= textureInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
|
2023-10-05 06:22:21 +00:00
|
|
|
auto textureData = static_cast<QQuickGraphsTextureData *>(model->texture->textureData());
|
2023-08-14 08:38:24 +00:00
|
|
|
textureData->createGradient(model->series->baseGradient());
|
|
|
|
|
textureInput->setTexture(model->texture);
|
|
|
|
|
|
|
|
|
|
QVariant heightInputAsVariant = material->property("height");
|
2023-10-13 08:56:18 +00:00
|
|
|
QQuick3DShaderUtilsTextureInput *heightInput
|
|
|
|
|
= heightInputAsVariant.value<QQuick3DShaderUtilsTextureInput *>();
|
2023-08-14 08:38:24 +00:00
|
|
|
heightInput->setTexture(model->heightTexture);
|
|
|
|
|
material->setParent(model->model);
|
|
|
|
|
material->setParentItem(model->model);
|
|
|
|
|
material->setCullMode(QQuick3DMaterial::NoCulling);
|
|
|
|
|
material->setProperty("flatShading", model->series->isFlatShadingEnabled());
|
2023-08-14 05:34:44 +00:00
|
|
|
}
|
2023-08-14 08:38:24 +00:00
|
|
|
|
|
|
|
|
if (textured) {
|
|
|
|
|
material->setProperty("colorStyle", 3);
|
2023-10-13 08:56:18 +00:00
|
|
|
QQuick3DShaderUtilsTextureInput *texInput = material->property("baseColor")
|
|
|
|
|
.value<QQuick3DShaderUtilsTextureInput *>();
|
2023-05-05 10:51:27 +00:00
|
|
|
if (!texInput->texture()) {
|
|
|
|
|
QQuick3DTexture *texture = new QQuick3DTexture();
|
2023-08-23 10:53:22 +00:00
|
|
|
texture->setParent(material);
|
|
|
|
|
texture->setParentItem(material);
|
2023-05-05 10:51:27 +00:00
|
|
|
texInput->setTexture(texture);
|
2023-05-23 07:44:55 +00:00
|
|
|
}
|
|
|
|
|
if (!model->series->textureFile().isEmpty()) {
|
2023-05-05 10:51:27 +00:00
|
|
|
texInput->texture()->setSource(QUrl::fromLocalFile(model->series->textureFile()));
|
2023-05-23 07:44:55 +00:00
|
|
|
} else if (!model->series->texture().isNull()) {
|
|
|
|
|
QImage image = model->series->texture();
|
2023-08-11 10:49:31 +00:00
|
|
|
image.convertTo(QImage::Format_RGBA32FPx4);
|
2023-10-05 06:22:21 +00:00
|
|
|
auto textureData = static_cast<QQuickGraphsTextureData *>(model->texture->textureData());
|
2023-08-11 10:49:31 +00:00
|
|
|
textureData->setFormat(QQuick3DTextureData::RGBA32F);
|
|
|
|
|
textureData->setSize(image.size());
|
2023-10-13 08:56:18 +00:00
|
|
|
textureData->setTextureData(
|
|
|
|
|
QByteArray(reinterpret_cast<const char *>(image.bits()), image.sizeInBytes()));
|
2023-08-11 10:49:31 +00:00
|
|
|
texInput->texture()->setTextureData(textureData);
|
2023-09-20 08:59:31 +00:00
|
|
|
texInput->texture()->setVerticalTiling(QQuick3DTexture::ClampToEdge);
|
|
|
|
|
texInput->texture()->setHorizontalTiling(QQuick3DTexture::ClampToEdge);
|
2023-05-23 07:44:55 +00:00
|
|
|
} else {
|
2023-05-05 10:51:27 +00:00
|
|
|
texInput->texture()->setSource(QUrl());
|
2023-05-23 07:44:55 +00:00
|
|
|
}
|
2024-01-15 08:40:23 +00:00
|
|
|
|
2024-01-17 11:41:50 +00:00
|
|
|
const QSurfaceDataArray &array = model->series->dataArray();
|
2024-01-15 08:40:23 +00:00
|
|
|
int maxRow = array.size() - 1;
|
|
|
|
|
int maxCol = array.at(0).size() - 1;
|
|
|
|
|
const bool ascendingX = array.at(0).at(0).x() < array.at(0).at(maxCol).x();
|
|
|
|
|
const bool ascendingZ = array.at(0).at(0).z() < array.at(maxRow).at(0).z();
|
|
|
|
|
material->setProperty("flipU", !ascendingX);
|
|
|
|
|
material->setProperty("flipV", !ascendingZ);
|
2023-05-23 07:44:55 +00:00
|
|
|
}
|
2023-08-14 08:38:24 +00:00
|
|
|
material->update();
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
QVector3D QQuickGraphsSurface::getNormalizedVertex(const QSurfaceDataItem &data,
|
|
|
|
|
bool polar,
|
|
|
|
|
bool flipXZ)
|
2023-03-02 13:45:23 +00:00
|
|
|
{
|
|
|
|
|
Q_UNUSED(flipXZ);
|
|
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
QValue3DAxis *axisXValue = static_cast<QValue3DAxis *>(axisX());
|
|
|
|
|
QValue3DAxis *axisYValue = static_cast<QValue3DAxis *>(axisY());
|
|
|
|
|
QValue3DAxis *axisZValue = static_cast<QValue3DAxis *>(axisZ());
|
2023-05-17 11:41:57 +00:00
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
float normalizedX = axisXValue->positionAt(data.x());
|
2023-05-17 11:41:57 +00:00
|
|
|
float normalizedY;
|
2023-09-26 22:59:15 +00:00
|
|
|
float normalizedZ = axisZValue->positionAt(data.z());
|
2023-05-17 11:41:57 +00:00
|
|
|
// TODO : Need to handle, flipXZ
|
|
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
float scale, translate;
|
2023-05-17 11:41:57 +00:00
|
|
|
if (polar) {
|
|
|
|
|
float angle = normalizedX * M_PI * 2.0f;
|
2023-09-27 12:56:05 +00:00
|
|
|
float radius = normalizedZ * this->scaleWithBackground().z();
|
2023-05-17 11:41:57 +00:00
|
|
|
normalizedX = radius * qSin(angle) * 1.0f;
|
|
|
|
|
normalizedZ = -(radius * qCos(angle)) * 1.0f;
|
|
|
|
|
} else {
|
2023-06-27 13:17:53 +00:00
|
|
|
scale = translate = this->scaleWithBackground().x();
|
2023-05-17 11:41:57 +00:00
|
|
|
normalizedX = normalizedX * scale * 2.0f - translate;
|
2023-06-27 13:17:53 +00:00
|
|
|
scale = translate = this->scaleWithBackground().z();
|
2023-05-17 11:41:57 +00:00
|
|
|
normalizedZ = normalizedZ * -scale * 2.0f + translate;
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
scale = translate = this->scale().y();
|
2023-09-26 22:59:15 +00:00
|
|
|
normalizedY = axisYValue->positionAt(data.y()) * scale * 2.0f - translate;
|
2023-03-02 13:45:23 +00:00
|
|
|
return QVector3D(normalizedX, normalizedY, normalizedZ);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::updateSliceGraph()
|
|
|
|
|
{
|
2023-11-23 18:02:05 +00:00
|
|
|
if (m_selectionDirty)
|
|
|
|
|
QQuickGraphsItem::updateSliceGraph();
|
2023-03-02 13:45:23 +00:00
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
setSelectedPointChanged(true);
|
2023-06-14 19:40:12 +00:00
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
if (!sliceView()->isVisible())
|
|
|
|
|
return;
|
|
|
|
|
|
2023-10-09 11:25:01 +00:00
|
|
|
QPointF worldCoord;
|
|
|
|
|
for (auto model : m_model) {
|
|
|
|
|
if (model->picked) {
|
|
|
|
|
QPoint coords = model->selectedVertex.coord;
|
|
|
|
|
worldCoord = mapCoordsToWorldSpace(model, coords);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
for (auto model : m_model) {
|
2023-07-25 06:58:01 +00:00
|
|
|
bool visible = model->series->isVisible();
|
|
|
|
|
|
|
|
|
|
model->sliceModel->setVisible(visible);
|
|
|
|
|
model->sliceGridModel->setVisible(visible);
|
|
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
if (!selectionMode().testFlag(QAbstract3DGraph::SelectionMultiSeries) && !model->picked) {
|
2023-07-25 06:58:01 +00:00
|
|
|
model->sliceModel->setVisible(false);
|
|
|
|
|
model->sliceGridModel->setVisible(false);
|
2023-03-02 13:45:23 +00:00
|
|
|
continue;
|
2023-07-25 06:58:01 +00:00
|
|
|
} else {
|
2023-10-13 08:56:18 +00:00
|
|
|
model->sliceGridModel->setVisible(
|
|
|
|
|
model->series->drawMode().testFlag(QSurface3DSeries::DrawWireframe));
|
2023-07-25 06:58:01 +00:00
|
|
|
if (model->series->drawMode().testFlag(QSurface3DSeries::DrawSurface))
|
|
|
|
|
model->sliceModel->setLocalOpacity(1.f);
|
|
|
|
|
else
|
|
|
|
|
model->sliceModel->setLocalOpacity(.0f);
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
|
|
|
|
|
QVector<SurfaceVertex> selectedSeries;
|
|
|
|
|
|
2023-09-20 08:59:31 +00:00
|
|
|
QRect sampleSpace = model->sampleSpace;
|
|
|
|
|
int rowStart = sampleSpace.top();
|
|
|
|
|
int columnStart = sampleSpace.left();
|
2024-01-09 13:14:11 +00:00
|
|
|
int rowEnd = sampleSpace.bottom() + 1;
|
|
|
|
|
int columnEnd = sampleSpace.right() + 1;
|
2023-09-20 08:59:31 +00:00
|
|
|
int rowCount = sampleSpace.height();
|
|
|
|
|
int columnCount = sampleSpace.width();
|
2023-08-21 05:32:46 +00:00
|
|
|
|
2023-10-09 11:25:01 +00:00
|
|
|
QPoint coord;
|
|
|
|
|
if (model->picked)
|
|
|
|
|
coord = model->selectedVertex.coord;
|
|
|
|
|
else
|
|
|
|
|
coord = mapCoordsToSampleSpace(model, worldCoord);
|
|
|
|
|
|
2023-05-11 23:38:31 +00:00
|
|
|
int indexCount = 0;
|
2024-01-17 11:41:50 +00:00
|
|
|
const QSurfaceDataArray &array = model->series->dataArray();
|
2024-01-11 07:53:53 +00:00
|
|
|
const int maxRow = array.size() - 1;
|
|
|
|
|
const int maxColumn = array.at(0).size() - 1;
|
|
|
|
|
const bool ascendingX = array.at(0).at(0).x() < array.at(0).at(maxColumn).x();
|
|
|
|
|
const bool ascendingZ = array.at(0).at(0).z() < array.at(maxRow).at(0).z();
|
2023-10-09 11:25:01 +00:00
|
|
|
if (selectionMode().testFlag(QAbstract3DGraph::SelectionRow) && coord.y() != -1) {
|
2023-05-11 23:38:31 +00:00
|
|
|
selectedSeries.reserve(columnCount * 2);
|
2023-03-02 13:45:23 +00:00
|
|
|
QVector<SurfaceVertex> list;
|
2024-01-09 13:14:11 +00:00
|
|
|
QSurfaceDataRow row = array.at(coord.y());
|
|
|
|
|
for (int i = columnStart; i < columnEnd; i++) {
|
2024-01-11 07:53:53 +00:00
|
|
|
int index = ascendingX ? i : rowEnd - i - rowStart - 1;
|
|
|
|
|
QVector3D pos = getNormalizedVertex(row.at(index), false, false);
|
2024-01-09 13:14:11 +00:00
|
|
|
SurfaceVertex vertex;
|
|
|
|
|
vertex.position = pos;
|
2023-03-02 13:45:23 +00:00
|
|
|
vertex.position.setY(vertex.position.y() - .025f);
|
|
|
|
|
vertex.position.setZ(.0f);
|
|
|
|
|
selectedSeries.append(vertex);
|
|
|
|
|
vertex.position.setY(vertex.position.y() + .05f);
|
|
|
|
|
list.append(vertex);
|
|
|
|
|
}
|
|
|
|
|
selectedSeries.append(list);
|
2023-09-20 08:59:31 +00:00
|
|
|
indexCount = columnCount - 1;
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-09 11:25:01 +00:00
|
|
|
if (selectionMode().testFlag(QAbstract3DGraph::SelectionColumn) && coord.x() != -1) {
|
2023-05-11 23:38:31 +00:00
|
|
|
selectedSeries.reserve(rowCount * 2);
|
2023-03-02 13:45:23 +00:00
|
|
|
QVector<SurfaceVertex> list;
|
2024-01-09 13:14:11 +00:00
|
|
|
for (int i = rowStart; i < rowEnd; i++) {
|
2024-01-11 07:53:53 +00:00
|
|
|
int index = ascendingZ ? i : rowEnd - i - rowStart - 1;
|
|
|
|
|
QVector3D pos = getNormalizedVertex(array.at(index).at(coord.x()), false, false);
|
2024-01-09 13:14:11 +00:00
|
|
|
SurfaceVertex vertex;
|
|
|
|
|
vertex.position = pos;
|
2023-05-11 23:38:31 +00:00
|
|
|
vertex.position.setX(-vertex.position.z());
|
2023-03-02 13:45:23 +00:00
|
|
|
vertex.position.setY(vertex.position.y() - .025f);
|
|
|
|
|
vertex.position.setZ(0);
|
|
|
|
|
selectedSeries.append(vertex);
|
|
|
|
|
vertex.position.setY(vertex.position.y() + .05f);
|
|
|
|
|
list.append(vertex);
|
|
|
|
|
}
|
|
|
|
|
selectedSeries.append(list);
|
2023-09-20 08:59:31 +00:00
|
|
|
indexCount = rowCount - 1;
|
2023-05-05 10:51:27 +00:00
|
|
|
|
|
|
|
|
QQmlListReference materialRef(model->sliceModel, "materials");
|
|
|
|
|
auto material = materialRef.at(0);
|
|
|
|
|
material->setProperty("isColumn", true);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVector<quint32> indices;
|
2023-05-11 23:38:31 +00:00
|
|
|
indices.reserve(indexCount * 6);
|
|
|
|
|
for (int i = 0; i < indexCount; i++) {
|
2023-03-02 13:45:23 +00:00
|
|
|
indices.push_back(i + 1);
|
2023-05-11 23:38:31 +00:00
|
|
|
indices.push_back(i + indexCount + 1);
|
2023-03-02 13:45:23 +00:00
|
|
|
indices.push_back(i);
|
2023-05-11 23:38:31 +00:00
|
|
|
indices.push_back(i + indexCount + 2);
|
|
|
|
|
indices.push_back(i + indexCount + 1);
|
2023-03-02 13:45:23 +00:00
|
|
|
indices.push_back(i + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto geometry = model->sliceModel->geometry();
|
2023-04-05 10:07:42 +00:00
|
|
|
geometry->vertexData().clear();
|
|
|
|
|
geometry->indexData().clear();
|
2023-03-02 13:45:23 +00:00
|
|
|
QByteArray vertexBuffer(reinterpret_cast<char *>(selectedSeries.data()),
|
|
|
|
|
selectedSeries.size() * sizeof(SurfaceVertex));
|
|
|
|
|
geometry->setVertexData(vertexBuffer);
|
|
|
|
|
QByteArray indexBuffer(reinterpret_cast<char *>(indices.data()),
|
|
|
|
|
indices.size() * sizeof(quint32));
|
|
|
|
|
geometry->setIndexData(indexBuffer);
|
|
|
|
|
geometry->update();
|
|
|
|
|
|
|
|
|
|
geometry = model->sliceGridModel->geometry();
|
2023-04-05 10:07:42 +00:00
|
|
|
geometry->vertexData().clear();
|
|
|
|
|
geometry->indexData().clear();
|
2023-03-02 13:45:23 +00:00
|
|
|
geometry->setVertexData(vertexBuffer);
|
|
|
|
|
|
|
|
|
|
QVector<quint32> gridIndices;
|
2023-05-11 23:38:31 +00:00
|
|
|
gridIndices.reserve(indexCount * 4);
|
|
|
|
|
for (int i = 0; i < indexCount; i++) {
|
2023-03-02 13:45:23 +00:00
|
|
|
gridIndices.push_back(i);
|
2023-05-11 23:38:31 +00:00
|
|
|
gridIndices.push_back(i + indexCount + 1);
|
2023-03-02 13:45:23 +00:00
|
|
|
|
|
|
|
|
gridIndices.push_back(i);
|
|
|
|
|
gridIndices.push_back(i + 1);
|
|
|
|
|
}
|
2023-04-05 10:07:42 +00:00
|
|
|
geometry->indexData().clear();
|
2023-03-02 13:45:23 +00:00
|
|
|
QByteArray gridIndexBuffer(reinterpret_cast<char *>(gridIndices.data()),
|
|
|
|
|
gridIndices.size() * sizeof(quint32));
|
|
|
|
|
geometry->setIndexData(gridIndexBuffer);
|
|
|
|
|
geometry->update();
|
|
|
|
|
|
|
|
|
|
QQmlListReference gridMaterialRef(model->sliceGridModel, "materials");
|
|
|
|
|
auto gridMaterial = static_cast<QQuick3DPrincipledMaterial *>(gridMaterialRef.at(0));
|
|
|
|
|
QColor gridColor = model->series->wireframeColor();
|
|
|
|
|
gridMaterial->setBaseColor(gridColor);
|
2023-05-11 23:38:31 +00:00
|
|
|
|
|
|
|
|
updateSelectedPoint();
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-09 11:25:01 +00:00
|
|
|
QPointF QQuickGraphsSurface::mapCoordsToWorldSpace(SurfaceModel *model, const QPointF &coords)
|
|
|
|
|
{
|
2024-01-17 11:41:50 +00:00
|
|
|
const QSurfaceDataArray &array = model->series->dataArray();
|
2023-10-09 11:25:01 +00:00
|
|
|
QSurfaceDataItem item = array.at(coords.y()).at(coords.x());
|
|
|
|
|
return QPointF(item.x(), item.z());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPoint QQuickGraphsSurface::mapCoordsToSampleSpace(SurfaceModel *model, const QPointF &coords)
|
|
|
|
|
{
|
2024-01-17 11:41:50 +00:00
|
|
|
const QSurfaceDataArray &array = model->series->dataArray();
|
2023-10-09 11:25:01 +00:00
|
|
|
int maxRow = array.size() - 1;
|
|
|
|
|
int maxCol = array.at(0).size() - 1;
|
|
|
|
|
const bool ascendingX = array.at(0).at(0).x() < array.at(0).at(maxCol).x();
|
|
|
|
|
const bool ascendingZ = array.at(0).at(0).z() < array.at(maxRow).at(0).z();
|
|
|
|
|
int botX = ascendingX ? 0 : maxCol;
|
|
|
|
|
int botZ = ascendingZ ? 0 : maxRow;
|
|
|
|
|
int topX = ascendingX ? maxCol : 0;
|
|
|
|
|
int topZ = ascendingZ ? maxRow : 0;
|
|
|
|
|
|
|
|
|
|
QPoint point(-1, -1);
|
|
|
|
|
|
|
|
|
|
QSurfaceDataItem bottomLeft = array.at(botZ).at(botX);
|
|
|
|
|
QSurfaceDataItem topRight = array.at(topZ).at(topX);
|
|
|
|
|
|
|
|
|
|
QPointF pointBL(bottomLeft.x(), bottomLeft.z());
|
|
|
|
|
QPointF pointTR(topRight.x(), topRight.z());
|
|
|
|
|
|
|
|
|
|
QPointF pointF = coords - pointBL;
|
|
|
|
|
QPointF span = pointTR - pointBL;
|
|
|
|
|
QPointF step = QPointF(span.x() / float(maxCol), span.y() / float(maxRow));
|
|
|
|
|
QPoint sample = QPoint((pointF.x() + (step.x() / 2.0)) / step.x(),
|
|
|
|
|
(pointF.y() + (step.y() / 2.0)) / step.y());
|
|
|
|
|
|
|
|
|
|
if (bottomLeft.x() <= coords.x() && topRight.x() >= coords.x())
|
|
|
|
|
point.setX(ascendingX ? sample.x() : maxCol - sample.x());
|
|
|
|
|
|
|
|
|
|
if (bottomLeft.z() <= coords.y() && topRight.z() >= coords.y())
|
|
|
|
|
point.setY(ascendingZ ? sample.y() : maxRow - sample.y());
|
|
|
|
|
return point;
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
|
2023-09-20 08:59:31 +00:00
|
|
|
void QQuickGraphsSurface::createIndices(SurfaceModel *model, int columnCount, int rowCount)
|
2023-03-02 13:45:23 +00:00
|
|
|
{
|
2023-09-20 08:59:31 +00:00
|
|
|
int endX = columnCount - 1;
|
2023-10-13 08:56:18 +00:00
|
|
|
int endY = rowCount - 1;
|
2023-03-02 13:45:23 +00:00
|
|
|
|
2023-09-20 08:59:31 +00:00
|
|
|
int indexCount = 6 * endX * endY;
|
2023-03-02 13:45:23 +00:00
|
|
|
QVector<quint32> *indices = &model->indices;
|
|
|
|
|
|
|
|
|
|
indices->clear();
|
|
|
|
|
indices->resize(indexCount);
|
|
|
|
|
|
|
|
|
|
int rowEnd = endY * columnCount;
|
2023-10-13 08:56:18 +00:00
|
|
|
for (int row = 0; row < rowEnd; row += columnCount) {
|
|
|
|
|
for (int j = 0; j < endX; j++) {
|
2023-09-26 22:59:15 +00:00
|
|
|
if (dataDimensions() == QQuickGraphsSurface::BothAscending
|
2023-10-13 08:56:18 +00:00
|
|
|
|| dataDimensions() == QQuickGraphsSurface::BothDescending) {
|
2023-03-02 13:45:23 +00:00
|
|
|
indices->push_back(row + j + 1);
|
|
|
|
|
indices->push_back(row + columnCount + j);
|
|
|
|
|
indices->push_back(row + j);
|
|
|
|
|
|
|
|
|
|
indices->push_back(row + columnCount + j + 1);
|
|
|
|
|
indices->push_back(row + columnCount + j);
|
|
|
|
|
indices->push_back(row + j + 1);
|
2023-09-26 22:59:15 +00:00
|
|
|
} else if (dataDimensions() == QQuickGraphsSurface::XDescending) {
|
2023-03-02 13:45:23 +00:00
|
|
|
indices->push_back(row + columnCount + j);
|
|
|
|
|
indices->push_back(row + columnCount + j + 1);
|
|
|
|
|
indices->push_back(row + j);
|
|
|
|
|
|
|
|
|
|
indices->push_back(row + j);
|
|
|
|
|
indices->push_back(row + columnCount + j + 1);
|
|
|
|
|
indices->push_back(row + j + 1);
|
|
|
|
|
} else {
|
|
|
|
|
indices->push_back(row + columnCount + j);
|
|
|
|
|
indices->push_back(row + columnCount + j + 1);
|
|
|
|
|
indices->push_back(row + j + 1);
|
|
|
|
|
|
|
|
|
|
indices->push_back(row + j);
|
2023-08-11 10:49:31 +00:00
|
|
|
indices->push_back(row + columnCount + j);
|
2023-03-02 13:45:23 +00:00
|
|
|
indices->push_back(row + j + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void QQuickGraphsSurface::createGridlineIndices(SurfaceModel *model, int x, int y, int endX, int endY)
|
|
|
|
|
{
|
|
|
|
|
int columnCount = model->columnCount;
|
|
|
|
|
int rowCount = model->rowCount;
|
|
|
|
|
|
|
|
|
|
if (endX >= columnCount)
|
|
|
|
|
endX = columnCount - 1;
|
|
|
|
|
if (endY >= rowCount)
|
|
|
|
|
endY = rowCount - 1;
|
|
|
|
|
if (x > endX)
|
|
|
|
|
x = endX - 1;
|
|
|
|
|
if (y > endY)
|
|
|
|
|
y = endY - 1;
|
|
|
|
|
|
|
|
|
|
int nColumns = endX - x + 1;
|
|
|
|
|
int nRows = endY - y + 1;
|
|
|
|
|
|
|
|
|
|
int gridIndexCount = 2 * nColumns * (nRows - 1) + 2 * nRows * (nColumns - 1);
|
|
|
|
|
model->gridIndices.clear();
|
2023-05-23 07:44:55 +00:00
|
|
|
model->gridIndices.resize(gridIndexCount);
|
2023-03-02 13:45:23 +00:00
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
for (int i = y, row = columnCount * y; i <= endY; i++, row += columnCount) {
|
|
|
|
|
for (int j = x; j < endX; j++) {
|
2023-03-02 13:45:23 +00:00
|
|
|
model->gridIndices.push_back(row + j);
|
|
|
|
|
model->gridIndices.push_back(row + j + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-13 08:56:18 +00:00
|
|
|
for (int i = y, row = columnCount * y; i < endY; i++, row += columnCount) {
|
|
|
|
|
for (int j = x; j <= endX; j++) {
|
2023-03-02 13:45:23 +00:00
|
|
|
model->gridIndices.push_back(row + j);
|
|
|
|
|
model->gridIndices.push_back(row + j + columnCount);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-15 10:00:37 +00:00
|
|
|
|
2023-05-23 07:02:19 +00:00
|
|
|
bool QQuickGraphsSurface::doPicking(const QPointF &position)
|
2023-05-15 10:00:37 +00:00
|
|
|
{
|
2024-04-03 14:20:45 +00:00
|
|
|
if (!m_pickThisFrame && m_proxyDirty) {
|
|
|
|
|
m_pickThisFrame = true;
|
|
|
|
|
m_lastPick = position;
|
|
|
|
|
for (auto model : m_model)
|
|
|
|
|
updateProxyModel(model);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-05-23 07:02:19 +00:00
|
|
|
if (!QQuickGraphsItem::doPicking(position))
|
|
|
|
|
return false;
|
|
|
|
|
|
2023-11-23 18:02:05 +00:00
|
|
|
m_selectionDirty = true;
|
2023-05-15 10:00:37 +00:00
|
|
|
auto pickResult = pickAll(position.x(), position.y());
|
|
|
|
|
QVector3D pickedPos(0.0f, 0.0f, 0.0f);
|
|
|
|
|
QQuick3DModel *pickedModel = nullptr;
|
|
|
|
|
|
2023-09-26 22:59:15 +00:00
|
|
|
if (!selectionMode().testFlag(QAbstract3DGraph::SelectionNone)) {
|
|
|
|
|
if (!sliceView() && selectionMode().testFlag(QAbstract3DGraph::SelectionSlice))
|
2023-08-09 22:50:04 +00:00
|
|
|
createSliceView();
|
|
|
|
|
|
2023-12-18 13:32:04 +00:00
|
|
|
if (!pickResult.isEmpty()) {
|
|
|
|
|
for (auto picked : pickResult) {
|
|
|
|
|
if (picked.objectHit()
|
|
|
|
|
&& picked.objectHit()->objectName().contains(QStringLiteral("ProxyModel"))) {
|
|
|
|
|
pickedPos = picked.position();
|
|
|
|
|
pickedModel = qobject_cast<QQuick3DModel *>(picked.objectHit()->parentItem());
|
|
|
|
|
bool visible = false;
|
|
|
|
|
for (auto model : m_model) {
|
|
|
|
|
if (model->model == pickedModel)
|
|
|
|
|
visible = model->series->isVisible();
|
|
|
|
|
}
|
|
|
|
|
if (!pickedPos.isNull() && visible)
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
clearSelection();
|
|
|
|
|
for (auto model : m_model)
|
|
|
|
|
model->picked = false;
|
2023-09-04 05:54:27 +00:00
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2023-12-18 13:32:04 +00:00
|
|
|
bool inRange = qAbs(pickedPos.x()) < scaleWithBackground().x()
|
|
|
|
|
&& qAbs(pickedPos.z()) < scaleWithBackground().z();
|
2023-09-04 05:54:27 +00:00
|
|
|
|
2023-12-18 13:32:04 +00:00
|
|
|
if (!pickedPos.isNull() && inRange) {
|
|
|
|
|
float min = -1.0f;
|
2023-03-02 13:45:23 +00:00
|
|
|
|
2023-12-18 13:32:04 +00:00
|
|
|
for (auto model : m_model) {
|
2024-01-03 10:00:34 +00:00
|
|
|
if (!model->series->isVisible()) {
|
|
|
|
|
model->picked = false;
|
2023-12-18 13:32:04 +00:00
|
|
|
continue;
|
2024-01-03 10:00:34 +00:00
|
|
|
}
|
2023-12-18 13:32:04 +00:00
|
|
|
|
|
|
|
|
model->picked = (model->model == pickedModel);
|
|
|
|
|
|
|
|
|
|
SurfaceVertex selectedVertex;
|
|
|
|
|
for (auto vertex : model->vertices) {
|
|
|
|
|
QVector3D pos = vertex.position;
|
|
|
|
|
float dist = pickedPos.distanceToPoint(pos);
|
|
|
|
|
if (selectedVertex.position.isNull() || dist < min) {
|
|
|
|
|
min = dist;
|
|
|
|
|
selectedVertex = vertex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
model->selectedVertex = selectedVertex;
|
|
|
|
|
if (!selectedVertex.position.isNull() && model->picked) {
|
|
|
|
|
model->series->setSelectedPoint(selectedVertex.coord);
|
|
|
|
|
setSlicingActive(false);
|
|
|
|
|
if (isSliceEnabled())
|
|
|
|
|
setSliceActivatedChanged(true);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
2023-05-15 10:00:37 +00:00
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
2023-12-18 13:32:04 +00:00
|
|
|
} else {
|
|
|
|
|
clearSelection();
|
|
|
|
|
for (auto model : m_model)
|
|
|
|
|
model->picked = false;
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-05-23 07:02:19 +00:00
|
|
|
return true;
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::updateSelectedPoint()
|
|
|
|
|
{
|
|
|
|
|
bool labelVisible = false;
|
|
|
|
|
m_instancing->resetPositions();
|
2023-04-05 10:07:42 +00:00
|
|
|
if (sliceView() && sliceView()->isVisible())
|
2023-03-02 13:45:23 +00:00
|
|
|
m_sliceInstancing->resetPositions();
|
2023-10-09 11:25:01 +00:00
|
|
|
|
|
|
|
|
QPointF worldCoord;
|
|
|
|
|
for (auto model : m_model) {
|
|
|
|
|
if (model->picked) {
|
|
|
|
|
QPoint coords = model->selectedVertex.coord;
|
|
|
|
|
worldCoord = mapCoordsToWorldSpace(model, coords);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-02 13:45:23 +00:00
|
|
|
for (auto model : m_model) {
|
2023-10-13 08:56:18 +00:00
|
|
|
if ((!selectionMode().testFlag(QAbstract3DGraph::SelectionMultiSeries) && !model->picked)
|
2023-12-18 13:32:04 +00:00
|
|
|
|| model->selectedVertex.position.isNull()) {
|
2023-05-11 23:38:31 +00:00
|
|
|
continue;
|
2023-12-18 13:32:04 +00:00
|
|
|
}
|
2023-10-09 11:25:01 +00:00
|
|
|
QPoint selectedCoord;
|
|
|
|
|
if (model->picked)
|
|
|
|
|
selectedCoord = model->selectedVertex.coord;
|
|
|
|
|
else
|
|
|
|
|
selectedCoord = mapCoordsToSampleSpace(model, worldCoord);
|
|
|
|
|
if (selectedCoord.x() == -1 || selectedCoord.y() == -1)
|
|
|
|
|
continue;
|
|
|
|
|
|
2024-01-09 13:14:11 +00:00
|
|
|
const QSurfaceDataItem &dataPos
|
2024-01-17 11:41:50 +00:00
|
|
|
= model->series->dataArray().at(selectedCoord.y()).at(selectedCoord.x());
|
2024-02-12 14:07:25 +00:00
|
|
|
QVector3D pos = getNormalizedVertex(dataPos, isPolar(), false);
|
|
|
|
|
|
2024-01-09 13:14:11 +00:00
|
|
|
SurfaceVertex selectedVertex;
|
|
|
|
|
selectedVertex.position = pos;
|
2024-02-15 07:35:17 +00:00
|
|
|
selectedVertex.coord = model->selectedVertex.coord;
|
2023-10-13 08:56:18 +00:00
|
|
|
if (model->series->isVisible() && !selectedVertex.position.isNull()
|
|
|
|
|
&& selectionMode().testFlag(QAbstract3DGraph::SelectionItem)) {
|
2023-03-02 13:45:23 +00:00
|
|
|
m_instancing->addPosition(selectedVertex.position);
|
2024-02-12 14:07:25 +00:00
|
|
|
QVector3D slicePosition = getNormalizedVertex(dataPos, false, false);
|
2023-04-05 10:07:42 +00:00
|
|
|
if (sliceView() && sliceView()->isVisible()) {
|
2023-09-26 22:59:15 +00:00
|
|
|
if (selectionMode().testFlag(QAbstract3DGraph::SelectionColumn))
|
2023-05-11 23:38:31 +00:00
|
|
|
slicePosition.setX(-slicePosition.z());
|
2023-03-02 13:45:23 +00:00
|
|
|
slicePosition.setZ(.0f);
|
|
|
|
|
m_sliceInstancing->addPosition(slicePosition);
|
|
|
|
|
}
|
|
|
|
|
if (model->picked) {
|
|
|
|
|
QVector3D labelPosition = selectedVertex.position;
|
2023-08-21 07:55:04 +00:00
|
|
|
QString label = model->series->itemLabel();
|
2023-09-26 22:59:15 +00:00
|
|
|
setSelectedPoint(selectedVertex.coord, model->series, false);
|
2023-06-14 19:40:12 +00:00
|
|
|
|
|
|
|
|
updateItemLabel(labelPosition);
|
2023-03-02 13:45:23 +00:00
|
|
|
itemLabel()->setProperty("labelText", label);
|
|
|
|
|
labelVisible = true;
|
2023-10-23 12:16:45 +00:00
|
|
|
if (sliceView() && sliceView()->isVisible())
|
|
|
|
|
updateSliceItemLabel(label, slicePosition);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
itemLabel()->setVisible(labelVisible);
|
2023-04-05 10:07:42 +00:00
|
|
|
if (sliceView() && sliceView()->isVisible())
|
2023-03-02 13:45:23 +00:00
|
|
|
sliceItemLabel()->setVisible(labelVisible);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QQuickGraphsSurface::addModel(QSurface3DSeries *series)
|
|
|
|
|
{
|
2023-03-20 19:36:09 +00:00
|
|
|
auto parent = graphNode();
|
2023-03-02 13:45:23 +00:00
|
|
|
bool visible = series->isVisible();
|
|
|
|
|
|
|
|
|
|
auto model = new QQuick3DModel();
|
2023-03-20 19:36:09 +00:00
|
|
|
model->setParent(parent);
|
|
|
|
|
model->setParentItem(parent);
|
2023-03-02 13:45:23 +00:00
|
|
|
model->setObjectName(QStringLiteral("SurfaceModel"));
|
|
|
|
|
model->setVisible(visible);
|
2023-09-26 22:59:15 +00:00
|
|
|
if (selectionMode().testFlag(QAbstract3DGraph::SelectionNone))
|
2023-03-02 13:45:23 +00:00
|
|
|
model->setPickable(false);
|
|
|
|
|
else
|
|
|
|
|
model->setPickable(true);
|
|
|
|
|
|
|
|
|
|
auto geometry = new QQuick3DGeometry();
|
2023-03-20 19:36:09 +00:00
|
|
|
geometry->setParent(model);
|
2023-03-02 13:45:23 +00:00
|
|
|
geometry->setStride(sizeof(SurfaceVertex));
|
|
|
|
|
geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
|
|
|
|
|
geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
|
|
|
|
|
0,
|
|
|
|
|
QQuick3DGeometry::Attribute::F32Type);
|
|
|
|
|
geometry->addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
|
|
|
|
|
sizeof(QVector3D),
|
|
|
|
|
QQuick3DGeometry::Attribute::F32Type);
|
|
|
|
|
geometry->addAttribute(QQuick3DGeometry::Attribute::IndexSemantic,
|
|
|
|
|
0,
|
|
|
|
|
QQuick3DGeometry::Attribute::U32Type);
|
|
|
|
|
model->setGeometry(geometry);
|
|
|
|
|
|
|
|
|
|
QQuick3DTexture *texture = new QQuick3DTexture();
|
2023-03-24 09:57:43 +00:00
|
|
|
texture->setHorizontalTiling(QQuick3DTexture::ClampToEdge);
|
|
|
|
|
texture->setVerticalTiling(QQuick3DTexture::ClampToEdge);
|
2023-10-05 06:22:21 +00:00
|
|
|
QQuickGraphsTextureData *textureData = new QQuickGraphsTextureData();
|
2023-03-24 09:57:43 +00:00
|
|
|
textureData->setParent(texture);
|
|
|
|
|
textureData->setParentItem(texture);
|
2023-03-02 13:45:23 +00:00
|
|
|
texture->setTextureData(textureData);
|
2023-05-23 07:44:55 +00:00
|
|
|
|
|
|
|
|
QQmlListReference materialRef(model, "materials");
|
2023-05-05 10:51:27 +00:00
|
|
|
|
2023-10-13 08:56:18 +00:00
|
|
|
QQuick3DCustomMaterial *customMaterial = createQmlCustomMaterial(
|
|
|
|
|
QStringLiteral(":/materials/SurfaceMaterial"));
|
2023-05-05 10:51:27 +00:00
|
|
|
|
2023-05-23 07:44:55 +00:00
|
|
|
customMaterial->setParent(model);
|
|
|
|
|
customMaterial->setParentItem(model);
|
|
|
|
|
customMaterial->setCullMode(QQuick3DMaterial::NoCulling);
|
|
|
|
|
QVariant textureInputAsVariant = customMaterial->property("custex");
|
2023-10-13 08:56:18 +00:00
|
|
|
QQuick3DShaderUtilsTextureInput *textureInput = textureInputAsVariant
|
|
|
|
|
.value<QQuick3DShaderUtilsTextureInput *>();
|
2023-03-24 09:57:43 +00:00
|
|
|
textureInput->setTexture(texture);
|
2023-05-23 07:44:55 +00:00
|
|
|
|
2023-08-23 10:53:22 +00:00
|
|
|
texture->setParent(customMaterial);
|
|
|
|
|
|
2023-05-23 07:44:55 +00:00
|
|
|
materialRef.append(customMaterial);
|
2023-03-02 13:45:23 +00:00
|
|
|
|
|
|
|
|
auto gridModel = new QQuick3DModel();
|
2023-03-20 19:36:09 +00:00
|
|
|
gridModel->setParent(parent);
|
|
|
|
|
gridModel->setParentItem(parent);
|
2023-03-30 05:23:03 +00:00
|
|
|
gridModel->setObjectName(QStringLiteral("SurfaceModel"));
|
2023-03-02 13:45:23 +00:00
|
|
|
gridModel->setVisible(visible);
|
|
|
|
|
gridModel->setDepthBias(1.0f);
|
|
|
|
|
auto gridGeometry = new QQuick3DGeometry();
|
|
|
|
|
gridGeometry->setParent(this);
|
|
|
|
|
gridGeometry->setStride(sizeof(SurfaceVertex));
|
|
|
|
|
gridGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
|
|
|
|
|
gridGeometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
|
|
|
|
|
0,
|
|
|
|
|
QQuick3DGeometry::Attribute::F32Type);
|
2023-05-05 10:51:27 +00:00
|
|
|
gridGeometry->addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
|
|
|
|
|
sizeof(QVector3D),
|
|
|
|
|
QQuick3DGeometry::Attribute::F32Type);
|
2023-03-02 13:45:23 +00:00
|
|
|
gridGeometry->addAttribute(QQuick3DGeometry::Attribute::IndexSemantic,
|
|
|
|
|
0,
|
|
|
|
|
QQuick3DGeometry::Attribute::U32Type);
|
|
|
|
|
gridModel->setGeometry(gridGeometry);
|
|
|
|
|
QQmlListReference gridMaterialRef(gridModel, "materials");
|
2023-05-05 10:51:27 +00:00
|
|
|
auto gridMaterial = createQmlCustomMaterial(QStringLiteral(":/materials/GridSurfaceMaterial"));
|
2023-03-20 19:36:09 +00:00
|
|
|
gridMaterial->setParent(gridModel);
|
2023-05-05 10:51:27 +00:00
|
|
|
gridMaterial->setParentItem(gridModel);
|
2023-03-02 13:45:23 +00:00
|
|
|
gridMaterialRef.append(gridMaterial);
|
|
|
|
|
|
|
|
|
|
SurfaceModel *surfaceModel = new SurfaceModel();
|
|
|
|
|
surfaceModel->model = model;
|
|
|
|
|
surfaceModel->gridModel = gridModel;
|
|
|
|
|
surfaceModel->series = series;
|
2023-03-24 09:57:43 +00:00
|
|
|
surfaceModel->texture = texture;
|
2023-05-23 07:44:55 +00:00
|
|
|
surfaceModel->customMaterial = customMaterial;
|
2023-03-02 13:45:23 +00:00
|
|
|
|
|
|
|
|
m_model.push_back(surfaceModel);
|
|
|
|
|
|
|
|
|
|
connect(series,
|
|
|
|
|
&QSurface3DSeries::flatShadingEnabledChanged,
|
|
|
|
|
this,
|
|
|
|
|
&QQuickGraphsSurface::handleFlatShadingEnabledChanged);
|
|
|
|
|
connect(series,
|
|
|
|
|
&QSurface3DSeries::wireframeColorChanged,
|
|
|
|
|
this,
|
|
|
|
|
&QQuickGraphsSurface::handleWireframeColorChanged);
|
2023-04-05 10:07:42 +00:00
|
|
|
|
|
|
|
|
if (sliceView())
|
|
|
|
|
addSliceModel(surfaceModel);
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2023-03-02 13:01:55 +00:00
|
|
|
void QQuickGraphsSurface::createSliceView()
|
|
|
|
|
{
|
|
|
|
|
QQuickGraphsItem::createSliceView();
|
|
|
|
|
|
2023-04-05 10:07:42 +00:00
|
|
|
for (auto surfaceModel : m_model)
|
|
|
|
|
addSliceModel(surfaceModel);
|
|
|
|
|
|
|
|
|
|
QQuick3DViewport *sliceParent = sliceView();
|
2023-03-02 13:01:55 +00:00
|
|
|
|
|
|
|
|
m_sliceSelectionPointer = new QQuick3DModel();
|
|
|
|
|
m_sliceSelectionPointer->setParent(sliceParent->scene());
|
|
|
|
|
m_sliceSelectionPointer->setParentItem(sliceParent->scene());
|
|
|
|
|
m_sliceSelectionPointer->setSource(QUrl(QStringLiteral("#Sphere")));
|
|
|
|
|
QQuick3DPrincipledMaterial *pointerMaterial = new QQuick3DPrincipledMaterial();
|
|
|
|
|
pointerMaterial->setParent(m_sliceSelectionPointer);
|
2023-09-26 22:59:15 +00:00
|
|
|
pointerMaterial->setBaseColor(theme()->singleHighlightColor());
|
2023-03-02 13:01:55 +00:00
|
|
|
QQmlListReference sliceMaterialRef(m_sliceSelectionPointer, "materials");
|
|
|
|
|
sliceMaterialRef.append(pointerMaterial);
|
|
|
|
|
m_sliceInstancing = new SurfaceSelectionInstancing();
|
|
|
|
|
m_sliceInstancing->setScale(QVector3D(0.001f, 0.001f, 0.001f));
|
|
|
|
|
m_sliceSelectionPointer->setInstancing(m_sliceInstancing);
|
2023-09-26 22:59:15 +00:00
|
|
|
m_sliceInstancing->setColor(theme()->singleHighlightColor());
|
2023-03-02 13:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-23 12:16:45 +00:00
|
|
|
void QQuickGraphsSurface::updateSliceItemLabel(QString label, const QVector3D &position)
|
|
|
|
|
{
|
2023-11-03 16:13:23 +00:00
|
|
|
QQuickGraphsItem::updateSliceItemLabel(label, position);
|
|
|
|
|
|
2023-10-23 12:16:45 +00:00
|
|
|
QFontMetrics fm(theme()->font());
|
|
|
|
|
float textPadding = 12.0f;
|
|
|
|
|
float labelHeight = fm.height() + textPadding;
|
|
|
|
|
float labelWidth = fm.horizontalAdvance(label) + textPadding;
|
|
|
|
|
sliceItemLabel()->setProperty("labelWidth", labelWidth);
|
|
|
|
|
sliceItemLabel()->setProperty("labelHeight", labelHeight);
|
|
|
|
|
QVector3D labelPosition = position;
|
|
|
|
|
labelPosition.setZ(.1f);
|
|
|
|
|
labelPosition.setY(position.y() + .05f);
|
|
|
|
|
sliceItemLabel()->setPosition(labelPosition);
|
|
|
|
|
sliceItemLabel()->setProperty("labelText", label);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 18:02:05 +00:00
|
|
|
void QQuickGraphsSurface::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)
|
|
|
|
|
{
|
|
|
|
|
checkSliceEnabled();
|
2024-01-03 10:00:34 +00:00
|
|
|
bool validSlice = mode.testFlag(QAbstract3DGraph::SelectionSlice)
|
|
|
|
|
&& m_selectedPoint != invalidSelectionPosition();
|
|
|
|
|
if (sliceView() && sliceView()->isVisible()) {
|
|
|
|
|
if (validSlice) {
|
|
|
|
|
updateSliceGraph();
|
|
|
|
|
} else {
|
2023-11-23 18:02:05 +00:00
|
|
|
m_selectionDirty = true;
|
|
|
|
|
setSliceActivatedChanged(true);
|
|
|
|
|
}
|
2024-01-03 10:00:34 +00:00
|
|
|
} else if (validSlice) {
|
|
|
|
|
m_selectionDirty = true;
|
|
|
|
|
setSliceActivatedChanged(true);
|
2023-11-23 18:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setSeriesVisualsDirty(true);
|
|
|
|
|
itemLabel()->setVisible(false);
|
|
|
|
|
if (sliceView() && sliceView()->isVisible())
|
|
|
|
|
sliceItemLabel()->setVisible(false);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-05 10:07:42 +00:00
|
|
|
void QQuickGraphsSurface::addSliceModel(SurfaceModel *model)
|
|
|
|
|
{
|
|
|
|
|
QQuick3DViewport *sliceParent = sliceView();
|
|
|
|
|
|
|
|
|
|
auto surfaceModel = new QQuick3DModel();
|
|
|
|
|
surfaceModel->setParent(sliceParent->scene());
|
|
|
|
|
surfaceModel->setParentItem(sliceParent->scene());
|
|
|
|
|
surfaceModel->setVisible(model->series->isVisible());
|
|
|
|
|
|
|
|
|
|
auto geometry = new QQuick3DGeometry();
|
|
|
|
|
geometry->setParent(surfaceModel);
|
|
|
|
|
geometry->setParentItem(surfaceModel);
|
|
|
|
|
geometry->setStride(sizeof(SurfaceVertex));
|
|
|
|
|
geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
|
|
|
|
|
geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
|
|
|
|
|
0,
|
|
|
|
|
QQuick3DGeometry::Attribute::F32Type);
|
|
|
|
|
geometry->addAttribute(QQuick3DGeometry::Attribute::TexCoord0Semantic,
|
|
|
|
|
sizeof(QVector3D),
|
|
|
|
|
QQuick3DGeometry::Attribute::F32Type);
|
|
|
|
|
geometry->addAttribute(QQuick3DGeometry::Attribute::IndexSemantic,
|
|
|
|
|
0,
|
|
|
|
|
QQuick3DGeometry::Attribute::U32Type);
|
|
|
|
|
surfaceModel->setGeometry(geometry);
|
|
|
|
|
|
|
|
|
|
QQmlListReference materialRef(surfaceModel, "materials");
|
2023-05-05 10:51:27 +00:00
|
|
|
auto material = createQmlCustomMaterial(QStringLiteral(":/materials/SurfaceSliceMaterial"));
|
2023-04-05 10:07:42 +00:00
|
|
|
material->setCullMode(QQuick3DMaterial::NoCulling);
|
|
|
|
|
QVariant textureInputAsVariant = material->property("custex");
|
2023-10-13 08:56:18 +00:00
|
|
|
QQuick3DShaderUtilsTextureInput *textureInput = textureInputAsVariant
|
|
|
|
|
.value<QQuick3DShaderUtilsTextureInput *>();
|
2023-04-05 10:07:42 +00:00
|
|
|
QQuick3DTexture *texture = model->texture;
|
|
|
|
|
textureInput->setTexture(texture);
|
|
|
|
|
materialRef.append(material);
|
|
|
|
|
|
|
|
|
|
model->sliceModel = surfaceModel;
|
|
|
|
|
|
|
|
|
|
QQuick3DModel *gridModel = new QQuick3DModel();
|
|
|
|
|
gridModel->setParent(sliceParent->scene());
|
|
|
|
|
gridModel->setParentItem(sliceParent->scene());
|
|
|
|
|
gridModel->setVisible(model->series->isVisible());
|
|
|
|
|
gridModel->setDepthBias(1.0f);
|
|
|
|
|
QQuick3DGeometry *gridGeometry = new QQuick3DGeometry();
|
|
|
|
|
gridGeometry->setParent(gridModel);
|
|
|
|
|
gridGeometry->setStride(sizeof(SurfaceVertex));
|
|
|
|
|
gridGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
|
|
|
|
|
gridGeometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,
|
|
|
|
|
0,
|
|
|
|
|
QQuick3DGeometry::Attribute::F32Type);
|
|
|
|
|
gridGeometry->addAttribute(QQuick3DGeometry::Attribute::IndexSemantic,
|
|
|
|
|
0,
|
|
|
|
|
QQuick3DGeometry::Attribute::U32Type);
|
|
|
|
|
gridModel->setGeometry(gridGeometry);
|
|
|
|
|
QQmlListReference gridMaterialRef(gridModel, "materials");
|
|
|
|
|
QQuick3DPrincipledMaterial *gridMaterial = new QQuick3DPrincipledMaterial();
|
|
|
|
|
gridMaterial->setParent(gridModel);
|
|
|
|
|
gridMaterial->setLighting(QQuick3DPrincipledMaterial::NoLighting);
|
|
|
|
|
gridMaterial->setParent(gridModel);
|
|
|
|
|
gridMaterialRef.append(gridMaterial);
|
|
|
|
|
|
|
|
|
|
model->sliceGridModel = gridModel;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
void QQuickGraphsSurface::updateSingleHighlightColor()
|
|
|
|
|
{
|
2023-09-26 22:59:15 +00:00
|
|
|
m_instancing->setColor(theme()->singleHighlightColor());
|
2023-03-02 13:45:23 +00:00
|
|
|
if (sliceView())
|
2023-09-26 22:59:15 +00:00
|
|
|
m_sliceInstancing->setColor(theme()->singleHighlightColor());
|
2023-03-02 13:45:23 +00:00
|
|
|
}
|
|
|
|
|
|
2023-08-18 10:40:03 +00:00
|
|
|
void QQuickGraphsSurface::updateLightStrength()
|
|
|
|
|
{
|
|
|
|
|
for (auto model : m_model) {
|
|
|
|
|
QQmlListReference materialRef(model->model, "materials");
|
|
|
|
|
QQuick3DCustomMaterial *material = qobject_cast<QQuick3DCustomMaterial *>(materialRef.at(0));
|
2023-10-13 08:56:18 +00:00
|
|
|
material->setProperty("specularBrightness", theme()->lightStrength() * 0.05);
|
2023-08-18 10:40:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 13:45:23 +00:00
|
|
|
void QQuickGraphsSurface::handleThemeTypeChange()
|
|
|
|
|
{
|
|
|
|
|
for (auto model : m_model)
|
|
|
|
|
updateMaterial(model);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|