增加启动显示参数
This commit is contained in:
parent
c1cfe7e560
commit
6a6dd55bec
@ -1,6 +1,7 @@
|
||||
#include "LaserDataLoader.h"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
@ -128,6 +129,34 @@ bool ReadDatLineRecord(std::ifstream& inputFile, int recordSize, DatLineScan3DDa
|
||||
std::memcpy(&lineData.lineStepping, buffer + 8, sizeof(lineData.lineStepping));
|
||||
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()
|
||||
@ -465,11 +494,11 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
|
||||
}
|
||||
|
||||
// 写入文件头
|
||||
sw << "LineNum:" << lineNum << std::endl;
|
||||
sw << "DataType: 0" << std::endl;
|
||||
sw << "ScanSpeed:" << scanSpeed << std::endl;
|
||||
sw << "PointAdjust: 1" << std::endl;
|
||||
sw << "MaxTimeStamp:" << maxTimeStamp << "_" << clockPerSecond << std::endl;
|
||||
sw << "LineNum:" << lineNum << '\n';
|
||||
sw << "DataType: 0" << '\n';
|
||||
sw << "ScanSpeed:" << scanSpeed << '\n';
|
||||
sw << "PointAdjust: 1" << '\n';
|
||||
sw << "MaxTimeStamp:" << maxTimeStamp << "_" << clockPerSecond << '\n';
|
||||
|
||||
// 写入每条扫描线数据
|
||||
for (const auto& linePair : laserLines) {
|
||||
@ -477,20 +506,29 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
|
||||
EVzResultDataType dataType = linePair.first;
|
||||
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) {
|
||||
// 写入XYZ格式数据
|
||||
const SVzNL3DPosition* points = static_cast<const SVzNL3DPosition*>(lineData.p3DPoint);
|
||||
const SVzNL2DPosition* points2D = static_cast<const SVzNL2DPosition*>(lineData.p2DPoint);
|
||||
for (int i = 0; i < lineData.nPointCount; i++) {
|
||||
constexpr int BATCH = 256;
|
||||
constexpr int PER_POINT = 128;
|
||||
for (int base = 0; base < lineData.nPointCount; base += BATCH) {
|
||||
int end = (base + BATCH < lineData.nPointCount) ? (base + BATCH) : lineData.nPointCount;
|
||||
std::string buffer;
|
||||
buffer.reserve(BATCH * PER_POINT);
|
||||
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);
|
||||
sw << "{ " << std::fixed << std::setprecision(6) << x << "," << y << "," << z << " }-";
|
||||
sw << "{ " << points2D[i].ptLeft2D.x << "," << points2D[i].ptLeft2D.y << " }-";
|
||||
sw << "{ " << points2D[i].ptRight2D.x << "," << points2D[i].ptRight2D.y << " }" << std::endl;
|
||||
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) {
|
||||
// LiDAR 点云:snprintf 批量写入,每 256 点 flush
|
||||
@ -515,7 +553,13 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
|
||||
// 写入RGBD格式数据
|
||||
const SVzNLPointXYZRGBA* points = static_cast<const SVzNLPointXYZRGBA*>(lineData.p3DPoint);
|
||||
const SVzNL2DLRPoint* points2D = static_cast<const SVzNL2DLRPoint*>(lineData.p2DPoint);
|
||||
for (int i = 0; i < lineData.nPointCount; i++) {
|
||||
constexpr int BATCH = 256;
|
||||
constexpr int PER_POINT = 160;
|
||||
for (int base = 0; base < lineData.nPointCount; base += BATCH) {
|
||||
int end = (base + BATCH < lineData.nPointCount) ? (base + BATCH) : lineData.nPointCount;
|
||||
std::string buffer;
|
||||
buffer.reserve(BATCH * PER_POINT);
|
||||
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);
|
||||
@ -523,14 +567,13 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
|
||||
int g = (points[i].nRGB >> 8) & 0xFF;
|
||||
int b = points[i].nRGB & 0xFF;
|
||||
|
||||
sw << "{" << std::fixed << std::setprecision(6) << x << ","
|
||||
<< std::fixed << std::setprecision(6) << y << ","
|
||||
<< std::fixed << std::setprecision(6) << z << ","
|
||||
<< std::fixed << std::setprecision(6) << b * 1.0f / 255 << ","
|
||||
<< std::fixed << std::setprecision(6) << g * 1.0f / 255 << ","
|
||||
<< std::fixed << std::setprecision(6) << r * 1.0f / 255 << "}-";
|
||||
sw << "{ " << points2D[i].sLeft.x << "," << points2D[i].sLeft.y << " }-";
|
||||
sw << "{ " << points2D[i].sRight.x << "," << points2D[i].sRight.y << " }" << std::endl;
|
||||
AppendFormattedLine(buffer, "{%.6f,%.6f,%.6f,%.6f,%.6f,%.6f}-{ %d,%d }-{ %d,%d }\n",
|
||||
x, y, z,
|
||||
b * 1.0f / 255, g * 1.0f / 255, r * 1.0f / 255,
|
||||
points2D[i].sLeft.x, points2D[i].sLeft.y,
|
||||
points2D[i].sRight.x, points2D[i].sRight.y);
|
||||
}
|
||||
sw.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -570,11 +613,11 @@ int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vecto
|
||||
}
|
||||
|
||||
// 写入文件头
|
||||
sw << "LineNum:" << xyzData.size() << std::endl;
|
||||
sw << "DataType: 0" << std::endl;
|
||||
sw << "ScanSpeed:" << 0 << std::endl;
|
||||
sw << "PointAdjust: 1" << std::endl;
|
||||
sw << "MaxTimeStamp:" << 0 << "_" << 0 << std::endl;
|
||||
sw << "LineNum:" << xyzData.size() << '\n';
|
||||
sw << "DataType: 0" << '\n';
|
||||
sw << "ScanSpeed:" << 0 << '\n';
|
||||
sw << "PointAdjust: 1" << '\n';
|
||||
sw << "MaxTimeStamp:" << 0 << "_" << 0 << '\n';
|
||||
|
||||
const size_t totalLines = xyzData.size();
|
||||
int index = 0;
|
||||
@ -587,19 +630,25 @@ int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vecto
|
||||
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格式数据
|
||||
float x = static_cast<float>(point.pt3D.x);
|
||||
float y = static_cast<float>(point.pt3D.y);
|
||||
float z = static_cast<float>(point.pt3D.z);
|
||||
sw << "{ "
|
||||
<< std::fixed << std::setprecision(6) << x << ", "
|
||||
<< std::fixed << std::setprecision(6) << y << ", "
|
||||
<< std::fixed << std::setprecision(6) << z << " } - ";
|
||||
sw << "{ 0, 0 } - { 0, 0 }" << std::endl;
|
||||
AppendFormattedLine(buffer, "{ %.6f, %.6f, %.6f } - { 0, 0 } - { 0, 0 }\n",
|
||||
x, y, z);
|
||||
}
|
||||
sw.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||
}
|
||||
|
||||
++index;
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDialog>
|
||||
#include <QStringList>
|
||||
#include <memory>
|
||||
|
||||
#include "PointCloudGLWidget.h"
|
||||
@ -26,6 +27,7 @@
|
||||
|
||||
class QTextEdit;
|
||||
class QTableWidget;
|
||||
class QCloseEvent;
|
||||
|
||||
/**
|
||||
* @brief 点云查看器主窗口
|
||||
@ -36,11 +38,52 @@ class CloudViewMainWindow : public QMainWindow
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct StartupFiles
|
||||
{
|
||||
QStringList displayFiles;
|
||||
QStringList bboxFiles;
|
||||
QStringList circleFiles;
|
||||
QStringList rectangleFiles;
|
||||
QStringList surfaceFiles;
|
||||
QStringList triangleFiles;
|
||||
bool sequential = false;
|
||||
};
|
||||
|
||||
explicit CloudViewMainWindow(QWidget* parent = nullptr);
|
||||
~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:
|
||||
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:
|
||||
/**
|
||||
@ -169,6 +212,10 @@ private slots:
|
||||
void onSavePlyPcdCloud(); // 保存 ply/pcd 点云
|
||||
void onBatchConvertTxtToPly(); // 批量转换 txt -> ply
|
||||
void onConvertEulerMatrix();
|
||||
void onShowCircleShape();
|
||||
void onShowRectangleShape();
|
||||
void onShowSurfaceShape();
|
||||
void onShowTriangleShape();
|
||||
|
||||
/**
|
||||
* @brief 姿态补偿计算
|
||||
@ -260,6 +307,8 @@ private:
|
||||
bool loadBoundingBoxFile(const QString& fileName);
|
||||
void applyTransformToAllClouds(const QMatrix4x4& matrix);
|
||||
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;
|
||||
@ -269,7 +318,6 @@ private:
|
||||
|
||||
// 文件操作控件
|
||||
QPushButton* m_btnOpenFile;
|
||||
QPushButton* m_btnOpenPose;
|
||||
QPushButton* m_btnOpenBBox;
|
||||
QPushButton* m_btnClearAll;
|
||||
|
||||
@ -354,6 +402,11 @@ private:
|
||||
// 悬浮缩放按钮
|
||||
QToolButton* m_btnZoomIn;
|
||||
QToolButton* m_btnZoomOut;
|
||||
|
||||
// 启动参数序列显示
|
||||
QVector<StartupItem> m_startupSequence;
|
||||
int m_startupSequenceIndex;
|
||||
bool m_startupSequentialMode;
|
||||
};
|
||||
|
||||
#endif // CLOUD_VIEW_MAIN_WINDOW_H
|
||||
|
||||
@ -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) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 姿态点数据
|
||||
*
|
||||
@ -137,6 +157,7 @@ public:
|
||||
void setPointCloudColor(PointCloudColor color);
|
||||
void setPointSize(float size);
|
||||
void resetView();
|
||||
void fitToContent();
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
/**
|
||||
@ -269,6 +290,26 @@ public:
|
||||
*/
|
||||
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 添加姿态点
|
||||
*/
|
||||
@ -398,6 +439,9 @@ private:
|
||||
void drawAxis();
|
||||
void drawSelectedLine(); // 绘制选中的线
|
||||
void drawLineSegments(); // 绘制线段
|
||||
void drawBasicShapeSurfaces(); // 绘制基础图形面片
|
||||
void drawBasicShapeSegments(); // 绘制基础图形
|
||||
void drawLineSegmentList(const QVector<LineSegment>& segments);
|
||||
void drawPosePoints(); // 绘制姿态点
|
||||
void drawAxisLabels(); // 绘制坐标轴 XYZ 标注
|
||||
void uploadToVBO(PointCloudData& data); // 上传数据到 VBO
|
||||
@ -443,6 +487,8 @@ private:
|
||||
|
||||
// 线段和姿态点数据
|
||||
QVector<LineSegment> m_lineSegments;
|
||||
QVector<LineSegment> m_basicShapeSegments;
|
||||
QVector<SurfaceQuad> m_basicShapeSurfaces;
|
||||
QVector<PosePoint> m_posePoints;
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -228,6 +228,8 @@ void PointCloudGLWidget::paintGL()
|
||||
drawMeasurementLine();
|
||||
drawSelectedLine();
|
||||
drawLineSegments();
|
||||
drawBasicShapeSurfaces();
|
||||
drawBasicShapeSegments();
|
||||
drawPosePoints();
|
||||
|
||||
// 最后绘制坐标系指示器(覆盖在所有内容之上)
|
||||
@ -450,6 +452,8 @@ void PointCloudGLWidget::clearPointClouds()
|
||||
m_selectedPoints.clear();
|
||||
m_selectedLine = SelectedLineInfo();
|
||||
m_lineSegments.clear();
|
||||
m_basicShapeSegments.clear();
|
||||
m_basicShapeSurfaces.clear();
|
||||
m_posePoints.clear();
|
||||
m_minBound = QVector3D(-50, -50, -50);
|
||||
m_maxBound = QVector3D(50, 50, 50);
|
||||
@ -494,6 +498,12 @@ void PointCloudGLWidget::resetView()
|
||||
update();
|
||||
}
|
||||
|
||||
void PointCloudGLWidget::fitToContent()
|
||||
{
|
||||
computeBoundingBox();
|
||||
resetView();
|
||||
}
|
||||
|
||||
void PointCloudGLWidget::zoomIn()
|
||||
{
|
||||
m_distance *= 0.9f;
|
||||
@ -847,31 +857,59 @@ size_t PointCloudGLWidget::getAllCloudsByLines(std::vector<std::vector<SVzNL3DPo
|
||||
|
||||
void PointCloudGLWidget::computeBoundingBox()
|
||||
{
|
||||
if (m_pointClouds.empty()) {
|
||||
m_minBound = QVector3D(-50, -50, -50);
|
||||
m_maxBound = QVector3D(50, 50, 50);
|
||||
m_center = QVector3D(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX;
|
||||
float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX;
|
||||
bool hasContent = false;
|
||||
|
||||
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];
|
||||
|
||||
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_maxBound = QVector3D(50, 50, 50);
|
||||
m_center = QVector3D(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
m_minBound = QVector3D(minX, minY, minZ);
|
||||
m_maxBound = QVector3D(maxX, maxY, maxZ);
|
||||
m_center = (m_minBound + m_maxBound) / 2.0f;
|
||||
@ -1539,6 +1577,43 @@ void PointCloudGLWidget::clearLineSegments()
|
||||
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)
|
||||
{
|
||||
m_posePoints.append(poses);
|
||||
@ -1553,15 +1628,49 @@ void PointCloudGLWidget::clearPosePoints()
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 按线宽分组绘制
|
||||
// 收集所有不同的线宽值(0 表示默认 2.0f)
|
||||
std::map<float, QVector<int>> widthGroups;
|
||||
for (int i = 0; i < m_lineSegments.size(); ++i) {
|
||||
float w = m_lineSegments[i].lineWidth > 0 ? m_lineSegments[i].lineWidth : 2.0f;
|
||||
for (int i = 0; i < segments.size(); ++i) {
|
||||
float w = segments[i].lineWidth > 0 ? segments[i].lineWidth : 2.0f;
|
||||
widthGroups[w].append(i);
|
||||
}
|
||||
|
||||
@ -1569,7 +1678,7 @@ void PointCloudGLWidget::drawLineSegments()
|
||||
glLineWidth(group.first);
|
||||
glBegin(GL_LINES);
|
||||
for (int idx : group.second) {
|
||||
const auto& seg = m_lineSegments[idx];
|
||||
const auto& seg = segments[idx];
|
||||
glColor3f(seg.r, seg.g, seg.b);
|
||||
glVertex3f(seg.x1, seg.y1, seg.z1);
|
||||
glVertex3f(seg.x2, seg.y2, seg.z2);
|
||||
|
||||
@ -1,3 +1,17 @@
|
||||
# 1.2.0
|
||||
- 增加基础图像的显示
|
||||
|
||||
# 1.1.6
|
||||
- 增加批量点云txt->ply的保存
|
||||
|
||||
# 1.1.5
|
||||
- 增加了ply点云保存
|
||||
|
||||
# 1.1.4
|
||||
- 增加了矩阵转换
|
||||
- 增加了生成点云
|
||||
- 增加了视角范围(为了显示雷达点云)
|
||||
|
||||
# 1.1.3
|
||||
- 修复崩溃
|
||||
- 修复选点序号不对
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
#include <QApplication>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QIcon>
|
||||
#include <QCommandLineParser>
|
||||
#include <QTimer>
|
||||
#include "CloudViewMainWindow.h"
|
||||
|
||||
#define APP_VERSION "1.1.6"
|
||||
#define APP_VERSION "1.2.0"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
@ -22,6 +24,59 @@ int main(int argc, char* argv[])
|
||||
app.setOrganizationName("CloudView");
|
||||
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"));
|
||||
|
||||
@ -31,5 +86,9 @@ int main(int argc, char* argv[])
|
||||
mainWindow.resize(1280, 720);
|
||||
mainWindow.show();
|
||||
|
||||
QTimer::singleShot(0, &mainWindow, [&mainWindow, startupFiles]() {
|
||||
mainWindow.loadStartupFiles(startupFiles);
|
||||
});
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user