增加启动显示参数

This commit is contained in:
杰仔 2026-07-11 18:33:31 +08:00
parent c1cfe7e560
commit 6a6dd55bec
7 changed files with 1686 additions and 174 deletions

View File

@ -1,6 +1,7 @@
#include "LaserDataLoader.h" #include "LaserDataLoader.h"
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <cstdarg>
#include <cstdio> #include <cstdio>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
@ -128,6 +129,34 @@ bool ReadDatLineRecord(std::ifstream& inputFile, int recordSize, DatLineScan3DDa
std::memcpy(&lineData.lineStepping, buffer + 8, sizeof(lineData.lineStepping)); std::memcpy(&lineData.lineStepping, buffer + 8, sizeof(lineData.lineStepping));
return true; return true;
} }
void AppendFormattedLine(std::string& output, const char* format, ...)
{
char stackBuffer[256];
va_list args;
va_start(args, format);
va_list argsCopy;
va_copy(argsCopy, args);
int written = vsnprintf(stackBuffer, sizeof(stackBuffer), format, args);
va_end(args);
if (written < 0) {
va_end(argsCopy);
return;
}
if (static_cast<size_t>(written) < sizeof(stackBuffer)) {
output.append(stackBuffer, static_cast<size_t>(written));
va_end(argsCopy);
return;
}
std::vector<char> dynamicBuffer(static_cast<size_t>(written) + 1);
vsnprintf(dynamicBuffer.data(), dynamicBuffer.size(), format, argsCopy);
va_end(argsCopy);
output.append(dynamicBuffer.data(), static_cast<size_t>(written));
}
} }
LaserDataLoader::LaserDataLoader() LaserDataLoader::LaserDataLoader()
@ -465,11 +494,11 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
} }
// 写入文件头 // 写入文件头
sw << "LineNum:" << lineNum << std::endl; sw << "LineNum:" << lineNum << '\n';
sw << "DataType: 0" << std::endl; sw << "DataType: 0" << '\n';
sw << "ScanSpeed:" << scanSpeed << std::endl; sw << "ScanSpeed:" << scanSpeed << '\n';
sw << "PointAdjust: 1" << std::endl; sw << "PointAdjust: 1" << '\n';
sw << "MaxTimeStamp:" << maxTimeStamp << "_" << clockPerSecond << std::endl; sw << "MaxTimeStamp:" << maxTimeStamp << "_" << clockPerSecond << '\n';
// 写入每条扫描线数据 // 写入每条扫描线数据
for (const auto& linePair : laserLines) { for (const auto& linePair : laserLines) {
@ -477,20 +506,29 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
EVzResultDataType dataType = linePair.first; EVzResultDataType dataType = linePair.first;
const SVzLaserLineData& lineData = linePair.second; const SVzLaserLineData& lineData = linePair.second;
sw << "Line_" << lineData.llFrameIdx << "_" << lineData.llTimeStamp << "_" << lineData.nPointCount << std::endl; sw << "Line_" << lineData.llFrameIdx << "_" << lineData.llTimeStamp << "_" << lineData.nPointCount << '\n';
// 根据数据类型写入点云数据 // 根据数据类型写入点云数据
if (dataType == keResultDataType_Position && lineData.p3DPoint) { if (dataType == keResultDataType_Position && lineData.p3DPoint) {
// 写入XYZ格式数据 // 写入XYZ格式数据
const SVzNL3DPosition* points = static_cast<const SVzNL3DPosition*>(lineData.p3DPoint); const SVzNL3DPosition* points = static_cast<const SVzNL3DPosition*>(lineData.p3DPoint);
const SVzNL2DPosition* points2D = static_cast<const SVzNL2DPosition*>(lineData.p2DPoint); const SVzNL2DPosition* points2D = static_cast<const SVzNL2DPosition*>(lineData.p2DPoint);
for (int i = 0; i < lineData.nPointCount; i++) { constexpr int BATCH = 256;
float x = static_cast<float>(points[i].pt3D.x); constexpr int PER_POINT = 128;
float y = static_cast<float>(points[i].pt3D.y); for (int base = 0; base < lineData.nPointCount; base += BATCH) {
float z = static_cast<float>(points[i].pt3D.z); int end = (base + BATCH < lineData.nPointCount) ? (base + BATCH) : lineData.nPointCount;
sw << "{ " << std::fixed << std::setprecision(6) << x << "," << y << "," << z << " }-"; std::string buffer;
sw << "{ " << points2D[i].ptLeft2D.x << "," << points2D[i].ptLeft2D.y << " }-"; buffer.reserve(BATCH * PER_POINT);
sw << "{ " << points2D[i].ptRight2D.x << "," << points2D[i].ptRight2D.y << " }" << std::endl; for (int i = base; i < end; ++i) {
float x = static_cast<float>(points[i].pt3D.x);
float y = static_cast<float>(points[i].pt3D.y);
float z = static_cast<float>(points[i].pt3D.z);
AppendFormattedLine(buffer, "{ %.6f,%.6f,%.6f }-{ %d,%d }-{ %d,%d }\n",
x, y, z,
points2D[i].ptLeft2D.x, points2D[i].ptLeft2D.y,
points2D[i].ptRight2D.x, points2D[i].ptRight2D.y);
}
sw.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
} }
} else if (dataType == keResultDataType_PointXYZI && lineData.p3DPoint) { } else if (dataType == keResultDataType_PointXYZI && lineData.p3DPoint) {
// LiDAR 点云snprintf 批量写入,每 256 点 flush // LiDAR 点云snprintf 批量写入,每 256 点 flush
@ -515,22 +553,27 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
// 写入RGBD格式数据 // 写入RGBD格式数据
const SVzNLPointXYZRGBA* points = static_cast<const SVzNLPointXYZRGBA*>(lineData.p3DPoint); const SVzNLPointXYZRGBA* points = static_cast<const SVzNLPointXYZRGBA*>(lineData.p3DPoint);
const SVzNL2DLRPoint* points2D = static_cast<const SVzNL2DLRPoint*>(lineData.p2DPoint); const SVzNL2DLRPoint* points2D = static_cast<const SVzNL2DLRPoint*>(lineData.p2DPoint);
for (int i = 0; i < lineData.nPointCount; i++) { constexpr int BATCH = 256;
float x = static_cast<float>(points[i].x); constexpr int PER_POINT = 160;
float y = static_cast<float>(points[i].y); for (int base = 0; base < lineData.nPointCount; base += BATCH) {
float z = static_cast<float>(points[i].z); int end = (base + BATCH < lineData.nPointCount) ? (base + BATCH) : lineData.nPointCount;
int r = (points[i].nRGB >> 16) & 0xFF; std::string buffer;
int g = (points[i].nRGB >> 8) & 0xFF; buffer.reserve(BATCH * PER_POINT);
int b = points[i].nRGB & 0xFF; for (int i = base; i < end; ++i) {
float x = static_cast<float>(points[i].x);
float y = static_cast<float>(points[i].y);
float z = static_cast<float>(points[i].z);
int r = (points[i].nRGB >> 16) & 0xFF;
int g = (points[i].nRGB >> 8) & 0xFF;
int b = points[i].nRGB & 0xFF;
sw << "{" << std::fixed << std::setprecision(6) << x << "," AppendFormattedLine(buffer, "{%.6f,%.6f,%.6f,%.6f,%.6f,%.6f}-{ %d,%d }-{ %d,%d }\n",
<< std::fixed << std::setprecision(6) << y << "," x, y, z,
<< std::fixed << std::setprecision(6) << z << "," b * 1.0f / 255, g * 1.0f / 255, r * 1.0f / 255,
<< std::fixed << std::setprecision(6) << b * 1.0f / 255 << "," points2D[i].sLeft.x, points2D[i].sLeft.y,
<< std::fixed << std::setprecision(6) << g * 1.0f / 255 << "," points2D[i].sRight.x, points2D[i].sRight.y);
<< std::fixed << std::setprecision(6) << r * 1.0f / 255 << "}-"; }
sw << "{ " << points2D[i].sLeft.x << "," << points2D[i].sLeft.y << " }-"; sw.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
sw << "{ " << points2D[i].sRight.x << "," << points2D[i].sRight.y << " }" << std::endl;
} }
} }
} }
@ -570,11 +613,11 @@ int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vecto
} }
// 写入文件头 // 写入文件头
sw << "LineNum:" << xyzData.size() << std::endl; sw << "LineNum:" << xyzData.size() << '\n';
sw << "DataType: 0" << std::endl; sw << "DataType: 0" << '\n';
sw << "ScanSpeed:" << 0 << std::endl; sw << "ScanSpeed:" << 0 << '\n';
sw << "PointAdjust: 1" << std::endl; sw << "PointAdjust: 1" << '\n';
sw << "MaxTimeStamp:" << 0 << "_" << 0 << std::endl; sw << "MaxTimeStamp:" << 0 << "_" << 0 << '\n';
const size_t totalLines = xyzData.size(); const size_t totalLines = xyzData.size();
int index = 0; int index = 0;
@ -587,19 +630,25 @@ int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vecto
return ERR_CODE(DEV_ARG_INVAILD); return ERR_CODE(DEV_ARG_INVAILD);
} }
sw << "Line_" << index << "_" << 0 << "_" << linePair.size() << std::endl; sw << "Line_" << index << "_" << 0 << "_" << linePair.size() << '\n';
// 根据数据类型写入点云数据 // 根据数据类型写入点云数据
for(const auto& point : linePair){ constexpr int BATCH = 256;
constexpr int PER_POINT = 96;
for (size_t base = 0; base < linePair.size(); base += BATCH) {
size_t end = std::min(base + static_cast<size_t>(BATCH), linePair.size());
std::string buffer;
buffer.reserve(BATCH * PER_POINT);
for (size_t i = base; i < end; ++i) {
const auto& point = linePair[i];
// 写入XYZ格式数据 // 写入XYZ格式数据
float x = static_cast<float>(point.pt3D.x); float x = static_cast<float>(point.pt3D.x);
float y = static_cast<float>(point.pt3D.y); float y = static_cast<float>(point.pt3D.y);
float z = static_cast<float>(point.pt3D.z); float z = static_cast<float>(point.pt3D.z);
sw << "{ " AppendFormattedLine(buffer, "{ %.6f, %.6f, %.6f } - { 0, 0 } - { 0, 0 }\n",
<< std::fixed << std::setprecision(6) << x << ", " x, y, z);
<< std::fixed << std::setprecision(6) << y << ", " }
<< std::fixed << std::setprecision(6) << z << " } - "; sw.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
sw << "{ 0, 0 } - { 0, 0 }" << std::endl;
} }
++index; ++index;

