VTK 显示大量点云数据及交互(点云拾取、着色、测量等)功能

发布于:2025-06-16 ⋅ 阅读:(24) ⋅ 点赞:(0)

        VTK (Visualization Toolkit) 是一个强大的开源可视化库,非常适合处理点云数据。下面将介绍如何使用 VTK 显示大量点云数据,并实现点云拾取、着色、测量等功能。

基本点云显示

创建一个基本的点云显示程序:

cpp

#include <vtkSmartPointer.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkVertexGlyphFilter.h>

int main() {
    // 创建点云数据
    vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
    
    // 添加大量点(这里示例添加100万个随机点)
    for (int i = 0; i < 1000000; ++i) {
        points->InsertNextPoint(
            (double)rand() / RAND_MAX * 100.0,
            (double)rand() / RAND_MAX * 100.0,
            (double)rand() / RAND_MAX * 100.0);
    }
    
    // 创建PolyData对象并设置点
    vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
    polydata->SetPoints(points);
    
    // 使用VertexGlyphFilter将点转换为可渲染的顶点
    vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();
    glyphFilter->SetInputData(polydata);
    glyphFilter->Update();
    
    // 创建mapper和actor
    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputConnection(glyphFilter->GetOutputPort());
    
    vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
    actor->SetMapper(mapper);
    actor->GetProperty()->SetPointSize(2); // 设置点大小
    
    // 创建渲染器、渲染窗口和交互器
    vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
    vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->AddRenderer(renderer);
    
    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = 
        vtkSmartPointer<vtkRenderWindowInteractor>::New();
    renderWindowInteractor->SetRenderWindow(renderWindow);
    
    // 添加actor到渲染器
    renderer->AddActor(actor);
    renderer->SetBackground(0.1, 0.2, 0.4); // 设置背景颜色
    
    // 开始渲染
    renderWindow->Render();
    renderWindowInteractor->Start();
    
    return 0;
}

点云拾取功能

要实现点云拾取,可以使用 VTK 的点拾取器 (vtkPointPicker):

cpp

#include <vtkPointPicker.h>

// 在main函数中创建交互器后添加以下代码

// 创建点拾取器
vtkSmartPointer<vtkPointPicker> pointPicker = vtkSmartPointer<vtkPointPicker>::New();
renderWindowInteractor->SetPicker(pointPicker);

// 添加观察者处理拾取事件
vtkSmartPointer<vtkCallbackCommand> pickCallback = vtkSmartPointer<vtkCallbackCommand>::New();
pickCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {
    vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
    vtkPointPicker* picker = static_cast<vtkPointPicker*>(interactor->GetPicker());
    
    int x = interactor->GetEventPosition()[0];
    int y = interactor->GetEventPosition()[1];
    
    picker->Pick(x, y, 0, interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
    
    vtkIdType pointId = picker->GetPointId();
    if (pointId != -1) {
        double* pos = picker->GetPickPosition();
        std::cout << "Picked point ID: " << pointId << std::endl;
        std::cout << "Position: (" << pos[0] << ", " << pos[1] << ", " << pos[2] << ")" << std::endl;
    }
});

renderWindowInteractor->AddObserver(vtkCommand::LeftButtonPressEvent, pickCallback);

点云着色功能

可以为点云添加颜色属性,实现着色效果:

cpp

#include <vtkUnsignedCharArray.h>

// 在创建点数据后添加颜色数组
vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
colors->SetNumberOfComponents(3); // RGB
colors->SetName("Colors");

// 为每个点添加随机颜色
for (int i = 0; i < points->GetNumberOfPoints(); ++i) {
    unsigned char color[3] = {
        static_cast<unsigned char>(rand() % 256),
        static_cast<unsigned char>(rand() % 256),
        static_cast<unsigned char>(rand() % 256)
    };
    colors->InsertNextTypedTuple(color);
}

polydata->GetPointData()->SetScalars(colors);

// 然后修改mapper以使用这些颜色
mapper->SetScalarVisibility(1); // 启用标量颜色
mapper->SetScalarModeToUsePointData(); // 使用点数据中的标量

