#include "ui/Panel/SurfacePanel.h" #include "ui/DockWidget.h" #include "ui/DockTitleBar.h" #include "common/SpdLogger.h" #include #include #include #include SurfacePanel::SurfacePanel(int index, const QString& filePath, QWidget* parent) : DataPanel(index, FileEntryType::Surface, filePath, parent) { m_iMinX = 0; m_iMaxX = 0; m_iMinY = 0; m_iMaxY = 0; m_iMinZ = 0; m_iMaxZ = 0; m_surfaceContainer = nullptr; m_surface = nullptr; m_p3DXAxis = nullptr; m_p3DYAxis = nullptr; m_p3DZAxis = nullptr; m_pSeries = nullptr; m_countX = 0; m_countY = 0; m_countZ = 0; m_title = ""; m_xTitle = ""; m_yTitle = ""; m_zTitle = ""; m_time = -1.0; m_thread = nullptr; m_mutex = nullptr; LOG_INFO("Created SurfacePanel {} for file: {}", index, filePath.toStdString()); } SurfacePanel::SurfacePanel(int index, std::shared_ptr fileEntry, QWidget* parent) : DataPanel(index, fileEntry, parent) { m_iMinX = 0; m_iMaxX = 0; m_iMinY = 0; m_iMaxY = 0; m_iMinZ = 0; m_iMaxZ = 0; m_surfaceContainer = nullptr; m_surface = nullptr; m_p3DXAxis = nullptr; m_p3DYAxis = nullptr; m_p3DZAxis = nullptr; m_pSeries = nullptr; m_countX = 0; m_countY = 0; m_countZ = 0; m_title = ""; m_xTitle = ""; m_yTitle = ""; m_zTitle = ""; m_time = -1.0; m_thread = nullptr; m_mutex = nullptr; if (fileEntry) { LOG_INFO("Created SurfacePanel {} for chart: {}", index, fileEntry->GetName().toStdString()); // Override the title with chart name title_ = QString("Surface Panel %1 - %2").arg(index).arg(fileEntry->GetName()); } else { LOG_WARN("Created SurfacePanel {} with null chart data", index); } } SurfacePanel::~SurfacePanel() { LOG_INFO("Destroyed SurfacePanel {}", GetIndex()); if (m_pSeries) { m_surface->removeSeries(m_pSeries); m_pSeries->deleteLater(); m_pSeries = nullptr; } if (m_surface) { m_surface->deleteLater(); m_surface = nullptr; } if (m_surfaceContainer) { m_surfaceContainer->deleteLater(); m_surfaceContainer = nullptr; } if (auto* layout = qobject_cast(this->layout())) { while (layout->count() > 0) { QLayoutItem* item = layout->takeAt(0); if (item) { delete item; } } } if (m_mutex) { delete m_mutex; } if (m_thread) { m_thread->requestExit(); m_thread->wait(); m_thread->deleteLater(); m_thread = nullptr; } } void SurfacePanel::RefreshPanel() { // Implement curve-specific refresh logic here DataPanel::RefreshPanel(); if (auto fileEntry = fileEntry_->AsSurface()) { OnDataPanelUpdated(fileEntry); } LOG_INFO("Refreshed TablePanel {}", GetIndex()); } void SurfacePanel::InitUI() { createSurface(); QHBoxLayout* mainLayout = new QHBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->addWidget(m_surfaceContainer); setLayout(mainLayout); m_mutex = new QMutex; } QString SurfacePanel::GetTypeDisplayName() const { return "Surface"; } void SurfacePanel::OnDataPanelUpdated(FileEntrySurface* fileEntry) { if (!m_surfaceContainer) { createSurface(); } QString strName = fileEntry->GetName(); updateTitle(strName); FileEntrySurface::ChartProperties propChart = fileEntry->GetChartProperties(); updateTitleAxis(propChart.xTitle, propChart.yTitle, propChart.zTitle); updateMinMaxX(propChart.xMin, propChart.xMax, propChart.xCount); updateMinMaxY(propChart.yMin, propChart.yMax, propChart.yCount); updateMinMaxZ(propChart.zMin, propChart.zMax, propChart.zCount); QString strFile = fileEntry->GetPath() + "/" + fileEntry->GetFileName(); FileEntrySurface::SurfaceProperties listCurve = fileEntry->GetSurfaceProperties(); updateParseFile(strFile, propChart.timeParam, listCurve); } void SurfacePanel::OnTimeChanged(double time) { m_time = time; if (m_surface) { QMutexLocker locker(m_mutex); if (m_data.size() > 0) { m_pSeries->dataProxy()->resetArray(nullptr); QMap< double, QMap< int, QVector< QVector > > >::const_iterator ite = m_data.lowerBound(time); if (ite == m_data.end()) { ite--; } QMap< int, QVector< QVector > > mapData = ite.value(); for (QMap< int, QVector< QVector > >::Iterator it = mapData.begin(); it != mapData.end(); it++) { int nIndex = it.key(); QVector< QVector > listRowData = it.value(); QSurfaceDataArray* data = new QSurfaceDataArray; for (int nI = 0; nI < listRowData.size(); nI++) { QSurfaceDataRow* dataRow = new QSurfaceDataRow; QVector listColData = listRowData[nI]; for (int nJ = 0; nJ < listColData.size(); nJ++) { QVector3D v3d = listColData[nJ]; *dataRow << v3d; } *data << dataRow; } m_pSeries->dataProxy()->resetArray(data); } } m_surface->setShadowQuality(QAbstract3DGraph::ShadowQuality::ShadowQualityNone); } } void SurfacePanel::updateTitle(const QString & title) { if (nullptr != dockWidget_) { m_title = title; dockWidget_->setWindowTitle(title); disconnect(dockWidget_, &QDockWidget::visibilityChanged, this, &SurfacePanel::onVisibilityChanged); connect(dockWidget_, &QDockWidget::visibilityChanged, this, &SurfacePanel::onVisibilityChanged); } } void SurfacePanel::updateTitleAxis(const QString & xTitle, const QString & yTitle, const QString & zTitle) { m_xTitle = xTitle; m_yTitle = yTitle; m_zTitle = zTitle; m_p3DXAxis->setTitle(xTitle); m_p3DXAxis->setTitleVisible(true); m_p3DYAxis->setTitle(yTitle); m_p3DYAxis->setTitleVisible(true); m_p3DZAxis->setTitle(zTitle); m_p3DZAxis->setTitleVisible(true); } void SurfacePanel::updateMinMaxX(float min, float max, int count) { if (max > min) { m_iMinX = min; m_iMaxX = max; if (count > 0) { m_countX = count; m_p3DXAxis->setSegmentCount(count); } m_p3DXAxis->setRange(min, max); } } void SurfacePanel::updateMinMaxY(float min, float max, int count) { if (max > min) { m_iMinY = min; m_iMaxY = max; if (count > 0) { m_countY = count; m_p3DYAxis->setSegmentCount(count); } m_p3DYAxis->setRange(min, max); } } void SurfacePanel::updateMinMaxZ(float min, float max, int count) { if (max > min) { m_iMinZ = min; m_iMaxZ = max; if (count > 0) { m_countZ = count; m_p3DZAxis->setSegmentCount(count); } m_p3DZAxis->setRange(min, max); } } void SurfacePanel::updateParseFile(const QString & strFile, int nT, FileEntrySurface::SurfaceProperties listCurve) { if (strFile.isEmpty()) { QMessageBox::information(nullptr, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("请检查数据文件路径!")); return; } { QMutexLocker locker(m_mutex); m_data.clear(); m_pSeries->dataProxy()->resetArray(nullptr); } for (int nI = 0; nI < listCurve.size(); nI++) { FileEntrySurface::SurfaceProperty surface = listCurve.at(nI); m_color = surface.color; QLinearGradient gr; gr.setColorAt(0.0, surface.color); gr.setColorAt(0.5, Qt::yellow); gr.setColorAt(0.8, Qt::red); gr.setColorAt(1.0, Qt::darkRed); m_pSeries->setBaseGradient(gr); m_pSeries->setColorStyle(Q3DTheme::ColorStyleRangeGradient); //m_pSeries->setSingleHighlightColor(Qt::green); } if (m_thread) { m_thread->requestExit(); m_thread->wait(); m_thread->deleteLater(); m_thread = nullptr; } m_thread = new LoadDataThread(this, strFile, nT, listCurve); connect(m_thread, &LoadDataThread::signalBeginLoadData, this, &SurfacePanel::slotBeginLoadData); connect(m_thread, &LoadDataThread::signalEndLoadData, this, &SurfacePanel::slotEndLoadData); m_thread->setMutex(m_mutex); m_thread->start(); FinalParseFile(); } void SurfacePanel::onVisibilityChanged(bool visible) { if (visible) { if (!m_surfaceContainer) { createSurface(); updateTitleAxis(m_xTitle, m_yTitle, m_zTitle); if (m_iMaxX == m_iMinX) { m_p3DXAxis->setAutoAdjustRange(true); } else { updateMinMaxX(m_iMinX, m_iMaxX, m_countX); } if (m_iMaxZ == m_iMinZ) { m_p3DZAxis->setAutoAdjustRange(true); } else { updateMinMaxZ(m_iMinZ, m_iMaxZ, m_countZ); } if (m_iMaxY == m_iMinY) { m_p3DYAxis->setAutoAdjustRange(true); } else { updateMinMaxY(m_iMaxY, m_iMinY, m_countY); } { QLinearGradient gr; gr.setColorAt(0.0, m_color); gr.setColorAt(0.5, Qt::yellow); gr.setColorAt(0.8, Qt::red); gr.setColorAt(1.0, Qt::darkRed); m_pSeries->setBaseGradient(gr); m_pSeries->setColorStyle(Q3DTheme::ColorStyleRangeGradient); } m_surface->setHorizontalAspectRatio(1.0); QHBoxLayout* layout = qobject_cast(this->layout()); if (layout) { layout->addWidget(m_surfaceContainer); } if (m_time > -1.0) { OnTimeChanged(m_time); } } } else { if (m_pSeries) { m_surface->removeSeries(m_pSeries); m_pSeries->deleteLater(); m_pSeries = nullptr; } if (m_surface) { m_surface->deleteLater(); m_surface = nullptr; } if (m_surfaceContainer) { m_surfaceContainer->deleteLater(); m_surfaceContainer = nullptr; } if (auto* layout = qobject_cast(this->layout())) { while (layout->count() > 0) { QLayoutItem* item = layout->takeAt(0); if (item) { delete item; } } } } } void SurfacePanel::createSurface() { m_surface = new Q3DSurface(); m_surface->setFlags(m_surface->flags()); m_surface->setFlipHorizontalGrid(false); m_p3DXAxis = new QValue3DAxis; m_p3DXAxis->setSegmentCount(10); m_p3DXAxis->setRange(-10, 10); m_p3DYAxis = new QValue3DAxis; m_p3DYAxis->setSegmentCount(10); m_p3DYAxis->setRange(-10, 10); m_p3DZAxis = new QValue3DAxis; m_p3DZAxis->setSegmentCount(10); m_p3DZAxis->setRange(-10, 10); m_surface->setAxisX(m_p3DXAxis); m_surface->setAxisY(m_p3DYAxis); m_surface->setAxisZ(m_p3DZAxis); m_surface->activeTheme()->setType(Q3DTheme::Theme(2)); m_pSeries = new QSurface3DSeries; m_pSeries->setDrawMode(QSurface3DSeries::DrawSurface); m_surface->addSeries(m_pSeries); m_surfaceContainer = QWidget::createWindowContainer(m_surface); m_surfaceContainer->setAutoFillBackground(true); m_surfaceContainer->setAttribute(Qt::WA_OpaquePaintEvent, true); m_surfaceContainer->setAttribute(Qt::WA_NoSystemBackground, false); m_surfaceContainer->setUpdatesEnabled(true); m_surfaceContainer->setMinimumHeight(100); m_pSeries->setBaseColor(Qt::green); m_pSeries->setColorStyle(Q3DTheme::ColorStyleUniform); m_pSeries->setSingleHighlightColor(Qt::green); m_pSeries->setMeshSmooth(false); m_pSeries->setFlatShadingEnabled(false); m_surface->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPreset(13)); } void SurfacePanel::slotBeginLoadData() { if (nullptr != dockWidget_) { QString title = m_title + " -- " + QString(tr("Begin LoadData")); dockWidget_->setWindowTitle(title); } } void SurfacePanel::slotEndLoadData() { if (nullptr != dockWidget_) { dockWidget_->setWindowTitle(m_title); } } void SurfacePanel::FinalParseFile() { if (m_iMaxX == m_iMinX) { m_p3DXAxis->setAutoAdjustRange(true); } if (m_iMaxZ == m_iMinZ) { m_p3DZAxis->setAutoAdjustRange(true); } if (m_iMaxY == m_iMinY) { m_p3DYAxis->setAutoAdjustRange(true); } m_surface->setHorizontalAspectRatio(1.0); } LoadDataThread::LoadDataThread(SurfacePanel *panel, QString file, int nT, FileEntrySurface::SurfaceProperties listCurve) { m_panel = panel; m_file = file; m_nT = nT; m_listCurve = listCurve; m_exit = false; } void LoadDataThread::run() { QFile file(m_file); if (file.open(QIODevice::ReadOnly)) { emit signalBeginLoadData(); while (!file.atEnd() && !m_exit) { QString strLine = file.readLine().simplified(); if (!strLine.isEmpty()) { QStringList listLine = strLine.split(" "); double t = listLine.at(m_nT).toDouble(); QMap< int, QVector< QVector > > mapData; for (int nI = 0; nI < m_listCurve.size(); nI++) { if (m_exit) { break; } FileEntrySurface::SurfaceProperty surface = m_listCurve.at(nI); int nStart = surface.start; int nStop = surface.stop; if (nStart == 0) { nStart = 1; } if (nStop == 0) { nStop = listLine.size(); } QString strX = surface.x; QString strY = surface.y; QString strZ = surface.z; double xInput = 0; double yInput = 0; double zInput = 0.0; QVector< QVector > listRowData; int nRow = 0; int nCol = 0; for (int nJ = nStart; nJ < nStop; nJ += 3) { if (m_exit) { break; } int x = listLine.at(nJ).toDouble(); int y = listLine.at(nJ + 1).toDouble(); double z = listLine.at(nJ + 2).toDouble(); if (strX == "x") { xInput = x; } else if (strX == "y") { xInput = y; } else if (strX == "z") { xInput = z; } if (strY == "x") { yInput = x; } else if (strY == "y") { yInput = y; } else if (strY == "z") { yInput = z; } if (strZ == "x") { zInput = x; } else if (strZ == "y") { zInput = y; } else if (strZ == "z") { zInput = z; } QVector3D v3d = QVector3D(xInput, yInput, zInput); if (nRow == x) { int nIndex = listRowData.size() - 1; QVector listColData = listRowData[nIndex]; listColData.push_back(v3d); listRowData.replace(nIndex, listColData); } else { QVector listColData; listColData.push_back(v3d); listRowData.push_back(listColData); } nRow = x; nCol = y; } mapData.insert(nI, listRowData); } { QMutexLocker locker(m_mutex); m_panel->m_data.insert(t, mapData); } } } file.close(); emit signalEndLoadData(); } }