529 lines
16 KiB
C++
529 lines
16 KiB
C++
#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");
|
||
}
|
||
}
|