点云测量功能

1. 点对点距离测量

首先实现最基本的点对点距离测量功能:

#include <vtkDistanceWidget.h>
#include <vtkDistanceRepresentation3D.h>
#include <vtkSphereSource.h>

// 在main函数中创建交互器后添加以下代码

// 创建距离测量工具
vtkSmartPointer<vtkDistanceWidget> distanceWidget = vtkSmartPointer<vtkDistanceWidget>::New();
distanceWidget->SetInteractor(renderWindowInteractor);
distanceWidget->CreateDefaultRepresentation();

// 设置距离表示的样式
vtkDistanceRepresentation3D* distanceRep = 
    vtkDistanceRepresentation3D::SafeDownCast(distanceWidget->GetRepresentation());
distanceRep->SetLabelFormat("%-#6.3g mm"); // 设置显示格式
distanceRep->GetAxisProperty()->SetColor(1, 0, 0); // 设置轴线颜色
distanceRep->GetAxisProperty()->SetLineWidth(2); // 设置线宽

// 添加标记球体
vtkSmartPointer<vtkSphereSource> sphere1 = vtkSmartPointer<vtkSphereSource>::New();
sphere1->SetRadius(1.0);
vtkSmartPointer<vtkSphereSource> sphere2 = vtkSmartPointer<vtkSphereSource>::New();
sphere2->SetRadius(1.0);

distanceRep->SetPoint1Representation(sphere1->GetOutput());
distanceRep->SetPoint2Representation(sphere2->GetOutput());

// 启用距离测量工具
distanceWidget->On();

2. 多点距离测量(折线测量)

如果需要测量多点间的累计距离,可以实现折线测量:

#include <vtkContourWidget.h>
#include <vtkOrientedGlyphContourRepresentation.h>
#include <vtkPolygonalSurfacePointPlacer.h>

// 创建折线测量工具
vtkSmartPointer<vtkContourWidget> contourWidget = vtkSmartPointer<vtkContourWidget>::New();
contourWidget->SetInteractor(renderWindowInteractor);
contourWidget->ContinuousDrawOn(); // 启用连续绘制

// 设置表示方式
vtkOrientedGlyphContourRepresentation* contourRep = 
    vtkOrientedGlyphContourRepresentation::SafeDownCast(contourWidget->GetRepresentation());
contourRep->GetLinesProperty()->SetColor(0, 1, 0); // 设置线颜色
contourRep->GetLinesProperty()->SetLineWidth(2); // 设置线宽

// 设置点放置器,使点吸附到点云上
vtkSmartPointer<vtkPolygonalSurfacePointPlacer> pointPlacer = 
    vtkSmartPointer<vtkPolygonalSurfacePointPlacer>::New();
pointPlacer->AddProp(actor);
pointPlacer->GetPolys()->AddItem(polydata);
contourRep->SetPointPlacer(pointPlacer);

// 添加回调函数计算总长度
vtkSmartPointer<vtkCallbackCommand> contourCallback = vtkSmartPointer<vtkCallbackCommand>::New();
contourCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {
    vtkContourWidget* widget = static_cast<vtkContourWidget*>(caller);
    vtkContourRepresentation* rep = widget->GetContourRepresentation();
    
    int numPoints = rep->GetNumberOfNodes();
    if (numPoints > 1) {
        double totalLength = 0.0;
        double pos1[3], pos2[3];
        
        rep->GetNthNodeWorldPosition(0, pos1);
        for (int i = 1; i < numPoints; ++i) {
            rep->GetNthNodeWorldPosition(i, pos2);
            totalLength += sqrt(vtkMath::Distance2BetweenPoints(pos1, pos2));
            memcpy(pos1, pos2, 3 * sizeof(double));
        }
        
        std::cout << "Total length: " << totalLength << " mm" << std::endl;
    }
});

contourWidget->AddObserver(vtkCommand::InteractionEvent, contourCallback);
contourWidget->On();

3. 面积测量(多边形区域)

对于闭合区域的面积测量:

