CloudView优化控制

This commit is contained in:
yiyi 2026-03-01 18:14:19 +08:00
parent eb0cbdfc54
commit c812176d84
4 changed files with 339 additions and 59 deletions

View File

@ -84,6 +84,16 @@ private slots:
*/ */
void onLineSelected(const SelectedLineInfo& line); void onLineSelected(const SelectedLineInfo& line);
/**
* @brief 1
*/
void onPoint1CoordChanged();
/**
* @brief 2
*/
void onPoint2CoordChanged();
/** /**
* @brief * @brief
*/ */
@ -255,6 +265,16 @@ private:
QLabel* m_lblPoint2; QLabel* m_lblPoint2;
QLabel* m_lblDistance; QLabel* m_lblDistance;
// 点1坐标编辑控件
QLineEdit* m_editPoint1X;
QLineEdit* m_editPoint1Y;
QLineEdit* m_editPoint1Z;
// 点2坐标编辑控件
QLineEdit* m_editPoint2X;
QLineEdit* m_editPoint2Y;
QLineEdit* m_editPoint2Z;
// 点1姿态输入控件 // 点1姿态输入控件
QLineEdit* m_editRx1; QLineEdit* m_editRx1;
QLineEdit* m_editRy1; QLineEdit* m_editRy1;

View File

@ -6,6 +6,7 @@
#include <QOpenGLBuffer> #include <QOpenGLBuffer>
#include <QMatrix4x4> #include <QMatrix4x4>
#include <QVector3D> #include <QVector3D>
#include <QQuaternion>
#include <QMouseEvent> #include <QMouseEvent>
#include <QWheelEvent> #include <QWheelEvent>
#include <vector> #include <vector>
@ -150,6 +151,15 @@ public:
void clearSelectedLine(); void clearSelectedLine();
float calculateDistance(const SelectedPointInfo& p1, const SelectedPointInfo& p2); float calculateDistance(const SelectedPointInfo& p1, const SelectedPointInfo& p2);
/**
* @brief
* @param index 01
* @param x X坐标
* @param y Y坐标
* @param z Z坐标
*/
void updateSelectedPointCoord(int index, float x, float y, float z);
/** /**
* @brief 线线 * @brief 线线
*/ */
@ -372,6 +382,7 @@ private:
float m_rotationX; float m_rotationX;
float m_rotationY; float m_rotationY;
float m_rotationZ; float m_rotationZ;
QQuaternion m_rotation; // 使用四元数存储旋转状态
QVector3D m_center; QVector3D m_center;
QVector3D m_pan; QVector3D m_pan;
QVector3D m_minBound; QVector3D m_minBound;

View File

