提取CloudView3D
This commit is contained in:
parent
6a6dd55bec
commit
517889ff90
@ -1,9 +1,9 @@
|
||||
# CloudUtils库项目配置文件
|
||||
# CloudUtils搴撻」鐩厤缃枃浠?
|
||||
|
||||
# 指定Qt模块
|
||||
# 鎸囧畾Qt妯″潡
|
||||
QT += core
|
||||
|
||||
# 指定项目模板为静态库
|
||||
# 鎸囧畾椤圭洰妯℃澘涓洪潤鎬佸簱
|
||||
TEMPLATE = lib
|
||||
CONFIG += staticlib
|
||||
|
||||
@ -12,24 +12,24 @@ win32-msvc {
|
||||
}
|
||||
|
||||
|
||||
# 指定C++标准
|
||||
# 鎸囧畾C++鏍囧噯
|
||||
CONFIG += c++11
|
||||
|
||||
INCLUDEPATH += $$PWD/../../SDK/Device/VzNLSDK/_Inc
|
||||
INCLUDEPATH += $$PWD/../../SDK/Device/VzNLSDK/Inc
|
||||
INCLUDEPATH += $$PWD/../../Device/SDK/VzNLSDK/_Inc
|
||||
INCLUDEPATH += $$PWD/../../Device/SDK/VzNLSDK/Inc
|
||||
|
||||
INCLUDEPATH += $$PWD/../VrCommon/Inc
|
||||
INCLUDEPATH += $$PWD/../VrUtils/Inc
|
||||
|
||||
# 指定包含路径
|
||||
# 鎸囧畾鍖呭惈璺緞
|
||||
INCLUDEPATH += ./Inc \
|
||||
./Src
|
||||
|
||||
# 指定头文件
|
||||
# 鎸囧畾澶存枃浠?
|
||||
HEADERS += Inc/LaserDataLoader.h \
|
||||
Inc/PointCloudImageUtils.h
|
||||
|
||||
# 指定源文件
|
||||
# 鎸囧畾婧愭枃浠?
|
||||
SOURCES += Src/LaserDataLoader.cpp \
|
||||
Src/PointCloudImageUtils.cpp
|
||||
|
||||
|
||||
@ -1,68 +1,70 @@
|
||||
# Qt 5 兼容:QOpenGLWidget 在 widgets 模块中
|
||||
QT += core gui widgets opengl
|
||||
|
||||
CONFIG += c++17
|
||||
CONFIG -= app_bundle
|
||||
|
||||
# Windows平台UTF-8编码支持
|
||||
win32-msvc {
|
||||
QMAKE_CXXFLAGS += /utf-8
|
||||
QMAKE_CXXFLAGS += /bigobj
|
||||
}
|
||||
|
||||
TARGET = CloudView
|
||||
TEMPLATE = app
|
||||
|
||||
# 项目目录
|
||||
INCLUDEPATH += $$PWD/Inc
|
||||
INCLUDEPATH += $$PWD/../CloudUtils/Inc
|
||||
INCLUDEPATH += $$PWD/../../SDK/Device/VzNLSDK/Inc
|
||||
INCLUDEPATH += $$PWD/../VrUtils/Inc
|
||||
INCLUDEPATH += $$PWD/../VrCommon/Inc
|
||||
|
||||
# 头文件
|
||||
HEADERS += \
|
||||
Inc/CloudViewMainWindow.h \
|
||||
Inc/PointCloudGLWidget.h \
|
||||
Inc/PointCloudConverter.h
|
||||
|
||||
# 源文件
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
Src/CloudViewMainWindow.cpp \
|
||||
Src/PointCloudGLWidget.cpp \
|
||||
Src/PointCloudConverter.cpp
|
||||
|
||||
# 资源文件
|
||||
RESOURCES += resource/resource.qrc
|
||||
RESOURCES += $$PWD/../../AppUtils/UICommon/common_resources.qrc
|
||||
|
||||
# Windows 应用图标
|
||||
RC_ICONS = resource/logo.ico
|
||||
|
||||
# Windows平台库链接
|
||||
win32:CONFIG(release, debug|release): {
|
||||
LIBS += -L../CloudUtils/release -lCloudUtils
|
||||
LIBS += -L../VrUtils/release -lVrUtils
|
||||
LIBS += -L../VrCommon/release -lVrCommon
|
||||
}
|
||||
else:win32:CONFIG(debug, debug|release): {
|
||||
LIBS += -L../CloudUtils/debug -lCloudUtils
|
||||
LIBS += -L../VrUtils/debug -lVrUtils
|
||||
LIBS += -L../VrCommon/debug -lVrCommon
|
||||
}
|
||||
|
||||
# Windows系统库
|
||||
win32 {
|
||||
LIBS += -lAdvapi32
|
||||
LIBS += -lopengl32
|
||||
LIBS += -lglu32
|
||||
}
|
||||
|
||||
|
||||
DEFINES += _USE_MATH_DEFINES
|
||||
|
||||
# Default rules for deployment
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
# Qt 5 鍏煎锛歈OpenGLWidget 鍦?widgets 妯″潡涓?
|
||||
QT += core gui widgets opengl
|
||||
|
||||
CONFIG += c++17
|
||||
CONFIG -= app_bundle
|
||||
|
||||
# Windows骞冲彴UTF-8缂栫爜鏀寔
|
||||
win32-msvc {
|
||||
QMAKE_CXXFLAGS += /utf-8
|
||||
QMAKE_CXXFLAGS += /bigobj
|
||||
}
|
||||
|
||||
TARGET = CloudView
|
||||
TEMPLATE = app
|
||||
|
||||
# 椤圭洰鐩綍
|
||||
INCLUDEPATH += $$PWD/Inc
|
||||
INCLUDEPATH += $$PWD/../CloudView3D/Inc
|
||||
INCLUDEPATH += $$PWD/../CloudUtils/Inc
|
||||
INCLUDEPATH += $$PWD/../../Device/SDK/VzNLSDK/Inc
|
||||
INCLUDEPATH += $$PWD/../VrUtils/Inc
|
||||
INCLUDEPATH += $$PWD/../VrCommon/Inc
|
||||
|
||||
# 澶存枃浠?
|
||||
HEADERS += \
|
||||
Inc/CloudViewMainWindow.h \
|
||||
Inc/PointCloudGLWidget.h \
|
||||
Inc/PointCloudConverter.h
|
||||
|
||||
# 婧愭枃浠?
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
Src/CloudViewMainWindow.cpp \
|
||||
Src/PointCloudConverter.cpp
|
||||
|
||||
# 璧勬簮鏂囦欢
|
||||
RESOURCES += resource/resource.qrc
|
||||
RESOURCES += $$PWD/../../AppUtils/UICommon/common_resources.qrc
|
||||
|
||||
# Windows 搴旂敤鍥炬爣
|
||||
RC_ICONS = resource/logo.ico
|
||||
|
||||
# Windows骞冲彴搴撻摼鎺?
|
||||
win32:CONFIG(release, debug|release): {
|
||||
LIBS += -L../CloudView3D/release -lCloudView3D
|
||||
LIBS += -L../CloudUtils/release -lCloudUtils
|
||||
LIBS += -L../VrUtils/release -lVrUtils
|
||||
LIBS += -L../VrCommon/release -lVrCommon
|
||||
}
|
||||
else:win32:CONFIG(debug, debug|release): {
|
||||
LIBS += -L../CloudView3D/debug -lCloudView3D
|
||||
LIBS += -L../CloudUtils/debug -lCloudUtils
|
||||
LIBS += -L../VrUtils/debug -lVrUtils
|
||||
LIBS += -L../VrCommon/debug -lVrCommon
|
||||
}
|
||||
|
||||
# Windows绯荤粺搴?
|
||||
win32 {
|
||||
LIBS += -lAdvapi32
|
||||
LIBS += -lopengl32
|
||||
LIBS += -lglu32
|
||||
}
|
||||
|
||||
|
||||
DEFINES += _USE_MATH_DEFINES
|
||||
|
||||
# Default rules for deployment
|
||||
qnx: target.path = /tmp/$${TARGET}/bin
|
||||
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
||||
@ -22,8 +22,8 @@
|
||||
#include <QStringList>
|
||||
#include <memory>
|
||||
|
||||
#include "PointCloudGLWidget.h"
|
||||
#include "PointCloudConverter.h"
|
||||
#include "CloudView3D.h"
|
||||
#include "PointCloudConverter.h"
|
||||
|
||||
class QTextEdit;
|
||||
class QTableWidget;
|
||||
@ -276,9 +276,14 @@ private:
|
||||
/**
|
||||
* @brief 更新选点信息显示
|
||||
*/
|
||||
void updateSelectedPointsDisplay();
|
||||
|
||||
/**
|
||||
void updateSelectedPointsDisplay();
|
||||
void clearMeasureResultDisplay();
|
||||
void updateMeasureResultDisplay(const SelectedPointInfo& p1,
|
||||
const SelectedPointInfo& p2,
|
||||
float distance,
|
||||
const QString& statusPrefix = QString());
|
||||
|
||||
/**
|
||||
* @brief 从原始数据获取线上点(包含0,0,0)
|
||||
*/
|
||||
QVector<QVector3D> getOriginalLinePoints(const SelectedLineInfo& lineInfo);
|
||||
@ -307,7 +312,9 @@ private:
|
||||
bool loadBoundingBoxFile(const QString& fileName);
|
||||
void applyTransformToAllClouds(const QMatrix4x4& matrix);
|
||||
void addGeneratedCloud(const PointCloudXYZ& cloud, const QString& name);
|
||||
void showBasicShape(const QString& shapeName, const QVector<LineSegment>& segments);
|
||||
void showBasicShape(const QString& shapeName,
|
||||
const QVector<LineSegment>& segments,
|
||||
const QVector<BasicShapePoint>& points = QVector<BasicShapePoint>());
|
||||
void showBasicSurface(const QString& shapeName, const QVector<SurfaceQuad>& surfaces, const QVector<LineSegment>& outlines);
|
||||
|
||||
// 点云显示控件
|
||||
@ -329,9 +336,10 @@ private:
|
||||
// 选点测距控件
|
||||
QCheckBox* m_cbMeasureDistance;
|
||||
QPushButton* m_btnClearPoints;
|
||||
QLabel* m_lblPoint1;
|
||||
QLabel* m_lblPoint2;
|
||||
QLabel* m_lblDistance;
|
||||
QLabel* m_lblPoint1;
|
||||
QLabel* m_lblPoint2;
|
||||
QLabel* m_lblDistance;
|
||||
QLabel* m_lblDeltaXYZ;
|
||||
|
||||
// 点1坐标编辑控件
|
||||
QLineEdit* m_editPoint1X;
|
||||
|
||||
@ -1,59 +1,14 @@
|
||||
#ifndef POINT_CLOUD_CONVERTER_H
|
||||
#define POINT_CLOUD_CONVERTER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
/**
|
||||
* @brief 简单的 3D 点结构
|
||||
*/
|
||||
struct Point3D
|
||||
{
|
||||
float x, y, z;
|
||||
Point3D() : x(0), y(0), z(0) {}
|
||||
Point3D(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 带颜色的 3D 点结构
|
||||
*/
|
||||
struct Point3DRGB
|
||||
{
|
||||
float x, y, z;
|
||||
uint8_t r, g, b;
|
||||
float pointSize; // 自定义点大小,0 表示使用全局默认值(来自 RGBA 中 A > 1 的值)
|
||||
Point3DRGB() : x(0), y(0), z(0), r(255), g(255), b(255), pointSize(0) {}
|
||||
Point3DRGB(float _x, float _y, float _z, uint8_t _r = 255, uint8_t _g = 255, uint8_t _b = 255, float _ps = 0)
|
||||
: x(_x), y(_y), z(_z), r(_r), g(_g), b(_b), pointSize(_ps) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 简单的点云容器
|
||||
*/
|
||||
template<typename PointT>
|
||||
class SimplePointCloud
|
||||
{
|
||||
public:
|
||||
std::vector<PointT> points;
|
||||
std::vector<int> lineIndices; // 每个点所属的线索引
|
||||
|
||||
void clear() { points.clear(); lineIndices.clear(); }
|
||||
size_t size() const { return points.size(); }
|
||||
bool empty() const { return points.empty(); }
|
||||
void reserve(size_t n) { points.reserve(n); lineIndices.reserve(n); }
|
||||
void push_back(const PointT& pt) { points.push_back(pt); }
|
||||
void push_back(const PointT& pt, int lineIdx) {
|
||||
points.push_back(pt);
|
||||
lineIndices.push_back(lineIdx);
|
||||
}
|
||||
};
|
||||
|
||||
using PointCloudXYZ = SimplePointCloud<Point3D>;
|
||||
using PointCloudXYZRGB = SimplePointCloud<Point3DRGB>;
|
||||
|
||||
/**
|
||||
* @brief 点云数据转换器
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "CloudView3DTypes.h"
|
||||
|
||||
/**
|
||||
* @brief 点云数据转换器
|
||||
* 负责加载 txt/dat、pcd、ply 格式的点云文件
|
||||
*/
|
||||
class PointCloudConverter
|
||||
|
||||
@ -1,495 +1,6 @@
|
||||
#ifndef POINT_CLOUD_GL_WIDGET_H
|
||||
#define POINT_CLOUD_GL_WIDGET_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QMatrix4x4>
|
||||
#include <QVector3D>
|
||||
#include <QQuaternion>
|
||||
#include <QMouseEvent>
|
||||
#include <QWheelEvent>
|
||||
#include <vector>
|
||||
|
||||
#include "PointCloudConverter.h"
|
||||
#include "VZNL_Types.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)
|
||||
float lineWidth; // 线宽,0 表示使用默认值(来自 RGBA 中 A > 1 的值)
|
||||
|
||||
LineSegment() : x1(0), y1(0), z1(0), x2(0), y2(0), z2(0), r(1), g(1), b(1), lineWidth(0) {}
|
||||
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, float _lw = 0)
|
||||
: x1(_x1), y1(_y1), z1(_z1), x2(_x2), y2(_y2), z2(_z2), r(_r), g(_g), b(_b), lineWidth(_lw) {}
|
||||
};
|
||||
#ifndef CLOUD_VIEW_POINT_CLOUD_GL_WIDGET_FORWARDER_H
|
||||
#define CLOUD_VIEW_POINT_CLOUD_GL_WIDGET_FORWARDER_H
|
||||
|
||||
/**
|
||||
* @brief 面片数据
|
||||
*/
|
||||
struct SurfaceQuad
|
||||
{
|
||||
QVector3D p1, p2, p3, p4;
|
||||
float r, g, b, a;
|
||||
#include "../../CloudView3D/Inc/PointCloudGLWidget.h"
|
||||
|
||||
SurfaceQuad() : p1(), p2(), p3(), p4(), r(0.0f), g(0.8f), b(1.0f), a(0.5f) {}
|
||||
SurfaceQuad(const QVector3D& _p1,
|
||||
const QVector3D& _p2,
|
||||
const QVector3D& _p3,
|
||||
const QVector3D& _p4,
|
||||
float _r = 0.0f,
|
||||
float _g = 0.8f,
|
||||
float _b = 1.0f,
|
||||
float _a = 0.5f)
|
||||
: p1(_p1), p2(_p2), p3(_p3), p4(_p4), r(_r), g(_g), b(_b), a(_a) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @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();
|
||||
void fitToContent();
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
/**
|
||||
* @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 更新选中点的坐标
|
||||
* @param index 点索引(0或1)
|
||||
* @param x 新的X坐标
|
||||
* @param y 新的Y坐标
|
||||
* @param z 新的Z坐标
|
||||
*/
|
||||
void updateSelectedPointCoord(int index, float x, float y, float z);
|
||||
|
||||
/**
|
||||
* @brief 设置选中点(如果不存在则创建)
|
||||
* @param index 点索引(0或1)
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @param z Z坐标
|
||||
*/
|
||||
void setSelectedPointCoord(int index, float x, float y, float z);
|
||||
|
||||
/**
|
||||
* @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 对所有点云中 Z 值小于阈值的点应用旋转矩阵
|
||||
* @param rotMatrix 旋转矩阵
|
||||
* @param zThreshold Z 轴阈值
|
||||
* @return 被旋转的点数
|
||||
*/
|
||||
size_t rotateCloudsByZThreshold(const QMatrix4x4& rotMatrix, float zThreshold);
|
||||
|
||||
/**
|
||||
* @brief 获取所有点云数据(按线分组),用于保存
|
||||
* @param scanLines 输出:每条扫描线的点列表
|
||||
* @return 总点数
|
||||
*/
|
||||
size_t getAllCloudsByLines(std::vector<std::vector<SVzNL3DPosition>>& scanLines) const;
|
||||
|
||||
/**
|
||||
* @brief 添加线段
|
||||
*/
|
||||
void addLineSegments(const QVector<LineSegment>& segments);
|
||||
|
||||
/**
|
||||
* @brief 清除所有线段
|
||||
*/
|
||||
void clearLineSegments();
|
||||
|
||||
/**
|
||||
* @brief 设置基础图形线段
|
||||
*/
|
||||
void setBasicShapeSegments(const QVector<LineSegment>& segments);
|
||||
|
||||
/**
|
||||
* @brief 追加基础图形线段
|
||||
*/
|
||||
void appendBasicShapeSegments(const QVector<LineSegment>& segments);
|
||||
|
||||
/**
|
||||
* @brief 追加基础图形面片
|
||||
*/
|
||||
void appendBasicShapeSurfaces(const QVector<SurfaceQuad>& surfaces);
|
||||
|
||||
/**
|
||||
* @brief 清除基础图形线段
|
||||
*/
|
||||
void clearBasicShapeSegments();
|
||||
|
||||
/**
|
||||
* @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 mouseDoubleClickEvent(QMouseEvent* 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)
|
||||
std::vector<int> pointInLineIndices; // 每个显示点在所属线中的索引(预计算,支持不等长线)
|
||||
bool hasColor;
|
||||
bool hasLineInfo; // 是否有线信息
|
||||
QString name;
|
||||
int colorIndex; // 点云颜色索引
|
||||
int totalLines; // 总线数
|
||||
int pointsPerLine; // 每线点数(网格化点云)
|
||||
|
||||
// 自定义点大小分组(RGBA 中 A > 1 的点按大小分组)
|
||||
struct PointSizeGroup {
|
||||
float pointSize; // 点大小
|
||||
std::vector<size_t> indices; // 该大小的点在顶点数组中的索引(顶点索引 = indices[i] * 3)
|
||||
};
|
||||
std::vector<PointSizeGroup> customPointSizeGroups;
|
||||
bool hasCustomPointSizes;
|
||||
|
||||
// VBO 缓冲区
|
||||
QOpenGLBuffer vertexBuffer; // 顶点 VBO
|
||||
QOpenGLBuffer colorBuffer; // 颜色 VBO
|
||||
bool vboCreated; // VBO 是否已创建
|
||||
|
||||
PointCloudData()
|
||||
: hasColor(false), hasLineInfo(false), colorIndex(0)
|
||||
, totalLines(0), pointsPerLine(0)
|
||||
, hasCustomPointSizes(false)
|
||||
, 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))
|
||||
, pointInLineIndices(std::move(other.pointInLineIndices))
|
||||
, hasColor(other.hasColor)
|
||||
, hasLineInfo(other.hasLineInfo)
|
||||
, name(std::move(other.name))
|
||||
, colorIndex(other.colorIndex)
|
||||
, totalLines(other.totalLines)
|
||||
, pointsPerLine(other.pointsPerLine)
|
||||
, customPointSizeGroups(std::move(other.customPointSizeGroups))
|
||||
, hasCustomPointSizes(other.hasCustomPointSizes)
|
||||
, 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);
|
||||
pointInLineIndices = std::move(other.pointInLineIndices);
|
||||
hasColor = other.hasColor;
|
||||
hasLineInfo = other.hasLineInfo;
|
||||
name = std::move(other.name);
|
||||
colorIndex = other.colorIndex;
|
||||
totalLines = other.totalLines;
|
||||
pointsPerLine = other.pointsPerLine;
|
||||
customPointSizeGroups = std::move(other.customPointSizeGroups);
|
||||
hasCustomPointSizes = other.hasCustomPointSizes;
|
||||
// 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 drawBasicShapeSurfaces(); // 绘制基础图形面片
|
||||
void drawBasicShapeSegments(); // 绘制基础图形
|
||||
void drawLineSegmentList(const QVector<LineSegment>& segments);
|
||||
void drawPosePoints(); // 绘制姿态点
|
||||
void drawAxisLabels(); // 绘制坐标轴 XYZ 标注
|
||||
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;
|
||||
QQuaternion m_rotation; // 使用四元数存储旋转状态
|
||||
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<LineSegment> m_basicShapeSegments;
|
||||
QVector<SurfaceQuad> m_basicShapeSurfaces;
|
||||
QVector<PosePoint> m_posePoints;
|
||||
};
|
||||
|
||||
#endif // POINT_CLOUD_GL_WIDGET_H
|
||||
#endif // CLOUD_VIEW_POINT_CLOUD_GL_WIDGET_FORWARDER_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
30
CloudView3D/CloudView3D.pro
Normal file
30
CloudView3D/CloudView3D.pro
Normal file
@ -0,0 +1,30 @@
|
||||
QT += core gui widgets opengl
|
||||
|
||||
CONFIG += c++17 staticlib
|
||||
TEMPLATE = lib
|
||||
TARGET = CloudView3D
|
||||
|
||||
win32-msvc {
|
||||
QMAKE_CXXFLAGS += /utf-8
|
||||
QMAKE_CXXFLAGS += /bigobj
|
||||
}
|
||||
|
||||
INCLUDEPATH += $$PWD/Inc
|
||||
INCLUDEPATH += $$PWD/../VrUtils/Inc
|
||||
INCLUDEPATH += $$PWD/../VrCommon/Inc
|
||||
INCLUDEPATH += $$PWD/../../Device/SDK/VzNLSDK/Inc
|
||||
|
||||
HEADERS += \
|
||||
Inc/CloudView3D.h \
|
||||
Inc/CloudView3DTypes.h \
|
||||
Inc/PointCloudGLWidget.h
|
||||
|
||||
SOURCES += \
|
||||
Src/PointCloudGLWidget.cpp
|
||||
|
||||
win32 {
|
||||
LIBS += -lopengl32
|
||||
LIBS += -lglu32
|
||||
}
|
||||
|
||||
DEFINES += _USE_MATH_DEFINES
|
||||
7
CloudView3D/Inc/CloudView3D.h
Normal file
7
CloudView3D/Inc/CloudView3D.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef CLOUD_VIEW_3D_H
|
||||
#define CLOUD_VIEW_3D_H
|
||||
|
||||
#include "CloudView3DTypes.h"
|
||||
#include "PointCloudGLWidget.h"
|
||||
|
||||
#endif // CLOUD_VIEW_3D_H
|
||||
54
CloudView3D/Inc/CloudView3DTypes.h
Normal file
54
CloudView3D/Inc/CloudView3DTypes.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef CLOUD_VIEW_3D_TYPES_H
|
||||
#define CLOUD_VIEW_3D_TYPES_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
struct Point3D
|
||||
{
|
||||
float x, y, z;
|
||||
Point3D() : x(0), y(0), z(0) {}
|
||||
Point3D(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
|
||||
};
|
||||
|
||||
struct Point3DRGB
|
||||
{
|
||||
float x, y, z;
|
||||
uint8_t r, g, b;
|
||||
float pointSize;
|
||||
|
||||
Point3DRGB() : x(0), y(0), z(0), r(255), g(255), b(255), pointSize(0) {}
|
||||
Point3DRGB(float _x,
|
||||
float _y,
|
||||
float _z,
|
||||
uint8_t _r = 255,
|
||||
uint8_t _g = 255,
|
||||
uint8_t _b = 255,
|
||||
float _ps = 0)
|
||||
: x(_x), y(_y), z(_z), r(_r), g(_g), b(_b), pointSize(_ps) {}
|
||||
};
|
||||
|
||||
template<typename PointT>
|
||||
class SimplePointCloud
|
||||
{
|
||||
public:
|
||||
std::vector<PointT> points;
|
||||
std::vector<int> lineIndices;
|
||||
|
||||
void clear() { points.clear(); lineIndices.clear(); }
|
||||
size_t size() const { return points.size(); }
|
||||
bool empty() const { return points.empty(); }
|
||||
void reserve(size_t n) { points.reserve(n); lineIndices.reserve(n); }
|
||||
void push_back(const PointT& pt) { points.push_back(pt); }
|
||||
void push_back(const PointT& pt, int lineIdx)
|
||||
{
|
||||
points.push_back(pt);
|
||||
lineIndices.push_back(lineIdx);
|
||||
}
|
||||
};
|
||||
|
||||
using PointCloudXYZ = SimplePointCloud<Point3D>;
|
||||
using PointCloudXYZRGB = SimplePointCloud<Point3DRGB>;
|
||||
|
||||
#endif // CLOUD_VIEW_3D_TYPES_H
|
||||
521
CloudView3D/Inc/PointCloudGLWidget.h
Normal file
521
CloudView3D/Inc/PointCloudGLWidget.h
Normal file
@ -0,0 +1,521 @@
|
||||
#ifndef POINT_CLOUD_GL_WIDGET_H
|
||||
#define POINT_CLOUD_GL_WIDGET_H
|
||||
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLBuffer>
|
||||
#include <QMatrix4x4>
|
||||
#include <QVector3D>
|
||||
#include <QQuaternion>
|
||||
#include <QMouseEvent>
|
||||
#include <QWheelEvent>
|
||||
#include <vector>
|
||||
|
||||
#include "CloudView3DTypes.h"
|
||||
#include "VZNL_Types.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)
|
||||
float lineWidth; // 线宽,0 表示使用默认值(来自 RGBA 中 A > 1 的值)
|
||||
|
||||
LineSegment() : x1(0), y1(0), z1(0), x2(0), y2(0), z2(0), r(1), g(1), b(1), lineWidth(0) {}
|
||||
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, float _lw = 0)
|
||||
: x1(_x1), y1(_y1), z1(_z1), x2(_x2), y2(_y2), z2(_z2), r(_r), g(_g), b(_b), lineWidth(_lw) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 面片数据
|
||||
*/
|
||||
struct SurfaceQuad
|
||||
{
|
||||
QVector3D p1, p2, p3, p4;
|
||||
float r, g, b, a;
|
||||
|
||||
SurfaceQuad() : p1(), p2(), p3(), p4(), r(0.0f), g(0.8f), b(1.0f), a(0.5f) {}
|
||||
SurfaceQuad(const QVector3D& _p1,
|
||||
const QVector3D& _p2,
|
||||
const QVector3D& _p3,
|
||||
const QVector3D& _p4,
|
||||
float _r = 0.0f,
|
||||
float _g = 0.8f,
|
||||
float _b = 1.0f,
|
||||
float _a = 0.5f)
|
||||
: p1(_p1), p2(_p2), p3(_p3), p4(_p4), r(_r), g(_g), b(_b), a(_a) {}
|
||||
};
|
||||
|
||||
struct BasicShapePoint
|
||||
{
|
||||
float x, y, z;
|
||||
float r, g, b;
|
||||
float pointSize;
|
||||
|
||||
BasicShapePoint() : x(0), y(0), z(0), r(1), g(1), b(1), pointSize(8.0f) {}
|
||||
BasicShapePoint(float _x,
|
||||
float _y,
|
||||
float _z,
|
||||
float _r = 1.0f,
|
||||
float _g = 1.0f,
|
||||
float _b = 1.0f,
|
||||
float _size = 8.0f)
|
||||
: x(_x), y(_y), z(_z), r(_r), g(_g), b(_b), pointSize(_size) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @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();
|
||||
void fitToContent();
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
/**
|
||||
* @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 更新选中点的坐标
|
||||
* @param index 点索引(0或1)
|
||||
* @param x 新的X坐标
|
||||
* @param y 新的Y坐标
|
||||
* @param z 新的Z坐标
|
||||
*/
|
||||
void updateSelectedPointCoord(int index, float x, float y, float z);
|
||||
|
||||
/**
|
||||
* @brief 设置选中点(如果不存在则创建)
|
||||
* @param index 点索引(0或1)
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @param z Z坐标
|
||||
*/
|
||||
void setSelectedPointCoord(int index, float x, float y, float z);
|
||||
|
||||
/**
|
||||
* @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 对所有点云中 Z 值小于阈值的点应用旋转矩阵
|
||||
* @param rotMatrix 旋转矩阵
|
||||
* @param zThreshold Z 轴阈值
|
||||
* @return 被旋转的点数
|
||||
*/
|
||||
size_t rotateCloudsByZThreshold(const QMatrix4x4& rotMatrix, float zThreshold);
|
||||
|
||||
/**
|
||||
* @brief 获取所有点云数据(按线分组),用于保存
|
||||
* @param scanLines 输出:每条扫描线的点列表
|
||||
* @return 总点数
|
||||
*/
|
||||
size_t getAllCloudsByLines(std::vector<std::vector<SVzNL3DPosition>>& scanLines) const;
|
||||
|
||||
/**
|
||||
* @brief 添加线段
|
||||
*/
|
||||
void addLineSegments(const QVector<LineSegment>& segments);
|
||||
|
||||
/**
|
||||
* @brief 清除所有线段
|
||||
*/
|
||||
void clearLineSegments();
|
||||
|
||||
/**
|
||||
* @brief 设置基础图形线段
|
||||
*/
|
||||
void setBasicShapeSegments(const QVector<LineSegment>& segments);
|
||||
|
||||
/**
|
||||
* @brief 追加基础图形线段
|
||||
*/
|
||||
void appendBasicShapeSegments(const QVector<LineSegment>& segments);
|
||||
|
||||
/**
|
||||
* @brief 追加基础图形面片
|
||||
*/
|
||||
void appendBasicShapeSurfaces(const QVector<SurfaceQuad>& surfaces);
|
||||
void appendBasicShapePoints(const QVector<BasicShapePoint>& points);
|
||||
|
||||
/**
|
||||
* @brief 清除基础图形线段
|
||||
*/
|
||||
void clearBasicShapeSegments();
|
||||
|
||||
/**
|
||||
* @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 measurementFinished(const SelectedPointInfo& p1,
|
||||
const SelectedPointInfo& p2,
|
||||
float distance,
|
||||
float dx,
|
||||
float dy,
|
||||
float dz);
|
||||
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 mouseDoubleClickEvent(QMouseEvent* 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)
|
||||
std::vector<int> pointInLineIndices; // 每个显示点在所属线中的索引(预计算,支持不等长线)
|
||||
bool hasColor;
|
||||
bool hasLineInfo; // 是否有线信息
|
||||
QString name;
|
||||
int colorIndex; // 点云颜色索引
|
||||
int totalLines; // 总线数
|
||||
int pointsPerLine; // 每线点数(网格化点云)
|
||||
|
||||
// 自定义点大小分组(RGBA 中 A > 1 的点按大小分组)
|
||||
struct PointSizeGroup {
|
||||
float pointSize; // 点大小
|
||||
std::vector<size_t> indices; // 该大小的点在顶点数组中的索引(顶点索引 = indices[i] * 3)
|
||||
};
|
||||
std::vector<PointSizeGroup> customPointSizeGroups;
|
||||
bool hasCustomPointSizes;
|
||||
|
||||
// VBO 缓冲区
|
||||
QOpenGLBuffer vertexBuffer; // 顶点 VBO
|
||||
QOpenGLBuffer colorBuffer; // 颜色 VBO
|
||||
bool vboCreated; // VBO 是否已创建
|
||||
|
||||
PointCloudData()
|
||||
: hasColor(false), hasLineInfo(false), colorIndex(0)
|
||||
, totalLines(0), pointsPerLine(0)
|
||||
, hasCustomPointSizes(false)
|
||||
, 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))
|
||||
, pointInLineIndices(std::move(other.pointInLineIndices))
|
||||
, hasColor(other.hasColor)
|
||||
, hasLineInfo(other.hasLineInfo)
|
||||
, name(std::move(other.name))
|
||||
, colorIndex(other.colorIndex)
|
||||
, totalLines(other.totalLines)
|
||||
, pointsPerLine(other.pointsPerLine)
|
||||
, customPointSizeGroups(std::move(other.customPointSizeGroups))
|
||||
, hasCustomPointSizes(other.hasCustomPointSizes)
|
||||
, 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);
|
||||
pointInLineIndices = std::move(other.pointInLineIndices);
|
||||
hasColor = other.hasColor;
|
||||
hasLineInfo = other.hasLineInfo;
|
||||
name = std::move(other.name);
|
||||
colorIndex = other.colorIndex;
|
||||
totalLines = other.totalLines;
|
||||
pointsPerLine = other.pointsPerLine;
|
||||
customPointSizeGroups = std::move(other.customPointSizeGroups);
|
||||
hasCustomPointSizes = other.hasCustomPointSizes;
|
||||
// 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 drawBasicShapeSurfaces(); // 绘制基础图形面片
|
||||
void drawBasicShapeSegments(); // 绘制基础图形
|
||||
void drawBasicShapePoints();
|
||||
void drawLineSegmentList(const QVector<LineSegment>& segments);
|
||||
void drawPosePoints(); // 绘制姿态点
|
||||
void drawAxisLabels(); // 绘制坐标轴 XYZ 标注
|
||||
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;
|
||||
QQuaternion m_rotation; // 使用四元数存储旋转状态
|
||||
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<LineSegment> m_basicShapeSegments;
|
||||
QVector<SurfaceQuad> m_basicShapeSurfaces;
|
||||
QVector<BasicShapePoint> m_basicShapePoints;
|
||||
QVector<PosePoint> m_posePoints;
|
||||
};
|
||||
|
||||
#endif // POINT_CLOUD_GL_WIDGET_H
|
||||
@ -230,6 +230,7 @@ void PointCloudGLWidget::paintGL()
|
||||
drawLineSegments();
|
||||
drawBasicShapeSurfaces();
|
||||
drawBasicShapeSegments();
|
||||
drawBasicShapePoints();
|
||||
drawPosePoints();
|
||||
|
||||
// 最后绘制坐标系指示器(覆盖在所有内容之上)
|
||||
@ -454,6 +455,7 @@ void PointCloudGLWidget::clearPointClouds()
|
||||
m_lineSegments.clear();
|
||||
m_basicShapeSegments.clear();
|
||||
m_basicShapeSurfaces.clear();
|
||||
m_basicShapePoints.clear();
|
||||
m_posePoints.clear();
|
||||
m_minBound = QVector3D(-50, -50, -50);
|
||||
m_maxBound = QVector3D(50, 50, 50);
|
||||
@ -897,6 +899,10 @@ void PointCloudGLWidget::computeBoundingBox()
|
||||
includePoint(surface.p4.x(), surface.p4.y(), surface.p4.z());
|
||||
}
|
||||
|
||||
for (const BasicShapePoint& point : m_basicShapePoints) {
|
||||
includePoint(point.x, point.y, point.z);
|
||||
}
|
||||
|
||||
for (const PosePoint& pose : m_posePoints) {
|
||||
const float scale = qMax(0.0f, pose.scale);
|
||||
includePoint(pose.x - scale, pose.y - scale, pose.z - scale);
|
||||
@ -1290,10 +1296,14 @@ void PointCloudGLWidget::mousePressEvent(QMouseEvent* event)
|
||||
|
||||
emit pointSelected(point);
|
||||
|
||||
if (m_selectedPoints.size() == 2) {
|
||||
float distance = calculateDistance(m_selectedPoints[0], m_selectedPoints[1]);
|
||||
emit twoPointsSelected(m_selectedPoints[0], m_selectedPoints[1], distance);
|
||||
}
|
||||
if (m_selectedPoints.size() == 2) {
|
||||
const float distance = calculateDistance(m_selectedPoints[0], m_selectedPoints[1]);
|
||||
const float dx = m_selectedPoints[1].x - m_selectedPoints[0].x;
|
||||
const float dy = m_selectedPoints[1].y - m_selectedPoints[0].y;
|
||||
const float dz = m_selectedPoints[1].z - m_selectedPoints[0].z;
|
||||
emit twoPointsSelected(m_selectedPoints[0], m_selectedPoints[1], distance);
|
||||
emit measurementFinished(m_selectedPoints[0], m_selectedPoints[1], distance, dx, dy, dz);
|
||||
}
|
||||
} else {
|
||||
// 未启用测距:只保留一个点
|
||||
m_selectedPoints.clear();
|
||||
@ -1374,10 +1384,14 @@ void PointCloudGLWidget::mouseDoubleClickEvent(QMouseEvent* event)
|
||||
|
||||
emit pointSelected(point);
|
||||
|
||||
if (m_selectedPoints.size() == 2) {
|
||||
float distance = calculateDistance(m_selectedPoints[0], m_selectedPoints[1]);
|
||||
emit twoPointsSelected(m_selectedPoints[0], m_selectedPoints[1], distance);
|
||||
}
|
||||
if (m_selectedPoints.size() == 2) {
|
||||
const float distance = calculateDistance(m_selectedPoints[0], m_selectedPoints[1]);
|
||||
const float dx = m_selectedPoints[1].x - m_selectedPoints[0].x;
|
||||
const float dy = m_selectedPoints[1].y - m_selectedPoints[0].y;
|
||||
const float dz = m_selectedPoints[1].z - m_selectedPoints[0].z;
|
||||
emit twoPointsSelected(m_selectedPoints[0], m_selectedPoints[1], distance);
|
||||
emit measurementFinished(m_selectedPoints[0], m_selectedPoints[1], distance, dx, dy, dz);
|
||||
}
|
||||
} else {
|
||||
// 未启用测距:只保留一个点
|
||||
m_selectedPoints.clear();
|
||||
@ -1607,10 +1621,23 @@ void PointCloudGLWidget::appendBasicShapeSurfaces(const QVector<SurfaceQuad>& su
|
||||
update();
|
||||
}
|
||||
|
||||
void PointCloudGLWidget::appendBasicShapePoints(const QVector<BasicShapePoint>& points)
|
||||
{
|
||||
if (points.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const BasicShapePoint& point : points) {
|
||||
m_basicShapePoints.append(point);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void PointCloudGLWidget::clearBasicShapeSegments()
|
||||
{
|
||||
m_basicShapeSegments.clear();
|
||||
m_basicShapeSurfaces.clear();
|
||||
m_basicShapePoints.clear();
|
||||
update();
|
||||
}
|
||||
|
||||
@ -1659,11 +1686,29 @@ void PointCloudGLWidget::drawBasicShapeSegments()
|
||||
{
|
||||
drawLineSegmentList(m_basicShapeSegments);
|
||||
}
|
||||
|
||||
void PointCloudGLWidget::drawLineSegmentList(const QVector<LineSegment>& segments)
|
||||
{
|
||||
if (segments.isEmpty()) {
|
||||
return;
|
||||
|
||||
void PointCloudGLWidget::drawBasicShapePoints()
|
||||
{
|
||||
if (m_basicShapePoints.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
for (const BasicShapePoint& point : m_basicShapePoints) {
|
||||
glPointSize(point.pointSize);
|
||||
glBegin(GL_POINTS);
|
||||
glColor3f(point.r, point.g, point.b);
|
||||
glVertex3f(point.x, point.y, point.z);
|
||||
glEnd();
|
||||
}
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glPointSize(m_pointSize);
|
||||
}
|
||||
|
||||
void PointCloudGLWidget::drawLineSegmentList(const QVector<LineSegment>& segments)
|
||||
{
|
||||
if (segments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 按线宽分组绘制
|
||||
@ -5,7 +5,7 @@ CONFIG -= qt
|
||||
TARGET = CloudMathClac
|
||||
|
||||
INCLUDEPATH += $$PWD/Inc
|
||||
INCLUDEPATH += $$PWD/../../../SDK/Device/VzNLSDK/Inc
|
||||
INCLUDEPATH += $$PWD/../../../Device/SDK/VzNLSDK/Inc
|
||||
INCLUDEPATH += $$PWD/../../../SDK/eigen-3.3.9
|
||||
|
||||
HEADERS += \
|
||||
@ -14,7 +14,7 @@ HEADERS += \
|
||||
SOURCES += \
|
||||
Src/CurveFitting.cpp
|
||||
|
||||
# 平台特定配置
|
||||
# 骞冲彴鐗瑰畾閰嶇疆
|
||||
unix {
|
||||
QMAKE_CXXFLAGS += -fPIC
|
||||
}
|
||||
|
||||
20
Utils.pro
20
Utils.pro
@ -3,9 +3,10 @@ TEMPLATE = subdirs
|
||||
# 定义子项目
|
||||
VrCommon.file = VrCommon/VrCommon.pro
|
||||
VrUtils.file = VrUtils/VrUtils.pro
|
||||
CloudUtils.file = CloudUtils/CloudUtils.pro
|
||||
DataUtils.file = DataUtils/DataUtils.pro
|
||||
CloudView.file = CloudView/CloudView.pro
|
||||
CloudUtils.file = CloudUtils/CloudUtils.pro
|
||||
DataUtils.file = DataUtils/DataUtils.pro
|
||||
CloudView3D.file = CloudView3D/CloudView3D.pro
|
||||
CloudView.file = CloudView/CloudView.pro
|
||||
|
||||
# 添加子项目
|
||||
SUBDIRS += \
|
||||
@ -14,12 +15,15 @@ SUBDIRS += \
|
||||
CloudUtils \
|
||||
DataUtils
|
||||
|
||||
win32-msvc {
|
||||
SUBDIRS += CloudView
|
||||
}
|
||||
win32-msvc {
|
||||
SUBDIRS += \
|
||||
CloudView3D \
|
||||
CloudView
|
||||
}
|
||||
|
||||
# 设置依赖关系
|
||||
VrUtils.depends = VrCommon
|
||||
CloudUtils.depends = VrCommon VrUtils
|
||||
DataUtils.depends = VrCommon
|
||||
CloudView.depends = VrCommon VrUtils CloudUtils
|
||||
DataUtils.depends = VrCommon
|
||||
CloudView3D.depends = VrCommon VrUtils
|
||||
CloudView.depends = VrCommon VrUtils CloudUtils CloudView3D
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user