#include <vtkPolygon.h>
#include <vtkCellArray.h>

// 修改contourWidget的回调函数以支持面积计算
contourCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {
    vtkContourWidget* widget = static_cast<vtkContourWidget*>(caller);
    vtkContourRepresentation* rep = widget->GetContourRepresentation();
    
    int numPoints = rep->GetNumberOfNodes();
    if (numPoints > 2) {
        // 计算周长
        double totalLength = 0.0;
        double pos1[3], pos2[3];
        rep->GetNthNodeWorldPosition(0, pos1);
        for (int i = 1; i < numPoints; ++i) {
            rep->GetNthNodeWorldPosition(i, pos2);
            totalLength += sqrt(vtkMath::Distance2BetweenPoints(pos1, pos2));
            memcpy(pos1, pos2, 3 * sizeof(double));
        }
        // 闭合路径
        rep->GetNthNodeWorldPosition(0, pos2);
        totalLength += sqrt(vtkMath::Distance2BetweenPoints(pos1, pos2));
        
        // 计算面积
        vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
        for (int i = 0; i < numPoints; ++i) {
            rep->GetNthNodeWorldPosition(i, pos1);
            points->InsertNextPoint(pos1);
        }
        
        vtkSmartPointer<vtkPolygon> polygon = vtkSmartPointer<vtkPolygon>::New();
        polygon->Initialize(numPoints, points->GetPoints(), points->GetBounds());
        
        double area = polygon->ComputeArea();
        std::cout << "Perimeter: " << totalLength << " mm" << std::endl;
        std::cout << "Area: " << area << " mm²" << std::endl;
    }
});

4. 点云高度差测量

对于三维点云,可以测量高度差:

// 在点拾取回调中添加高度差计算
pickCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {
    static double lastPos[3] = {0, 0, 0};
    static bool firstPick = true;
    
    vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
    vtkPointPicker* picker = static_cast<vtkPointPicker*>(interactor->GetPicker());
    
    int x = interactor->GetEventPosition()[0];
    int y = interactor->GetEventPosition()[1];
    
    picker->Pick(x, y, 0, interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
    
    vtkIdType pointId = picker->GetPointId();
    if (pointId != -1) {
        double* pos = picker->GetPickPosition();
        std::cout << "Picked point ID: " << pointId << std::endl;
        std::cout << "Position: (" << pos[0] << ", " << pos[1] << ", " << pos[2] << ")" << std::endl;
        
        if (!firstPick) {
            double distance = sqrt(vtkMath::Distance2BetweenPoints(pos, lastPos));
            double heightDiff = fabs(pos[2] - lastPos[2]);
            std::cout << "Distance from last point: " << distance << " mm" << std::endl;
            std::cout << "Height difference: " << heightDiff << " mm" << std::endl;
        }
        
        memcpy(lastPos, pos, 3 * sizeof(double));
        firstPick = false;
    }
});

5. 测量结果显示在3D视图

将测量结果直接显示在3D视图上:

#include <vtkTextActor.h>
#include <vtkTextProperty.h>

// 创建文本actor用于显示测量结果
vtkSmartPointer<vtkTextActor> textActor = vtkSmartPointer<vtkTextActor>::New();
textActor->SetPosition(10, 10);
textActor->GetTextProperty()->SetFontSize(18);
textActor->GetTextProperty()->SetColor(1.0, 1.0, 1.0);
renderer->AddActor2D(textActor);

