refactor(cloudview): 重构项目结构与代码格式,优化构建配置
1. 调整.gitignore规则,新增资源文件忽略项 2. 重构子项目依赖配置,统一项目构建路径 3. 修复CloudView头文件和pro文件的包含路径 4. 格式化代码缩进与注释风格,调整类成员布局 5. 为CloudView新增--help/-h和--version/-v命令行参数 6. 重构点云渲染模块,新增GLES2 shader渲染支持与悬浮控件
This commit is contained in:
commit
44180b7f85
2
.gitignore
vendored
2
.gitignore
vendored
@ -16,6 +16,8 @@ Makefile*
|
||||
moc_*.cpp
|
||||
moc_*.h
|
||||
qrc_*.cpp
|
||||
*_resource.rc
|
||||
*_resource.res
|
||||
ui_*.h
|
||||
*.autosave
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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绯荤粺搴?
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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()
|
||||
{
|
||||
// 读取编辑框中的坐标
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
15
Utils.pro
15
Utils.pro
@ -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
|
||||
}
|
||||
|
||||
# 设置依赖关系
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user