EnergySpectrumAnalyer/src/ThreeDimensionalConformityAnalysisView/ThreeDDisplay.cpp
2026-03-27 10:24:41 +08:00

529 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
// 设置轴范围
// 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()<<getMaxCount(dataMatrix);
m_surface->axisY()->setRange(0, getMaxCount(dataMatrix));
}
float ThreeDDisplay::getMaxCount(const QVector<QVector<float> > &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<SurfacePoint> &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<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");
}
}