GrabBag/Tools/CloudView/Inc/PointCloudGLWidget.h
2026-02-11 00:53:51 +08:00

394 lines
12 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.

#ifndef POINT_CLOUD_GL_WIDGET_H
#define POINT_CLOUD_GL_WIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QMatrix4x4>
#include <QVector3D>
#include <QMouseEvent>
#include <QWheelEvent>
#include <vector>
#include "PointCloudConverter.h"
/**
* @brief 点云显示颜色枚举
*/
enum class PointCloudColor
{
White,
Red,
Green,
Blue,
Yellow,
Cyan,
Magenta,
Original
};
/**
* @brief 选中的点信息
*/
struct SelectedPointInfo
{
bool valid;
size_t index;
float x, y, z;
int cloudIndex;
int lineIndex; // 所属线索引
int pointIndexInLine; // 点在线中的索引
SelectedPointInfo() : valid(false), index(0), x(0), y(0), z(0), cloudIndex(-1), lineIndex(-1), pointIndexInLine(-1) {}
};
/**
* @brief 选线模式
*/
enum class LineSelectMode
{
Vertical, // 纵向选线(同一条扫描线)
Horizontal // 横向选线(所有线的相同索引点)
};
/**
* @brief 欧拉角旋转顺序
*/
enum class EulerRotationOrder
{
ZYX, // Yaw-Pitch-Roll最常用
XYZ, // Roll-Pitch-Yaw
ZXY, // Yaw-Roll-Pitch
YXZ, // Pitch-Roll-Yaw
XZY, // Roll-Yaw-Pitch
YZX // Pitch-Yaw-Roll
};
/**
* @brief 选中的线信息
*/
struct SelectedLineInfo
{
bool valid;
int cloudIndex; // 点云索引
int lineIndex; // 线索引(纵向选线时使用)
int pointIndex; // 点索引(横向选线时使用)
int pointCount; // 该线上的点数
LineSelectMode mode; // 选线模式
SelectedLineInfo() : valid(false), cloudIndex(-1), lineIndex(-1), pointIndex(-1), pointCount(0), mode(LineSelectMode::Vertical) {}
};
/**
* @brief 线段数据
*/
struct LineSegment
{
float x1, y1, z1; // 起点
float x2, y2, z2; // 终点
float r, g, b; // 颜色 (0-1)
LineSegment() : x1(0), y1(0), z1(0), x2(0), y2(0), z2(0), r(1), g(1), b(1) {}
LineSegment(float _x1, float _y1, float _z1, float _x2, float _y2, float _z2, float _r = 1.0f, float _g = 1.0f, float _b = 1.0f)
: x1(_x1), y1(_y1), z1(_z1), x2(_x2), y2(_y2), z2(_z2), r(_r), g(_g), b(_b) {}
};
/**
* @brief 姿态点数据
*
* 坐标系约定(右手坐标系):
* - X轴红色指向右
* - Y轴绿色指向上
* - Z轴蓝色指向观察者
*
* 欧拉角旋转顺序ZYXYaw-Pitch-Roll
* - rz: 绕Z轴旋转偏航角Yaw
* - ry: 绕Y轴旋转俯仰角Pitch
* - rx: 绕X轴旋转滚转角Roll
*/
struct PosePoint
{
float x, y, z; // 位置
float rx, ry, rz; // 欧拉角(度)
float scale; // 坐标系大小
PosePoint() : x(0), y(0), z(0), rx(0), ry(0), rz(0), scale(10.0f) {}
PosePoint(float _x, float _y, float _z, float _rx, float _ry, float _rz, float _scale = 10.0f)
: x(_x), y(_y), z(_z), rx(_rx), ry(_ry), rz(_rz), scale(_scale) {}
};
/**
* @brief 点云 OpenGL 渲染控件
*/
class PointCloudGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit PointCloudGLWidget(QWidget* parent = nullptr);
~PointCloudGLWidget() override;
void addPointCloud(const PointCloudXYZ& cloud, const QString& name = "");
void addPointCloud(const PointCloudXYZRGB& cloud, const QString& name = "");
void clearPointClouds();
void setPointCloudColor(PointCloudColor color);
void setPointSize(float size);
void resetView();
/**
* @brief 设置视角旋转角度(不改变缩放和平移)
* @param rotX X轴旋转角度俯仰
* @param rotY Y轴旋转角度偏航
* @param rotZ Z轴旋转角度滚转
*/
void setViewAngles(float rotX, float rotY, float rotZ = 0.0f);
QVector<SelectedPointInfo> getSelectedPoints() const { return m_selectedPoints; }
SelectedLineInfo getSelectedLine() const { return m_selectedLine; }
void clearSelectedPoints();
void clearSelectedLine();
float calculateDistance(const SelectedPointInfo& p1, const SelectedPointInfo& p2);
/**
* @brief 通过线索引选择线(纵向)
*/
bool selectLineByIndex(int lineIndex);
/**
* @brief 通过点索引选择横向线(所有线的相同索引点)
*/
bool selectHorizontalLineByIndex(int pointIndex);
/**
* @brief 设置选线模式
*/
void setLineSelectMode(LineSelectMode mode) { m_lineSelectMode = mode; }
/**
* @brief 设置欧拉角旋转顺序
*/
void setEulerRotationOrder(EulerRotationOrder order) { m_eulerRotationOrder = order; }
/**
* @brief 获取欧拉角旋转顺序
*/
EulerRotationOrder getEulerRotationOrder() const { return m_eulerRotationOrder; }
/**
* @brief 设置是否启用测距功能
*/
void setMeasureDistanceEnabled(bool enabled) { m_measureDistanceEnabled = enabled; }
/**
* @brief 获取测距功能是否启用
*/
bool isMeasureDistanceEnabled() const { return m_measureDistanceEnabled; }
/**
* @brief 获取选中线上的所有点坐标
* @return 点坐标列表,每个元素为 (x, y, z)
*/
QVector<QVector3D> getSelectedLinePoints() const;
/**
* @brief 设置列表高亮点(与选点功能区分)
* @param point 点坐标,如果为空则清除高亮
*/
void setListHighlightPoint(const QVector3D& point);
/**
* @brief 清除列表高亮点
*/
void clearListHighlightPoint();
/**
* @brief 获取第一个点云的数据(用于旋转和保存)
*/
bool getFirstCloudData(PointCloudXYZ& cloud) const;
/**
* @brief 替换第一个点云(用于显示旋转后的数据)
*/
void replaceFirstCloud(const PointCloudXYZ& cloud, const QString& name);
/**
* @brief 获取点云数量
*/
size_t getCloudCount() const { return m_pointClouds.size(); }
/**
* @brief 对所有点云应用 4x4 变换矩阵
* @param matrix 4x4 变换矩阵
*/
void transformAllClouds(const QMatrix4x4& matrix);
/**
* @brief 添加线段
*/
void addLineSegments(const QVector<LineSegment>& segments);
/**
* @brief 清除所有线段
*/
void clearLineSegments();
/**
* @brief 添加姿态点
*/
void addPosePoints(const QVector<PosePoint>& poses);
/**
* @brief 清除所有姿态点
*/
void clearPosePoints();
signals:
void pointSelected(const SelectedPointInfo& point);
void twoPointsSelected(const SelectedPointInfo& p1, const SelectedPointInfo& p2, float distance);
void lineSelected(const SelectedLineInfo& line);
void viewAnglesChanged(float rotX, float rotY, float rotZ);
protected:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
private:
struct PointCloudData
{
std::vector<float> vertices;
std::vector<float> colors;
std::vector<int> lineIndices; // 每个点所属的线索引
std::vector<int> originalIndices; // 每个显示点在原始点云中的索引用于计算原始index
bool hasColor;
bool hasLineInfo; // 是否有线信息
QString name;
int colorIndex; // 点云颜色索引
int totalLines; // 总线数
int pointsPerLine; // 每线点数(网格化点云)
// VBO 缓冲区
QOpenGLBuffer vertexBuffer; // 顶点 VBO
QOpenGLBuffer colorBuffer; // 颜色 VBO
bool vboCreated; // VBO 是否已创建
PointCloudData()
: hasColor(false), hasLineInfo(false), colorIndex(0)
, totalLines(0), pointsPerLine(0)
, vertexBuffer(QOpenGLBuffer::VertexBuffer)
, colorBuffer(QOpenGLBuffer::VertexBuffer)
, vboCreated(false)
{}
// 移动构造函数QOpenGLBuffer 不支持拷贝)
PointCloudData(PointCloudData&& other) noexcept
: vertices(std::move(other.vertices))
, colors(std::move(other.colors))
, lineIndices(std::move(other.lineIndices))
, originalIndices(std::move(other.originalIndices))
, hasColor(other.hasColor)
, hasLineInfo(other.hasLineInfo)
, name(std::move(other.name))
, colorIndex(other.colorIndex)
, totalLines(other.totalLines)
, pointsPerLine(other.pointsPerLine)
, vertexBuffer(QOpenGLBuffer::VertexBuffer)
, colorBuffer(QOpenGLBuffer::VertexBuffer)
, vboCreated(false)
{
// VBO 需要在 GL 上下文中重建,标记为未创建
other.vboCreated = false;
}
PointCloudData& operator=(PointCloudData&& other) noexcept
{
if (this != &other) {
// 释放自身 VBO
if (vboCreated) {
vertexBuffer.destroy();
colorBuffer.destroy();
vboCreated = false;
}
vertices = std::move(other.vertices);
colors = std::move(other.colors);
lineIndices = std::move(other.lineIndices);
originalIndices = std::move(other.originalIndices);
hasColor = other.hasColor;
hasLineInfo = other.hasLineInfo;
name = std::move(other.name);
colorIndex = other.colorIndex;
totalLines = other.totalLines;
pointsPerLine = other.pointsPerLine;
// VBO 需要重建
other.vboCreated = false;
}
return *this;
}
// 禁止拷贝
PointCloudData(const PointCloudData&) = delete;
PointCloudData& operator=(const PointCloudData&) = delete;
};
void computeBoundingBox();
void setCurrentColor(PointCloudColor color);
void setColorByIndex(int colorIndex); // 根据索引设置颜色
SelectedPointInfo pickPoint(int screenX, int screenY);
void drawSelectedPoints();
void drawMeasurementLine();
void drawAxis();
void drawSelectedLine(); // 绘制选中的线
void drawLineSegments(); // 绘制线段
void drawPosePoints(); // 绘制姿态点
void uploadToVBO(PointCloudData& data); // 上传数据到 VBO
void releaseVBO(PointCloudData& data); // 释放 VBO 资源
std::vector<PointCloudData> m_pointClouds;
QMatrix4x4 m_projection;
QMatrix4x4 m_view;
QMatrix4x4 m_model;
float m_distance;
float m_rotationX;
float m_rotationY;
float m_rotationZ;
QVector3D m_center;
QVector3D m_pan;
QVector3D m_minBound;
QVector3D m_maxBound;
QPoint m_lastMousePos;
bool m_leftButtonPressed;
bool m_rightButtonPressed;
bool m_middleButtonPressed;
PointCloudColor m_currentColor;
float m_pointSize;
LineSelectMode m_lineSelectMode;
EulerRotationOrder m_eulerRotationOrder;
bool m_measureDistanceEnabled;
QVector<SelectedPointInfo> m_selectedPoints;
SelectedLineInfo m_selectedLine;
static const int MAX_SELECTED_POINTS = 2;
// 列表高亮点(与选点功能区分)
bool m_hasListHighlightPoint;
QVector3D m_listHighlightPoint;
int m_colorIndex; // 颜色轮换索引
static const int COLOR_COUNT = 7; // 可用颜色数量
// 线段和姿态点数据
QVector<LineSegment> m_lineSegments;
QVector<PosePoint> m_posePoints;
};
#endif // POINT_CLOUD_GL_WIDGET_H