refactor(cloudview): 重构项目结构与代码格式,优化构建配置

1.  调整.gitignore规则,新增资源文件忽略项
2.  重构子项目依赖配置,统一项目构建路径
3.  修复CloudView头文件和pro文件的包含路径
4.  格式化代码缩进与注释风格,调整类成员布局
5.  为CloudView新增--help/-h和--version/-v命令行参数
6.  重构点云渲染模块,新增GLES2 shader渲染支持与悬浮控件
This commit is contained in:
杰仔 2026-08-18 12:26:08 +08:00
commit 44180b7f85
13 changed files with 3851 additions and 3218 deletions

2
.gitignore vendored
View File

@ -16,6 +16,8 @@ Makefile*
moc_*.cpp
moc_*.h
qrc_*.cpp
*_resource.rc
*_resource.res
ui_*.h
*.autosave

View File

@ -7,6 +7,13 @@ QT += core
TEMPLATE = lib
CONFIG += staticlib
win32:CONFIG(debug, debug|release) {
DESTDIR = $$PWD/debug
}
win32:CONFIG(release, debug|release) {
DESTDIR = $$PWD/release
}
win32-msvc {
QMAKE_CXXFLAGS += /utf-8
}

View File

@ -212,24 +212,55 @@ int LaserDataLoader::LoadLaserScanData(const std::string& fileName,
bool bFindLineNum = true;
int nLaserPointIdx = 0;
auto failTextLoad = [&](int code, const std::string& message) -> int {
m_lastError = message;
LOG_ERROR("%s\n", message.c_str());
if (!bFindLineNum) {
laserLines.push_back(std::make_pair(eDataType, sLaserData));
bFindLineNum = true;
}
FreeLaserScanData(laserLines);
lineNum = 0;
return ERR_CODE(code);
};
auto commitCurrentLine = [&]() -> int {
if (bFindLineNum) return SUCCESS;
// if (nLaserPointIdx != sLaserData.nPointCount) {
// std::ostringstream message;
// message << "Incomplete laser line: expected " << sLaserData.nPointCount
// << " points, got " << nLaserPointIdx;
// return failTextLoad(FILE_ERR_FORMAT, message.str());
// }
laserLines.push_back(std::make_pair(eDataType, sLaserData));
bFindLineNum = true;
return SUCCESS;
};
while (std::getline(inputFile, line)) {
// 去除行末的 \r 字符(处理 Windows 格式的 CR LF 换行符)
TrimCarriageReturn(line);
if (line.find("LineNum:") == 0) {
sscanf(line.c_str(), "LineNum:%d", &lineNum);
if (1 != sscanf(line.c_str(), "LineNum:%d", &lineNum) || lineNum <= 0) {
return failTextLoad(FILE_ERR_FORMAT, "Invalid LineNum header: " + line);
}
} else if (line.find("DataType:") == 0) {
} else if (line.find("Line_") == 0) {
if(false == bFindLineNum) {
laserLines.push_back(std::make_pair(eDataType, sLaserData));
result = commitCurrentLine();
if (SUCCESS != result) return result;
}
int lineIndex;
unsigned int timeStamp;
int lineIndex = 0;
unsigned int timeStamp = 0;
int ptNum = 0;
sscanf(line.c_str(), "Line_%d_%u_%d", &lineIndex, &timeStamp, &ptNum);
if (3 != sscanf(line.c_str(), "Line_%d_%u_%d", &lineIndex, &timeStamp, &ptNum) ||
ptNum < 0) {
return failTextLoad(FILE_ERR_FORMAT, "Invalid laser line header: " + line);
}
sLaserData.llFrameIdx = lineIndex;
sLaserData.llTimeStamp = timeStamp;
@ -256,7 +287,10 @@ int LaserDataLoader::LoadLaserScanData(const std::string& fileName,
polyNum = std::atoi(line.c_str() + lastUnderscore + 1);
}
for (int skip = 0; skip < polyNum; ) {
if (!std::getline(inputFile, line)) break;
if (!std::getline(inputFile, line)) {
return failTextLoad(inputFile.bad() ? FILE_ERR_READ : FILE_ERR_FORMAT,
"Incomplete Poly block in laser file: " + fileName);
}
TrimCarriageReturn(line);
if (!line.empty()) {
++skip;
@ -283,16 +317,17 @@ int LaserDataLoader::LoadLaserScanData(const std::string& fileName,
if (eDataType == keResultDataType_PointXYZRGBA) {
SVzNLPointXYZRGBA* pRGBAPoints = static_cast<SVzNLPointXYZRGBA*>(sLaserData.p3DPoint);
SVzNL2DLRPoint* p2DPoints = static_cast<SVzNL2DLRPoint*>(sLaserData.p2DPoint);
_ParseLaserScanPoint(line, pRGBAPoints[nLaserPointIdx], p2DPoints[nLaserPointIdx]);
result = _ParseLaserScanPoint(line, pRGBAPoints[nLaserPointIdx], p2DPoints[nLaserPointIdx]);
if (SUCCESS != result) {
return failTextLoad(FILE_ERR_FORMAT, "Invalid RGBD point: " + line);
}
nLaserPointIdx++;
} else if (eDataType == keResultDataType_Position) {
} else {
SVzNL3DPosition* p3DPoints = static_cast<SVzNL3DPosition*>(sLaserData.p3DPoint);
SVzNL2DPosition* p2DPoints = static_cast<SVzNL2DPosition*>(sLaserData.p2DPoint);
result = _ParseLaserScanPoint(line, p3DPoints[nLaserPointIdx], p2DPoints[nLaserPointIdx]);
if (result != SUCCESS) {
m_lastError = "Invalid XYZUV point line: " + line;
LOG_ERROR("%s\n", m_lastError.c_str());
return result;
if (SUCCESS != result) {
return failTextLoad(FILE_ERR_FORMAT, "Invalid XYZ point: " + line);
}
p3DPoints[nLaserPointIdx].nPointIdx = nLaserPointIdx;
nLaserPointIdx++;
@ -300,9 +335,20 @@ int LaserDataLoader::LoadLaserScanData(const std::string& fileName,
}
}
if (inputFile.bad()) {
return failTextLoad(FILE_ERR_READ, "Failed while reading laser file: " + fileName);
}
// 添加最后一条扫描线数据
if (!bFindLineNum) {
laserLines.push_back(std::make_pair(eDataType, sLaserData));
result = commitCurrentLine();
if (SUCCESS != result) return result;
}
if (lineNum <= 0 || laserLines.size() != static_cast<size_t>(lineNum)) {
std::ostringstream message;
message << "Laser line count mismatch: expected " << lineNum
<< ", got " << laserLines.size();
return failTextLoad(FILE_ERR_FORMAT, message.str());
}
inputFile.close();
@ -500,7 +546,7 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
// 写入文件头
sw << "LineNum:" << lineNum << '\n';
sw << "DataType: 0" << '\n';
sw << "DataType:" << static_cast<int>(fileDataType) << '\n';
sw << "ScanSpeed:" << scanSpeed << '\n';
sw << "PointAdjust: 1" << '\n';
sw << "MaxTimeStamp:" << maxTimeStamp << "_" << clockPerSecond << '\n';
@ -603,6 +649,12 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
}
}
sw.flush();
if (!sw.good()) {
m_lastError = "Failed while writing file: " + fileName;
LOG_ERROR("Failed while writing file: %s\n", fileName.c_str());
return ERR_CODE(FILE_ERR_WRITE);
}
sw.close();
return SUCCESS;
@ -682,6 +734,12 @@ int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vecto
}
}
sw.flush();
if (!sw.good()) {
m_lastError = "Failed while writing file: " + fileName;
LOG_ERROR("Failed while writing file: %s\n", fileName.c_str());
return ERR_CODE(FILE_ERR_WRITE);
}
sw.close();
return SUCCESS;
@ -736,8 +794,12 @@ int LaserDataLoader::LoadPolySegments(const std::string& fileName,
// 读取 num 个点坐标
std::vector<SVzNLPointXYZRGBA> points;
bool incompleteBlock = false;
for (int i = 0; i < num; ) {
if (!std::getline(inputFile, line)) break;
if (!std::getline(inputFile, line)) {
incompleteBlock = true;
break;
}
TrimCarriageReturn(line);
if (line.empty()) {
continue; // 跳过空行,不计数
@ -820,9 +882,20 @@ int LaserDataLoader::LoadPolySegments(const std::string& fileName,
++i;
}
if (points.size() >= 2) {
polyLines.push_back(std::move(points));
if (incompleteBlock || points.size() != static_cast<size_t>(num)) {
m_lastError = "Incomplete or invalid Poly block in file: " + fileName;
LOG_ERROR("%s\n", m_lastError.c_str());
polyLines.clear();
return ERR_CODE(inputFile.bad() ? FILE_ERR_READ : FILE_ERR_FORMAT);
}
polyLines.push_back(std::move(points));
}
if (inputFile.bad()) {
m_lastError = "Failed while reading Poly file: " + fileName;
LOG_ERROR("%s\n", m_lastError.c_str());
polyLines.clear();
return ERR_CODE(FILE_ERR_READ);
}
inputFile.close();
@ -1203,9 +1276,9 @@ int LaserDataLoader::_ParseLaserScanPoint(const std::string& data, SVzNL3DPositi
int LaserDataLoader::_ParseLaserScanPoint(const std::string& data, SVzNLPointXYZRGBA& sData, SVzNL2DLRPoint& s2DData)
{
float X, Y, Z;
float leftX, leftY;
float rightX, rightY;
float X = 0.0f, Y = 0.0f, Z = 0.0f;
float leftX = 0.0f, leftY = 0.0f;
float rightX = 0.0f, rightY = 0.0f;
// 尝试新格式: {x,y,z}-{lx,ly}-{rx,ry}-{R,G,B,A}
int R, G, B, A;
@ -1232,10 +1305,12 @@ int LaserDataLoader::_ParseLaserScanPoint(const std::string& data, SVzNLPointXYZ
}
// 旧格式: {x,y,z,r,g,b}-{lx,ly}-{rx,ry} r,g,b 为 0~1 浮点值
float r, g, b;
sscanf(data.c_str(),
" { %f , %f , %f , %f , %f , %f } - { %f , %f } - { %f , %f }",
&X, &Y, &Z, &r, &g, &b, &leftX, &leftY, &rightX, &rightY);
float r = 0.0f, g = 0.0f, b = 0.0f;
if (10 != sscanf(data.c_str(),
" { %f , %f , %f , %f , %f , %f } - { %f , %f } - { %f , %f }",
&X, &Y, &Z, &r, &g, &b, &leftX, &leftY, &rightX, &rightY)) {
return ERR_CODE(FILE_ERR_FORMAT);
}
sData.x = X;
sData.y = Y;
sData.z = Z;

View File

@ -15,9 +15,9 @@ TEMPLATE = app
# 椤圭洰鐩綍
INCLUDEPATH += $$PWD/Inc
INCLUDEPATH += $$PWD/../../Device/SDK/VzNLSDK/Inc
INCLUDEPATH += $$PWD/../CloudView3D/Inc
INCLUDEPATH += $$PWD/../CloudUtils/Inc
INCLUDEPATH += $$PWD/../../Device/SDK/VzNLSDK/Inc
INCLUDEPATH += $$PWD/../VrUtils/Inc
INCLUDEPATH += $$PWD/../VrCommon/Inc
@ -35,23 +35,28 @@ SOURCES += \
# 璧勬簮鏂囦欢
RESOURCES += resource/resource.qrc
RESOURCES += $$PWD/../../AppUtils/UICommon/common_resources.qrc
COMMON_RESOURCES = $$PWD/../../AppUtils/UICommon/common_resources.qrc
exists($$COMMON_RESOURCES) {
RESOURCES += $$COMMON_RESOURCES
} else {
message("CloudView: optional common_resources.qrc not found; using text view buttons")
}
# 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
LIBS += -L$$PWD/../CloudView3D/release -lCloudView3D
LIBS += -L$$PWD/../CloudUtils/release -lCloudUtils
LIBS += -L$$PWD/../VrUtils/release -lVrUtils
LIBS += -L$$PWD/../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
LIBS += -L$$PWD/../CloudView3D/debug -lCloudView3D
LIBS += -L$$PWD/../CloudUtils/debug -lCloudUtils
LIBS += -L$$PWD/../VrUtils/debug -lVrUtils
LIBS += -L$$PWD/../VrCommon/debug -lVrCommon
}
# Windows绯荤粺搴?

View File

@ -146,11 +146,6 @@ private slots:
*/
void onPoint2CoordChanged();
/**
* @brief 处理视角旋转角度改变事件
*/
void onViewAnglesChanged(float rotX, float rotY, float rotZ);
/**
* @brief 通过输入线号选择线
*/
@ -243,11 +238,6 @@ private:
*/
QGroupBox* createFileGroup();
/**
* @brief 创建视图方向工具栏(纵向排列,位于3D视图左侧)
*/
QWidget* createViewToolbar();
/**
* @brief 创建选点测距组
*/
@ -328,11 +318,6 @@ private:
QPushButton* m_btnOpenBBox;
QPushButton* m_btnClearAll;
// 视图角度输入控件
QLineEdit* m_editRotX;
QLineEdit* m_editRotY;
QLineEdit* m_editRotZ;
// 选点测距控件
QCheckBox* m_cbMeasureDistance;
QPushButton* m_btnClearPoints;
@ -407,10 +392,6 @@ private:
QTableWidget* m_linePointsTable;
QVector<QVector3D> m_currentLinePoints; // 当前线的原始点坐标
// 悬浮缩放按钮
QToolButton* m_btnZoomIn;
QToolButton* m_btnZoomOut;
// 启动参数序列显示
QVector<StartupItem> m_startupSequence;
int m_startupSequenceIndex;

View File

@ -1115,19 +1115,7 @@ void CloudViewMainWindow::fitViewToContent()
bool CloudViewMainWindow::eventFilter(QObject* obj, QEvent* event)
{
if (obj == m_glWidget && event->type() == QEvent::Resize) {
auto* resizeEvt = static_cast<QResizeEvent*>(event);
int w = resizeEvt->size().width();
int h = resizeEvt->size().height();
int btnW = m_btnZoomIn->width();
int btnH = m_btnZoomIn->height();
int margin = 8;
int gap = 4;
int x = w - btnW - margin;
int y = (h - btnH * 2 - gap) / 2;
m_btnZoomIn->move(x, y);
m_btnZoomOut->move(x, y + btnH + gap);
}
// 缩放按钮已移至 CloudView3D,事件过滤器不再需要处理缩放按钮定位
return QMainWindow::eventFilter(obj, event);
}
@ -1206,16 +1194,12 @@ void CloudViewMainWindow::setupUI()
// 创建分割器
QSplitter* splitter = new QSplitter(Qt::Horizontal, centralWidget);
// 左侧:视图工具栏 + 点云显示区域
// 左侧:点云显示区域(视图工具栏已移至 CloudView3D)
QWidget* viewerContainer = new QWidget(splitter);
QHBoxLayout* viewerLayout = new QHBoxLayout(viewerContainer);
viewerLayout->setContentsMargins(0, 0, 0, 0);
viewerLayout->setSpacing(2);
// 视图方向工具栏(纵向排列,在3D视图左侧)
QWidget* viewToolbar = createViewToolbar();
viewerLayout->addWidget(viewToolbar);
// 点云显示区域
QWidget* viewerArea = createViewerArea();
viewerLayout->addWidget(viewerArea);
@ -1246,50 +1230,12 @@ QWidget* CloudViewMainWindow::createViewerArea()
m_glWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
layout->addWidget(m_glWidget);
// 悬浮缩放按钮(父控件为 m_glWidget,悬浮在右侧中间)
int zoomBtnSize = 28;
QString zoomBtnStyle =
"QToolButton {"
" background-color: rgba(50, 50, 50, 180);"
" color: white;"
" border: 1px solid rgba(120, 120, 120, 150);"
" border-radius: 4px;"
" font-size: 32px;"
" font-weight: bold;"
"}"
"QToolButton:hover {"
" background-color: rgba(80, 80, 80, 220);"
"}"
"QToolButton:pressed {"
" background-color: rgba(30, 30, 30, 240);"
"}";
m_btnZoomIn = new QToolButton(m_glWidget);
m_btnZoomIn->setText("+");
m_btnZoomIn->setFixedSize(zoomBtnSize, zoomBtnSize);
m_btnZoomIn->setStyleSheet(zoomBtnStyle);
m_btnZoomIn->setAutoRepeat(true);
m_btnZoomIn->setAutoRepeatDelay(300);
m_btnZoomIn->setAutoRepeatInterval(50);
connect(m_btnZoomIn, &QToolButton::clicked, m_glWidget, &PointCloudGLWidget::zoomIn);
m_btnZoomOut = new QToolButton(m_glWidget);
m_btnZoomOut->setText("-");
m_btnZoomOut->setFixedSize(zoomBtnSize, zoomBtnSize);
m_btnZoomOut->setStyleSheet(zoomBtnStyle);
m_btnZoomOut->setAutoRepeat(true);
m_btnZoomOut->setAutoRepeatDelay(300);
m_btnZoomOut->setAutoRepeatInterval(50);
connect(m_btnZoomOut, &QToolButton::clicked, m_glWidget, &PointCloudGLWidget::zoomOut);
// 安装事件过滤器,在 GL 控件 resize 时更新按钮位置
m_glWidget->installEventFilter(this);
// 缩放按钮 / 旋转角度显示已移至 CloudView3D,此处不再创建
// 连接信号
connect(m_glWidget, &PointCloudGLWidget::pointSelected, this, &CloudViewMainWindow::onPointSelected);
connect(m_glWidget, &PointCloudGLWidget::twoPointsSelected, this, &CloudViewMainWindow::onTwoPointsSelected);
connect(m_glWidget, &PointCloudGLWidget::lineSelected, this, &CloudViewMainWindow::onLineSelected);
connect(m_glWidget, &PointCloudGLWidget::viewAnglesChanged, this, &CloudViewMainWindow::onViewAnglesChanged);
return widget;
}
@ -1349,105 +1295,6 @@ QGroupBox* CloudViewMainWindow::createFileGroup()
return group;
}
QWidget* CloudViewMainWindow::createViewToolbar()
{
QWidget* widget = new QWidget(this);
QVBoxLayout* layout = new QVBoxLayout(widget);
layout->setContentsMargins(2, 2, 2, 2);
layout->setSpacing(3);
// 视图预设:图标、提示、旋转角度
struct ViewPreset {
const char* iconPath;
const char* tooltip;
float rotX;
float rotY;
float rotZ;
};
// 坐标系定义:X向右,Y向下,Z朝后
ViewPreset presets[] = {
{":/common/resource/view_front.png", "正视 (XY面)", 180.0f, 0.0f, 0.0f},
{":/common/resource/view_back.png", "后视", 180.0f, 180.0f, 0.0f},
{":/common/resource/view_left.png", "左视 (YZ面)", 180.0f, 90.0f, 0.0f},
{":/common/resource/view_right.png", "右视", 180.0f, -90.0f, 0.0f},
{":/common/resource/view_top.png", "俯视 (XZ面)", 90.0f, 0.0f, 0.0f},
{":/common/resource/view_bottom.png", "仰视", -90.0f, 0.0f, 0.0f},
{":/common/resource/view_robot.png", "机械臂", -90.0f, 90.0f, 0.0f},
};
int btnSize = 32;
int iconSize = 24;
for (const auto& preset : presets) {
QToolButton* btn = new QToolButton(widget);
btn->setIcon(QIcon(preset.iconPath));
btn->setIconSize(QSize(iconSize, iconSize));
btn->setFixedSize(btnSize, btnSize);
btn->setToolTip(preset.tooltip);
btn->setAutoRaise(true);
float rx = preset.rotX;
float ry = preset.rotY;
float rz = preset.rotZ;
connect(btn, &QToolButton::clicked, this, [this, rx, ry, rz]() {
m_glWidget->setViewAngles(rx, ry, rz);
m_editRotX->setText(QString::number(rx, 'f', 1));
m_editRotY->setText(QString::number(ry, 'f', 1));
m_editRotZ->setText(QString::number(rz, 'f', 1));
});
layout->addWidget(btn);
}
layout->addStretch();
// 旋转角度输入(纵向紧凑布局)
m_editRotX = new QLineEdit("180.0", widget);
m_editRotY = new QLineEdit("0.0", widget);
m_editRotZ = new QLineEdit("0.0", widget);
auto makeAngleRow = [&](const QString& label, QLineEdit* edit) {
QWidget* row = new QWidget(widget);
QVBoxLayout* rowLayout = new QVBoxLayout(row);
rowLayout->setContentsMargins(0, 0, 0, 0);
rowLayout->setSpacing(0);
QLabel* lbl = new QLabel(label, row);
lbl->setStyleSheet("font-size: 10px; color: gray;");
rowLayout->addWidget(lbl);
edit->setFixedWidth(btnSize);
edit->setMaximumHeight(20);
edit->setStyleSheet("font-size: 10px;");
rowLayout->addWidget(edit);
return row;
};
layout->addWidget(makeAngleRow("RX", m_editRotX));
layout->addWidget(makeAngleRow("RY", m_editRotY));
layout->addWidget(makeAngleRow("RZ", m_editRotZ));
// 应用按钮
QPushButton* btnApply = new QPushButton("GO", widget);
btnApply->setFixedSize(btnSize, 20);
btnApply->setStyleSheet("font-size: 10px;");
auto applyAngles = [this]() {
bool okX, okY, okZ;
float rotX = m_editRotX->text().toFloat(&okX);
float rotY = m_editRotY->text().toFloat(&okY);
float rotZ = m_editRotZ->text().toFloat(&okZ);
if (okX && okY && okZ) {
m_glWidget->setViewAngles(rotX, rotY, rotZ);
}
};
connect(btnApply, &QPushButton::clicked, this, applyAngles);
connect(m_editRotX, &QLineEdit::returnPressed, this, applyAngles);
connect(m_editRotY, &QLineEdit::returnPressed, this, applyAngles);
connect(m_editRotZ, &QLineEdit::returnPressed, this, applyAngles);
layout->addWidget(btnApply);
widget->setFixedWidth(btnSize + 8);
return widget;
}
QWidget* CloudViewMainWindow::createMeasurePage()
{
QWidget* page = new QWidget(this);
@ -2379,6 +2226,7 @@ void CloudViewMainWindow::showBasicShape(const QString& shapeName,
if (!points.isEmpty()) {
m_glWidget->appendBasicShapePoints(points);
}
m_glWidget->fitToContent();
statusBar()->showMessage(QString("已添加 %1").arg(shapeName));
LOG_INFO("[CloudView] Add basic shape: %s, segments=%d, points=%d\n",
shapeName.toStdString().c_str(), segments.size(), points.size());
@ -2392,6 +2240,7 @@ void CloudViewMainWindow::showBasicSurface(const QString& shapeName, const QVect
m_glWidget->appendBasicShapeSurfaces(surfaces);
m_glWidget->appendBasicShapeSegments(outlines);
m_glWidget->fitToContent();
statusBar()->showMessage(QString("已添加 %1").arg(shapeName));
LOG_INFO("[CloudView] Add basic surface: %s, surfaces=%d, outlines=%d\n",
shapeName.toStdString().c_str(), surfaces.size(), outlines.size());
@ -4591,14 +4440,6 @@ void CloudViewMainWindow::onEulerOrderChanged(int index)
LOG_INFO("[CloudView] Euler rotation order changed to: %s\n", orderName.toStdString().c_str());
}
void CloudViewMainWindow::onViewAnglesChanged(float rotX, float rotY, float rotZ)
{
// 更新显示的角度值
m_editRotX->setText(QString::number(rotX, 'f', 1));
m_editRotY->setText(QString::number(rotY, 'f', 1));
m_editRotZ->setText(QString::number(rotZ, 'f', 1));
}
void CloudViewMainWindow::onPoint1CoordChanged()
{
// 读取编辑框中的坐标

View File

@ -65,6 +65,14 @@ int main(int argc, char* argv[])
parser.addOption(triangleOption);
parser.addOption(sequenceOption);
parser.addPositionalArgument("files", "Point cloud or segment files to load.");
if (app.arguments().contains(QStringLiteral("--help")) ||
app.arguments().contains(QStringLiteral("-h"))) {
parser.showHelp(0);
}
if (app.arguments().contains(QStringLiteral("--version")) ||
app.arguments().contains(QStringLiteral("-v"))) {
parser.showVersion();
}
parser.process(app);
CloudViewMainWindow::StartupFiles startupFiles;

View File

@ -4,6 +4,13 @@ CONFIG += c++17 staticlib
TEMPLATE = lib
TARGET = CloudView3D
win32:CONFIG(debug, debug|release) {
DESTDIR = $$PWD/debug
}
win32:CONFIG(release, debug|release) {
DESTDIR = $$PWD/release
}
win32-msvc {
QMAKE_CXXFLAGS += /utf-8
QMAKE_CXXFLAGS += /bigobj

View File

@ -4,6 +4,7 @@
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QMatrix4x4>
#include <QVector3D>
#include <QQuaternion>
@ -14,6 +15,11 @@
#include "CloudView3DTypes.h"
#include "VZNL_Types.h"
class QToolButton;
class QPushButton;
class QPinchGesture;
class QResizeEvent;
/**
* @brief 点云显示颜色枚举
*/
@ -170,6 +176,7 @@ public:
void addPointCloud(const PointCloudXYZ& cloud, const QString& name = "");
void addPointCloud(const PointCloudXYZRGB& cloud, const QString& name = "");
void appendPointCloud(const PointCloudXYZRGB& cloud, const QString& name = "");
void clearPointClouds();
void setPointCloudColor(PointCloudColor color);
void setPointSize(float size);
@ -351,6 +358,7 @@ signals:
void viewAnglesChanged(float rotX, float rotY, float rotZ);
protected:
bool event(QEvent* event) override;
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
@ -360,6 +368,7 @@ protected:
void wheelEvent(QWheelEvent* event) override;
void mouseDoubleClickEvent(QMouseEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
private:
struct PointCloudData
@ -455,8 +464,10 @@ private:
};
void computeBoundingBox();
void setCurrentColor(PointCloudColor color);
void setColorByIndex(int colorIndex); // 根据索引设置颜色
void updateProjection(const QMatrix4x4& modelView);
float fittedCameraDistance(float padding = 1.05f) const;
float minimumCameraDistance() const;
QVector3D colorForIndex(int colorIndex) const;
SelectedPointInfo pickPoint(int screenX, int screenY);
void drawSelectedPoints();
void drawMeasurementLine();
@ -472,13 +483,48 @@ private:
void uploadToVBO(PointCloudData& data); // 上传数据到 VBO
void releaseVBO(PointCloudData& data); // 释放 VBO 资源
// GLES2 shader 渲染
bool ensureShader();
void drawCloud(PointCloudData& data, float pointSize);
void drawPrimitives(const QMatrix4x4& mvp,
const float* positions,
int vertexCount,
GLenum mode,
const float* colors,
const QVector3D& uniformColor,
float pointSize,
float alpha,
bool depthTest);
void resetRenderState();
// 覆盖控件:左侧竖向视图按钮 / 左下角旋转角度 / 右侧缩放
void createOverlayControls();
void repositionOverlayControls();
void showViewAngleDialog();
void updateViewAngleDisplay();
bool handlePinchGesture(QPinchGesture* gesture);
std::vector<PointCloudData> m_pointClouds;
QMatrix4x4 m_projection;
QMatrix4x4 m_view;
QMatrix4x4 m_model;
QMatrix4x4 m_mvp; // 当前帧的模型视图投影矩阵(点云主渲染用)
// GLES2 shader 渲染资源
QOpenGLShaderProgram* m_shader;
QOpenGLBuffer m_dynamicVbo; // 动态叠加图元(选中点/线/轴/姿态等)的临时 VBO
bool m_shaderReady;
GLint m_mvpLocation;
GLint m_positionLocation;
GLint m_colorLocation;
GLint m_pointSizeLocation;
GLint m_alphaLocation;
GLint m_useVertexColorLocation;
GLint m_uniformColorLocation;
float m_distance;
float m_pinchStartDistance;
float m_rotationX;
float m_rotationY;
float m_rotationZ;
@ -516,6 +562,13 @@ private:
QVector<SurfaceQuad> m_basicShapeSurfaces;
QVector<BasicShapePoint> m_basicShapePoints;
QVector<PosePoint> m_posePoints;
// 覆盖控件
QWidget* m_viewToolbar; // 左侧竖向视图按钮容器
QVector<QToolButton*> m_viewButtons;
QPushButton* m_viewAngleButton; // 左下角旋转角度显示按钮
QToolButton* m_zoomInButton;
QToolButton* m_zoomOutButton;
};
#endif // POINT_CLOUD_GL_WIDGET_H

File diff suppressed because it is too large Load Diff

View File

@ -11,18 +11,9 @@ CloudView.file = CloudView/CloudView.pro
# 添加子项目
SUBDIRS += \
VrCommon \
VrUtils
!equals(TARGET_APP, "ParkingSpaceGuideView") {
SUBDIRS += \
CloudUtils \
DataUtils
}
win32-msvc:!equals(TARGET_APP, "ParkingSpaceGuideView") {
SUBDIRS += \
CloudView3D \
CloudView
VrUtils \
CloudView3D \
CloudView
}
# 设置依赖关系

View File

@ -4,6 +4,13 @@ TEMPLATE = lib
CONFIG += staticlib
CONFIG += c++17
win32:CONFIG(debug, debug|release) {
DESTDIR = $$PWD/debug
}
win32:CONFIG(release, debug|release) {
DESTDIR = $$PWD/release
}
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0

View File

@ -8,6 +8,13 @@ DEFINES += LOG4CPP_HAVE_SSTREAM=1
DEFINES += VR_UTILS_EXPORTS
CONFIG += c++11
win32:CONFIG(debug, debug|release) {
DESTDIR = $$PWD/debug
}
win32:CONFIG(release, debug|release) {
DESTDIR = $$PWD/release
}
# Unix平台特定配置
unix:!macx {
DEFINES += LOG4CPP_HAVE_UNISTD_H