// 修改距离测量的回调函数
distanceWidget->AddObserver(vtkCommand::InteractionEvent, [textActor](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {
    vtkDistanceWidget* widget = static_cast<vtkDistanceWidget*>(caller);
    vtkDistanceRepresentation3D* rep = vtkDistanceRepresentation3D::SafeDownCast(widget->GetRepresentation());
    
    double distance = rep->GetDistance();
    std::stringstream ss;
    ss << "Distance: " << std::fixed << std::setprecision(2) << distance << " mm";
    textActor->SetInput(ss.str().c_str());
});

6. 测量工具切换

添加键盘控制来切换不同的测量工具:

// 添加键盘回调
vtkSmartPointer<vtkCallbackCommand> keyCallback = vtkSmartPointer<vtkCallbackCommand>::New();
keyCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {
    vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
    std::string key = interactor->GetKeySym();
    
    static vtkDistanceWidget* distanceWidget = static_cast<vtkDistanceWidget*>(clientData);
    static vtkContourWidget* contourWidget = static_cast<vtkContourWidget*>(clientData);
    
    if (key == "d") {
        // 切换距离测量
        distanceWidget->SetEnabled(!distanceWidget->GetEnabled());
        contourWidget->SetEnabled(false);
    } else if (key == "c") {
        // 切换轮廓测量
        contourWidget->SetEnabled(!contourWidget->GetEnabled());
        distanceWidget->SetEnabled(false);
    } else if (key == "a") {
        // 切换面积测量
        bool enabled = contourWidget->GetEnabled();
        contourWidget->SetEnabled(!enabled);
        distanceWidget->SetEnabled(false);
        if (enabled) {
            contourWidget->CloseLoop();
        }
    }
});

renderWindowInteractor->AddObserver(vtkCommand::KeyPressEvent, keyCallback);

性能优化

对于大量点云数据,可以考虑以下优化措施:

1、使用vtkOctreePointLocator 加速点查找

#include <vtkOctreePointLocator.h>

vtkSmartPointer<vtkOctreePointLocator> pointLocator = vtkSmartPointer<vtkOctreePointLocator>::New();
pointLocator->SetDataSet(polydata);
pointLocator->BuildLocator();

// 拾取时使用locator查找最近点
double searchPoint[3] = {x, y, z};
vtkIdType pointId = pointLocator->FindClosestPoint(searchPoint);

2、使用点云简化 减少显示点数

#include <vtkQuadricClustering.h>

vtkSmartPointer<vtkQuadricClustering> decimate = vtkSmartPointer<vtkQuadricClustering>::New();
decimate->SetInputData(polydata);
decimate->SetNumberOfDivisions(50, 50, 50); // 调整简化程度
decimate->Update();

3、使用GPU加速

#include <vtkGPUInfo.h>
#include <vtkGPUInfoList.h>

// 检查GPU信息
vtkSmartPointer<vtkGPUInfoList> infoList = vtkSmartPointer<vtkGPUInfoList>::New();
infoList->Probe();

if (infoList->GetNumberOfGPUs() > 0) {
    mapper->SetUseHardwareShading(true);
}

完整示例

完整示例1(点云显示+点云拾取+点云着色)

#include <vtkSmartPointer.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkVertexGlyphFilter.h>
#include <vtkUnsignedCharArray.h>
#include <vtkPointPicker.h>
#include <vtkCallbackCommand.h>
#include <vtkOctreePointLocator.h>
#include <iostream>