@ -163,13 +163,15 @@ QGroupBox* CloudViewMainWindow::createViewGroup()
float rotZ; float rotZ;
}; };
// 坐标系定义X向右Y向下Z朝后
// 正视图看XY平面Z轴朝后
ViewPreset presets[] = { ViewPreset presets[] = {
{"正视", 0.0f, 0.0f, 0.0f}, {"正视", 0.0f, 0.0f, 0.0f}, // 看XY平面Z朝后
{"后视", 0.0f, 180.0f, 0.0f}, {"后视", 0.0f, 180.0f, 0.0f}, // 看XY平面Z朝前
{"左侧", 0.0f, -90.0f, 0.0f}, {"左侧", 0.0f, 90.0f, 0.0f}, // 看YZ平面X朝前
{"右侧", 0.0f, 90.0f, 0.0f}, {"右侧", 0.0f, -90.0f, 0.0f}, // 看YZ平面X朝后
{"俯视", -90.0f, 0.0f, 0.0f}, {"俯视", 90.0f, 0.0f, 0.0f}, // 看XZ平面Y朝后
{"仰视", 90.0f, 0.0f, 0.0f}, {"仰视", -90.0f, 0.0f, 0.0f}, // 看XZ平面Y朝前
}; };
QHBoxLayout* btnLayout = new QHBoxLayout(); QHBoxLayout* btnLayout = new QHBoxLayout();
@ -230,7 +232,7 @@ QGroupBox* CloudViewMainWindow::createViewGroup()
QPushButton* btnApply = new QPushButton("应用", group); QPushButton* btnApply = new QPushButton("应用", group);
btnApply->setMaximumWidth(50); btnApply->setMaximumWidth(50);
btnApply->setMaximumHeight(24); btnApply->setMaximumHeight(24);
connect(btnApply, &QPushButton::clicked, this, [this]() { auto applyAngles = [this]() {
bool okX, okY, okZ; bool okX, okY, okZ;
float rotX = m_editRotX->text().toFloat(&okX); float rotX = m_editRotX->text().toFloat(&okX);
float rotY = m_editRotY->text().toFloat(&okY); float rotY = m_editRotY->text().toFloat(&okY);
@ -238,9 +240,15 @@ QGroupBox* CloudViewMainWindow::createViewGroup()
if (okX && okY && okZ) { if (okX && okY && okZ) {
m_glWidget->setViewAngles(rotX, rotY, rotZ); m_glWidget->setViewAngles(rotX, rotY, rotZ);
} }
}); };
connect(btnApply, &QPushButton::clicked, this, applyAngles);
angleGrid->addWidget(btnApply, 0, 6); angleGrid->addWidget(btnApply, 0, 6);
// 连接回车信号到应用功能
connect(m_editRotX, &QLineEdit::returnPressed, this, applyAngles);
connect(m_editRotY, &QLineEdit::returnPressed, this, applyAngles);
connect(m_editRotZ, &QLineEdit::returnPressed, this, applyAngles);
mainLayout->addLayout(angleGrid); mainLayout->addLayout(angleGrid);
return group; return group;
@ -329,6 +337,14 @@ QWidget* CloudViewMainWindow::createLinePage()
btnLayout->addWidget(m_btnClearLine2); btnLayout->addWidget(m_btnClearLine2);
inputLayout->addLayout(btnLayout); inputLayout->addLayout(btnLayout);
// 连接输入线段坐标框的回车信号
connect(m_editLineX1, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowInputLine);
connect(m_editLineY1, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowInputLine);
connect(m_editLineZ1, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowInputLine);
connect(m_editLineX2, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowInputLine);
connect(m_editLineY2, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowInputLine);
connect(m_editLineZ2, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowInputLine);
layout->addWidget(inputLineGroup); layout->addWidget(inputLineGroup);
layout->addStretch(); layout->addStretch();
return page; return page;
@ -356,6 +372,12 @@ QGroupBox* CloudViewMainWindow::createMeasureGroup()
m_lblPoint1->setText("点1: --"); m_lblPoint1->setText("点1: --");
m_lblPoint2->setText("点2: --"); m_lblPoint2->setText("点2: --");
m_lblDistance->setText("--"); m_lblDistance->setText("--");
m_editPoint1X->setText("--");
m_editPoint1Y->setText("--");
m_editPoint1Z->setText("--");
m_editPoint2X->setText("--");
m_editPoint2Y->setText("--");
m_editPoint2Z->setText("--");
}); });
layout->addWidget(m_cbMeasureDistance); layout->addWidget(m_cbMeasureDistance);
@ -377,6 +399,34 @@ QGroupBox* CloudViewMainWindow::createMeasureGroup()
m_lblPoint1->setStyleSheet("font-weight: bold; font-size: 11px;"); m_lblPoint1->setStyleSheet("font-weight: bold; font-size: 11px;");
layout->addWidget(m_lblPoint1); layout->addWidget(m_lblPoint1);
// 点1坐标编辑可修改
QHBoxLayout* coord1Layout = new QHBoxLayout();
coord1Layout->setSpacing(5);
coord1Layout->addWidget(new QLabel("X:", group));
m_editPoint1X = new QLineEdit("--", group);
m_editPoint1X->setMaximumWidth(70);
m_editPoint1X->setMaximumHeight(24);
coord1Layout->addWidget(m_editPoint1X);
coord1Layout->addWidget(new QLabel("Y:", group));
m_editPoint1Y = new QLineEdit("--", group);
m_editPoint1Y->setMaximumWidth(70);
m_editPoint1Y->setMaximumHeight(24);
coord1Layout->addWidget(m_editPoint1Y);
coord1Layout->addWidget(new QLabel("Z:", group));
m_editPoint1Z = new QLineEdit("--", group);
m_editPoint1Z->setMaximumWidth(70);
m_editPoint1Z->setMaximumHeight(24);
coord1Layout->addWidget(m_editPoint1Z);
coord1Layout->addStretch();
layout->addLayout(coord1Layout);
// 连接回车信号
connect(m_editPoint1X, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onPoint1CoordChanged);
connect(m_editPoint1Y, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onPoint1CoordChanged);
connect(m_editPoint1Z, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onPoint1CoordChanged);
// 点1姿态输入紧凑布局 // 点1姿态输入紧凑布局
QHBoxLayout* pose1Layout = new QHBoxLayout(); QHBoxLayout* pose1Layout = new QHBoxLayout();
pose1Layout->setSpacing(5); pose1Layout->setSpacing(5);
@ -402,6 +452,11 @@ QGroupBox* CloudViewMainWindow::createMeasureGroup()
connect(m_btnShowPose1, &QPushButton::clicked, this, &CloudViewMainWindow::onShowPose1); connect(m_btnShowPose1, &QPushButton::clicked, this, &CloudViewMainWindow::onShowPose1);
layout->addWidget(m_btnShowPose1); layout->addWidget(m_btnShowPose1);
// 连接姿态输入框的回车信号
connect(m_editRx1, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowPose1);
connect(m_editRy1, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowPose1);
connect(m_editRz1, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowPose1);
// 分隔线 // 分隔线
QFrame* line2 = new QFrame(group); QFrame* line2 = new QFrame(group);
line2->setFrameShape(QFrame::HLine); line2->setFrameShape(QFrame::HLine);
@ -414,6 +469,34 @@ QGroupBox* CloudViewMainWindow::createMeasureGroup()
m_lblPoint2->setStyleSheet("font-weight: bold; font-size: 11px;"); m_lblPoint2->setStyleSheet("font-weight: bold; font-size: 11px;");
layout->addWidget(m_lblPoint2); layout->addWidget(m_lblPoint2);
// 点2坐标编辑可修改
QHBoxLayout* coord2Layout = new QHBoxLayout();
coord2Layout->setSpacing(5);
coord2Layout->addWidget(new QLabel("X:", group));
m_editPoint2X = new QLineEdit("--", group);
m_editPoint2X->setMaximumWidth(70);
m_editPoint2X->setMaximumHeight(24);
coord2Layout->addWidget(m_editPoint2X);
coord2Layout->addWidget(new QLabel("Y:", group));
m_editPoint2Y = new QLineEdit("--", group);
m_editPoint2Y->setMaximumWidth(70);
m_editPoint2Y->setMaximumHeight(24);
coord2Layout->addWidget(m_editPoint2Y);
coord2Layout->addWidget(new QLabel("Z:", group));
m_editPoint2Z = new QLineEdit("--", group);
m_editPoint2Z->setMaximumWidth(70);
m_editPoint2Z->setMaximumHeight(24);
coord2Layout->addWidget(m_editPoint2Z);
coord2Layout->addStretch();
layout->addLayout(coord2Layout);
// 连接回车信号
connect(m_editPoint2X, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onPoint2CoordChanged);
connect(m_editPoint2Y, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onPoint2CoordChanged);
connect(m_editPoint2Z, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onPoint2CoordChanged);
// 点2姿态输入紧凑布局 // 点2姿态输入紧凑布局
QHBoxLayout* pose2Layout = new QHBoxLayout(); QHBoxLayout* pose2Layout = new QHBoxLayout();
pose2Layout->setSpacing(5); pose2Layout->setSpacing(5);
@ -439,6 +522,11 @@ QGroupBox* CloudViewMainWindow::createMeasureGroup()
connect(m_btnShowPose2, &QPushButton::clicked, this, &CloudViewMainWindow::onShowPose2); connect(m_btnShowPose2, &QPushButton::clicked, this, &CloudViewMainWindow::onShowPose2);
layout->addWidget(m_btnShowPose2); layout->addWidget(m_btnShowPose2);
// 连接姿态输入框的回车信号
connect(m_editRx2, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowPose2);
connect(m_editRy2, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowPose2);
connect(m_editRz2, &QLineEdit::returnPressed, this, &CloudViewMainWindow::onShowPose2);
// 分隔线 // 分隔线
QFrame* line3 = new QFrame(group); QFrame* line3 = new QFrame(group);
line3->setFrameShape(QFrame::HLine); line3->setFrameShape(QFrame::HLine);
@ -838,6 +926,12 @@ void CloudViewMainWindow::onClearAll()
m_lblPoint1->setText("点1: --"); m_lblPoint1->setText("点1: --");
m_lblPoint2->setText("点2: --"); m_lblPoint2->setText("点2: --");
m_lblDistance->setText("--"); m_lblDistance->setText("--");
m_editPoint1X->setText("--");
m_editPoint1Y->setText("--");
m_editPoint1Z->setText("--");
m_editPoint2X->setText("--");
m_editPoint2Y->setText("--");
m_editPoint2Z->setText("--");
// 清除选线信息 // 清除选线信息
m_lblLineIndex->setText("--"); m_lblLineIndex->setText("--");
@ -859,6 +953,12 @@ void CloudViewMainWindow::onClearSelectedPoints()
m_lblPoint1->setText("点1: --"); m_lblPoint1->setText("点1: --");
m_lblPoint2->setText("点2: --"); m_lblPoint2->setText("点2: --");
m_lblDistance->setText("--"); m_lblDistance->setText("--");
m_editPoint1X->setText("--");
m_editPoint1Y->setText("--");
m_editPoint1Z->setText("--");
m_editPoint2X->setText("--");
m_editPoint2Y->setText("--");
m_editPoint2Z->setText("--");
statusBar()->showMessage("已清除选中的点"); statusBar()->showMessage("已清除选中的点");
} }
@ -901,41 +1001,45 @@ void CloudViewMainWindow::updateSelectedPointsDisplay()
if (selectedPoints.size() >= 1 && selectedPoints[0].valid) { if (selectedPoints.size() >= 1 && selectedPoints[0].valid) {
QString text; QString text;
if (selectedPoints[0].lineIndex >= 0) { if (selectedPoints[0].lineIndex >= 0) {
text = QString("点1: 线号:%1 点序:%2 x:%3 y:%4 z:%5") text = QString("点1: 线号:%1 点序:%2")
.arg(selectedPoints[0].lineIndex) .arg(selectedPoints[0].lineIndex)
.arg(selectedPoints[0].pointIndexInLine) .arg(selectedPoints[0].pointIndexInLine);
.arg(selectedPoints[0].x, 0, 'f', 3)
.arg(selectedPoints[0].y, 0, 'f', 3)
.arg(selectedPoints[0].z, 0, 'f', 3);
} else { } else {
text = QString("点1: x:%1 y:%2 z:%3") text = QString("点1:");
.arg(selectedPoints[0].x, 0, 'f', 3)
.arg(selectedPoints[0].y, 0, 'f', 3)
.arg(selectedPoints[0].z, 0, 'f', 3);
} }
m_lblPoint1->setText(text); m_lblPoint1->setText(text);
// 更新坐标编辑框
m_editPoint1X->setText(QString::number(selectedPoints[0].x, 'f', 3));
m_editPoint1Y->setText(QString::number(selectedPoints[0].y, 'f', 3));
m_editPoint1Z->setText(QString::number(selectedPoints[0].z, 'f', 3));
} else { } else {
m_lblPoint1->setText("点1: --"); m_lblPoint1->setText("点1: --");
m_editPoint1X->setText("--");
m_editPoint1Y->setText("--");
m_editPoint1Z->setText("--");
} }
if (selectedPoints.size() >= 2 && selectedPoints[1].valid) { if (selectedPoints.size() >= 2 && selectedPoints[1].valid) {
QString text; QString text;
if (selectedPoints[1].lineIndex >= 0) { if (selectedPoints[1].lineIndex >= 0) {
text = QString("点2: 线号:%1 点序:%2 x:%3 y:%4 z:%5") text = QString("点2: 线号:%1 点序:%2")
.arg(selectedPoints[1].lineIndex) .arg(selectedPoints[1].lineIndex)
.arg(selectedPoints[1].pointIndexInLine) .arg(selectedPoints[1].pointIndexInLine);
.arg(selectedPoints[1].x, 0, 'f', 3)
.arg(selectedPoints[1].y, 0, 'f', 3)
.arg(selectedPoints[1].z, 0, 'f', 3);
} else { } else {
text = QString("点2: x:%1 y:%2 z:%3") text = QString("点2:");
.arg(selectedPoints[1].x, 0, 'f', 3)
.arg(selectedPoints[1].y, 0, 'f', 3)
.arg(selectedPoints[1].z, 0, 'f', 3);
} }
m_lblPoint2->setText(text); m_lblPoint2->setText(text);
// 更新坐标编辑框
m_editPoint2X->setText(QString::number(selectedPoints[1].x, 'f', 3));
m_editPoint2Y->setText(QString::number(selectedPoints[1].y, 'f', 3));
m_editPoint2Z->setText(QString::number(selectedPoints[1].z, 'f', 3));
} else { } else {
m_lblPoint2->setText("点2: --"); m_lblPoint2->setText("点2: --");
m_editPoint2X->setText("--");
m_editPoint2Y->setText("--");
m_editPoint2Z->setText("--");
} }
} }
@ -1273,9 +1377,9 @@ void CloudViewMainWindow::onShowPose1()
// 添加到显示 // 添加到显示
m_glWidget->addPosePoints(poses); m_glWidget->addPosePoints(poses);
statusBar()->showMessage(QString("已显示点1姿态 (%.3f, %.3f, %.3f) 旋转(%.1f°, %.1f°, %.1f°)") statusBar()->showMessage(QString("已显示点1姿态 (%1, %2, %3) 旋转(%4°, %5°, %6°)")
.arg(point.x).arg(point.y).arg(point.z) .arg(point.x, 0, 'f', 3).arg(point.y, 0, 'f', 3).arg(point.z, 0, 'f', 3)
.arg(rx).arg(ry).arg(rz)); .arg(rx, 0, 'f', 1).arg(ry, 0, 'f', 1).arg(rz, 0, 'f', 1));
LOG_INFO("[CloudView] Show pose1 at (%.3f, %.3f, %.3f) with rotation (%.1f, %.1f, %.1f)\n", LOG_INFO("[CloudView] Show pose1 at (%.3f, %.3f, %.3f) with rotation (%.1f, %.1f, %.1f)\n",
point.x, point.y, point.z, rx, ry, rz); point.x, point.y, point.z, rx, ry, rz);
@ -1338,9 +1442,9 @@ void CloudViewMainWindow::onShowPose2()
// 添加到显示 // 添加到显示
m_glWidget->addPosePoints(poses); m_glWidget->addPosePoints(poses);
statusBar()->showMessage(QString("已显示点2姿态 (%.3f, %.3f, %.3f) 旋转(%.1f°, %.1f°, %.1f°)") statusBar()->showMessage(QString("已显示点2姿态 (%1, %2, %3) 旋转(%4°, %5°, %6°)")
.arg(point.x).arg(point.y).arg(point.z) .arg(point.x, 0, 'f', 3).arg(point.y, 0, 'f', 3).arg(point.z, 0, 'f', 3)
.arg(rx).arg(ry).arg(rz)); .arg(rx, 0, 'f', 1).arg(ry, 0, 'f', 1).arg(rz, 0, 'f', 1));
LOG_INFO("[CloudView] Show pose2 at (%.3f, %.3f, %.3f) with rotation (%.1f, %.1f, %.1f)\n", LOG_INFO("[CloudView] Show pose2 at (%.3f, %.3f, %.3f) with rotation (%.1f, %.1f, %.1f)\n",
point.x, point.y, point.z, rx, ry, rz); point.x, point.y, point.z, rx, ry, rz);
@ -1405,9 +1509,9 @@ void CloudViewMainWindow::onShowInputLine()
float distance = std::sqrt(dx * dx + dy * dy + dz * dz); float distance = std::sqrt(dx * dx + dy * dy + dz * dz);
statusBar()->showMessage(QString("已显示线段 (%1,%2,%3) → (%4,%5,%6) 长度: %7") statusBar()->showMessage(QString("已显示线段 (%1,%2,%3) → (%4,%5,%6) 长度: %7")
.arg(x1).arg(y1).arg(z1) .arg(x1, 0, 'f', 1).arg(y1, 0, 'f', 1).arg(z1, 0, 'f', 1)
.arg(x2).arg(y2).arg(z2) .arg(x2, 0, 'f', 1).arg(y2, 0, 'f', 1).arg(z2, 0, 'f', 1)
.arg(distance)); .arg(distance, 0, 'f', 3));
LOG_INFO("[CloudView] Show input line from (%.3f, %.3f, %.3f) to (%.3f, %.3f, %.3f) length=%.3f\n", LOG_INFO("[CloudView] Show input line from (%.3f, %.3f, %.3f) to (%.3f, %.3f, %.3f) length=%.3f\n",
x1, y1, z1, x2, y2, z2, distance); x1, y1, z1, x2, y2, z2, distance);
@ -1665,3 +1769,95 @@ void CloudViewMainWindow::onViewAnglesChanged(float rotX, float rotY, float rotZ
m_editRotZ->setText(QString::number(rotZ, 'f', 1)); m_editRotZ->setText(QString::number(rotZ, 'f', 1));
} }
void CloudViewMainWindow::onPoint1CoordChanged()
{
auto selectedPoints = m_glWidget->getSelectedPoints();
if (selectedPoints.isEmpty() || !selectedPoints[0].valid) {
QMessageBox::warning(this, "提示", "请先选择点1");
return;
}
// 读取编辑框中的坐标
bool ok = true;
float x = m_editPoint1X->text().toFloat(&ok);
if (!ok) {
QMessageBox::warning(this, "错误", "点1 X 值无效");
return;
}
float y = m_editPoint1Y->text().toFloat(&ok);
if (!ok) {
QMessageBox::warning(this, "错误", "点1 Y 值无效");
return;
}
float z = m_editPoint1Z->text().toFloat(&ok);
if (!ok) {
QMessageBox::warning(this, "错误", "点1 Z 值无效");
return;
}
// 更新选中点的坐标
m_glWidget->updateSelectedPointCoord(0, x, y, z);
// 如果启用了测距且有两个点,重新计算距离
if (m_glWidget->isMeasureDistanceEnabled() && selectedPoints.size() >= 2 && selectedPoints[1].valid) {
auto updatedPoints = m_glWidget->getSelectedPoints();
if (updatedPoints.size() >= 2) {
float distance = m_glWidget->calculateDistance(updatedPoints[0], updatedPoints[1]);
m_lblDistance->setText(QString("%1 mm").arg(distance, 0, 'f', 3));
statusBar()->showMessage(QString("点1坐标已更新距离: %1 mm").arg(distance, 0, 'f', 3));
}
} else {
statusBar()->showMessage(QString("点1坐标已更新为 (%1, %2, %3)").arg(x, 0, 'f', 3).arg(y, 0, 'f', 3).arg(z, 0, 'f', 3));
}
LOG_INFO("[CloudView] Point1 coord updated to (%.3f, %.3f, %.3f)\n", x, y, z);
}
void CloudViewMainWindow::onPoint2CoordChanged()
{
auto selectedPoints = m_glWidget->getSelectedPoints();
if (selectedPoints.size() < 2 || !selectedPoints[1].valid) {
QMessageBox::warning(this, "提示", "请先选择点2");
return;
}
// 读取编辑框中的坐标
bool ok = true;
float x = m_editPoint2X->text().toFloat(&ok);
if (!ok) {
QMessageBox::warning(this, "错误", "点2 X 值无效");
return;
}
float y = m_editPoint2Y->text().toFloat(&ok);
if (!ok) {
QMessageBox::warning(this, "错误", "点2 Y 值无效");
return;
}
float z = m_editPoint2Z->text().toFloat(&ok);
if (!ok) {
QMessageBox::warning(this, "错误", "点2 Z 值无效");
return;
}
// 更新选中点的坐标
m_glWidget->updateSelectedPointCoord(1, x, y, z);
// 如果启用了测距,重新计算距离
if (m_glWidget->isMeasureDistanceEnabled()) {
auto updatedPoints = m_glWidget->getSelectedPoints();
if (updatedPoints.size() >= 2) {
float distance = m_glWidget->calculateDistance(updatedPoints[0], updatedPoints[1]);
m_lblDistance->setText(QString("%1 mm").arg(distance, 0, 'f', 3));
statusBar()->showMessage(QString("点2坐标已更新距离: %1 mm").arg(distance, 0, 'f', 3));
}
} else {
statusBar()->showMessage(QString("点2坐标已更新为 (%1, %2, %3)").arg(x, 0, 'f', 3).arg(y, 0, 'f', 3).arg(z, 0, 'f', 3));
}
LOG_INFO("[CloudView] Point2 coord updated to (%.3f, %.3f, %.3f)\n", x, y, z);
}

