Compare commits
1 Commits
CloudView_
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
550ed280f9 |
@ -54,6 +54,11 @@ public:
|
||||
std::vector<std::vector<SVzNL3DPosition>>& scanLines,
|
||||
size_t* validPointCount = nullptr);
|
||||
|
||||
// 从文件加载Poly折线数据(带颜色)
|
||||
// polyLines: 每条折线的点坐标列表,polyLines[i] 是第i条折线的所有点(含RGB颜色)
|
||||
int LoadPolySegments(const std::string& fileName,
|
||||
std::vector<std::vector<SVzNLPointXYZRGBA>>& polyLines);
|
||||
|
||||
// 获取最后的错误信息
|
||||
std::string GetLastError() const { return m_lastError; }
|
||||
|
||||
|
||||
@ -102,6 +102,21 @@ int LaserDataLoader::LoadLaserScanData(const std::string& fileName,
|
||||
nLaserPointIdx = 0;
|
||||
bFindLineNum = false;
|
||||
|
||||
} else if (line.find("Poly_") == 0) {
|
||||
// 跳过 Poly 折线头及后续点坐标数据
|
||||
size_t lastUnderscore = line.rfind('_');
|
||||
int polyNum = 0;
|
||||
if (lastUnderscore != std::string::npos && lastUnderscore + 1 < line.size()) {
|
||||
polyNum = std::atoi(line.c_str() + lastUnderscore + 1);
|
||||
}
|
||||
for (int skip = 0; skip < polyNum; ) {
|
||||
if (!std::getline(inputFile, line)) break;
|
||||
TrimCarriageReturn(line);
|
||||
if (!line.empty()) {
|
||||
++skip;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (line.find("{") == 0) {
|
||||
// 使用正则表达式判断是XYZ还是RGBD格式
|
||||
// XYZ格式: {x,y,z}-{leftX,leftY}-{rightX,rightY}
|
||||
@ -278,6 +293,141 @@ int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vecto
|
||||
}
|
||||
}
|
||||
|
||||
int LaserDataLoader::LoadPolySegments(const std::string& fileName,
|
||||
std::vector<std::vector<SVzNLPointXYZRGBA>>& polyLines)
|
||||
{
|
||||
LOG_INFO("Loading poly segments from file: %s\n", fileName.c_str());
|
||||
|
||||
polyLines.clear();
|
||||
|
||||
std::ifstream inputFile(fileName);
|
||||
if (!inputFile.is_open()) {
|
||||
m_lastError = "Cannot open file: " + fileName;
|
||||
LOG_ERROR("Cannot open file: %s\n", fileName.c_str());
|
||||
return ERR_CODE(FILE_ERR_NOEXIST);
|
||||
}
|
||||
|
||||
std::string line;
|
||||
|
||||
while (std::getline(inputFile, line)) {
|
||||
TrimCarriageReturn(line);
|
||||
|
||||
// 跳过空行和注释
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查找 Poly_index_num 头
|
||||
if (line.find("Poly_") != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 解析点数量(最后一个下划线后的数字)
|
||||
size_t lastUnderscore = line.rfind('_');
|
||||
if (lastUnderscore == std::string::npos || lastUnderscore + 1 >= line.size()) {
|
||||
LOG_WARN("[LaserDataLoader] Invalid Poly header: %s\n", line.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
int num = std::atoi(line.c_str() + lastUnderscore + 1);
|
||||
if (num < 2) {
|
||||
LOG_WARN("[LaserDataLoader] Invalid point count in Poly header: %s\n", line.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 读取 num 个点坐标
|
||||
std::vector<SVzNLPointXYZRGBA> points;
|
||||
for (int i = 0; i < num; ) {
|
||||
if (!std::getline(inputFile, line)) break;
|
||||
TrimCarriageReturn(line);
|
||||
if (line.empty()) {
|
||||
continue; // 跳过空行,不计数
|
||||
}
|
||||
|
||||
SVzNLPointXYZRGBA pt;
|
||||
memset(&pt, 0, sizeof(pt));
|
||||
pt.nRGB = 0x00FFFFFF; // 默认白色
|
||||
bool parsed = false;
|
||||
|
||||
// 尝试花括号格式
|
||||
if (line.find('{') != std::string::npos) {
|
||||
float fx, fy, fz;
|
||||
int R = 255, G = 255, B = 255, A = 255;
|
||||
|
||||
// 新RGBD格式: {x,y,z}-{lx,ly}-{rx,ry}-{R,G,B,A}
|
||||
int cnt = sscanf(line.c_str(),
|
||||
" { %f , %f , %f } - { %*f , %*f } - { %*f , %*f } - { %d , %d , %d , %d }",
|
||||
&fx, &fy, &fz, &R, &G, &B, &A);
|
||||
if (cnt >= 3) {
|
||||
pt.x = fx;
|
||||
pt.y = fy;
|
||||
pt.z = fz;
|
||||
if (cnt >= 7) {
|
||||
// 解析到了颜色 R,G,B,A(0-255)
|
||||
int nRGB = B; nRGB <<= 8; nRGB += G; nRGB <<= 8; nRGB += R;
|
||||
pt.nRGB = nRGB;
|
||||
}
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
// 旧RGBD格式: {x,y,z,r,g,b}-{lx,ly}-{rx,ry}
|
||||
if (!parsed) {
|
||||
float r, g, b;
|
||||
if (sscanf(line.c_str(), " { %f , %f , %f , %f , %f , %f }",
|
||||
&fx, &fy, &fz, &r, &g, &b) == 6) {
|
||||
pt.x = fx;
|
||||
pt.y = fy;
|
||||
pt.z = fz;
|
||||
int nRGB = (int)(b * 255); nRGB <<= 8;
|
||||
nRGB += (int)(g * 255); nRGB <<= 8;
|
||||
nRGB += (int)(r * 255);
|
||||
pt.nRGB = nRGB;
|
||||
parsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// XYZ格式: {x,y,z}-{...}-{...}
|
||||
if (!parsed) {
|
||||
if (sscanf(line.c_str(), " { %f , %f , %f }", &fx, &fy, &fz) == 3) {
|
||||
pt.x = fx;
|
||||
pt.y = fy;
|
||||
pt.z = fz;
|
||||
parsed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试纯数字格式: x y z(空格/Tab/逗号分隔)
|
||||
if (!parsed) {
|
||||
float fx, fy, fz;
|
||||
std::string lineCopy = line;
|
||||
for (char& c : lineCopy) {
|
||||
if (c == ',' || c == '\t') c = ' ';
|
||||
}
|
||||
if (sscanf(lineCopy.c_str(), "%f %f %f", &fx, &fy, &fz) == 3) {
|
||||
pt.x = fx;
|
||||
pt.y = fy;
|
||||
pt.z = fz;
|
||||
parsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed) {
|
||||
points.push_back(pt);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
if (points.size() >= 2) {
|
||||
polyLines.push_back(std::move(points));
|
||||
}
|
||||
}
|
||||
|
||||
inputFile.close();
|
||||
LOG_INFO("Loaded %zu poly lines from file: %s\n", polyLines.size(), fileName.c_str());
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
void LaserDataLoader::FreeLaserScanData(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines)
|
||||
{
|
||||
LOG_DEBUG("Freeing unified laser scan data, line count: %zu\n", laserLines.size());
|
||||
@ -487,10 +637,39 @@ int LaserDataLoader::_ParseLaserScanPoint(const std::string& data, SVzNL3DPositi
|
||||
int LaserDataLoader::_ParseLaserScanPoint(const std::string& data, SVzNLPointXYZRGBA& sData, SVzNL2DLRPoint& s2DData)
|
||||
{
|
||||
float X, Y, Z;
|
||||
float r, g, b;
|
||||
float leftX, leftY;
|
||||
float rightX, rightY;
|
||||
sscanf(data.c_str(), "{%f,%f,%f,%f,%f,%f}-{%f,%f}-{%f,%f}", &X, &Y, &Z, &r, &g, &b, &leftX, &leftY, &rightX, &rightY);
|
||||
|
||||
// 尝试新格式: {x,y,z}-{lx,ly}-{rx,ry}-{R,G,B,A}
|
||||
int R, G, B, A;
|
||||
int parsed = sscanf(data.c_str(),
|
||||
" { %f , %f , %f } - { %f , %f } - { %f , %f } - { %d , %d , %d , %d }",
|
||||
&X, &Y, &Z, &leftX, &leftY, &rightX, &rightY, &R, &G, &B, &A);
|
||||
if (parsed >= 11) {
|
||||
sData.x = X;
|
||||
sData.y = Y;
|
||||
sData.z = Z;
|
||||
|
||||
// R,G,B 是 0-255 整数值
|
||||
int nRGB = B;
|
||||
nRGB <<= 8;
|
||||
nRGB += G;
|
||||
nRGB <<= 8;
|
||||
nRGB += R;
|
||||
sData.nRGB = nRGB;
|
||||
|
||||
s2DData.sLeft.x = leftX;
|
||||
s2DData.sLeft.y = leftY;
|
||||
s2DData.sRight.x = rightX;
|
||||
s2DData.sRight.y = rightY;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// 旧格式: {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);
|
||||
sData.x = X;
|
||||
sData.y = Y;
|
||||
sData.z = Z;
|
||||
@ -524,27 +703,38 @@ int LaserDataLoader::_GetLaserType(const std::string& fileName, EVzResultDataTyp
|
||||
|
||||
bool bFind = false;
|
||||
while (std::getline(inputFile, linedata)) {
|
||||
// 去除行末的 \r 字符(处理 Windows 格式的 CR LF 换行符)
|
||||
TrimCarriageReturn(linedata);
|
||||
|
||||
if (linedata.find("{") == 0) {
|
||||
// 修复正则表达式以匹配实际数据格式
|
||||
// XYZ格式: {x,y,z}-{leftX,leftY}-{rightX,rightY}
|
||||
// RGBD格式: {x,y,z,r,g,b}-{leftX,leftY}-{rightX,rightY}
|
||||
// 更宽松的正则表达式,允许更多的空格变化
|
||||
std::regex xyzPattern(R"(\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\}\s*-\s*\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\}\s*-\s*\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\})");
|
||||
std::regex rgbdPattern(R"(\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\}\s*-\s*\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\}\s*-\s*\{\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*,\s*[+-]?(?:\d+\.?\d*|\.\d+)\s*\})");
|
||||
// 统计 { 的数量判断格式
|
||||
int braceCount = 0;
|
||||
for (char c : linedata) {
|
||||
if (c == '{') braceCount++;
|
||||
}
|
||||
|
||||
// 先尝试匹配RGBD格式(6个数字)
|
||||
if (std::regex_match(linedata, rgbdPattern)) {
|
||||
if (braceCount == 4) {
|
||||
// 新RGBD格式: {x,y,z}-{lx,ly}-{rx,ry}-{R,G,B,A}
|
||||
eDataType = keResultDataType_PointXYZRGBA;
|
||||
bFind = true;
|
||||
} else if (braceCount == 3) {
|
||||
// 检查第一组的逗号数量区分XYZ和旧RGBD
|
||||
size_t firstClose = linedata.find('}');
|
||||
if (firstClose != std::string::npos) {
|
||||
int commaCount = 0;
|
||||
for (size_t i = 0; i < firstClose; ++i) {
|
||||
if (linedata[i] == ',') commaCount++;
|
||||
}
|
||||
// 再尝试匹配XYZ格式(3个数字)
|
||||
else if (std::regex_match(linedata, xyzPattern)) {
|
||||
if (commaCount >= 5) {
|
||||
// 旧RGBD格式: {x,y,z,r,g,b}-{lx,ly}-{rx,ry}
|
||||
eDataType = keResultDataType_PointXYZRGBA;
|
||||
bFind = true;
|
||||
} else if (commaCount >= 2) {
|
||||
// XYZ格式: {x,y,z}-{lx,ly}-{rx,ry}
|
||||
eDataType = keResultDataType_Position;
|
||||
bFind = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,11 +44,6 @@ private slots:
|
||||
*/
|
||||
void onOpenFile();
|
||||
|
||||
/**
|
||||
* @brief 打开线段文件
|
||||
*/
|
||||
void onOpenSegmentFile();
|
||||
|
||||
/**
|
||||
* @brief 打开姿态点文件
|
||||
*/
|
||||
@ -225,6 +220,18 @@ private:
|
||||
*/
|
||||
void updateLinePointsDialog();
|
||||
|
||||
/**
|
||||
* @brief 加载点云文件
|
||||
* @return 是否成功加载到点云数据
|
||||
*/
|
||||
bool loadPointCloudFile(const QString& fileName);
|
||||
|
||||
/**
|
||||
* @brief 加载线段文件(Poly格式)
|
||||
* @return 是否成功加载到线段数据
|
||||
*/
|
||||
bool loadSegmentFile(const QString& fileName);
|
||||
|
||||
// 点云显示控件
|
||||
PointCloudGLWidget* m_glWidget;
|
||||
|
||||
@ -233,7 +240,6 @@ private:
|
||||
|
||||
// 文件操作控件
|
||||
QPushButton* m_btnOpenFile;
|
||||
QPushButton* m_btnOpenSegment;
|
||||
QPushButton* m_btnOpenPose;
|
||||
QPushButton* m_btnClearAll;
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include <QGridLayout>
|
||||
#include <cmath>
|
||||
#include "VrLog.h"
|
||||
#include "LaserDataLoader.h"
|
||||
|
||||
CloudViewMainWindow::CloudViewMainWindow(QWidget* parent)
|
||||
: QMainWindow(parent)
|
||||
@ -125,18 +126,12 @@ QGroupBox* CloudViewMainWindow::createFileGroup()
|
||||
layout->setSpacing(3);
|
||||
layout->setContentsMargins(5, 5, 5, 5);
|
||||
|
||||
m_btnOpenFile = new QPushButton("打开点云", group);
|
||||
m_btnOpenFile = new QPushButton("打开文件", group);
|
||||
m_btnOpenFile->setMinimumHeight(24);
|
||||
m_btnOpenFile->setMaximumHeight(24);
|
||||
connect(m_btnOpenFile, &QPushButton::clicked, this, &CloudViewMainWindow::onOpenFile);
|
||||
layout->addWidget(m_btnOpenFile);
|
||||
|
||||
m_btnOpenSegment = new QPushButton("打开线段 {x,y,z}-{x,y,z}", group);
|
||||
m_btnOpenSegment->setMinimumHeight(24);
|
||||
m_btnOpenSegment->setMaximumHeight(24);
|
||||
connect(m_btnOpenSegment, &QPushButton::clicked, this, &CloudViewMainWindow::onOpenSegmentFile);
|
||||
layout->addWidget(m_btnOpenSegment);
|
||||
|
||||
m_btnOpenPose = new QPushButton("打开姿态点 {x,y,z}-{r,p,y}", group);
|
||||
m_btnOpenPose->setMinimumHeight(24);
|
||||
m_btnOpenPose->setMaximumHeight(24);
|
||||
@ -523,6 +518,7 @@ QGroupBox* CloudViewMainWindow::createLineGroup()
|
||||
m_btnSelectByNumber->setMaximumHeight(24);
|
||||
m_btnSelectByNumber->setMaximumWidth(50);
|
||||
connect(m_btnSelectByNumber, &QPushButton::clicked, this, &CloudViewMainWindow::onSelectLineByNumber);
|
||||
connect(m_lineNumberInput, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onSelectLineByNumber);
|
||||
inputLayout->addWidget(m_lineNumberInput, 1);
|
||||
inputLayout->addWidget(m_btnSelectByNumber);
|
||||
layout->addLayout(inputLayout);
|
||||
@ -579,17 +575,42 @@ QGroupBox* CloudViewMainWindow::createCloudListGroup()
|
||||
|
||||
void CloudViewMainWindow::onOpenFile()
|
||||
{
|
||||
// 如果已有打开的文件,提示是否清除
|
||||
if (m_cloudList->count() > 0) {
|
||||
auto ret = QMessageBox::question(this, "提示",
|
||||
"当前已有打开的文件,是否清除?",
|
||||
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
||||
if (ret == QMessageBox::Cancel) {
|
||||
return;
|
||||
}
|
||||
if (ret == QMessageBox::Yes) {
|
||||
onClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
QString fileName = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
"打开点云文件",
|
||||
"打开文件",
|
||||
QString(),
|
||||
"点云文件 (*.pcd *.txt);;PCD 文件 (*.pcd);;TXT 文件 (*.txt);;所有文件 (*.*)"
|
||||
"所有支持格式 (*.pcd *.txt);;PCD 文件 (*.pcd);;TXT 文件 (*.txt);;所有文件 (*.*)"
|
||||
);
|
||||
|
||||
if (fileName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 同一文件中可能同时包含点云和线段,两者都尝试加载
|
||||
bool cloudOk = loadPointCloudFile(fileName);
|
||||
bool segmentOk = loadSegmentFile(fileName);
|
||||
|
||||
if (!cloudOk && !segmentOk) {
|
||||
QMessageBox::critical(this, "错误", "无法从文件中加载点云或线段数据");
|
||||
statusBar()->showMessage("加载失败");
|
||||
}
|
||||
}
|
||||
|
||||
bool CloudViewMainWindow::loadPointCloudFile(const QString& fileName)
|
||||
{
|
||||
statusBar()->showMessage("正在加载点云...");
|
||||
|
||||
QFileInfo fileInfo(fileName);
|
||||
@ -601,9 +622,8 @@ void CloudViewMainWindow::onOpenFile()
|
||||
int result = m_converter->loadFromFile(fileName.toStdString(), rgbCloud);
|
||||
|
||||
if (result != 0) {
|
||||
QMessageBox::critical(this, "错误", QString("加载点云失败: %1").arg(QString::fromStdString(m_converter->getLastError())));
|
||||
statusBar()->showMessage("加载失败");
|
||||
return;
|
||||
LOG_INFO("[CloudView] Load point cloud failed: %s\n", m_converter->getLastError().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存原始完整点云 XYZ(用于旋转/线上点等功能)
|
||||
@ -632,11 +652,9 @@ void CloudViewMainWindow::onOpenFile()
|
||||
}
|
||||
|
||||
if (hadColor) {
|
||||
// 有颜色数据:使用 addPointCloud(PointCloudXYZRGB) 显示原始颜色
|
||||
m_glWidget->addPointCloud(rgbCloud, cloudName);
|
||||
LOG_INFO("[CloudView] Loaded with original color, points: %zu\n", rgbCloud.size());
|
||||
} else {
|
||||
// 无颜色数据:使用 addPointCloud(PointCloudXYZ) 显示(颜色表轮换)
|
||||
m_glWidget->addPointCloud(m_originalCloud, cloudName);
|
||||
LOG_INFO("[CloudView] Loaded without color (color table), points: %zu\n", m_originalCloud.size());
|
||||
}
|
||||
@ -664,86 +682,60 @@ void CloudViewMainWindow::onOpenFile()
|
||||
m_cloudList->addItem(itemText);
|
||||
|
||||
statusBar()->showMessage(QString("已加载 %1 个点%2").arg(m_converter->getLoadedPointCount()).arg(hadColor ? " (彩色)" : ""));
|
||||
return true;
|
||||
}
|
||||
|
||||
void CloudViewMainWindow::onOpenSegmentFile()
|
||||
bool CloudViewMainWindow::loadSegmentFile(const QString& fileName)
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
"打开线段文件",
|
||||
QString(),
|
||||
"文本文件 (*.txt);;所有文件 (*.*)"
|
||||
);
|
||||
|
||||
if (fileName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
statusBar()->showMessage("正在加载线段...");
|
||||
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(this, "错误", QString("无法打开文件: %1").arg(fileName));
|
||||
statusBar()->showMessage("加载失败");
|
||||
return;
|
||||
LaserDataLoader loader;
|
||||
std::vector<std::vector<SVzNLPointXYZRGBA>> polyLines;
|
||||
int result = loader.LoadPolySegments(fileName.toStdString(), polyLines);
|
||||
|
||||
if (result != 0) {
|
||||
LOG_INFO("[CloudView] Load segments failed: %s\n", loader.GetLastError().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (polyLines.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 将折线点转换为线段(使用点自带的颜色)
|
||||
QVector<LineSegment> segments;
|
||||
QTextStream in(&file);
|
||||
int lineNum = 0;
|
||||
int validCount = 0;
|
||||
|
||||
while (!in.atEnd()) {
|
||||
QString line = in.readLine().trimmed();
|
||||
lineNum++;
|
||||
|
||||
// 跳过空行和注释
|
||||
if (line.isEmpty() || line.startsWith('#')) {
|
||||
continue;
|
||||
for (const auto& polyLine : polyLines) {
|
||||
for (size_t i = 0; i + 1 < polyLine.size(); ++i) {
|
||||
const auto& p1 = polyLine[i];
|
||||
const auto& p2 = polyLine[i + 1];
|
||||
// 取起点颜色作为线段颜色,nRGB 为 BGR 打包格式
|
||||
float r = ((p1.nRGB >> 0) & 0xFF) / 255.0f;
|
||||
float g = ((p1.nRGB >> 8) & 0xFF) / 255.0f;
|
||||
float b = ((p1.nRGB >> 16) & 0xFF) / 255.0f;
|
||||
segments.append(LineSegment(
|
||||
p1.x, p1.y, p1.z,
|
||||
p2.x, p2.y, p2.z,
|
||||
r, g, b));
|
||||
}
|
||||
|
||||
// 解析格式:{x,y,z}-{x,y,z}
|
||||
QRegExp regex("\\{([^}]+)\\}-\\{([^}]+)\\}");
|
||||
if (regex.indexIn(line) == -1) {
|
||||
LOG_WARN("[CloudView] Line %d: Invalid format, expected {x,y,z}-{x,y,z}\n", lineNum);
|
||||
continue;
|
||||
}
|
||||
|
||||
QString point1Str = regex.cap(1);
|
||||
QString point2Str = regex.cap(2);
|
||||
|
||||
QStringList p1 = point1Str.split(',');
|
||||
QStringList p2 = point2Str.split(',');
|
||||
|
||||
if (p1.size() != 3 || p2.size() != 3) {
|
||||
LOG_WARN("[CloudView] Line %d: Invalid point format\n", lineNum);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
float x1 = p1[0].toFloat(&ok); if (!ok) continue;
|
||||
float y1 = p1[1].toFloat(&ok); if (!ok) continue;
|
||||
float z1 = p1[2].toFloat(&ok); if (!ok) continue;
|
||||
float x2 = p2[0].toFloat(&ok); if (!ok) continue;
|
||||
float y2 = p2[1].toFloat(&ok); if (!ok) continue;
|
||||
float z2 = p2[2].toFloat(&ok); if (!ok) continue;
|
||||
|
||||
// 默认白色
|
||||
segments.append(LineSegment(x1, y1, z1, x2, y2, z2, 1.0f, 1.0f, 1.0f));
|
||||
validCount++;
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
if (segments.isEmpty()) {
|
||||
QMessageBox::warning(this, "警告", "文件中没有有效的线段数据");
|
||||
statusBar()->showMessage("加载失败");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_glWidget->addLineSegments(segments);
|
||||
statusBar()->showMessage(QString("已加载 %1 条线段").arg(validCount));
|
||||
LOG_INFO("[CloudView] Loaded %d line segments from %s\n", validCount, fileName.toStdString().c_str());
|
||||
|
||||
// 添加到列表
|
||||
QFileInfo fileInfo(fileName);
|
||||
int totalPolyCount = static_cast<int>(polyLines.size());
|
||||
QString itemName = QString("Segments (%1)").arg(fileInfo.fileName());
|
||||
m_cloudList->addItem(QString("%1 - %2 条折线, %3 条线段")
|
||||
.arg(itemName).arg(totalPolyCount).arg(segments.size()));
|
||||
|
||||
statusBar()->showMessage(QString("已加载 %1 条折线, %2 条线段").arg(totalPolyCount).arg(segments.size()));
|
||||
LOG_INFO("[CloudView] Loaded %d polylines, %d segments from %s\n",
|
||||
totalPolyCount, segments.size(), fileName.toStdString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void CloudViewMainWindow::onOpenPoseFile()
|
||||
@ -831,6 +823,8 @@ void CloudViewMainWindow::onOpenPoseFile()
|
||||
void CloudViewMainWindow::onClearAll()
|
||||
{
|
||||
m_glWidget->clearPointClouds();
|
||||
m_glWidget->clearLineSegments();
|
||||
m_glWidget->clearPosePoints();
|
||||
m_cloudList->clear();
|
||||
m_cloudCount = 0;
|
||||
m_currentLineNum = 0;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include <QIcon>
|
||||
#include "CloudViewMainWindow.h"
|
||||
|
||||
#define APP_VERSION "1.1.0"
|
||||
#define APP_VERSION "1.1.1"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user