Compare commits
2 Commits
CloudView_
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb0cbdfc54 | ||
|
|
550ed280f9 |
@ -54,6 +54,11 @@ public:
|
|||||||
std::vector<std::vector<SVzNL3DPosition>>& scanLines,
|
std::vector<std::vector<SVzNL3DPosition>>& scanLines,
|
||||||
size_t* validPointCount = nullptr);
|
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; }
|
std::string GetLastError() const { return m_lastError; }
|
||||||
|
|
||||||
|
|||||||
@ -102,6 +102,21 @@ int LaserDataLoader::LoadLaserScanData(const std::string& fileName,
|
|||||||
nLaserPointIdx = 0;
|
nLaserPointIdx = 0;
|
||||||
bFindLineNum = false;
|
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) {
|
} else if (line.find("{") == 0) {
|
||||||
// 使用正则表达式判断是XYZ还是RGBD格式
|
// 使用正则表达式判断是XYZ还是RGBD格式
|
||||||
// XYZ格式: {x,y,z}-{leftX,leftY}-{rightX,rightY}
|
// XYZ格式: {x,y,z}-{leftX,leftY}-{rightX,rightY}
|
||||||
@ -278,6 +293,144 @@ 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),Alpha 存储在高 8 位
|
||||||
|
unsigned int nRGB = static_cast<unsigned int>(A) << 24;
|
||||||
|
nRGB |= static_cast<unsigned int>(B) << 16;
|
||||||
|
nRGB |= static_cast<unsigned int>(G) << 8;
|
||||||
|
nRGB |= static_cast<unsigned int>(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)
|
void LaserDataLoader::FreeLaserScanData(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines)
|
||||||
{
|
{
|
||||||
LOG_DEBUG("Freeing unified laser scan data, line count: %zu\n", laserLines.size());
|
LOG_DEBUG("Freeing unified laser scan data, line count: %zu\n", laserLines.size());
|
||||||
@ -487,10 +640,38 @@ int LaserDataLoader::_ParseLaserScanPoint(const std::string& data, SVzNL3DPositi
|
|||||||
int LaserDataLoader::_ParseLaserScanPoint(const std::string& data, SVzNLPointXYZRGBA& sData, SVzNL2DLRPoint& s2DData)
|
int LaserDataLoader::_ParseLaserScanPoint(const std::string& data, SVzNLPointXYZRGBA& sData, SVzNL2DLRPoint& s2DData)
|
||||||
{
|
{
|
||||||
float X, Y, Z;
|
float X, Y, Z;
|
||||||
float r, g, b;
|
|
||||||
float leftX, leftY;
|
float leftX, leftY;
|
||||||
float rightX, rightY;
|
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,A 是 0-255 整数值,Alpha 存储在高 8 位
|
||||||
|
unsigned int nRGB = static_cast<unsigned int>(A) << 24;
|
||||||
|
nRGB |= static_cast<unsigned int>(B) << 16;
|
||||||
|
nRGB |= static_cast<unsigned int>(G) << 8;
|
||||||
|
nRGB |= static_cast<unsigned int>(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.x = X;
|
||||||
sData.y = Y;
|
sData.y = Y;
|
||||||
sData.z = Z;
|
sData.z = Z;
|
||||||
@ -524,26 +705,37 @@ int LaserDataLoader::_GetLaserType(const std::string& fileName, EVzResultDataTyp
|
|||||||
|
|
||||||
bool bFind = false;
|
bool bFind = false;
|
||||||
while (std::getline(inputFile, linedata)) {
|
while (std::getline(inputFile, linedata)) {
|
||||||
// 去除行末的 \r 字符(处理 Windows 格式的 CR LF 换行符)
|
|
||||||
TrimCarriageReturn(linedata);
|
TrimCarriageReturn(linedata);
|
||||||
|
|
||||||
if (linedata.find("{") == 0) {
|
if (linedata.find("{") == 0) {
|
||||||
// 修复正则表达式以匹配实际数据格式
|
// 统计 { 的数量判断格式
|
||||||
// XYZ格式: {x,y,z}-{leftX,leftY}-{rightX,rightY}
|
int braceCount = 0;
|
||||||
// RGBD格式: {x,y,z,r,g,b}-{leftX,leftY}-{rightX,rightY}
|
for (char c : linedata) {
|
||||||
// 更宽松的正则表达式,允许更多的空格变化
|
if (c == '{') braceCount++;
|
||||||
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*\})");
|
|
||||||
|
|
||||||
// 先尝试匹配RGBD格式(6个数字)
|
if (braceCount == 4) {
|
||||||
if (std::regex_match(linedata, rgbdPattern)) {
|
// 新RGBD格式: {x,y,z}-{lx,ly}-{rx,ry}-{R,G,B,A}
|
||||||
eDataType = keResultDataType_PointXYZRGBA;
|
eDataType = keResultDataType_PointXYZRGBA;
|
||||||
bFind = true;
|
bFind = true;
|
||||||
}
|
} else if (braceCount == 3) {
|
||||||
// 再尝试匹配XYZ格式(3个数字)
|
// 检查第一组的逗号数量区分XYZ和旧RGBD
|
||||||
else if (std::regex_match(linedata, xyzPattern)) {
|
size_t firstClose = linedata.find('}');
|
||||||
eDataType = keResultDataType_Position;
|
if (firstClose != std::string::npos) {
|
||||||
bFind = true;
|
int commaCount = 0;
|
||||||
|
for (size_t i = 0; i < firstClose; ++i) {
|
||||||
|
if (linedata[i] == ',') commaCount++;
|
||||||
|
}
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,11 +44,6 @@ private slots:
|
|||||||
*/
|
*/
|
||||||
void onOpenFile();
|
void onOpenFile();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 打开线段文件
|
|
||||||
*/
|
|
||||||
void onOpenSegmentFile();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 打开姿态点文件
|
* @brief 打开姿态点文件
|
||||||
*/
|
*/
|
||||||
@ -225,6 +220,18 @@ private:
|
|||||||
*/
|
*/
|
||||||
void updateLinePointsDialog();
|
void updateLinePointsDialog();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 加载点云文件
|
||||||
|
* @return 是否成功加载到点云数据
|
||||||
|
*/
|
||||||
|
bool loadPointCloudFile(const QString& fileName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 加载线段文件(Poly格式)
|
||||||
|
* @return 是否成功加载到线段数据
|
||||||
|
*/
|
||||||
|
bool loadSegmentFile(const QString& fileName);
|
||||||
|
|
||||||
// 点云显示控件
|
// 点云显示控件
|
||||||
PointCloudGLWidget* m_glWidget;
|
PointCloudGLWidget* m_glWidget;
|
||||||
|
|
||||||
@ -233,7 +240,6 @@ private:
|
|||||||
|
|
||||||
// 文件操作控件
|
// 文件操作控件
|
||||||
QPushButton* m_btnOpenFile;
|
QPushButton* m_btnOpenFile;
|
||||||
QPushButton* m_btnOpenSegment;
|
|
||||||
QPushButton* m_btnOpenPose;
|
QPushButton* m_btnOpenPose;
|
||||||
QPushButton* m_btnClearAll;
|
QPushButton* m_btnClearAll;
|
||||||
|
|
||||||
|
|||||||
@ -22,9 +22,10 @@ struct Point3DRGB
|
|||||||
{
|
{
|
||||||
float x, y, z;
|
float x, y, z;
|
||||||
uint8_t r, g, b;
|
uint8_t r, g, b;
|
||||||
Point3DRGB() : x(0), y(0), z(0), r(255), g(255), b(255) {}
|
float pointSize; // 自定义点大小,0 表示使用全局默认值(来自 RGBA 中 A > 1 的值)
|
||||||
Point3DRGB(float _x, float _y, float _z, uint8_t _r = 255, uint8_t _g = 255, uint8_t _b = 255)
|
Point3DRGB() : x(0), y(0), z(0), r(255), g(255), b(255), pointSize(0) {}
|
||||||
: x(_x), y(_y), z(_z), r(_r), g(_g), b(_b) {}
|
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) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -87,10 +87,11 @@ struct LineSegment
|
|||||||
float x1, y1, z1; // 起点
|
float x1, y1, z1; // 起点
|
||||||
float x2, y2, z2; // 终点
|
float x2, y2, z2; // 终点
|
||||||
float r, g, b; // 颜色 (0-1)
|
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) {}
|
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)
|
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) {}
|
: x1(_x1), y1(_y1), z1(_z1), x2(_x2), y2(_y2), z2(_z2), r(_r), g(_g), b(_b), lineWidth(_lw) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -272,6 +273,14 @@ private:
|
|||||||
int totalLines; // 总线数
|
int totalLines; // 总线数
|
||||||
int pointsPerLine; // 每线点数(网格化点云)
|
int pointsPerLine; // 每线点数(网格化点云)
|
||||||
|
|
||||||
|
// 自定义点大小分组(RGBA 中 A > 1 的点按大小分组)
|
||||||
|
struct PointSizeGroup {
|
||||||
|
float pointSize; // 点大小
|
||||||
|
std::vector<size_t> indices; // 该大小的点在顶点数组中的索引(顶点索引 = indices[i] * 3)
|
||||||
|
};
|
||||||
|
std::vector<PointSizeGroup> customPointSizeGroups;
|
||||||
|
bool hasCustomPointSizes;
|
||||||
|
|
||||||
// VBO 缓冲区
|
// VBO 缓冲区
|
||||||
QOpenGLBuffer vertexBuffer; // 顶点 VBO
|
QOpenGLBuffer vertexBuffer; // 顶点 VBO
|
||||||
QOpenGLBuffer colorBuffer; // 颜色 VBO
|
QOpenGLBuffer colorBuffer; // 颜色 VBO
|
||||||
@ -280,6 +289,7 @@ private:
|
|||||||
PointCloudData()
|
PointCloudData()
|
||||||
: hasColor(false), hasLineInfo(false), colorIndex(0)
|
: hasColor(false), hasLineInfo(false), colorIndex(0)
|
||||||
, totalLines(0), pointsPerLine(0)
|
, totalLines(0), pointsPerLine(0)
|
||||||
|
, hasCustomPointSizes(false)
|
||||||
, vertexBuffer(QOpenGLBuffer::VertexBuffer)
|
, vertexBuffer(QOpenGLBuffer::VertexBuffer)
|
||||||
, colorBuffer(QOpenGLBuffer::VertexBuffer)
|
, colorBuffer(QOpenGLBuffer::VertexBuffer)
|
||||||
, vboCreated(false)
|
, vboCreated(false)
|
||||||
@ -297,6 +307,8 @@ private:
|
|||||||
, colorIndex(other.colorIndex)
|
, colorIndex(other.colorIndex)
|
||||||
, totalLines(other.totalLines)
|
, totalLines(other.totalLines)
|
||||||
, pointsPerLine(other.pointsPerLine)
|
, pointsPerLine(other.pointsPerLine)
|
||||||
|
, customPointSizeGroups(std::move(other.customPointSizeGroups))
|
||||||
|
, hasCustomPointSizes(other.hasCustomPointSizes)
|
||||||
, vertexBuffer(QOpenGLBuffer::VertexBuffer)
|
, vertexBuffer(QOpenGLBuffer::VertexBuffer)
|
||||||
, colorBuffer(QOpenGLBuffer::VertexBuffer)
|
, colorBuffer(QOpenGLBuffer::VertexBuffer)
|
||||||
, vboCreated(false)
|
, vboCreated(false)
|
||||||
@ -324,6 +336,8 @@ private:
|
|||||||
colorIndex = other.colorIndex;
|
colorIndex = other.colorIndex;
|
||||||
totalLines = other.totalLines;
|
totalLines = other.totalLines;
|
||||||
pointsPerLine = other.pointsPerLine;
|
pointsPerLine = other.pointsPerLine;
|
||||||
|
customPointSizeGroups = std::move(other.customPointSizeGroups);
|
||||||
|
hasCustomPointSizes = other.hasCustomPointSizes;
|
||||||
// VBO 需要重建
|
// VBO 需要重建
|
||||||
other.vboCreated = false;
|
other.vboCreated = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "VrLog.h"
|
#include "VrLog.h"
|
||||||
|
#include "LaserDataLoader.h"
|
||||||
|
|
||||||
CloudViewMainWindow::CloudViewMainWindow(QWidget* parent)
|
CloudViewMainWindow::CloudViewMainWindow(QWidget* parent)
|
||||||
: QMainWindow(parent)
|
: QMainWindow(parent)
|
||||||
@ -125,18 +126,12 @@ QGroupBox* CloudViewMainWindow::createFileGroup()
|
|||||||
layout->setSpacing(3);
|
layout->setSpacing(3);
|
||||||
layout->setContentsMargins(5, 5, 5, 5);
|
layout->setContentsMargins(5, 5, 5, 5);
|
||||||
|
|
||||||
m_btnOpenFile = new QPushButton("打开点云", group);
|
m_btnOpenFile = new QPushButton("打开文件", group);
|
||||||
m_btnOpenFile->setMinimumHeight(24);
|
m_btnOpenFile->setMinimumHeight(24);
|
||||||
m_btnOpenFile->setMaximumHeight(24);
|
m_btnOpenFile->setMaximumHeight(24);
|
||||||
connect(m_btnOpenFile, &QPushButton::clicked, this, &CloudViewMainWindow::onOpenFile);
|
connect(m_btnOpenFile, &QPushButton::clicked, this, &CloudViewMainWindow::onOpenFile);
|
||||||
layout->addWidget(m_btnOpenFile);
|
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 = new QPushButton("打开姿态点 {x,y,z}-{r,p,y}", group);
|
||||||
m_btnOpenPose->setMinimumHeight(24);
|
m_btnOpenPose->setMinimumHeight(24);
|
||||||
m_btnOpenPose->setMaximumHeight(24);
|
m_btnOpenPose->setMaximumHeight(24);
|
||||||
@ -523,6 +518,7 @@ QGroupBox* CloudViewMainWindow::createLineGroup()
|
|||||||
m_btnSelectByNumber->setMaximumHeight(24);
|
m_btnSelectByNumber->setMaximumHeight(24);
|
||||||
m_btnSelectByNumber->setMaximumWidth(50);
|
m_btnSelectByNumber->setMaximumWidth(50);
|
||||||
connect(m_btnSelectByNumber, &QPushButton::clicked, this, &CloudViewMainWindow::onSelectLineByNumber);
|
connect(m_btnSelectByNumber, &QPushButton::clicked, this, &CloudViewMainWindow::onSelectLineByNumber);
|
||||||
|
connect(m_lineNumberInput, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onSelectLineByNumber);
|
||||||
inputLayout->addWidget(m_lineNumberInput, 1);
|
inputLayout->addWidget(m_lineNumberInput, 1);
|
||||||
inputLayout->addWidget(m_btnSelectByNumber);
|
inputLayout->addWidget(m_btnSelectByNumber);
|
||||||
layout->addLayout(inputLayout);
|
layout->addLayout(inputLayout);
|
||||||
@ -579,17 +575,42 @@ QGroupBox* CloudViewMainWindow::createCloudListGroup()
|
|||||||
|
|
||||||
void CloudViewMainWindow::onOpenFile()
|
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(
|
QString fileName = QFileDialog::getOpenFileName(
|
||||||
this,
|
this,
|
||||||
"打开点云文件",
|
"打开文件",
|
||||||
QString(),
|
QString(),
|
||||||
"点云文件 (*.pcd *.txt);;PCD 文件 (*.pcd);;TXT 文件 (*.txt);;所有文件 (*.*)"
|
"所有支持格式 (*.pcd *.txt);;PCD 文件 (*.pcd);;TXT 文件 (*.txt);;所有文件 (*.*)"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (fileName.isEmpty()) {
|
if (fileName.isEmpty()) {
|
||||||
return;
|
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("正在加载点云...");
|
statusBar()->showMessage("正在加载点云...");
|
||||||
|
|
||||||
QFileInfo fileInfo(fileName);
|
QFileInfo fileInfo(fileName);
|
||||||
@ -601,9 +622,8 @@ void CloudViewMainWindow::onOpenFile()
|
|||||||
int result = m_converter->loadFromFile(fileName.toStdString(), rgbCloud);
|
int result = m_converter->loadFromFile(fileName.toStdString(), rgbCloud);
|
||||||
|
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
QMessageBox::critical(this, "错误", QString("加载点云失败: %1").arg(QString::fromStdString(m_converter->getLastError())));
|
LOG_INFO("[CloudView] Load point cloud failed: %s\n", m_converter->getLastError().c_str());
|
||||||
statusBar()->showMessage("加载失败");
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存原始完整点云 XYZ(用于旋转/线上点等功能)
|
// 保存原始完整点云 XYZ(用于旋转/线上点等功能)
|
||||||
@ -632,11 +652,9 @@ void CloudViewMainWindow::onOpenFile()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hadColor) {
|
if (hadColor) {
|
||||||
// 有颜色数据:使用 addPointCloud(PointCloudXYZRGB) 显示原始颜色
|
|
||||||
m_glWidget->addPointCloud(rgbCloud, cloudName);
|
m_glWidget->addPointCloud(rgbCloud, cloudName);
|
||||||
LOG_INFO("[CloudView] Loaded with original color, points: %zu\n", rgbCloud.size());
|
LOG_INFO("[CloudView] Loaded with original color, points: %zu\n", rgbCloud.size());
|
||||||
} else {
|
} else {
|
||||||
// 无颜色数据:使用 addPointCloud(PointCloudXYZ) 显示(颜色表轮换)
|
|
||||||
m_glWidget->addPointCloud(m_originalCloud, cloudName);
|
m_glWidget->addPointCloud(m_originalCloud, cloudName);
|
||||||
LOG_INFO("[CloudView] Loaded without color (color table), points: %zu\n", m_originalCloud.size());
|
LOG_INFO("[CloudView] Loaded without color (color table), points: %zu\n", m_originalCloud.size());
|
||||||
}
|
}
|
||||||
@ -664,86 +682,63 @@ void CloudViewMainWindow::onOpenFile()
|
|||||||
m_cloudList->addItem(itemText);
|
m_cloudList->addItem(itemText);
|
||||||
|
|
||||||
statusBar()->showMessage(QString("已加载 %1 个点%2").arg(m_converter->getLoadedPointCount()).arg(hadColor ? " (彩色)" : ""));
|
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("正在加载线段...");
|
statusBar()->showMessage("正在加载线段...");
|
||||||
|
|
||||||
QFile file(fileName);
|
LaserDataLoader loader;
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
std::vector<std::vector<SVzNLPointXYZRGBA>> polyLines;
|
||||||
QMessageBox::critical(this, "错误", QString("无法打开文件: %1").arg(fileName));
|
int result = loader.LoadPolySegments(fileName.toStdString(), polyLines);
|
||||||
statusBar()->showMessage("加载失败");
|
|
||||||
return;
|
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;
|
QVector<LineSegment> segments;
|
||||||
QTextStream in(&file);
|
for (const auto& polyLine : polyLines) {
|
||||||
int lineNum = 0;
|
for (size_t i = 0; i + 1 < polyLine.size(); ++i) {
|
||||||
int validCount = 0;
|
const auto& p1 = polyLine[i];
|
||||||
|
const auto& p2 = polyLine[i + 1];
|
||||||
while (!in.atEnd()) {
|
// 取起点颜色作为线段颜色,nRGB 为 (A<<24)|BGR 打包格式
|
||||||
QString line = in.readLine().trimmed();
|
float r = ((p1.nRGB >> 0) & 0xFF) / 255.0f;
|
||||||
lineNum++;
|
float g = ((p1.nRGB >> 8) & 0xFF) / 255.0f;
|
||||||
|
float b = ((p1.nRGB >> 16) & 0xFF) / 255.0f;
|
||||||
// 跳过空行和注释
|
uint8_t a = static_cast<uint8_t>((p1.nRGB >> 24) & 0xFF);
|
||||||
if (line.isEmpty() || line.startsWith('#')) {
|
// A > 1 时作为线宽使用
|
||||||
continue;
|
float lineWidth = (a > 1) ? static_cast<float>(a) : 0.0f;
|
||||||
|
segments.append(LineSegment(
|
||||||
|
p1.x, p1.y, p1.z,
|
||||||
|
p2.x, p2.y, p2.z,
|
||||||
|
r, g, b, lineWidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析格式:{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()) {
|
if (segments.isEmpty()) {
|
||||||
QMessageBox::warning(this, "警告", "文件中没有有效的线段数据");
|
return false;
|
||||||
statusBar()->showMessage("加载失败");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_glWidget->addLineSegments(segments);
|
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()
|
void CloudViewMainWindow::onOpenPoseFile()
|
||||||
@ -831,6 +826,8 @@ void CloudViewMainWindow::onOpenPoseFile()
|
|||||||
void CloudViewMainWindow::onClearAll()
|
void CloudViewMainWindow::onClearAll()
|
||||||
{
|
{
|
||||||
m_glWidget->clearPointClouds();
|
m_glWidget->clearPointClouds();
|
||||||
|
m_glWidget->clearLineSegments();
|
||||||
|
m_glWidget->clearPosePoints();
|
||||||
m_cloudList->clear();
|
m_cloudList->clear();
|
||||||
m_cloudCount = 0;
|
m_cloudCount = 0;
|
||||||
m_currentLineNum = 0;
|
m_currentLineNum = 0;
|
||||||
|
|||||||
@ -137,12 +137,15 @@ int PointCloudConverter::loadFromTxt(const std::string& fileName, PointCloudXYZR
|
|||||||
for (const auto& line : rgbdData) {
|
for (const auto& line : rgbdData) {
|
||||||
for (int i = 0; i < line.nPointCnt; ++i) {
|
for (int i = 0; i < line.nPointCnt; ++i) {
|
||||||
const SVzNLPointXYZRGBA& pt = line.p3DPoint[i];
|
const SVzNLPointXYZRGBA& pt = line.p3DPoint[i];
|
||||||
// 解包颜色:nRGB 格式为 (B << 16) | (G << 8) | R
|
// 解包颜色:nRGB 格式为 (A << 24) | (B << 16) | (G << 8) | R
|
||||||
uint8_t r = static_cast<uint8_t>(pt.nRGB & 0xFF);
|
uint8_t r = static_cast<uint8_t>(pt.nRGB & 0xFF);
|
||||||
uint8_t g = static_cast<uint8_t>((pt.nRGB >> 8) & 0xFF);
|
uint8_t g = static_cast<uint8_t>((pt.nRGB >> 8) & 0xFF);
|
||||||
uint8_t b = static_cast<uint8_t>((pt.nRGB >> 16) & 0xFF);
|
uint8_t b = static_cast<uint8_t>((pt.nRGB >> 16) & 0xFF);
|
||||||
|
uint8_t a = static_cast<uint8_t>((pt.nRGB >> 24) & 0xFF);
|
||||||
|
// A > 1 时作为点大小使用
|
||||||
|
float pointSize = (a > 1) ? static_cast<float>(a) : 0.0f;
|
||||||
|
|
||||||
Point3DRGB point(pt.x, pt.y, pt.z, r, g, b);
|
Point3DRGB point(pt.x, pt.y, pt.z, r, g, b, pointSize);
|
||||||
cloud.push_back(point, lineIndex);
|
cloud.push_back(point, lineIndex);
|
||||||
totalCount++;
|
totalCount++;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
|
#include <map>
|
||||||
#include "VrLog.h"
|
#include "VrLog.h"
|
||||||
|
|
||||||
// OpenGL/GLU 头文件
|
// OpenGL/GLU 头文件
|
||||||
@ -196,6 +197,27 @@ void PointCloudGLWidget::paintGL()
|
|||||||
|
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
glDisableClientState(GL_COLOR_ARRAY);
|
glDisableClientState(GL_COLOR_ARRAY);
|
||||||
|
|
||||||
|
// 绘制自定义大小的点(RGBA 中 A > 1 的点)
|
||||||
|
if (cloudData.hasCustomPointSizes) {
|
||||||
|
glDepthFunc(GL_LEQUAL); // 允许在相同深度覆盖绘制
|
||||||
|
for (const auto& group : cloudData.customPointSizeGroups) {
|
||||||
|
glPointSize(group.pointSize);
|
||||||
|
glBegin(GL_POINTS);
|
||||||
|
for (size_t idx : group.indices) {
|
||||||
|
size_t vi = idx * 3;
|
||||||
|
if (vi + 2 < cloudData.vertices.size()) {
|
||||||
|
if (cloudData.hasColor && vi + 2 < cloudData.colors.size()) {
|
||||||
|
glColor3f(cloudData.colors[vi], cloudData.colors[vi + 1], cloudData.colors[vi + 2]);
|
||||||
|
}
|
||||||
|
glVertex3f(cloudData.vertices[vi], cloudData.vertices[vi + 1], cloudData.vertices[vi + 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
glDepthFunc(GL_LESS); // 恢复默认深度测试
|
||||||
|
glPointSize(m_pointSize); // 恢复默认点大小
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawSelectedPoints();
|
drawSelectedPoints();
|
||||||
@ -296,12 +318,19 @@ void PointCloudGLWidget::addPointCloud(const PointCloudXYZRGB& cloud, const QStr
|
|||||||
data.colors.reserve(cloud.size() * 3);
|
data.colors.reserve(cloud.size() * 3);
|
||||||
data.totalLines = 0;
|
data.totalLines = 0;
|
||||||
data.pointsPerLine = 0;
|
data.pointsPerLine = 0;
|
||||||
|
data.hasCustomPointSizes = false;
|
||||||
|
|
||||||
|
// 用于按点大小分组的临时 map
|
||||||
|
std::map<float, std::vector<size_t>> sizeGroupMap;
|
||||||
|
|
||||||
for (size_t i = 0; i < cloud.points.size(); ++i) {
|
for (size_t i = 0; i < cloud.points.size(); ++i) {
|
||||||
const auto& pt = cloud.points[i];
|
const auto& pt = cloud.points[i];
|
||||||
if (!std::isfinite(pt.x) || !std::isfinite(pt.y) || !std::isfinite(pt.z)) {
|
if (!std::isfinite(pt.x) || !std::isfinite(pt.y) || !std::isfinite(pt.z)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t pointIndex = data.vertices.size() / 3;
|
||||||
|
|
||||||
data.vertices.push_back(pt.x);
|
data.vertices.push_back(pt.x);
|
||||||
data.vertices.push_back(pt.y);
|
data.vertices.push_back(pt.y);
|
||||||
data.vertices.push_back(pt.z);
|
data.vertices.push_back(pt.z);
|
||||||
@ -321,6 +350,23 @@ void PointCloudGLWidget::addPointCloud(const PointCloudXYZRGB& cloud, const QStr
|
|||||||
data.totalLines = lineIdx + 1;
|
data.totalLines = lineIdx + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 收集自定义点大小(A > 1)
|
||||||
|
if (pt.pointSize > 1.0f) {
|
||||||
|
sizeGroupMap[pt.pointSize].push_back(pointIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建自定义点大小分组
|
||||||
|
if (!sizeGroupMap.empty()) {
|
||||||
|
data.hasCustomPointSizes = true;
|
||||||
|
for (auto& pair : sizeGroupMap) {
|
||||||
|
PointCloudData::PointSizeGroup group;
|
||||||
|
group.pointSize = pair.first;
|
||||||
|
group.indices = std::move(pair.second);
|
||||||
|
data.customPointSizeGroups.push_back(std::move(group));
|
||||||
|
}
|
||||||
|
LOG_INFO("[CloudView] Found %zu custom point size groups\n", data.customPointSizeGroups.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算每线点数
|
// 计算每线点数
|
||||||
@ -1187,16 +1233,26 @@ void PointCloudGLWidget::drawLineSegments()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
glLineWidth(2.0f);
|
// 按线宽分组绘制
|
||||||
glBegin(GL_LINES);
|
// 收集所有不同的线宽值(0 表示默认 2.0f)
|
||||||
|
std::map<float, QVector<int>> widthGroups;
|
||||||
for (const auto& seg : m_lineSegments) {
|
for (int i = 0; i < m_lineSegments.size(); ++i) {
|
||||||
glColor3f(seg.r, seg.g, seg.b);
|
float w = m_lineSegments[i].lineWidth > 0 ? m_lineSegments[i].lineWidth : 2.0f;
|
||||||
glVertex3f(seg.x1, seg.y1, seg.z1);
|
widthGroups[w].append(i);
|
||||||
glVertex3f(seg.x2, seg.y2, seg.z2);
|
}
|
||||||
|
|
||||||
|
for (const auto& group : widthGroups) {
|
||||||
|
glLineWidth(group.first);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
for (int idx : group.second) {
|
||||||
|
const auto& seg = m_lineSegments[idx];
|
||||||
|
glColor3f(seg.r, seg.g, seg.b);
|
||||||
|
glVertex3f(seg.x1, seg.y1, seg.z1);
|
||||||
|
glVertex3f(seg.x2, seg.y2, seg.z2);
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
glEnd();
|
|
||||||
glLineWidth(1.0f);
|
glLineWidth(1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include "CloudViewMainWindow.h"
|
#include "CloudViewMainWindow.h"
|
||||||
|
|
||||||
#define APP_VERSION "1.1.0"
|
#define APP_VERSION "1.1.2"
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user