View File

@ -21,6 +21,7 @@ PointCloudGLWidget::PointCloudGLWidget(QWidget* parent)
, m_rotationX(0.0f) , m_rotationX(0.0f)
, m_rotationY(0.0f) , m_rotationY(0.0f)
, m_rotationZ(0.0f) , m_rotationZ(0.0f)
, m_rotation(QQuaternion()) // 初始化为单位四元数
, m_center(0, 0, 0) , m_center(0, 0, 0)
, m_pan(0, 0, 0) , m_pan(0, 0, 0)
, m_minBound(-50, -50, -50) , m_minBound(-50, -50, -50)
@ -128,18 +129,18 @@ void PointCloudGLWidget::paintGL()
// 平移在相机空间中进行,使鼠标拖动方向与屏幕方向一致 // 平移在相机空间中进行,使鼠标拖动方向与屏幕方向一致
m_view.translate(-m_pan.x(), -m_pan.y(), -m_distance); m_view.translate(-m_pan.x(), -m_pan.y(), -m_distance);
// 使用轨迹球旋转方式先Y后X后Z这样更直观 // 使用四元数旋转(物体坐标系旋转)
m_view.rotate(m_rotationY, 0, 1, 0); m_view.rotate(m_rotation);
m_view.rotate(m_rotationX, 1, 0, 0);
m_view.rotate(m_rotationZ, 0, 0, 1);
m_view.translate(-m_center); m_view.translate(-m_center);
// 调试输出每100帧输出一次当前旋转角度 // 调试输出每100帧输出一次当前旋转角度
static int frameCount = 0; static int frameCount = 0;
if (++frameCount % 100 == 0) { if (++frameCount % 100 == 0) {
LOG_INFO("[CloudView] Current rotation: rotX=%.1f, rotY=%.1f, rotZ=%.1f\n", // 从四元数转换为欧拉角用于显示
m_rotationX, m_rotationY, m_rotationZ); QVector3D euler = m_rotation.toEulerAngles();
LOG_INFO("[CloudView] Current rotation: pitch=%.1f, yaw=%.1f, roll=%.1f\n",
euler.x(), euler.y(), euler.z());
} }
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
@ -427,7 +428,8 @@ void PointCloudGLWidget::resetView()
// 确保最小视距 // 确保最小视距
m_distance = qMax(maxSize * 2.0f, 10.0f); m_distance = qMax(maxSize * 2.0f, 10.0f);
// 默认视角右手坐标系X轴朝右Y轴朝上Z轴朝向观察者 // 重置旋转为单位四元数
m_rotation = QQuaternion();
m_rotationX = 0.0f; m_rotationX = 0.0f;
m_rotationY = 0.0f; m_rotationY = 0.0f;
m_rotationZ = 0.0f; m_rotationZ = 0.0f;
@ -447,6 +449,9 @@ void PointCloudGLWidget::setViewAngles(float rotX, float rotY, float rotZ)
m_rotationZ = rotZ; m_rotationZ = rotZ;
m_pan = QVector3D(0, 0, 0); m_pan = QVector3D(0, 0, 0);
// 从欧拉角转换为四元数ZYX顺序
m_rotation = QQuaternion::fromEulerAngles(rotX, rotY, rotZ);
LOG_INFO("[CloudView] setViewAngles: rotX=%.1f, rotY=%.1f, rotZ=%.1f\n", rotX, rotY, rotZ); LOG_INFO("[CloudView] setViewAngles: rotX=%.1f, rotY=%.1f, rotZ=%.1f\n", rotX, rotY, rotZ);
emit viewAnglesChanged(m_rotationX, m_rotationY, m_rotationZ); emit viewAnglesChanged(m_rotationX, m_rotationY, m_rotationZ);
@ -548,6 +553,27 @@ float PointCloudGLWidget::calculateDistance(const SelectedPointInfo& p1, const S
return std::sqrt(dx * dx + dy * dy + dz * dz); return std::sqrt(dx * dx + dy * dy + dz * dz);
} }
void PointCloudGLWidget::updateSelectedPointCoord(int index, float x, float y, float z)
{
if (index < 0 || index >= m_selectedPoints.size()) {
return;
}
if (!m_selectedPoints[index].valid) {
return;
}
// 更新选中点的坐标
m_selectedPoints[index].x = x;
m_selectedPoints[index].y = y;
m_selectedPoints[index].z = z;
LOG_INFO("[CloudView] Updated selected point %d to (%.3f, %.3f, %.3f)\n", index, x, y, z);
// 刷新显示
update();
}
QVector<QVector3D> PointCloudGLWidget::getSelectedLinePoints() const QVector<QVector3D> PointCloudGLWidget::getSelectedLinePoints() const
{ {
QVector<QVector3D> points; QVector<QVector3D> points;
@ -901,10 +927,11 @@ void PointCloudGLWidget::drawSelectedLine()
void PointCloudGLWidget::drawAxis() void PointCloudGLWidget::drawAxis()
{ {
// 在右下角绘制坐标系指示器(右手坐标系) // 在右下角绘制坐标系指示器
// 坐标系定义X向右Y向下Z朝后
// X轴红色指向右 // X轴红色指向右
// Y轴绿色指向 // Y轴绿色指向
// Z轴蓝色指向观察者(屏幕外 // Z轴蓝色指向后(远离观察者)
GLint viewport[4]; GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport); glGetIntegerv(GL_VIEWPORT, viewport);
@ -927,10 +954,10 @@ void PointCloudGLWidget::drawAxis()
// 移动到右下角中心位置 // 移动到右下角中心位置
glTranslatef(axisX + axisSize / 2.0f, axisY + axisSize / 2.0f, 0.0f); glTranslatef(axisX + axisSize / 2.0f, axisY + axisSize / 2.0f, 0.0f);
// 应用当前视图的旋转(与主视图同步 // 应用当前视图的旋转(使用四元数转换为矩阵
glRotatef(m_rotationY, 0, 1, 0); QMatrix4x4 rotMatrix;
glRotatef(m_rotationX, 1, 0, 0); rotMatrix.rotate(m_rotation);
glRotatef(m_rotationZ, 0, 0, 1); glMultMatrixf(rotMatrix.constData());
// 关闭深度测试,确保坐标系始终可见 // 关闭深度测试,确保坐标系始终可见
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
@ -939,17 +966,17 @@ void PointCloudGLWidget::drawAxis()
glLineWidth(2.0f); glLineWidth(2.0f);
glBegin(GL_LINES); glBegin(GL_LINES);
// X 轴 - 红色(右手坐标系:向右) // X 轴 - 红色(向右)
glColor3f(1.0f, 0.0f, 0.0f); glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(axisLength, 0.0f, 0.0f); glVertex3f(axisLength, 0.0f, 0.0f);
// Y 轴 - 绿色(右手坐标系:向上 // Y 轴 - 绿色(向下,在屏幕坐标中是向上显示
glColor3f(0.0f, 1.0f, 0.0f); glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, axisLength, 0.0f); glVertex3f(0.0f, axisLength, 0.0f);
// Z 轴 - 蓝色(右手坐标系:向观察者) // Z 轴 - 蓝色(向后,在屏幕坐标中是向观察者)
glColor3f(0.0f, 0.0f, 1.0f); glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, axisLength); glVertex3f(0.0f, 0.0f, axisLength);
@ -1060,15 +1087,37 @@ void PointCloudGLWidget::mouseMoveEvent(QMouseEvent* event)
m_lastMousePos = event->pos(); m_lastMousePos = event->pos();
if (m_leftButtonPressed) { if (m_leftButtonPressed) {
// Alt+左键拖动:旋转rotZ(滚转) // Alt+左键拖动:绕视线方向旋转(滚转)
if (event->modifiers() & Qt::AltModifier) { if (event->modifiers() & Qt::AltModifier) {
m_rotationZ += delta.x() * 0.5f; // 绕Z轴视线方向旋转修正方向
float angle = -delta.x() * 0.5f; // 取反修正方向
QQuaternion deltaRotation = QQuaternion::fromAxisAndAngle(QVector3D(0, 0, 1), angle);
m_rotation = deltaRotation * m_rotation;
m_rotationZ += angle;
} else { } else {
// 普通左键拖动旋转rotX和rotY // 普通左键拖动:基于当前物体坐标系的旋转
m_rotationY += delta.x() * 0.5f; // 鼠标水平移动 -> 绕当前Y轴旋转
m_rotationX += delta.y() * 0.5f; // 鼠标垂直移动 -> 绕当前X轴旋转
// 计算旋转角度
float angleX = delta.y() * 0.5f; // 垂直移动
float angleY = delta.x() * 0.5f; // 水平移动
// 创建增量旋转四元数(在相机空间中)
// 先绕X轴旋转俯仰再绕Y轴旋转偏航
QQuaternion deltaRotationX = QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), angleX);
QQuaternion deltaRotationY = QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0), angleY);
QQuaternion deltaRotation = deltaRotationY * deltaRotationX;
// 应用增量旋转(左乘,相机空间旋转)
m_rotation = deltaRotation * m_rotation;
// 更新欧拉角(用于显示)
m_rotationX += angleX;
m_rotationY += angleY;
} }
// 移除角度限制,允许无限旋转
// 发送角度变化信号
emit viewAnglesChanged(m_rotationX, m_rotationY, m_rotationZ); emit viewAnglesChanged(m_rotationX, m_rotationY, m_rotationZ);
update(); update();
} else if (m_middleButtonPressed) { } else if (m_middleButtonPressed) {
@ -1077,7 +1126,11 @@ void PointCloudGLWidget::mouseMoveEvent(QMouseEvent* event)
m_pan.setY(m_pan.y() + delta.y() * factor); m_pan.setY(m_pan.y() + delta.y() * factor);
update(); update();
} else if (m_rightButtonPressed) { } else if (m_rightButtonPressed) {
m_rotationZ += delta.x() * 0.5f; // 右键拖动:绕视线方向旋转(滚转)(修正方向)
float angle = -delta.x() * 0.5f; // 取反修正方向
QQuaternion deltaRotation = QQuaternion::fromAxisAndAngle(QVector3D(0, 0, 1), angle);
m_rotation = deltaRotation * m_rotation;
m_rotationZ += angle;
emit viewAnglesChanged(m_rotationX, m_rotationY, m_rotationZ); emit viewAnglesChanged(m_rotationX, m_rotationY, m_rotationZ);
update(); update();
} }