VTK布尔运算


本文的主要内容:简单介绍VTK中与布尔运算相关功能。 主要涉及vtkBooleanOperationPolyDataFilter/vtkLoopBooleanPolyDataFilter,vtkImplicitBoolean,vtkBooleanTexture三个类。
哪些人适合阅读本文:有一定VTK基础的人。

一、vtkBooleanOperationPolyDataFilter

vtkBooleanOperationPolyDataFilter和vtkLoopBooleanPolyDataFilter的用法、用途完全一样,这里只说其中一个。用途就是输入两个PolyeData,输出它们的交并差。
VTK官网描述:
计算根据两个输入曲面定义的体积计算出的并集、交集或差异体积的边界。
这两个表面不需要是流形的,但如果不是流形,可能会得到意想不到的结果。所得到的表面在过滤器的第一个输出中可用。第二个输出包含一组折线,表示两个输入曲面之间的交点。
警告:
该过滤器不是为执行2D布尔运算而设计的,事实上,它依赖于没有共面、重叠单元的输入。

写得很清楚,它对输入模型的要求比较高,对于共面或有重叠部分的输入模型无效。不只是会得到意想不到的结果,还可能直接导致程序崩溃。一般在使用之前将模型经过vtkTriangleFilter和vtkCleanPolyData进行处理。就算进行了处理,对于非结构化的输入模型还是大概率会计算失败。总而言之,这个算法不稳定,不推荐使用。

	vtkSmartPointer<vtkPolyData> input1;
    vtkSmartPointer<vtkPolyData> input2;
    std::string operation("intersection");

    vtkNew<vtkSphereSource> sphereSource1;
    sphereSource1->SetCenter(0.25, 0, 0);
    sphereSource1->SetPhiResolution(41);
    sphereSource1->SetThetaResolution(41);
    sphereSource1->Update();
    input1 = sphereSource1->GetOutput();

    vtkNew<vtkSphereSource> sphereSource2;
    sphereSource2->SetPhiResolution(41);
    sphereSource2->SetThetaResolution(41);
    sphereSource2->Update();
    input2 = sphereSource2->GetOutput();
    
	vtkNew<vtkBooleanOperationPolyDataFilter> booleanOperation;
    if (operation == "union")
    {
        booleanOperation->SetOperationToUnion();
    }
    else if (operation == "intersection")
    {
        booleanOperation->SetOperationToIntersection();
    }
    else if (operation == "difference")
    {
        booleanOperation->SetOperationToDifference();
    }
    else
    {
        std::cout << "Unknown operation: " << operation << std::endl;
        return;
    }
    booleanOperation->SetInputData(0, input1);
    booleanOperation->SetInputData(1, input2);

    vtkNew<vtkPolyDataMapper> booleanOperationMapper;
    booleanOperationMapper->SetInputConnection(booleanOperation->GetOutputPort());
    booleanOperationMapper->ScalarVisibilityOff();

    vtkNew<vtkActor> booleanOperationActor;
    booleanOperationActor->SetMapper(booleanOperationMapper);
    booleanOperationActor->GetProperty()->SetDiffuseColor(colors->GetColor3d("Banana").GetData());
    booleanOperationActor->GetProperty()->SetSpecular(.6);
    booleanOperationActor->GetProperty()->SetSpecularPower(20);

在这里插入图片描述
红色和绿色是输入的两个模型,黄色是它们相交的结果。

二、vtkImplicitBoolean

这个类从名字也看得出来,简单来说就是输入隐函数,输出交并差。
VTK官网描述:
由隐函数的布尔组合组成的隐函数
vtkImplicitBoolean是一个隐式函数,由隐式函数的布尔组合组成。该类有一个函数列表(FunctionList),这些函数根据指定的运算符(VTK_UNION、VTK_INTERSECTION或VTK_DIFFERENCE)组合在一起。您可以使用vtkImplicitFunction(和/或vtkImplicaitBoolean)的嵌套组合来创建复杂的隐式函数。vtkImplicitBoolean是vtkImplicaitFunction的具体实现。
操作员的工作原理如下。VTK_UNION运算符取所有隐式函数的最小值。VTK_INTERSECTION运算符取所有隐式函数的最大值。VTK_DIFFERENCE运算符从第一个隐函数中减去第二个到最后一个隐函数。VTK_UNION_OF_MAGNITUDES取隐式函数的最小绝对值。

