#include "ThreeDDisplay.h" #include "ui_ThreeDDisplay.h" ThreeDDisplay::ThreeDDisplay(QWidget *parent) : QWidget(parent), ui(new Ui::ThreeDDisplay) { ui->setupUi(this); _init3DSurface(); // 初始化颜色渐变 initColorGradient(); } ThreeDDisplay::~ThreeDDisplay() { delete ui; } void ThreeDDisplay::_init3DSurface() { // 连接按钮的信号槽 connect(ui->pBtn_magnify, &QPushButton::clicked, this, &ThreeDDisplay::_zoomOut); connect(ui->pBtn_reduce, &QPushButton::clicked, this, &ThreeDDisplay::_zoomIn); connect(ui->pBtn_restore, &QPushButton::clicked, this, &ThreeDDisplay::_resetView); // 创建3D曲面图 m_surface = new Q3DSurface(); m_surface->setAxisX(new QValue3DAxis); m_surface->setAxisY(new QValue3DAxis); m_surface->setAxisZ(new QValue3DAxis); // 设置场景和样式 m_surface->activeTheme()->setType(Q3DTheme::ThemeQt); m_surface->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); m_surface->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricRight); // 创建数据代理和系列 m_dataProxy = new QSurfaceDataProxy(); m_series = new QSurface3DSeries(m_dataProxy); //m_series->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe); m_series->setDrawMode(QSurface3DSeries::DrawSurface);//无网格线 m_series->setFlatShadingEnabled(true); // 设置颜色渐变 QLinearGradient gradient; gradient.setColorAt(0.0, Qt::blue); gradient.setColorAt(0.25, Qt::cyan); gradient.setColorAt(0.5, Qt::green); gradient.setColorAt(0.75, Qt::yellow); gradient.setColorAt(1.0, Qt::red); m_series->setBaseGradient(gradient); m_series->setColorStyle(Q3DTheme::ColorStyleRangeGradient); // 添加到图表 m_surface->addSeries(m_series); // 创建容器widget m_surfaceContainer = QWidget::createWindowContainer(m_surface, this); //m_surfaceContainer->setMinimumSize(800, 600); // 添加到UI布局中(假设有一个QFrame或QVBoxLayout) // 这里需要根据你的实际UI布局进行调整 ui->hLayout3D->addWidget(m_surfaceContainer); ui->hLayout3D->setContentsMargins(0, 0, 0, 0); // 设置轴标签和标题 m_surface->axisX()->setTitle("初级粒子能量 (MeV)"); m_surface->axisY()->setTitle("符合事件计数"); m_surface->axisZ()->setTitle("次级粒子能量和 (MeV)"); // 创建自定义主题(或者修改当前主题) Q3DTheme *customTheme = m_surface->activeTheme(); customTheme->setType(Q3DTheme::ThemeQt); // 使用Qt主题为基础 // 禁用标签边框和背景 customTheme->setLabelBorderEnabled(false); // 禁用标签边框 customTheme->setLabelBackgroundEnabled(false); // 禁用标签背景 // 设置坐标轴样式 // 保留网格线(如果需要的话),如果不想要网格线可以设为透明 // 如果希望保留淡化的网格线参考 customTheme->setGridLineColor(QColor(200, 200, 200, 50)); // 半透明灰色网格线 // 设置坐标轴标签颜色(确保标签可见) customTheme->setLabelTextColor(Qt::black); customTheme->setLabelBackgroundColor(Qt::transparent); // 透明背景 // 应用自定义主题 m_surface->setActiveTheme(customTheme); // 也可以直接对坐标轴进行设置 // 设置坐标轴标签格式(可选) m_surface->axisX()->setLabelFormat("%.1f"); m_surface->axisY()->setLabelFormat("%.0f"); m_surface->axisZ()->setLabelFormat("%.1f"); // 调整标签位置和显示 m_surface->axisX()->setLabelAutoRotation(30); // 标签适当旋转 m_surface->axisY()->setLabelAutoRotation(30); m_surface->axisZ()->setLabelAutoRotation(30); // 设置标题 m_surface->setTitle("符合事件能量分布曲面图"); //m_surface->setTitleFont(QFont("Arial", 20, QFont::Bold)); // 启用鼠标交互(默认已启用,这里确保设置) m_surface->setSelectionMode(QAbstract3DGraph::SelectionNone); // 禁用选择模式,避免干扰 m_surface->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetIsometricRight);// 设置初始相机位置 _saveInitialCameraState(); // 添加注释 _addAnnotations(); } void ThreeDDisplay::_addAnnotations() { // 添加注释到曲面图 QCustom3DLabel* titleLabel = new QCustom3DLabel(); titleLabel->setText("符合事件分析曲面图\nX: 初级粒子能量\nY: 符合事件计数\nZ: 次级粒子能量和"); titleLabel->setFont(QFont("Arial", 12)); titleLabel->setPosition(QVector3D(0.5f, 1.0f, 0.0f)); titleLabel->setScaling(QVector3D(0.1f, 0.1f, 0.1f)); m_surface->addCustomItem(titleLabel); } void ThreeDDisplay::_updateSurfaceData() { if (m_surfaceData.isEmpty()) return; // 准备数据数组 const int ROWS = 600 / 2; // Y轴分段数 const int COLS = 360 / 2; // X轴分段数 // 找出能量范围 float minPrimary = std::numeric_limits::max(); float maxPrimary = std::numeric_limits::min(); float minSecondary = std::numeric_limits::max(); float maxSecondary = std::numeric_limits::min(); for (const auto& point : m_surfaceData) { minPrimary = std::min(minPrimary, point.primaryEnergy); maxPrimary = std::max(maxPrimary, point.primaryEnergy); minSecondary = std::min(minSecondary, point.secondaryEnergySum); maxSecondary = std::max(maxSecondary, point.secondaryEnergySum); } // 创建数据矩阵 QVector> dataMatrix(ROWS, QVector(COLS, 0)); // 填充数据矩阵(简单的二维直方图) float primaryStep = (maxPrimary - minPrimary) / COLS; float secondaryStep = (maxSecondary - minSecondary) / ROWS; for (const auto& point : m_surfaceData) { int col = std::min(COLS - 1, static_cast((point.primaryEnergy - minPrimary) / primaryStep)); int row = std::min(ROWS - 1, static_cast((point.secondaryEnergySum - minSecondary) / secondaryStep)); if (row >= 0 && row < ROWS && col >= 0 && col < COLS) { dataMatrix[row][col] += point.count; } } // 创建曲面数据 QSurfaceDataArray* dataArray = new QSurfaceDataArray; dataArray->reserve(ROWS); for (int row = 0; row < ROWS; row++) { QSurfaceDataRow* newRow = new QSurfaceDataRow(COLS); float yPos = minSecondary + row * secondaryStep; for (int col = 0; col < COLS; col++) { float xPos = minPrimary + col * primaryStep; float zVal = dataMatrix[row][col]; // 计数作为高度 (*newRow)[col].setPosition(QVector3D(xPos, zVal, yPos)); } dataArray->append(newRow); } // 更新代理数据 m_dataProxy->resetArray(dataArray); // 设置轴范围 // m_surface->axisX()->setRange(minPrimary, maxPrimary); // m_surface->axisY()->setRange(0, getMaxCount(dataMatrix)); // m_surface->axisZ()->setRange(minSecondary, maxSecondary); if (flag) { qDebug()<< maxPrimary << maxSecondary; m_surface->axisX()->setRange(0, maxPrimary); m_surface->axisZ()->setRange(0, maxSecondary); flag = false; } qDebug()<axisY()->setRange(0, getMaxCount(dataMatrix)); } float ThreeDDisplay::getMaxCount(const QVector > &dataMatrix) { float maxVal = 0; for (const auto& row : dataMatrix) { for (float val : row) { maxVal = std::max(maxVal, val); } } return maxVal + 1; // 加1以便显示 } void ThreeDDisplay::_saveInitialCameraState() { Q3DCamera *camera = m_surface->scene()->activeCamera(); m_initialCameraTarget = camera->target(); m_initialCameraXRotation = camera->xRotation(); m_initialCameraYRotation = camera->yRotation(); m_initialCameraZoomLevel = camera->zoomLevel(); } // 缩小 void ThreeDDisplay::_zoomIn() { Q3DCamera *camera = m_surface->scene()->activeCamera(); float currentZoom = camera->zoomLevel(); // 减小zoomLevel可以放大视图 // zoomLevel越小,视图越大 float newZoom = currentZoom - 10.0f; // 限制最小缩放级别,避免过度放大 if (newZoom >= 10.0f) { camera->setZoomLevel(newZoom); } else { camera->setZoomLevel(10.0f); } } // 放大 void ThreeDDisplay::_zoomOut() { Q3DCamera *camera = m_surface->scene()->activeCamera(); float currentZoom = camera->zoomLevel(); // 增加zoomLevel可以缩小视图 // zoomLevel越大,视图越小 float newZoom = currentZoom + 10.0f; // 限制最大缩放级别,避免过度缩小 if (newZoom <= 500.0f) { camera->setZoomLevel(newZoom); } else { camera->setZoomLevel(500.0f); } } // 还原视图 void ThreeDDisplay::_resetView() { Q3DCamera *camera = m_surface->scene()->activeCamera(); // 恢复到初始的相机预设 //camera->setCameraPreset(Q3DCamera::CameraPresetIsometricRight); //或者恢复到保存的初始状态 camera->setTarget(m_initialCameraTarget); camera->setXRotation(m_initialCameraXRotation); camera->setYRotation(m_initialCameraYRotation); camera->setZoomLevel(m_initialCameraZoomLevel); } void ThreeDDisplay::setSurfaceData(const QVector &surfaceData) { m_surfaceData.clear(); m_surfaceData = surfaceData; _updateSurfaceData(); } void ThreeDDisplay::setBasicParticle(double startValue, double endValue) { ui->lineEdit_begin_start->setText(QString::number(startValue)); ui->lineEdit_begin_end->setText(QString::number(endValue)); } void ThreeDDisplay::setSecondParticle(double startValue, double endValue) { ui->lineEdit_second_start->setText(QString::number(startValue)); ui->lineEdit_second_end->setText(QString::number(endValue)); } void ThreeDDisplay::setComplyWithEvent(double value) { ui->lineEdit_count->setText(QString::number(value)); } /*--------------------设置颜色---------------------------*/ // 初始化颜色渐变 void ThreeDDisplay::initColorGradient() { // 设置默认渐变类型 m_gradientType = Gradient_Custom; // 初始化颜色范围 m_colorRange.minValue = 0; m_colorRange.maxValue = 100; m_colorRange.range = 100.0; //// 初始化颜色 //m_gradientColors.clear(); //m_gradientColors << QColor("#0E508A") // 蓝色 // << QColor("#D5A914") // 亮蓝 // << QColor("#D57C14") // 青色 // << QColor("#D52A3D"); // 青绿; //// << QColor(0, 255, 0) // 绿色 //// << QColor(128, 255, 0) // 黄绿 //// << QColor(255, 255, 0) // 黄色 //// << QColor(255, 192, 0) // 橙色 //// << QColor(255, 128, 0) // 深橙 //// << QColor(255, 0, 0); // 红色 } // 根据数值获取颜色 QColor ThreeDDisplay::getColorForValue(int value) { if (m_colorRange.range < 1e-6) { return QColor("#0E508A"); // 默认蓝色 } // 计算归一化比例 (0.0 - 1.0) double ratio = static_cast(value - m_colorRange.minValue) / m_colorRange.range; ratio = qBound(0.0, ratio, 1.0); // 根据渐变类型返回颜色 switch (m_gradientType) { case Gradient_HeatMap: return getHeatMapColor(ratio); case Gradient_BlueToRed: return getBlueToRedColor(ratio); case Gradient_GreenToRed: return getGreenToRedColor(ratio); case Gradient_TrafficLight: return getTrafficLightColor(ratio); case Gradient_Custom: return getCustomColor(ratio); default: return getHeatMapColor(ratio); } } // 热力图颜色 QColor ThreeDDisplay::getHeatMapColor(double ratio) { if (m_gradientColors.isEmpty()) { return QColor("#0E508A"); } int colorCount = m_gradientColors.size(); double colorStep = 1.0 / (colorCount - 1); int colorIndex = static_cast(ratio / colorStep); colorIndex = qMin(colorIndex, colorCount - 2); double colorRatio = (ratio - colorIndex * colorStep) / colorStep; const QColor& color1 = m_gradientColors[colorIndex]; const QColor& color2 = m_gradientColors[colorIndex + 1]; return QColor( color1.red() + (color2.red() - color1.red()) * colorRatio, color1.green() + (color2.green() - color1.green()) * colorRatio, color1.blue() + (color2.blue() - color1.blue()) * colorRatio ); } // 蓝到红渐变 QColor ThreeDDisplay::getBlueToRedColor(double ratio) { // 蓝(0,0,255) -> 紫(128,0,128) -> 红(255,0,0) if (ratio < 0.5) { double r = ratio / 0.5; return QColor( static_cast(128 * r), // R: 0->128 0, // G: 0 255 - static_cast(127 * r) // B: 255->128 ); } else { double r = (ratio - 0.5) / 0.5; return QColor( 128 + static_cast(127 * r), // R: 128->255 0, // G: 0 128 - static_cast(128 * r) // B: 128->0 ); } } // 绿到红渐变 QColor ThreeDDisplay::getGreenToRedColor(double ratio) { // 绿(0,255,0) -> 黄(255,255,0) -> 红(255,0,0) if (ratio < 0.5) { double r = ratio / 0.5; return QColor( static_cast(255 * r), // R: 0->255 255, // G: 255 0 // B: 0 ); } else { double r = (ratio - 0.5) / 0.5; return QColor( 255, // R: 255 255 - static_cast(255 * r), // G: 255->0 0 // B: 0 ); } } // 红黄绿三色 QColor ThreeDDisplay::getTrafficLightColor(double ratio) { if (ratio < 0.33) { return QColor(0, 255, 0); // 绿色 } else if (ratio < 0.66) { return QColor(255, 255, 0); // 黄色 } else { return QColor(255, 0, 0); // 红色 } } // 自定义 QColor ThreeDDisplay::getCustomColor(double ratio) { if (ratio < 0.05) { return QColor("#0E508A"); } else if (ratio < 0.10) { return QColor("#1A7ED6"); } else if (ratio < 0.15) { return QColor("#125997"); } else if (ratio < 0.20) { return QColor("#1B66A7"); } else if (ratio < 0.25) { return QColor("#4CA9F9"); } else if (ratio < 0.30) { return QColor("#D5A914"); } else if (ratio < 0.35) { return QColor("#F4C422"); } else if (ratio < 0.40) { return QColor("#EBC236"); } else if (ratio < 0.45) { return QColor("#EFC32F"); } else if (ratio < 0.50) { return QColor("#FFF8D2"); } else if (ratio < 0.55) { return QColor("#D57C14"); } else if (ratio < 0.60) { return QColor("#EF9B39"); } else if (ratio < 0.65) { return QColor("#F09B38"); } else if (ratio < 0.70) { return QColor("#EF9831"); } else if (ratio < 0.75) { return QColor("#FFECD1"); } else if (ratio < 0.80) { return QColor("#D52A3D"); } else if (ratio < 0.85) { return QColor("#FF334C"); } else if (ratio < 0.90) { return QColor("#F55B5B");// } else if (ratio < 0.95) { return QColor("#E93E51"); } else { return QColor("#FFD4D7"); } }