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

        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和actorvtkSmartPointer<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和actorvtkSmartPointer<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和actorvtkSmartPointer<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 键退出所有测量模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/85395.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/85395.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

性能优化 - 高级进阶: 性能优化全方位总结

文章目录 Pre1. 概述&#xff1a;性能优化提纲与使用场景2. 准备阶段2.1 明确优化范围与目标2.2 环境与工具准备 3. 数据收集与指标确认3.1 关键资源维度与指标项3.2 监控体系搭建与初始采集3.3 日志与追踪配置 4. 问题定位思路4.1 从整体到局部的分析流程4.2 常见瓶颈维度检查…

Mybatis之Integer类型字段为0,入库为null

背景&#xff1a; 由于项目某个功能用到优先级字段来判断&#xff0c;需要在mysql表中定义一个字段XX&#xff0c;类型为int&#xff0c;默认为0&#xff0c;具体值由后台配置&#xff0c;正常入库即可 问题&#xff1a; 由于后台配置存量其他类型的数据无需该字段&#xff0c…

上海市计算机学会竞赛平台2022年3月月赛丙组洗牌

题目描述 给定一个整数 nn&#xff0c;表示 nn 张牌&#xff0c;牌的编号为 11 到 nn。 再给定一个洗牌置换 f1,f2,…,fnf1​,f2​,…,fn​&#xff0c;进行一次洗牌操作时&#xff0c;应将第一号位置的牌交换到第 f1f1​ 号位置&#xff0c;将第 ii 号位置的牌交换到第 fifi…

DINO-R1:激励推理能力的视觉基础模型

摘要 近期&#xff0c;人们对大型语言模型&#xff08;如DeepSeek-R1&#xff09;推理能力的关注呈爆炸式增长&#xff0c;通过基于强化学习的微调框架&#xff08;如组相对策略优化&#xff08;Group Relative Policy Optimization&#xff0c;GRPO&#xff09;方法&#xff…

Linux--LVM逻辑卷扩容

Linux–LVM逻辑卷扩容 文章目录 Linux--LVM逻辑卷扩容📚 LVM 常用命令分类及基本格式✅ 1. 物理卷(PV)相关命令✅ 2. 卷组(VG)相关命令✅ 3. 逻辑卷(LV)相关命令🔍 三、查看类命令简写说明使用命令及基本格式:lvm逻辑卷扩容步骤:1.添加硬盘设备2.检测新增硬盘 添加…

C#基础语法与控制台操作

1. 控制台操作基础 控制台程序是学习C#的起点。以下是一些常用的控制台操作方法&#xff1a; 1.1. 清除控制台 Console.Clear(); // 清除控制台内容1.2. 输出字符串 Console.WriteLine("Hello World!"); // 在屏幕的当前位置换行输出字符串 Console.Write("…

100.Complex[]同时储存实数和虚数两组double的数组 C#例子

在信号处理中&#xff0c;IQ 数据&#xff08;In-phase and Quadrature&#xff09;通常表示复数形式的信号&#xff0c;其中实部表示同相分量&#xff0c;虚部表示正交分量。Complex[] data 是一个包含 IQ 数据的数组&#xff0c;每个元素是一个复数&#xff0c;表示一个信号样…

停止追逐 React 重渲染

大多数开发者都在浪费时间对抗多余的重渲染。真正的 React 架构师根本让问题无从产生——下面就来揭开他们的思路&#xff0c;以及为何大多数所谓的性能优化技巧反而拖慢了你的应用。 重渲染的无尽轮回 先来直击痛点&#xff1a;如果还在项目里到处撒 useMemo、useCallback&…

流水线的安全与合规 - 构建可信的交付链

流水线的安全与合规 - 构建可信的交付链 “安全左移 (Shift-Left Security)”的理念 “安全左移”是 DevSecOps 的核心理念,指的是将安全测试和考量,从软件开发生命周期 (SDLC) 的末端(发布前),尽可能地向左移动到更早的阶段(如编码、构建、测试阶段)。 为何对 SRE 至…