因为都是由隐函数组成,相比vtkBooleanOperationPolyDataFilter这个就稳定得多。但是它的输入只能是vtkSphere、vtkBox或者自定义的隐函数,不能是任意模型,更不能是从stl等文件读取的模型。另外如果想要将结果显示出来通常还要用到采样函数vtkSampleFunction和vtkContourFilter,这就导致如果想要做实时的布尔运算,会一直累加隐函数,然后采样的时间消耗就会非常大。

	vtkNew<vtkNamedColors> colors;

    // create a sphere
    vtkNew<vtkSphere> sphere;
    sphere->SetRadius(1);
    sphere->SetCenter(1, 0, 0);

    // create a box
    vtkNew<vtkBox> box;
    box->SetBounds(-1, 1, -1, 1, -1, 1);

    // combine the two implicit functions
    vtkNew<vtkImplicitBoolean> boolean;
    boolean->AddFunction(box);
    boolean->AddFunction(sphere);

    std::vector<vtkNew<vtkRenderer>> ren;
    ren.push_back(vtkNew<vtkRenderer>());
    ren.push_back(vtkNew<vtkRenderer>());
    ren.push_back(vtkNew<vtkRenderer>());
    ren[0]->SetViewport(0, 0, 1.0 / 3.0, 1);         // Difference
    ren[1]->SetViewport(1.0 / 3.0, 0, 2.0 / 3.0, 1); // Union
    ren[2]->SetViewport(2.0 / 3.0, 0, 1, 1);         // Intersection

    // Shared camera
    vtkNew<vtkCamera> camera;
    camera->Azimuth(30.0);
    camera->Elevation(30.0);

    vtkNew<vtkColorSeries> colorSeries;
    colorSeries->SetColorScheme(vtkColorSeries::BREWER_DIVERGING_SPECTRAL_3);

    for (int i = 0; i < 3; ++i)
    {
        if (i == 0)
        {
            boolean->SetOperationTypeToDifference();
        }
        else if (i == 1)
        {
            boolean->SetOperationTypeToUnion();
        }
        else
        {
            boolean->SetOperationTypeToIntersection();
        }

        // The sample function generates a distance function from the implicit
        // function. This is then contoured to get a polygonal surface.
        vtkNew<vtkSampleFunction> sample;
        sample->SetImplicitFunction(boolean);
        sample->SetModelBounds(-1, 2, -1, 1, -1, 1);
        sample->SetSampleDimensions(100, 100, 100);
        sample->ComputeNormalsOff();

        // contour
        vtkNew<vtkContourFilter> surface;
        surface->SetInputConnection(sample->GetOutputPort());
        surface->SetValue(0, 0.0);
        surface->Update();

        vtkNew<vtkPolyData> polyData;
        polyData->DeepCopy(surface->GetOutput());

        // mapper
        vtkNew<vtkPolyDataMapper> mapper;
        mapper->SetInputData(polyData);
        mapper->ScalarVisibilityOff();

        vtkNew<vtkActor> actor;
        actor->SetMapper(mapper);
        //actor->GetProperty()->EdgeVisibilityOn();
        actor->GetProperty()->SetColor(colors->GetColor3d("peacock").GetData());

        // add the actor
        ren[i]->SetBackground(colorSeries->GetColor(i).GetRed() / 255.0,
            colorSeries->GetColor(i).GetGreen() / 255.0,
            colorSeries->GetColor(i).GetBlue() / 255.0);
        ren[i]->SetActiveCamera(camera);
        ren[i]->AddActor(actor);
        ren[i]->ResetCamera();
    }

    // render window
    renderWin->AddRenderer(ren[0]);
    renderWin->AddRenderer(ren[1]);
    renderWin->AddRenderer(ren[2]);

    renderWin->Render();