int main() {
    // 创建点云数据
    vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
    
    // 添加10万个随机点
    for (int i = 0; i < 100000; ++i) {
        points->InsertNextPoint(
            (double)rand() / RAND_MAX * 100.0,
            (double)rand() / RAND_MAX * 100.0,
            (double)rand() / RAND_MAX * 100.0);
    }
    
    // 创建颜色数组
    vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
    colors->SetNumberOfComponents(3);
    colors->SetName("Colors");
    
    for (int i = 0; i < points->GetNumberOfPoints(); ++i) {
        unsigned char color[3] = {
            static_cast<unsigned char>(rand() % 256),
            static_cast<unsigned char>(rand() % 256),
            static_cast<unsigned char>(rand() % 256)
        };
        colors->InsertNextTypedTuple(color);
    }
    
    // 创建PolyData对象
    vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
    polydata->SetPoints(points);
    polydata->GetPointData()->SetScalars(colors);
    
    // 创建点定位器(用于加速拾取)
    vtkSmartPointer<vtkOctreePointLocator> pointLocator = vtkSmartPointer<vtkOctreePointLocator>::New();
    pointLocator->SetDataSet(polydata);
    pointLocator->BuildLocator();
    
    // 将点转换为可渲染的顶点
    vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();
    glyphFilter->SetInputData(polydata);
    glyphFilter->Update();
    
    // 创建mapper和actor
    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputConnection(glyphFilter->GetOutputPort());
    mapper->SetScalarVisibility(1);
    mapper->SetScalarModeToUsePointData();
    
    vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
    actor->SetMapper(mapper);
    actor->GetProperty()->SetPointSize(3);
    
    // 创建渲染器和窗口
    vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
    vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->AddRenderer(renderer);
    renderWindow->SetSize(800, 600);
    
    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = 
        vtkSmartPointer<vtkRenderWindowInteractor>::New();
    renderWindowInteractor->SetRenderWindow(renderWindow);
    
    // 设置点拾取器
    vtkSmartPointer<vtkPointPicker> pointPicker = vtkSmartPointer<vtkPointPicker>::New();
    renderWindowInteractor->SetPicker(pointPicker);
    
    // 添加拾取回调
    vtkSmartPointer<vtkCallbackCommand> pickCallback = vtkSmartPointer<vtkCallbackCommand>::New();
    pickCallback->SetCallback([](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {
        vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
        
        int x = interactor->GetEventPosition()[0];
        int y = interactor->GetEventPosition()[1];
        
        interactor->GetPicker()->Pick(x, y, 0, interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
        vtkPointPicker* picker = static_cast<vtkPointPicker*>(interactor->GetPicker());
        
        vtkIdType pointId = picker->GetPointId();
        if (pointId != -1) {
            double* pos = picker->GetPickPosition();
            std::cout << "Picked point ID: " << pointId << std::endl;
            std::cout << "Position: (" << pos[0] << ", " << pos[1] << ", " << pos[2] << ")" << std::endl;
            
            // 高亮显示选中的点(例如改变颜色)
            // 这里可以添加代码修改选中点的颜色
        }
    });
    
    renderWindowInteractor->AddObserver(vtkCommand::LeftButtonPressEvent, pickCallback);
    
    // 添加actor到渲染器
    renderer->AddActor(actor);
    renderer->SetBackground(0.1, 0.2, 0.4);
    
    // 开始渲染
    renderWindow->Render();
    renderWindowInteractor->Start();
    
    return 0;
}

完整示例2(点云测量)

#include <vtkSmartPointer.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkVertexGlyphFilter.h>
#include <vtkUnsignedCharArray.h>
#include <vtkPointPicker.h>
#include <vtkCallbackCommand.h>
#include <vtkDistanceWidget.h>
#include <vtkDistanceRepresentation3D.h>
#include <vtkSphereSource.h>
#include <vtkContourWidget.h>
#include <vtkOrientedGlyphContourRepresentation.h>
#include <vtkPolygonalSurfacePointPlacer.h>
#include <vtkTextActor.h>
#include <vtkTextProperty.h>
#include <vtkMath.h>
#include <iostream>
#include <sstream>
#include <iomanip>

int main() {
    // 创建点云数据
    vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
    
    // 添加随机点
    for (int i = 0; i < 50000; ++i) {
        points->InsertNextPoint(
            (double)rand() / RAND_MAX * 100.0,
            (double)rand() / RAND_MAX * 100.0,
            (double)rand() / RAND_MAX * 20.0); // Z轴范围小一些,便于观察高度差
    }
    
    // 创建颜色数组
    vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
    colors->SetNumberOfComponents(3);
    colors->SetName("Colors");
    
    for (int i = 0; i < points->GetNumberOfPoints(); ++i) {
        unsigned char color[3] = {
            static_cast<unsigned char>(rand() % 256),
            static_cast<unsigned char>(rand() % 256),
            static_cast<unsigned char>(rand() % 256)
        };
        colors->InsertNextTypedTuple(color);
    }
    
    // 创建PolyData对象
    vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
    polydata->SetPoints(points);
    polydata->GetPointData()->SetScalars(colors);
    
    // 将点转换为可渲染的顶点
    vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();
    glyphFilter->SetInputData(polydata);
    glyphFilter->Update();
    
    // 创建mapper和actor
    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputConnection(glyphFilter->GetOutputPort());
    mapper->SetScalarVisibility(1);
    mapper->SetScalarModeToUsePointData();
    
    vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
    actor->SetMapper(mapper);
    actor->GetProperty()->SetPointSize(3);
    
    // 创建渲染器和窗口
    vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
    vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->AddRenderer(renderer);
    renderWindow->SetSize(800, 600);
    
    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = 
        vtkSmartPointer<vtkRenderWindowInteractor>::New();
    renderWindowInteractor->SetRenderWindow(renderWindow);
    
    // 设置点拾取器
    vtkSmartPointer<vtkPointPicker> pointPicker = vtkSmartPointer<vtkPointPicker>::New();
    renderWindowInteractor->SetPicker(pointPicker);
    
    // 创建文本actor用于显示测量结果
    vtkSmartPointer<vtkTextActor> textActor = vtkSmartPointer<vtkTextActor>::New();
    textActor->SetPosition(10, 10);
    textActor->GetTextProperty()->SetFontSize(18);
    textActor->GetTextProperty()->SetColor(1.0, 1.0, 1.0);
    renderer->AddActor2D(textActor);
    
    // 创建距离测量工具
    vtkSmartPointer<vtkDistanceWidget> distanceWidget = vtkSmartPointer<vtkDistanceWidget>::New();
    distanceWidget->SetInteractor(renderWindowInteractor);
    distanceWidget->CreateDefaultRepresentation();
    
    vtkDistanceRepresentation3D* distanceRep = 
        vtkDistanceRepresentation3D::SafeDownCast(distanceWidget->GetRepresentation());
    distanceRep->SetLabelFormat("%-#6.3g mm");
    distanceRep->GetAxisProperty()->SetColor(1, 0, 0);
    distanceRep->GetAxisProperty()->SetLineWidth(2);
    
    vtkSmartPointer<vtkSphereSource> sphere1 = vtkSmartPointer<vtkSphereSource>::New();
    sphere1->SetRadius(1.0);
    vtkSmartPointer<vtkSphereSource> sphere2 = vtkSmartPointer<vtkSphereSource>::New();
    sphere2->SetRadius(1.0);
    
    distanceRep->SetPoint1Representation(sphere1->GetOutput());
    distanceRep->SetPoint2Representation(sphere2->GetOutput());
    
    distanceWidget->AddObserver(vtkCommand::InteractionEvent, [textActor](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {
        vtkDistanceWidget* widget = static_cast<vtkDistanceWidget*>(caller);
        vtkDistanceRepresentation3D* rep = vtkDistanceRepresentation3D::SafeDownCast(widget->GetRepresentation());
        
        double distance = rep->GetDistance();
        std::stringstream ss;
        ss << "Distance: " << std::fixed << std::setprecision(2) << distance << " mm";
        textActor->SetInput(ss.str().c_str());
    });
    
    // 创建轮廓测量工具
    vtkSmartPointer<vtkContourWidget> contourWidget = vtkSmartPointer<vtkContourWidget>::New();
    contourWidget->SetInteractor(renderWindowInteractor);
    contourWidget->ContinuousDrawOn();
    
    vtkOrientedGlyphContourRepresentation* contourRep = 
        vtkOrientedGlyphContourRepresentation::SafeDownCast(contourWidget->GetRepresentation());
    contourRep->GetLinesProperty()->SetColor(0, 1, 0);
    contourRep->GetLinesProperty()->SetLineWidth(2);
    
    vtkSmartPointer<vtkPolygonalSurfacePointPlacer> pointPlacer = 
        vtkSmartPointer<vtkPolygonalSurfacePointPlacer>::New();
    pointPlacer->AddProp(actor);
    pointPlacer->GetPolys()->AddItem(polydata);
    contourRep->SetPointPlacer(pointPlacer);
    
    contourWidget->AddObserver(vtkCommand::InteractionEvent, [textActor](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {
        vtkContourWidget* widget = static_cast<vtkContourWidget*>(caller);
        vtkContourRepresentation* rep = widget->GetContourRepresentation();
        
        int numPoints = rep->GetNumberOfNodes();
        if (numPoints > 1) {
            // 计算周长
            double totalLength = 0.0;
            double pos1[3], pos2[3];
            rep->GetNthNodeWorldPosition(0, pos1);
            for (int i = 1; i < numPoints; ++i) {
                rep->GetNthNodeWorldPosition(i, pos2);
                totalLength += sqrt(vtkMath::Distance2BetweenPoints(pos1, pos2));
                memcpy(pos1, pos2, 3 * sizeof(double));
            }
            
            std::stringstream ss;
            ss << "Perimeter: " << std::fixed << std::setprecision(2) << totalLength << " mm";
            
            // 如果闭合,计算面积
            if (numPoints > 2 && rep->IsClosed()) {
                vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
                for (int i = 0; i < numPoints; ++i) {
                    rep->GetNthNodeWorldPosition(i, pos1);
                    points->InsertNextPoint(pos1);
                }
                
                vtkSmartPointer<vtkPolygon> polygon = vtkSmartPointer<vtkPolygon>::New();
                polygon->Initialize(numPoints, points->GetPoints(), points->GetBounds());
                
                double area = polygon->ComputeArea();
                ss << "\nArea: " << std::fixed << std::setprecision(2) << area << " mm²";
            }
            
            textActor->SetInput(ss.str().c_str());
        }
    });
    
    // 添加键盘控制
    vtkSmartPointer<vtkCallbackCommand> keyCallback = vtkSmartPointer<vtkCallbackCommand>::New();
    keyCallback->SetCallback([distanceWidget, contourWidget, textActor](vtkObject* caller, unsigned long eventId, void* clientData, void* callData) {
        vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
        std::string key = interactor->GetKeySym();
        
        if (key == "d" || key == "D") {
            // 切换距离测量
            distanceWidget->SetEnabled(!distanceWidget->GetEnabled());
            contourWidget->SetEnabled(false);
            textActor->SetInput("Distance measurement mode (click two points)");
        } else if (key == "c" || key == "C") {
            // 切换轮廓测量
            contourWidget->SetEnabled(!contourWidget->GetEnabled());
            distanceWidget->SetEnabled(false);
            textActor->SetInput("Contour measurement mode (click multiple points)");
        } else if (key == "a" || key == "A") {
            // 切换面积测量
            bool enabled = !contourWidget->GetEnabled();
            contourWidget->SetEnabled(enabled);
            distanceWidget->SetEnabled(false);
            if (enabled) {
                textActor->SetInput("Area measurement mode (click points to close loop)");
                contourWidget->CloseLoop();
            }
        } else if (key == "Escape") {
            // 退出所有测量模式
            distanceWidget->SetEnabled(false);
            contourWidget->SetEnabled(false);
            textActor->SetInput("");
        }
    });
    
    renderWindowInteractor->AddObserver(vtkCommand::KeyPressEvent, keyCallback);
    
    // 添加actor到渲染器
    renderer->AddActor(actor);
    renderer->SetBackground(0.1, 0.2, 0.4);
    
    // 开始渲染
    renderWindow->Render();
    
    // 显示操作提示
    std::cout << "Measurement Controls:" << std::endl;
    std::cout << "  D - Toggle distance measurement" << std::endl;
    std::cout << "  C - Toggle contour measurement" << std::endl;
    std::cout << "  A - Toggle area measurement" << std::endl;
    std::cout << "  ESC - Exit measurement mode" << std::endl;
    
    renderWindowInteractor->Start();
    
    return 0;
}

功能说明

  1. 距离测量:按 'D' 键激活,点击两个点测量它们之间的距离

  2. 轮廓测量:按 'C' 键激活,点击多个点测量折线总长度

  3. 面积测量:按 'A' 键激活,点击多个点后闭合区域测量面积

  4. 结果显示:测量结果实时显示在3D视图左上角

  5. 退出测量:按 ESC 键退出所有测量模式


网站公告

今日签到

点亮在社区的每一天
去签到