VTK中在屏幕绘制多边形
背景介绍
最近项目需要实现一个多边形的功能,通过vtkInteractorStyleDrawPolygon虽然可以进行绘制,但其使用方法是要鼠标左键按下不松,移动时绘制多边形,导致屏幕会闪烁,因此需要自定义绘制方法。
具体需求
1、鼠标左键按下,记录当前点
2、鼠标移动时,绘制上一个点到鼠标当前位置的线
3、当记录的点大于3时,以鼠标当前位置绘制多边形
4、右键结束绘制
实现方法
要实现以上功能,需要自定义VTK交互,继承vtkInteractorStyleTrackballCamera,使用 vtkPoints 存储屏幕像素坐标,通过 vtkActor2D 和 vtkPolyDataMapper2D 显示线条。
接下来看下主要调用方法:通过一个按钮控制是否绘制多边形
cpp
void MainWindow::OnClipPointCloudBtn(bool bChecked)
{
if (bChecked)
{
if (m_polygonStyle == nullptr)
{
m_polygonStyle = vtkSmartPointer<PolygonRubberBandStyle>::New();
m_polygonStyle->AddToRenderer(m_Controls->RegistrationWidget_2->renderWindow()->GetRenderers()->GetFirstRenderer());
}
this->m_Controls->RegistrationWidget_2->renderWindow()->GetInteractor()->SetInteractorStyle(m_polygonStyle);
}
else
{
// 恢复为标准 TrackballCamera(或其他默认)
auto defaultStyle = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
//恢复交互样式
this->m_Controls->RegistrationWidget_2->renderWindow()->GetInteractor()->SetInteractorStyle(defaultStyle);
}
}
头文件
cpp
class PolygonRubberBandStyle :
public vtkInteractorStyleTrackballCamera
{
public:
static PolygonRubberBandStyle* New() { return new PolygonRubberBandStyle; }
vtkTypeMacro(PolygonRubberBandStyle, vtkInteractorStyleTrackballCamera);
public:
void AddToRenderer(vtkRenderer* ren);
PolygonRubberBandStyle();
// 鼠标按下:确定一个新顶点
void OnLeftButtonDown() override;
// 鼠标移动:绘制预览线
void OnMouseMove() override;
void OnRightButtonDown() override;
private:
void Update2DDrawing(int curX, int curY);
void ExecuteClipping();
private:
std::vector<std::vector<double>> PixelPoints; // 存储像素坐标
vtkSmartPointer<vtkPoints> Points2D;
vtkSmartPointer<vtkPolyData> PolyData2D;
vtkSmartPointer<vtkActor2D> Actor2D;
};
cpp文件
cpp
PolygonRubberBandStyle::PolygonRubberBandStyle()
{
// 1. 设置 2D 辅助线 (Overlay)
this->Points2D = vtkSmartPointer<vtkPoints>::New();
this->Points2D->SetDataTypeToDouble(); // 关键:强制设为 Double 类型
this->PolyData2D = vtkSmartPointer<vtkPolyData>::New();
this->PolyData2D->SetPoints(this->Points2D);
auto mapper2D = vtkSmartPointer<vtkPolyDataMapper2D>::New();
mapper2D->SetInputData(this->PolyData2D);
this->Actor2D = vtkSmartPointer<vtkActor2D>::New();
this->Actor2D->SetMapper(mapper2D);
this->Actor2D->GetProperty()->SetColor(0.0, 1.0, 0.0); // 绿色 2D 线
this->Actor2D->GetProperty()->SetLineWidth(2.0);
}
// 鼠标按下:确定一个新顶点
void PolygonRubberBandStyle::OnLeftButtonDown() {
int* pos = this->GetInteractor()->GetEventPosition();
this->PixelPoints.push_back({ (double)pos[0], (double)pos[1], 0.0 });
this->Update2DDrawing(pos[0], pos[1]);
}
// 鼠标移动:绘制预览线
void PolygonRubberBandStyle::OnMouseMove() {
if (!this->PixelPoints.empty()) {
int* pos = this->GetInteractor()->GetEventPosition();
this->Update2DDrawing(pos[0], pos[1]);
}
vtkInteractorStyleTrackballCamera::OnMouseMove();
}
void PolygonRubberBandStyle::OnRightButtonDown()
{
if (this->PixelPoints.size() < 3) return;
// 清理并刷新
this->PixelPoints.clear();
this->Points2D->Reset();
this->PolyData2D->Modified();
this->GetInteractor()->GetRenderWindow()->Render();
}
void PolygonRubberBandStyle::Update2DDrawing(int curX, int curY) {
this->Points2D->Reset();
for (const auto& pt : this->PixelPoints) {
this->Points2D->InsertNextPoint(pt[0], pt[1], 0);
}
this->Points2D->InsertNextPoint(curX, curY, 0); // 预览当前鼠标
auto polyLine = vtkSmartPointer<vtkPolyLine>::New();
int n = this->Points2D->GetNumberOfPoints();
polyLine->GetPointIds()->SetNumberOfIds(n + 1);
for (int i = 0; i < n; ++i) polyLine->GetPointIds()->SetId(i, i);
polyLine->GetPointIds()->SetId(n, 0); // 闭合
auto cells = vtkSmartPointer<vtkCellArray>::New();
cells->InsertNextCell(polyLine);
this->PolyData2D->SetLines(cells);
this->PolyData2D->Modified();
this->GetInteractor()->GetRenderWindow()->Render();
}
void PolygonRubberBandStyle::AddToRenderer(vtkRenderer* ren) {
this->SetDefaultRenderer(ren);
ren->AddActor2D(this->Actor2D);
}