View File

@ -19,6 +19,7 @@
#include <QCheckBox> #include <QCheckBox>
#include <QComboBox> #include <QComboBox>
#include <QDialog> #include <QDialog>
#include <QStringList>
#include <memory> #include <memory>
#include "PointCloudGLWidget.h" #include "PointCloudGLWidget.h"
@ -26,6 +27,7 @@
class QTextEdit; class QTextEdit;
class QTableWidget; class QTableWidget;
class QCloseEvent;
/** /**
* @brief * @brief
@ -36,11 +38,52 @@ class CloudViewMainWindow : public QMainWindow
Q_OBJECT Q_OBJECT
public: public:
struct StartupFiles
{
QStringList displayFiles;
QStringList bboxFiles;
QStringList circleFiles;
QStringList rectangleFiles;
QStringList surfaceFiles;
QStringList triangleFiles;
bool sequential = false;
};
explicit CloudViewMainWindow(QWidget* parent = nullptr); explicit CloudViewMainWindow(QWidget* parent = nullptr);
~CloudViewMainWindow() override; ~CloudViewMainWindow() override;
bool loadStartupFiles(const StartupFiles& files);
bool loadDisplayFile(const QString& fileName);
bool loadCircleShapeFile(const QString& fileName);
bool loadRectangleShapeFile(const QString& fileName);
bool loadSurfaceShapeFile(const QString& fileName);
bool loadTriangleShapeFile(const QString& fileName);
void fitViewToContent();
protected: protected:
bool eventFilter(QObject* obj, QEvent* event) override; bool eventFilter(QObject* obj, QEvent* event) override;
void closeEvent(QCloseEvent* event) override;
private:
enum class StartupFileType
{
Display,
BBox,
Circle,
Rectangle,
Surface,
Triangle
};
struct StartupItem
{
StartupFileType type;
QString fileName;
};
void buildStartupSequence(const StartupFiles& files);
bool loadStartupItem(const StartupItem& item);
bool loadCurrentStartupSequenceItem();
private slots: private slots:
/** /**
@ -169,6 +212,10 @@ private slots:
void onSavePlyPcdCloud(); // 保存 ply/pcd 点云 void onSavePlyPcdCloud(); // 保存 ply/pcd 点云
void onBatchConvertTxtToPly(); // 批量转换 txt -> ply void onBatchConvertTxtToPly(); // 批量转换 txt -> ply
void onConvertEulerMatrix(); void onConvertEulerMatrix();
void onShowCircleShape();
void onShowRectangleShape();
void onShowSurfaceShape();
void onShowTriangleShape();
/** /**
* @brief 姿 * @brief 姿
@ -260,6 +307,8 @@ private:
bool loadBoundingBoxFile(const QString& fileName); bool loadBoundingBoxFile(const QString& fileName);
void applyTransformToAllClouds(const QMatrix4x4& matrix); void applyTransformToAllClouds(const QMatrix4x4& matrix);
void addGeneratedCloud(const PointCloudXYZ& cloud, const QString& name); void addGeneratedCloud(const PointCloudXYZ& cloud, const QString& name);
void showBasicShape(const QString& shapeName, const QVector<LineSegment>& segments);
void showBasicSurface(const QString& shapeName, const QVector<SurfaceQuad>& surfaces, const QVector<LineSegment>& outlines);
// 点云显示控件 // 点云显示控件
PointCloudGLWidget* m_glWidget; PointCloudGLWidget* m_glWidget;
@ -269,7 +318,6 @@ private:
// 文件操作控件 // 文件操作控件
QPushButton* m_btnOpenFile; QPushButton* m_btnOpenFile;
QPushButton* m_btnOpenPose;
QPushButton* m_btnOpenBBox; QPushButton* m_btnOpenBBox;
QPushButton* m_btnClearAll; QPushButton* m_btnClearAll;
@ -354,6 +402,11 @@ private:
// 悬浮缩放按钮 // 悬浮缩放按钮
QToolButton* m_btnZoomIn; QToolButton* m_btnZoomIn;
QToolButton* m_btnZoomOut; QToolButton* m_btnZoomOut;
// 启动参数序列显示
QVector<StartupItem> m_startupSequence;
int m_startupSequenceIndex;
bool m_startupSequentialMode;
}; };
#endif // CLOUD_VIEW_MAIN_WINDOW_H #endif // CLOUD_VIEW_MAIN_WINDOW_H

View File

@ -96,6 +96,26 @@ struct LineSegment
: x1(_x1), y1(_y1), z1(_z1), x2(_x2), y2(_y2), z2(_z2), r(_r), g(_g), b(_b), lineWidth(_lw) {} : x1(_x1), y1(_y1), z1(_z1), x2(_x2), y2(_y2), z2(_z2), r(_r), g(_g), b(_b), lineWidth(_lw) {}
}; };
/**
* @brief
*/
struct SurfaceQuad
{
QVector3D p1, p2, p3, p4;
float r, g, b, a;
SurfaceQuad() : p1(), p2(), p3(), p4(), r(0.0f), g(0.8f), b(1.0f), a(0.5f) {}
SurfaceQuad(const QVector3D& _p1,
const QVector3D& _p2,
const QVector3D& _p3,
const QVector3D& _p4,
float _r = 0.0f,
float _g = 0.8f,
float _b = 1.0f,
float _a = 0.5f)
: p1(_p1), p2(_p2), p3(_p3), p4(_p4), r(_r), g(_g), b(_b), a(_a) {}
};
/** /**
* @brief 姿 * @brief 姿
* *
@ -137,6 +157,7 @@ public:
void setPointCloudColor(PointCloudColor color); void setPointCloudColor(PointCloudColor color);
void setPointSize(float size); void setPointSize(float size);
void resetView(); void resetView();
void fitToContent();
void zoomIn(); void zoomIn();
void zoomOut(); void zoomOut();
/** /**
@ -269,6 +290,26 @@ public:
*/ */
void clearLineSegments(); void clearLineSegments();
/**
* @brief 线
*/
void setBasicShapeSegments(const QVector<LineSegment>& segments);
/**
* @brief 线
*/
void appendBasicShapeSegments(const QVector<LineSegment>& segments);
/**
* @brief
*/
void appendBasicShapeSurfaces(const QVector<SurfaceQuad>& surfaces);
/**
* @brief 线
*/
void clearBasicShapeSegments();
/** /**
* @brief 姿 * @brief 姿
*/ */
@ -398,6 +439,9 @@ private:
void drawAxis(); void drawAxis();
void drawSelectedLine(); // 绘制选中的线 void drawSelectedLine(); // 绘制选中的线
void drawLineSegments(); // 绘制线段 void drawLineSegments(); // 绘制线段
void drawBasicShapeSurfaces(); // 绘制基础图形面片
void drawBasicShapeSegments(); // 绘制基础图形
void drawLineSegmentList(const QVector<LineSegment>& segments);
void drawPosePoints(); // 绘制姿态点 void drawPosePoints(); // 绘制姿态点
void drawAxisLabels(); // 绘制坐标轴 XYZ 标注 void drawAxisLabels(); // 绘制坐标轴 XYZ 标注
void uploadToVBO(PointCloudData& data); // 上传数据到 VBO void uploadToVBO(PointCloudData& data); // 上传数据到 VBO
@ -443,6 +487,8 @@ private:
// 线段和姿态点数据 // 线段和姿态点数据
QVector<LineSegment> m_lineSegments; QVector<LineSegment> m_lineSegments;
QVector<LineSegment> m_basicShapeSegments;
QVector<SurfaceQuad> m_basicShapeSurfaces;
QVector<PosePoint> m_posePoints; QVector<PosePoint> m_posePoints;
}; };