在这里插入图片描述
从左到右依次是交并差。

三、vtkBooleanTexture

这个类,是通过隐函数可以识别内(1)、外(-1)、上(0)的能力,从而对不同部位的模型设置不同的纹理显示模式clear、solid、edge等,从而达到一种看起来做了布尔运算的效果。实际上是没有将数据集用布尔进行区分,所以也非常稳定。
VTK官网描述:
基于内部、外部和区域边界的组合生成2D纹理图。
vtkBooleanTexture是一个过滤器,用于根据内部、外部和区域边界的组合生成2D纹理图。“区域”通过2D纹理坐标隐式表示。这些纹理坐标通常使用类似vtkImplicitTextureCoords的过滤器生成,该过滤器为任何隐式函数生成纹理坐标。
vtkBooleanTexture根据s-t纹理坐标以及在区域内、区域上或区域外的概念生成贴图。区域内是指纹理坐标在(0,0.5-厚度/2)之间。外区域是纹理坐标为(0.5+厚度/2)的地方。导通区域介于(0.5厚度/2,0.5+厚度/2)之间。每个s-t纹理坐标的in、on和out组合产生16种可能的组合(见正文)。对于每种组合,可以指定不同的强度和透明度值。要指定最大强度和/或不透明度,请使用值255。最小值0会产生黑色区域(用于强度)和完全透明区域(用于透明度)。

核心代码如下:

	vtkNew<vtkNamedColors> colors;
    vtkNew<vtkRenderer> aren;

    // define two elliptical cylinders
    vtkNew<vtkQuadric> quadric1;
    quadric1->SetCoefficients(1, 2, 0, 0, 0, 0, 0, 0, 0, -.07);

    vtkNew<vtkQuadric> quadric2;
    quadric2->SetCoefficients(2, 1, 0, 0, 0, 0, 0, 0, 0, -.07);

    // create a sphere for all to use
    vtkNew<vtkSphereSource> aSphere;
    aSphere->SetPhiResolution(21);
    aSphere->SetThetaResolution(21);

    // create texture coordinates for all
    vtkNew<vtkImplicitTextureCoords> tcoords;
    tcoords->SetInputConnection(aSphere->GetOutputPort());
    tcoords->SetRFunction(quadric1);
    tcoords->SetSFunction(quadric2);

    vtkNew<vtkDataSetMapper> aMapper;
    aMapper->SetInputConnection(tcoords->GetOutputPort());

    // create a mapper, sphere and texture map for each case
    for (int i = 0; i < 16; i++)
    {
        auto aBoolean = MakeBooleanTexture(i, 64, 0);

        vtkNew<vtkTexture> aTexture2;
        aTexture2->SetInputConnection(aBoolean->GetOutputPort());
        aTexture2->InterpolateOff();
        aTexture2->RepeatOff();

        vtkNew<vtkActor> anActor2;

        anActor2->SetMapper(aMapper);
        anActor2->SetTexture(aTexture2);
        anActor2->SetPosition(&positions[i][0]);
        anActor2->SetScale(2.0, 2.0, 2.0);
        anActor2->GetProperty()->SetColor(colors->GetColor3d("MistyRose").GetData());

        aren->AddActor(anActor2);
    }

    aren->SetBackground(colors->GetColor3d("SlateGray").GetData());
    renderWin->AddRenderer(aren);
    renderWin->Render();

在这里插入图片描述
显示了16种布尔的效果。可以看出显示效果还可以,但是有个问题就是切割之后的面不封闭。

总结:
如果想要对任意输入的两个模型进行稳定得布尔运算,只用VTK大概率是实现不了的。下一篇将会介绍几种相对稳定的布尔运算实现方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巴普蒂斯塔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值