EnergySpectrumAnalyer/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp

529 lines
16 KiB
C++
Raw Normal View History

2026-03-26 17:35:58 +08:00
#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<float>::max();
float maxPrimary = std::numeric_limits<float>::min();
float minSecondary = std::numeric_limits<float>::max();
float maxSecondary = std::numeric_limits<float>::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<QVector<float>> dataMatrix(ROWS, QVector<float>(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<int>((point.primaryEnergy - minPrimary) / primaryStep));
int row = std::min(ROWS - 1, static_cast<int>((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);
// 设置轴范围
2026-03-27 10:24:41 +08:00
// m_surface->axisX()->setRange(minPrimary, maxPrimary);
// m_surface->axisY()->setRange(0, getMaxCount(dataMatrix));
// m_surface->axisZ()->setRange(minSecondary, maxSecondary);
2026-03-26 17:35:58 +08:00
if (flag)
{
qDebug()<< maxPrimary << maxSecondary;
m_surface->axisX()->setRange(0, maxPrimary);
m_surface->axisZ()->setRange(0, maxSecondary);
flag = false;
}
qDebug()<<getMaxCount(dataMatrix);
m_surface->axisY()->setRange(0, getMaxCount(dataMatrix));
}
float ThreeDDisplay::getMaxCount(const QVector<QVector<float> > &dataMatrix)
{
2026-03-27 10:24:41 +08:00
float maxVal = 0;
for (const auto& row : dataMatrix) {
for (float val : row) {
maxVal = std::max(maxVal, val);
}
}
return maxVal + 1; // 加1以便显示
2026-03-26 17:35:58 +08:00
}
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<SurfacePoint> &surfaceData)
{
2026-03-27 10:24:41 +08:00
m_surfaceData.clear();
2026-03-26 17:35:58 +08:00
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<double>(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<int>(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<int>(128 * r), // R: 0->128
0, // G: 0
255 - static_cast<int>(127 * r) // B: 255->128
);
}
else {
double r = (ratio - 0.5) / 0.5;
return QColor(
128 + static_cast<int>(127 * r), // R: 128->255
0, // G: 0
128 - static_cast<int>(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<int>(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<int>(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");
}
}