​​​​​​​神经网络基础讲解 一

​​一.神经网络 ​ ​​1. 全连接神经网络&#xff08;Fully Connected Network, FCN&#xff09;​​ ​​核心概念&#xff1a;​​ ​​输入层​​&#xff1a;接收原始数据&#xff08;如数字、图片像素等&#xff09; 数字矩阵 。​​隐藏层​​&#xff1a;对数据…

MySQL 8.0 OCP 英文题库解析(二十二)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题201~210 试题2…

【大模型推理】PD分离场景下decoder负载均衡,如何选取decoder

https://mp.weixin.qq.com/s?__bizMzg4NTczNzg2OA&mid2247507420&idx1&sn4b32726abd205c7f94144bcb9105330f&chksmce64b9fc7f1d8de04a40b0153302dee52262c6f104c67195e2586e75c8093b8be493f252c8a3#rd 在非 Local 场景下&#xff0c;Prefill 定时获取 Decode …

【IP地址】IP应用场景的使用方向

网络安全领域 通过IP地址查询&#xff0c;安全系统能够实时监控网络流量&#xff0c;识别异常访问行为。例如&#xff0c;当某个IP地址在短时间内频繁发起大量请求&#xff0c;且访问模式与正常用户存在明显差异时&#xff0c;系统可将其标记为可疑IP&#xff0c;触发风险预警…

3-18 WPS JS宏 颜色设置实例应用(按条件设置单元格颜色)学习笔记

前面讲解了关于单元格的一些格式的设置&#xff0c;本节课再讲解一下各种清除方法。 1.函数解析与用法 Range().clear()//清除全部 Range().Value2null //清除内容 Range().ClearContents()//清除内容 Range().ClearFormats()//清除格式 Range().EntireRow.Range()//以Ra…

从零开始的云计算生活——第二十天,脚踏实地,SSH与Rsync服务

目录 一.故事背景 二.SSH带外管理 1.概述 2. 配置文件 3.命令解析 4.登录方式配置 a.用户名密码登录 b.公钥验证登录 5.实操生成密钥对 三.Rsyncsersync实现数据实时同步 1.rsync概述 2.rsync运行原理 3.rsync部署 4.备份测试 配置备份目录 5.rsyncsersync 实现…

SpringAI + DeepSeek大模型应用开发 - 初识篇

一、认识AI 1. AI的发展 AI&#xff0c;人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;使机器能像人类一样思考、学习和解决问题的技术。 2. 大模型及其原理 在自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;中&#xff0c;…

c++第八天-多态

虚函数虚析构函数纯虚函数与抽象类 多态实现的条件&#xff1a;&#xff08;1&#xff09;公有继承 &#xff08;2&#xff09;派生类重写基类虚函数 &#xff08;3&#xff09;基类指针/引用指向派生类对象 虚函数不能是构造函数&#xff0c;不能是静态函数&#xff0c;不能…

全景图渲染Vue3+TS使用Photo Sphere Viewer插件实现

1.Photo Sphere Viewer插件安装: title=插件安装 体验AI代码助手 代码解读复制代码npm install photo-sphere-viewer -S 或 yarn add photo-sphere-viewer -S 2.原始全景图展示 初始化标签容器 体验AI代码助手 代码解读复制代码 // 全景图的根节点必须要具备宽高 TS引用,创建…

Redis之分布式锁(3)

这篇文章我们来详细介绍一下如何正确地基于Redis实现分布式锁。 基于Redis的分布式锁实现 组件依赖 首先通过Maven引入Jedis开源组件&#xff0c;在pom.xml文件加入下面的代码&#xff1a; <dependency><groupId>redis.clients</groupId><artifactId&g…

Java课堂笔记11

三个修饰符 一、abstract&#xff08;抽象&#xff09; 1.抽象方法只能在抽象的类里&#xff0c;只有方法的声明&#xff0c;没有方法的实现。&#xff08;没有{}直接&#xff1b;结尾&#xff09;。 2.abstract修饰的类称为抽象类。 注意&#xff1a;&#xff08;1&#x…