效果
点云拾取并显示标签
背景
通过鼠标交互,显示当前选取点的坐标值
基本原理
在主程序中添加鼠标响应的回调函数,该函数中,首先获取鼠标在窗口中点下的屏幕坐标,有一点和垂直屏幕方向产生射线,若射线与着色器中的点云有满足邻近距离小于容差值的,则返回当前点的id,由此获得它的坐标。
实现方式
首先,在主程序中添加vtkCallbackCommand
// 创建vtkCallbackCommand指针对象
vtkNew<vtkCallbackCommand> pickCallback;
// 设置该响应命令的客户数据入口为主程序的类,此处的this在我的代码中指的是QMainWindow
pickCallback->SetClientData(this);
// 设置该响应命令的具体实现函数为handle_point_picker
pickCallback->SetCallback(handle_point_picker);
// 定义一个与vtk窗口交互的点拾取器 vtkPointPicker
vtkNew<vtkPointPicker> picker;
// 设置拾取器的容差为0.005
picker->SetTolerance(0.005);
// 将该拾取器添加到vtk窗口交互中
ui.openGLWidget->renderWindow()->GetInteractor()->SetPicker(picker);
// 定义右键按下鼠标事件的响应为前面创建的pickCallback
ui.openGLWidget->renderWindow()->GetInteractor()->AddObserver(vtkCommand::RightButtonPressEvent, pickCallback);
由此,当我们在vtk窗口中按下鼠标右键时,就会进入pickCallback,进一步的进入它的具体实现方式,也就是handle_point_picker函数中,下面是该函数具体实现
void handle_point_picker(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata)
{
// 将clientdata转换为QVtkDemo2类型的指针,这是一个自定义的类。
QVtkDemo2* demo = (QVtkDemo2*)clientdata;
// 获取当前窗口的渲染窗口。
vtkRenderWindow *window = demo->ui.openGLWidget->renderWindow();
// 获取渲染窗口的第一个渲染器。
vtkRenderer* renderer = window->GetRenderers()->GetFirstRenderer();
// 获取渲染窗口的交互器。
vtkRenderWindowInteractor* interactor = window->GetInteractor();
// 创建一个整型数组来存储交互器事件的位置。
int pos[2];
// 获取鼠标按下的屏幕坐标,单位是像素
interactor->GetEventPosition(pos);
// 输出事件位置到控制台。
std::cout << "pos: " << pos[0] << " " << pos[1] << std::endl;
// 根据鼠标选取点和容差,判断是否有三维点被选中
int picresult = interactor->GetPicker()->Pick(pos[0], pos[1], 0, renderer);
// 输出拾取结果到控制台。
std::cout << "picresult: " << picresult << std::endl;
// 如果拾取成功(picresult非零)。
if (picresult)
{
// 存储拾取点的世界坐标。
double world[3];
// 获取拾取点的世界坐标。
interactor->GetPicker()->GetPickPosition(world);
// 输出拾取点的世界坐标到控制台。
std::cout << "world: " << world[0] << " " << world[1] << " " << world[2] << std::endl;
// 创建一个缓冲区来格式化拾取点的世界坐标。
char buffer[20];
sprintf_s(buffer, 20, "%0.2f, %0.2f, %0.2f", world[0], world[1], world[2]);
// 获取openGLWidget的宽度和高度。
int width = demo->ui.openGLWidget->width();
int height = demo->ui.openGLWidget->height();
// 计算拾取点在窗口中的归一化位置。
double ratiox = (double)pos[0] / width + 0.05;
double ratioy = (double)pos[1] / height + 0.05;
// 输出归一化位置到控制台。
std::cout << "ratiox: " << ratiox << " ratioy: " << ratioy << std::endl;
// 设置标题表示的文本。
demo->captionRepresentation->GetCaptionActor2D()->SetCaption("VTK caption!");
// 设置标题表示的锚点位置为拾取点的世界坐标。
demo->captionRepresentation->SetAnchorPosition(world);
// 设置标题控件的交互器、表示、选择性和可调整性。
demo->captionWidget->SetInteractor(interactor);
demo->captionWidget->SetRepresentation(demo->captionRepresentation);
demo->captionWidget->SetSelectable(0);
demo->captionWidget->SetResizable(0);
demo->captionWidget->PickingManagedOff();
demo->captionWidget->SetEnabled(1);
// 设置标题控件的边框和文本属性。
demo->captionWidget->GetCaptionActor2D()->SetBorder(1);
demo->captionWidget->GetCaptionActor2D()->SetCaption(buffer);
// 设置标题文本的垂直和水平对齐方式。
demo->captionWidget->GetCaptionActor2D()->GetCaptionTextProperty()->SetVerticalJustificationToCentered();
demo->captionWidget->GetCaptionActor2D()->GetCaptionTextProperty()->SetJustificationToCentered();
// 设置标题控件边框的位置。
demo->captionWidget->GetBorderRepresentation()->SetPosition(ratiox, ratioy);
demo->captionWidget->GetBorderRepresentation()->SetPosition2(0.1, 0.05);
// 设置标题控件的引导符号大小。
demo->captionWidget->GetCaptionActor2D()->SetLeaderGlyphSize(0.01);
// 输出引导符号大小到控制台。
std::cout << "size: " << demo->captionWidget->GetCaptionActor2D()->GetLeaderGlyphSize() << std::endl;
}
}
需要特别注意的是: vtkNew captionWidget; vtkNew captionRepresentation;定义这两个变量必须是全局的,否则无法显示。这个和之前博客中,显示坐标系是一样的,在头文件中定义全局变量