#include "mainwindow.h"
#include <vtkVertexGlyphFilter.h>
#include <vtkProperty.h>
#include <vtkMatrix4x4.h>
#include <vtkMath.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QImage>
#include <QPixmap>
#include <QMessageBox>
#include <QFileDialog>
#include <QKeyEvent>
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/console/print.h>
#include <cmath>
#include <QApplication>
#include <QDebug>
#ifdef _WIN32
#include <windows.h>
#endif
// ImageLabel实现
ImageLabel::ImageLabel(Mw* mainWin, QWidget* parent)
: QLabel(parent), m_mainWin(mainWin)
{
setAlignment(Qt::AlignCenter);
setStyleSheet("border:1px solid #444; background:#222; color:#fff;");
setScaledContents(false);
setMinimumSize(200, 150);
setFocusPolicy(Qt::ClickFocus);
}
void ImageLabel::setMatImage(const cv::Mat& mat)
{
if (mat.empty()) {
setText("无图像数据");
return;
}
cv::Mat rgbMat;
if (mat.channels() == 3)
cv::cvtColor(mat, rgbMat, cv::COLOR_BGR2RGB);
else if (mat.channels() == 1)
cv::cvtColor(mat, rgbMat, cv::COLOR_GRAY2RGB);
else {
setText("不支持的图像格式");
return;
}
QImage qImg(rgbMat.data, rgbMat.cols, rgbMat.rows,
static_cast<int>(rgbMat.step), QImage::Format_RGB888);
originalPixmap = QPixmap::fromImage(qImg);
loadImage();
}
void ImageLabel::keyPressEvent(QKeyEvent* event)
{
emit keyPressed(this, event);
QLabel::keyPressEvent(event);
}
void ImageLabel::resizeEvent(QResizeEvent* event)
{
QLabel::resizeEvent(event);
loadImage();
}
void ImageLabel::loadImage()
{
if (!originalPixmap.isNull()) {
QPixmap scaledPix = originalPixmap.scaled(size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
setPixmap(scaledPix);
setText("");
}
}
// 主窗口实现
Mw::Mw(QWidget *parent) : QMainWindow(parent)
{
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
setWindowTitle("点云投影查看器");
resize(1200, 800);
m_originalCloud.reset(new pcl::PointCloud<pcl::PointXYZ>());
setupUI();
loadDefaultImages();
loadPointCloud();
}
Mw::~Mw()
{
if (pointCloudRenderer) pointCloudRenderer->Delete();
if (cloudActor) cloudActor->Delete();
if (obbActor) obbActor->Delete();
if (axesActor) axesActor->Delete();
if (boxWidget) boxWidget->Delete();
}
pcl::PointCloud<pcl::PointXYZ>::ConstPtr Mw::getOriginalCloud() const
{
return m_originalCloud;
}
// 调用投影接口(使用PointCloudProjection.h中的结构体和函数)
void Mw::callDllProjection(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr& cloud,
const std::vector<float>& obbValues)
{
if (obbValues.size() != 9) {
showError("OBB参数数量错误(需9个值)", "错误");
return;
}
if (!cloud || cloud->empty()) {
showError("点云为空或无效", "错误");
return;
}
float dx = obbValues[6], dy = obbValues[7], dz = obbValues[8];
if (sqrt(dx*dx + dy*dy + dz*dz) < 1e-3f) {
showError("OBB范围过小,无法生成投影", "错误");
return;
}
if (!isOBBContainsPoints(cloud, obbValues) || hasInvalidValues(obbValues)) {
showError("OBB参数无效或未包含点云", "错误");
return;
}
// 使用PointCloudProjection.h中的ProjResult和ObbData
ProjResult results[3];
std::vector<ObbData> obbDataList(1);
ObbData& obbData = obbDataList[0];
// 填充OBB数据(对应PointCloudProjection.h中的ObbData结构)
obbData.pos_x = obbValues[0];
obbData.pos_y = obbValues[1];
obbData.pos_z = obbValues[2];
obbData.rot_x = obbValues[3];
obbData.rot_y = obbValues[4];
obbData.rot_z = obbValues[5];
obbData.scale_x = obbValues[6];
obbData.scale_y = obbValues[7];
obbData.scale_z = obbValues[8];
obbData.obj_type = "custom";
// 调用PointCloudProjection.h中声明的get_proj_results(带use_all_points参数)
int ret = get_proj_results(cloud, obbDataList, results, false);
if (ret == 0) {
// 显示投影图像(使用ProjResult中的img)
imgLabel1->setMatImage(results[0].img);
imgLabel2->setMatImage(results[1].img);
imgLabel3->setMatImage(results[2].img);
// 调用PointCloudProjection.h中声明的free_proj_results释放内存
free_proj_results(results);
} else {
showError(QString("投影失败(错误码: %1)").arg(ret), "动态库调用失败");
}
}
void Mw::showError(const QString& message, const QString& title)
{
QMessageBox::critical(this, title, message);
std::cerr << "[错误] " << title.toStdString() << ": " << message.toStdString() << std::endl;
}
void Mw::adjustOBB(float dx, float dy, float dz,
float rx, float ry, float rz,
float scaleRatio)
{
if (!boxWidget) return;
vtkBoxRepresentation* rep = vtkBoxRepresentation::SafeDownCast(boxWidget->GetRepresentation());
if (!rep) return;
// 获取当前变换
vtkNew<vtkTransform> currentTransform;
rep->GetTransform(currentTransform);
// === 提取原始状态 ===
double pos[3];
currentTransform->GetPosition(pos);
double orient[3]; // in degrees
currentTransform->GetOrientation(orient);
double scale[3];
currentTransform->GetScale(scale);
// 构建新变换:严格按照 T * R * S 顺序(推荐使用 pre-multiply 避免混乱)
vtkNew<vtkTransform> newTransform;
newTransform->PostMultiply(); // 确保按调用顺序执行
// 1. 应用缩放(仅当需要)
if (std::abs(scaleRatio - 1.0f) > 1e-6) {
newTransform->Scale(scale[0] * scaleRatio, scale[1] * scaleRatio, scale[2] * scaleRatio);
} else {
newTransform->Scale(scale[0], scale[1], scale[2]); // 保持原缩放
}
// 2. 应用旋转
newTransform->RotateX(orient[0] + rx);
newTransform->RotateY(orient[1] + ry);
newTransform->RotateZ(orient[2] + rz);
// 3. 应用平移(局部→世界)
vtkMatrix4x4* mat = newTransform->GetMatrix();
Eigen::Matrix3f rot;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 3; ++j)
rot(i,j) = static_cast<float>(mat->GetElement(i,j));
Eigen::Vector3f local(dx, dy, dz);
Eigen::Vector3f world = rot * local;
newTransform->Translate(pos[0] + world.x(), pos[1] + world.y(), pos[2] + world.z());
// === 更新 ===
rep->SetTransform(newTransform);
obbActor->SetUserTransform(newTransform);
vtkWidget->GetRenderWindow()->Render();
}
void Mw::updateOBBProjection()
{
if (!boxWidget || !m_originalCloud || m_originalCloud->empty()) return;
vtkTransform* currentTransform = vtkTransform::New();
vtkBoxRepresentation::SafeDownCast(boxWidget->GetRepresentation())->GetTransform(currentTransform);
vtkBoxCallback callback;
callback.SetMainWindow(this);
std::vector<float> obbValues = callback.createOBBValuesFromTransform(currentTransform);
callDllProjection(m_originalCloud, obbValues);
currentTransform->Delete();
}
void Mw::keyPressEvent(QKeyEvent* event)
{
event->ignore();
}
void Mw::onImageLabelKeyPressed(ImageLabel* label, QKeyEvent* event)
{
int index = -1;
if (label == imgLabel1) index = 0; // X+ 视角
else if (label == imgLabel2) index = 1; // Y+ 视角
else if (label == imgLabel3) index = 2; // Z+ 视角
const float TRANSLATE_STEP = 0.5f;
const float ROTATE_STEP = 1.0f;
const float SCALE_RATIO = 1.1f;
switch(event->key())
{
// WASD 移动
case Qt::Key_W:
if (index == 0) adjustOBB(0, TRANSLATE_STEP, 0, 0, 0, 0, 1.0f);
else if (index == 1) adjustOBB(0, 0, TRANSLATE_STEP, 0, 0, 0, 1.0f);
else if (index == 2) adjustOBB(0, TRANSLATE_STEP, 0, 0, 0, 0, 1.0f);
break;
case Qt::Key_S:
if (index == 0) adjustOBB(0, -TRANSLATE_STEP, 0, 0, 0, 0, 1.0f);
else if (index == 1) adjustOBB(0, 0, -TRANSLATE_STEP, 0, 0, 0, 1.0f);
else if (index == 2) adjustOBB(0, -TRANSLATE_STEP, 0, 0, 0, 0, 1.0f);
break;
case Qt::Key_A:
if (index == 0) adjustOBB(0, 0, -TRANSLATE_STEP, 0, 0, 0, 1.0f);
else if (index == 1) adjustOBB(-TRANSLATE_STEP, 0, 0, 0, 0, 0, 1.0f);
else if (index == 2) adjustOBB(-TRANSLATE_STEP, 0, 0, 0, 0, 0, 1.0f);
break;
case Qt::Key_D:
if (index == 0) adjustOBB(0, 0, TRANSLATE_STEP, 0, 0, 0, 1.0f);
else if (index == 1) adjustOBB(TRANSLATE_STEP, 0, 0, 0, 0, 0, 1.0f);
else if (index == 2) adjustOBB(TRANSLATE_STEP, 0, 0, 0, 0, 0, 1.0f);
break;
// QE 旋转
case Qt::Key_Q:
if (index == 0) adjustOBB(0, 0, 0, ROTATE_STEP, 0, 0, 1.0f);
else if (index == 1) adjustOBB(0, 0, 0, 0, ROTATE_STEP, 0, 1.0f);
else if (index == 2) adjustOBB(0, 0, 0, 0, 0, ROTATE_STEP, 1.0f);
break;
case Qt::Key_E:
if (index == 0) adjustOBB(0, 0, 0, -ROTATE_STEP, 0, 0, 1.0f);
else if (index == 1) adjustOBB(0, 0, 0, 0, -ROTATE_STEP, 0, 1.0f);
else if (index == 2) adjustOBB(0, 0, 0, 0, 0, -ROTATE_STEP, 1.0f);
break;
// 缩放
case Qt::Key_Equal: adjustOBB(0, 0, 0, 0, 0, 0, SCALE_RATIO); break;
case Qt::Key_Minus: adjustOBB(0, 0, 0, 0, 0, 0, 1.0f/SCALE_RATIO); break;
default: event->ignore(); return;
}
updateOBBProjection();
event->accept();
}
bool Mw::isOBBContainsPoints(const pcl::PointCloud<pcl::PointXYZ>::ConstPtr& cloud,
const std::vector<float>& obbValues)
{
float cx = obbValues[0], cy = obbValues[1], cz = obbValues[2];
float rx = obbValues[3], ry = obbValues[4], rz = obbValues[5];
float hx = obbValues[6]/2, hy = obbValues[7]/2, hz = obbValues[8]/2;
Eigen::Matrix3f rot;
rot = Eigen::AngleAxisf(rx, Eigen::Vector3f::UnitX()).toRotationMatrix() *
Eigen::AngleAxisf(ry, Eigen::Vector3f::UnitY()).toRotationMatrix() *
Eigen::AngleAxisf(rz, Eigen::Vector3f::UnitZ()).toRotationMatrix();
Eigen::Matrix3f invRot = rot.inverse();
size_t checkCount = std::min(cloud->size(), (size_t)10000);
for (size_t i = 0; i < checkCount; ++i) {
const auto& pt = cloud->points[i];
if (std::isnan(pt.x) || std::isnan(pt.y) || std::isnan(pt.z)) continue;
Eigen::Vector3f p(pt.x - cx, pt.y - cy, pt.z - cz);
Eigen::Vector3f pLocal = invRot * p;
if (fabs(pLocal.x()) <= hx + 1e-4f &&
fabs(pLocal.y()) <= hy + 1e-4f &&
fabs(pLocal.z()) <= hz + 1e-4f) {
return true;
}
}
return false;
}
bool Mw::hasInvalidValues(const std::vector<float>& values)
{
for (float v : values)
if (std::isnan(v) || std::isinf(v))
return true;
return false;
}
void Mw::setupUI()
{
QWidget* centralWidget = new QWidget(this);
QVBoxLayout* mainLayout = new QVBoxLayout(centralWidget);
setCentralWidget(centralWidget);
// VTK显示区域
vtkWidget = new QVTKWidget(this);
vtkWidget->setMinimumHeight(400);
mainLayout->addWidget(vtkWidget);
// 渲染器与相机设置
pointCloudRenderer = vtkRenderer::New();
pointCloudRenderer->SetBackground(0.1, 0.1, 0.1);
vtkWidget->GetRenderWindow()->AddRenderer(pointCloudRenderer);
vtkCamera* camera = pointCloudRenderer->GetActiveCamera();
camera->ParallelProjectionOn();
camera->SetPosition(30, 30, 30);
camera->SetFocalPoint(0, 0, 0);
camera->SetViewUp(0, 1, 0);
camera->SetParallelScale(15);
pointCloudRenderer->ResetCameraClippingRange();
// 坐标轴
axesActor = vtkAxesActor::New();
axesActor->SetTotalLength(3, 3, 3);
pointCloudRenderer->AddActor(axesActor);
// 图像显示区域
QHBoxLayout* imagesLayout = new QHBoxLayout();
imagesLayout->setStretch(0, 1);
imagesLayout->setStretch(1, 1);
imagesLayout->setStretch(2, 1);
mainLayout->addLayout(imagesLayout);
imgLabel1 = new ImageLabel(this, this);
imgLabel2 = new ImageLabel(this, this);
imgLabel3 = new ImageLabel(this, this);
connect(imgLabel1, &ImageLabel::keyPressed, this, &Mw::onImageLabelKeyPressed);
connect(imgLabel2, &ImageLabel::keyPressed, this, &Mw::onImageLabelKeyPressed);
connect(imgLabel3, &ImageLabel::keyPressed, this, &Mw::onImageLabelKeyPressed);
imagesLayout->addWidget(imgLabel1);
imagesLayout->addWidget(imgLabel2);
imagesLayout->addWidget(imgLabel3);
imgLabel1->setText("X+ 视角");
imgLabel2->setText("Y+ 视角");
imgLabel3->setText("Z+ 视角");
}
void Mw::loadDefaultImages()
{
cv::Mat defaultImg(640, 1280, CV_8UC3, cv::Scalar(50, 50, 50));
cv::putText(defaultImg, "OBB",
cv::Point(400, 320), cv::FONT_HERSHEY_SIMPLEX,
1.2, cv::Scalar(200, 200, 200), 2);
imgLabel1->setMatImage(defaultImg);
imgLabel2->setMatImage(defaultImg);
imgLabel3->setMatImage(defaultImg);
}
bool Mw::loadPointCloud()
{
QString pcdPath = QFileDialog::getOpenFileName(
this, "选择点云文件", "", "PCD Files (*.pcd)"
);
if (pcdPath.isEmpty()) {
showError("未选择点云文件", "提示");
return false;
}
pcl::console::setVerbosityLevel(pcl::console::L_ERROR);
if (pcl::io::loadPCDFile<pcl::PointXYZ>(pcdPath.toStdString(), *m_originalCloud) == -1) {
showError("点云加载失败", "错误");
return false;
}
if (m_originalCloud->empty()) {
showError("点云为空", "错误");
return false;
}
// 点云转VTK格式
vtkSmartPointer<vtkPoints> cloudPoints = vtkSmartPointer<vtkPoints>::New();
for (const auto& pt : m_originalCloud->points)
if (!std::isnan(pt.x) && !std::isnan(pt.y) && !std::isnan(pt.z))
cloudPoints->InsertNextPoint(pt.x, pt.y, pt.z);
vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
polyData->SetPoints(cloudPoints);
vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();
glyphFilter->SetInputData(polyData);
glyphFilter->Update();
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(glyphFilter->GetOutputPort());
cloudActor = vtkActor::New();
cloudActor->SetMapper(mapper);
cloudActor->GetProperty()->SetPointSize(1);
cloudActor->GetProperty()->SetColor(1.0, 1.0, 1.0);
pointCloudRenderer->AddActor(cloudActor);
pointCloudRenderer->ResetCamera();
initBoxWidget();
vtkWidget->GetRenderWindow()->Render();
return true;
}
// 单面色立方体生成(用于OBB可视化,单个面红色标识方向)
vtkSmartPointer<vtkPolyData> Mw::createSingleColoredCube()
{
// 立方体顶点坐标
double cubeVerts[8][3] = {
{-0.5, -0.5, -0.5}, {0.5, -0.5, -0.5}, {0.5, 0.5, -0.5}, {-0.5, 0.5, -0.5}, // 前面(Z-)- 染色面
{-0.5, -0.5, 0.5}, {0.5, -0.5, 0.5}, {0.5, 0.5, 0.5}, {-0.5, 0.5, 0.5} // 其他面
};
// 面索引
vtkIdType faces[6][4] = {
{0, 1, 2, 3}, // 前面(Z-)
{4, 5, 6, 7}, // 后面(Z+)
{0, 1, 5, 4}, // 下面(Y-)
{2, 3, 7, 6}, // 上面(Y+)
{0, 3, 7, 4}, // 左面(X-)
{1, 2, 6, 5} // 右面(X+)
};
// 颜色(仅前面有颜色,其他面透明)
unsigned char colors[6][4] = {
{0, 0, 0, 0}, // 其他面:全透明
{0, 255, 0, 200},
{0, 0, 0, 0},
{0, 0, 0, 0},
{255, 0, 0, 200}, // 前面:红色半透明(RGBA)
{0, 0, 0, 0}
};
// 创建点集
vtkSmartPointer<vtkPoints> cubePoints = vtkSmartPointer<vtkPoints>::New();
for (auto& p : cubeVerts)
cubePoints->InsertNextPoint(p);
// 创建面集
vtkSmartPointer<vtkCellArray> cubeFaces = vtkSmartPointer<vtkCellArray>::New();
for (auto& f : faces)
cubeFaces->InsertNextCell(4, f);
// 颜色数组(VTK 8.1兼容写法)
vtkSmartPointer<vtkUnsignedCharArray> faceColors = vtkSmartPointer<vtkUnsignedCharArray>::New();
faceColors->SetNumberOfComponents(4);
faceColors->SetName("FaceColors");
for (int i = 0; i < 6; ++i) {
faceColors->InsertNextValue(colors[i][0]); // R
faceColors->InsertNextValue(colors[i][1]); // G
faceColors->InsertNextValue(colors[i][2]); // B
faceColors->InsertNextValue(colors[i][3]); // A
}
// 组装PolyData
vtkSmartPointer<vtkPolyData> cube = vtkSmartPointer<vtkPolyData>::New();
cube->SetPoints(cubePoints);
cube->SetPolys(cubeFaces);
cube->GetCellData()->SetScalars(faceColors);
return cube;
}
void Mw::initBoxWidget()
{
// 加载单面色立方体作为OBB可视化载体
vtkSmartPointer<vtkPolyData> singleColoredCube = createSingleColoredCube();
vtkSmartPointer<vtkPolyDataMapper> cubeMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
cubeMapper->SetInputData(singleColoredCube);
cubeMapper->SetScalarModeToUseCellData(); // 使用面颜色数据
obbActor = vtkActor::New();
obbActor->SetMapper(cubeMapper);
obbActor->GetProperty()->SetEdgeColor(0, 1, 0); // 线框绿色
obbActor->GetProperty()->SetLineWidth(2);
obbActor->GetProperty()->SetRepresentationToSurface(); // 表面渲染
obbActor->GetProperty()->SetOpacity(0.8); // 半透明,避免遮挡点云
pointCloudRenderer->AddActor(obbActor);
// OBB交互控件
boxWidget = vtkBoxWidget2::New();
boxWidget->SetInteractor(vtkWidget->GetRenderWindow()->GetInteractor());
double bounds[6];
singleColoredCube->GetBounds(bounds);
vtkBoxRepresentation* boxRep = vtkBoxRepresentation::New();
boxRep->SetPlaceFactor(1.0);
boxRep->PlaceWidget(bounds);
boxWidget->SetRepresentation(boxRep);
boxRep->Delete();
// 设置交互框样式
boxRep = vtkBoxRepresentation::SafeDownCast(boxWidget->GetRepresentation());
boxRep->GetOutlineProperty()->SetColor(1, 1, 0); // 交互框黄色
boxRep->GetOutlineProperty()->SetLineWidth(2);
// 绑定交互回调
vtkBoxCallback* callback = vtkBoxCallback::New();
callback->SetMainWindow(this);
callback->SetActor(obbActor);
boxWidget->AddObserver(vtkCommand::InteractionEvent, callback);
boxWidget->AddObserver(vtkCommand::EndInteractionEvent, callback);
callback->Delete();
// 相机交互样式
vtkSmartPointer<vtkInteractorStyleTrackballCamera> style =
vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
vtkWidget->GetRenderWindow()->GetInteractor()->SetInteractorStyle(style);
boxWidget->On(); // 启用交互
}
// vtkBoxCallback实现(OBB交互回调)
void vtkBoxCallback::Execute(vtkObject* caller, unsigned long eventId, void*)
{
if (!m_actor || !m_mainWin) return;
// 获取交互控件和变换矩阵
vtkBoxWidget2* boxWidget = vtkBoxWidget2::SafeDownCast(caller);
vtkTransform* transform = vtkTransform::New();
vtkBoxRepresentation::SafeDownCast(boxWidget->GetRepresentation())->GetTransform(transform);
// 更新OBB演员的变换
m_actor->SetUserTransform(transform);
// 交互结束后触发投影
if (eventId == vtkCommand::EndInteractionEvent) {
auto cloud = m_mainWin->getOriginalCloud();
if (cloud && !cloud->empty()) {
// std::vector<float> obbValues = createOBBValuesFromTransform(transform);
std::vector<float> obbValues = getOBBFromBoxWidget(boxWidget);
m_mainWin->callDllProjection(cloud, obbValues);
boxWidget->GetInteractor()->GetRenderWindow()->Render();
}
}
transform->Delete();
}
// 从VTK变换生成OBB参数(适配PointCloudProjection.h的ObbData)
std::vector<float> vtkBoxCallback::createOBBValuesFromTransform(vtkTransform* transform)
{
std::vector<float> obbValues(9, 0.0f);
const float INIT_CUBE_SIZE = 1.0f; // 你原始设定的初始立方体基准尺寸
const float MIN_SCALE = 1e-3f; // 你原始设定的最小缩放阈值
const float MAX_SCALE_FACTOR = 1000.0f; // 你原始设定的最大缩放阈值
// 1. 提取OBB中心点坐标(逻辑完全一致)
double center[3];
transform->GetPosition(center);
obbValues[0] = static_cast<float>(center[0]); // pos_x
obbValues[1] = static_cast<float>(center[1]); // pos_y
obbValues[2] = static_cast<float>(center[2]); // pos_z
// 2. 提取旋转角(roll, pitch, yaw,弧度)(逻辑完全一致)
vtkMatrix4x4* mat = transform->GetMatrix();
double rotMat[9] = {
mat->GetElement(0,0), mat->GetElement(0,1), mat->GetElement(0,2),
mat->GetElement(1,0), mat->GetElement(1,1), mat->GetElement(1,2),
mat->GetElement(2,0), mat->GetElement(2,1), mat->GetElement(2,2)
};
double roll = 0.0, pitch = 0.0, yaw = 0.0;
const double eps = 1e-8;
if (rotMat[6] < 1.0 - eps) {
if (rotMat[6] > -1.0 + eps) {
pitch = asin(-rotMat[6]);
roll = atan2(rotMat[7], rotMat[8]);
yaw = atan2(rotMat[2], rotMat[0]);
} else {
pitch = -vtkMath::Pi() / 2.0;
roll = -atan2(-rotMat[1], rotMat[4]);
}
} else {
pitch = vtkMath::Pi() / 2.0;
roll = atan2(-rotMat[1], rotMat[4]);
}
obbValues[3] = static_cast<float>(roll); // rot_x(你原始映射:roll对应rot_x)
obbValues[4] = static_cast<float>(pitch); // rot_y(你原始映射:pitch对应rot_y)
obbValues[5] = static_cast<float>(yaw); // rot_z(你原始映射:yaw对应rot_z)
// 3. 提取缩放尺寸(你原始的核心逻辑:缩放值 × 初始立方体尺寸)
double scale[3];
transform->GetScale(scale);
// 你原始的缩放值处理逻辑:取绝对值、限制最大范围
auto processScale = [&](double rawScale) {
return std::min(std::fabs(static_cast<float>(rawScale)), MAX_SCALE_FACTOR);
};
// 你原始的尺寸计算:处理后的缩放值 × 初始立方体尺寸,再限制最小范围
obbValues[6] = std::max(processScale(scale[0]) * INIT_CUBE_SIZE, MIN_SCALE); // scale_x
obbValues[7] = std::max(processScale(scale[1]) * INIT_CUBE_SIZE, MIN_SCALE); // scale_y
obbValues[8] = std::max(processScale(scale[2]) * INIT_CUBE_SIZE, MIN_SCALE); // scale_z
return obbValues;
}
std::vector<float> vtkBoxCallback::getOBBFromBoxWidget(vtkBoxWidget2* boxWidget) {
std::vector<float> obb(9, 0.0f); // center[3] + rpy_rad[3] + half_extents[3]
vtkBoxRepresentation* rep = vtkBoxRepresentation::SafeDownCast(boxWidget->GetRepresentation());
if (!rep) {
qDebug() << "Failed to get representation.";
return obb;
}
// --- Step 1: 获取变换(用于中心和旋转)
vtkNew<vtkTransform> transform;
rep->GetTransform(transform); // 这个函数在 VTK 8.1 存在
// 提取位置(即 OBB 中心)
double pos[3];
transform->GetPosition(pos);
obb[0] = static_cast<float>(pos[0]);
obb[1] = static_cast<float>(pos[1]);
obb[2] = static_cast<float>(pos[2]);
// 提取欧拉角(注意:单位是 degree)
double rpy_deg[3];
transform->GetOrientation(rpy_deg); // ZYX 顺序:Yaw Pitch Roll(角度制)
obb[3] = static_cast<float>(rpy_deg[0] * M_PI / 180.0); // roll (x)
obb[4] = static_cast<float>(rpy_deg[1] * M_PI / 180.0); // pitch (y)
obb[5] = static_cast<float>(rpy_deg[2] * M_PI / 180.0); // yaw (z)
// --- Step 2: 获取包围盒尺寸(使用 GetBounds 返回值)
double* rawBounds = rep->GetBounds(); // ✅ 返回 double*,不要传参!
bool valid = true;
for (int i = 0; i < 6; ++i) {
if (!std::isfinite(rawBounds[i])) {
valid = false;
break;
}
}
if (valid && rawBounds[0] <= rawBounds[1]) {
float sizeX = static_cast<float>((rawBounds[1] - rawBounds[0]) * 0.5f);
float sizeY = static_cast<float>((rawBounds[3] - rawBounds[2]) * 0.5f);
float sizeZ = static_cast<float>((rawBounds[5] - rawBounds[4]) * 0.5f);
obb[6] = std::max(sizeX, 1e-3f);
obb[7] = std::max(sizeY, 1e-3f);
obb[8] = std::max(sizeZ, 1e-3f);
} else {
// 如果 bounds 无效,使用默认半长
obb[6] = obb[7] = obb[8] = 0.5f;
}
return obb;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Mw w;
w.show();
return a.exec();
}
你写好那个函数就还好,不要画蛇添足