#ifndef POINT_CLOUD_GL_WIDGET_H #define POINT_CLOUD_GL_WIDGET_H #include #include #include #include #include #include #include #include #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轴:蓝色,指向观察者 * * 欧拉角旋转顺序:ZYX(Yaw-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 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 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& segments); /** * @brief 清除所有线段 */ void clearLineSegments(); /** * @brief 添加姿态点 */ void addPosePoints(const QVector& 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 vertices; std::vector colors; std::vector lineIndices; // 每个点所属的线索引 std::vector 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 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 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 m_lineSegments; QVector m_posePoints; }; #endif // POINT_CLOUD_GL_WIDGET_H