File diff suppressed because it is too large Load Diff

View File

@ -228,6 +228,8 @@ void PointCloudGLWidget::paintGL()
drawMeasurementLine(); drawMeasurementLine();
drawSelectedLine(); drawSelectedLine();
drawLineSegments(); drawLineSegments();
drawBasicShapeSurfaces();
drawBasicShapeSegments();
drawPosePoints(); drawPosePoints();
// 最后绘制坐标系指示器(覆盖在所有内容之上) // 最后绘制坐标系指示器(覆盖在所有内容之上)
@ -450,6 +452,8 @@ void PointCloudGLWidget::clearPointClouds()
m_selectedPoints.clear(); m_selectedPoints.clear();
m_selectedLine = SelectedLineInfo(); m_selectedLine = SelectedLineInfo();
m_lineSegments.clear(); m_lineSegments.clear();
m_basicShapeSegments.clear();
m_basicShapeSurfaces.clear();
m_posePoints.clear(); m_posePoints.clear();
m_minBound = QVector3D(-50, -50, -50); m_minBound = QVector3D(-50, -50, -50);
m_maxBound = QVector3D(50, 50, 50); m_maxBound = QVector3D(50, 50, 50);
@ -494,6 +498,12 @@ void PointCloudGLWidget::resetView()
update(); update();
} }
void PointCloudGLWidget::fitToContent()
{
computeBoundingBox();
resetView();
}
void PointCloudGLWidget::zoomIn() void PointCloudGLWidget::zoomIn()
{ {
m_distance *= 0.9f; m_distance *= 0.9f;
@ -847,31 +857,59 @@ size_t PointCloudGLWidget::getAllCloudsByLines(std::vector<std::vector<SVzNL3DPo
void PointCloudGLWidget::computeBoundingBox() void PointCloudGLWidget::computeBoundingBox()
{ {
if (m_pointClouds.empty()) { float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX;
float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX;
bool hasContent = false;
auto includePoint = [&](float x, float y, float z) {
minX = qMin(minX, x);
minY = qMin(minY, y);
minZ = qMin(minZ, z);
maxX = qMax(maxX, x);
maxY = qMax(maxY, y);
maxZ = qMax(maxZ, z);
hasContent = true;
};
auto includeSegment = [&](const LineSegment& segment) {
includePoint(segment.x1, segment.y1, segment.z1);
includePoint(segment.x2, segment.y2, segment.z2);
};
for (const auto& cloudData : m_pointClouds) {
for (size_t i = 0; i < cloudData.vertices.size(); i += 3) {
includePoint(cloudData.vertices[i], cloudData.vertices[i + 1], cloudData.vertices[i + 2]);
}
}
for (const LineSegment& segment : m_lineSegments) {
includeSegment(segment);
}
for (const LineSegment& segment : m_basicShapeSegments) {
includeSegment(segment);
}
for (const SurfaceQuad& surface : m_basicShapeSurfaces) {
includePoint(surface.p1.x(), surface.p1.y(), surface.p1.z());
includePoint(surface.p2.x(), surface.p2.y(), surface.p2.z());
includePoint(surface.p3.x(), surface.p3.y(), surface.p3.z());
includePoint(surface.p4.x(), surface.p4.y(), surface.p4.z());
}
for (const PosePoint& pose : m_posePoints) {
const float scale = qMax(0.0f, pose.scale);
includePoint(pose.x - scale, pose.y - scale, pose.z - scale);
includePoint(pose.x + scale, pose.y + scale, pose.z + scale);
}
if (!hasContent) {
m_minBound = QVector3D(-50, -50, -50); m_minBound = QVector3D(-50, -50, -50);
m_maxBound = QVector3D(50, 50, 50); m_maxBound = QVector3D(50, 50, 50);
m_center = QVector3D(0, 0, 0); m_center = QVector3D(0, 0, 0);
return; return;
} }
float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX;
float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX;
for (const auto& cloudData : m_pointClouds) {
for (size_t i = 0; i < cloudData.vertices.size(); i += 3) {
float x = cloudData.vertices[i];
float y = cloudData.vertices[i + 1];
float z = cloudData.vertices[i + 2];
minX = qMin(minX, x);
minY = qMin(minY, y);
minZ = qMin(minZ, z);
maxX = qMax(maxX, x);
maxY = qMax(maxY, y);
maxZ = qMax(maxZ, z);
}
}
m_minBound = QVector3D(minX, minY, minZ); m_minBound = QVector3D(minX, minY, minZ);
m_maxBound = QVector3D(maxX, maxY, maxZ); m_maxBound = QVector3D(maxX, maxY, maxZ);
m_center = (m_minBound + m_maxBound) / 2.0f; m_center = (m_minBound + m_maxBound) / 2.0f;
@ -1539,6 +1577,43 @@ void PointCloudGLWidget::clearLineSegments()
update(); update();
} }
void PointCloudGLWidget::setBasicShapeSegments(const QVector<LineSegment>& segments)
{
m_basicShapeSegments = segments;
update();
}
void PointCloudGLWidget::appendBasicShapeSegments(const QVector<LineSegment>& segments)
{
if (segments.isEmpty()) {
return;
}
for (const LineSegment& segment : segments) {
m_basicShapeSegments.append(segment);
}
update();
}
void PointCloudGLWidget::appendBasicShapeSurfaces(const QVector<SurfaceQuad>& surfaces)
{
if (surfaces.isEmpty()) {
return;
}
for (const SurfaceQuad& surface : surfaces) {
m_basicShapeSurfaces.append(surface);
}
update();
}
void PointCloudGLWidget::clearBasicShapeSegments()
{
m_basicShapeSegments.clear();
m_basicShapeSurfaces.clear();
update();
}
void PointCloudGLWidget::addPosePoints(const QVector<PosePoint>& poses) void PointCloudGLWidget::addPosePoints(const QVector<PosePoint>& poses)
{ {
m_posePoints.append(poses); m_posePoints.append(poses);
@ -1553,15 +1628,49 @@ void PointCloudGLWidget::clearPosePoints()
void PointCloudGLWidget::drawLineSegments() void PointCloudGLWidget::drawLineSegments()
{ {
if (m_lineSegments.isEmpty()) { drawLineSegmentList(m_lineSegments);
}
void PointCloudGLWidget::drawBasicShapeSurfaces()
{
if (m_basicShapeSurfaces.isEmpty()) {
return;
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
glBegin(GL_QUADS);
for (const SurfaceQuad& surface : m_basicShapeSurfaces) {
glColor4f(surface.r, surface.g, surface.b, surface.a);
glVertex3f(surface.p1.x(), surface.p1.y(), surface.p1.z());
glVertex3f(surface.p2.x(), surface.p2.y(), surface.p2.z());
glVertex3f(surface.p3.x(), surface.p3.y(), surface.p3.z());
glVertex3f(surface.p4.x(), surface.p4.y(), surface.p4.z());
}
glEnd();
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
}
void PointCloudGLWidget::drawBasicShapeSegments()
{
drawLineSegmentList(m_basicShapeSegments);
}
void PointCloudGLWidget::drawLineSegmentList(const QVector<LineSegment>& segments)
{
if (segments.isEmpty()) {
return; return;
} }
// 按线宽分组绘制 // 按线宽分组绘制
// 收集所有不同的线宽值0 表示默认 2.0f // 收集所有不同的线宽值0 表示默认 2.0f
std::map<float, QVector<int>> widthGroups; std::map<float, QVector<int>> widthGroups;
for (int i = 0; i < m_lineSegments.size(); ++i) { for (int i = 0; i < segments.size(); ++i) {
float w = m_lineSegments[i].lineWidth > 0 ? m_lineSegments[i].lineWidth : 2.0f; float w = segments[i].lineWidth > 0 ? segments[i].lineWidth : 2.0f;
widthGroups[w].append(i); widthGroups[w].append(i);
} }
@ -1569,7 +1678,7 @@ void PointCloudGLWidget::drawLineSegments()
glLineWidth(group.first); glLineWidth(group.first);
glBegin(GL_LINES); glBegin(GL_LINES);
for (int idx : group.second) { for (int idx : group.second) {
const auto& seg = m_lineSegments[idx]; const auto& seg = segments[idx];
glColor3f(seg.r, seg.g, seg.b); glColor3f(seg.r, seg.g, seg.b);
glVertex3f(seg.x1, seg.y1, seg.z1); glVertex3f(seg.x1, seg.y1, seg.z1);
glVertex3f(seg.x2, seg.y2, seg.z2); glVertex3f(seg.x2, seg.y2, seg.z2);

View File

@ -1,3 +1,17 @@
# 1.2.0
- 增加基础图像的显示
# 1.1.6
- 增加批量点云txt->ply的保存
# 1.1.5
- 增加了ply点云保存
# 1.1.4
- 增加了矩阵转换
- 增加了生成点云
- 增加了视角范围(为了显示雷达点云)
# 1.1.3 # 1.1.3
- 修复崩溃 - 修复崩溃
- 修复选点序号不对 - 修复选点序号不对

View File

@ -1,9 +1,11 @@
#include <QApplication> #include <QApplication>
#include <QSurfaceFormat> #include <QSurfaceFormat>
#include <QIcon> #include <QIcon>
#include <QCommandLineParser>
#include <QTimer>
#include "CloudViewMainWindow.h" #include "CloudViewMainWindow.h"
#define APP_VERSION "1.1.6" #define APP_VERSION "1.2.0"
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
@ -22,6 +24,59 @@ int main(int argc, char* argv[])
app.setOrganizationName("CloudView"); app.setOrganizationName("CloudView");
app.setApplicationVersion(APP_VERSION); app.setApplicationVersion(APP_VERSION);
QCommandLineParser parser;
parser.setApplicationDescription("CloudView point cloud and shape viewer");
parser.addHelpOption();
parser.addVersionOption();
QCommandLineOption cloudOption(
QStringList() << "cloud" << "point-cloud",
"Load a point cloud or segment file. Positional files are treated the same way.",
"file");
QCommandLineOption bboxOption(
QStringList() << "bbox",
"Load a bounding-box JSON file.",
"file");
QCommandLineOption circleOption(
QStringList() << "circle",
"Load a circle data file. Row format: {x,y,z}-{r,p,y}-{radius}.",
"file");
QCommandLineOption rectangleOption(
QStringList() << "rectangle",
"Load a rectangle data file. Row format: {x,y,z}-{r,p,y}-{width,height}.",
"file");
QCommandLineOption surfaceOption(
QStringList() << "surface",
"Load a surface data file. Row format: {x,y,z}-{length,width} or {x,y,z}-{r,p,y}-{length,width}.",
"file");
QCommandLineOption triangleOption(
QStringList() << "triangle",
"Load a triangle data file. Row format: {x,y,z}-{x,y,z}-{x,y,z}.",
"file");
QCommandLineOption sequenceOption(
QStringList() << "sequence" << "sequential",
"Display startup files one at a time. Closing the window loads the next file.");
parser.addOption(cloudOption);
parser.addOption(bboxOption);
parser.addOption(circleOption);
parser.addOption(rectangleOption);
parser.addOption(surfaceOption);
parser.addOption(triangleOption);
parser.addOption(sequenceOption);
parser.addPositionalArgument("files", "Point cloud or segment files to load.");
parser.process(app);
CloudViewMainWindow::StartupFiles startupFiles;
startupFiles.displayFiles = parser.values(cloudOption);
startupFiles.displayFiles.append(parser.positionalArguments());
startupFiles.bboxFiles = parser.values(bboxOption);
startupFiles.circleFiles = parser.values(circleOption);
startupFiles.rectangleFiles = parser.values(rectangleOption);
startupFiles.surfaceFiles = parser.values(surfaceOption);
startupFiles.triangleFiles = parser.values(triangleOption);
startupFiles.sequential = parser.isSet(sequenceOption);
// 设置应用程序图标 // 设置应用程序图标
app.setWindowIcon(QIcon(":/logo.png")); app.setWindowIcon(QIcon(":/logo.png"));
@ -31,5 +86,9 @@ int main(int argc, char* argv[])
mainWindow.resize(1280, 720); mainWindow.resize(1280, 720);
mainWindow.show(); mainWindow.show();
QTimer::singleShot(0, &mainWindow, [&mainWindow, startupFiles]() {
mainWindow.loadStartupFiles(startupFiles);
});
return app.exec(); return app.exec();
} }