在实际的开发中,提供有限的连续线段组成一条曲线,需要将该条曲线进行插值处理,生成更高采样率的曲线;这种情况下就需要进行插值;VTK中的线段插值是通过B样条插值实现,使用vtkSplineFilter完成这一功能;
vtkSplineFilter
vtkSplineFilter是一个从多段线的输入集生成输出多段线的Filter。
通过用户可以指定的vtkSpline类(默认情况下使用vtkCardinalSpline),可以对多段线进行统一细分和生成。可以通过多种方式控制线的细分数目。用户可以指定细分的数量,也可以提供每个细分的长度(vtkSplineFilter类将计算出整个多段线上需要计算出多少个细分段)。也可以设置最大细分数。
此Filter的输出是每个输入多段线(或线)的多段线。将创建新的点和纹理坐标。对点数据进行插值,并传递单元数据。任何少于两点的多段线或具有重合点的多段线都将被忽略。
接口
细分段个数
vtkSetClampMacro(NumberOfSubdivisions, int, 1, VTK_INT_MAX);
vtkGetMacro(NumberOfSubdivisions, int);
vtkSetClampMacro(MaximumNumberOfSubdivisions, int, 1, VTK_INT_MAX);
vtkGetMacro(MaximumNumberOfSubdivisions, int);
void SetSubdivideToSpecified() { this->SetSubdivide(VTK_SUBDIVIDE_SPECIFIED); }
void SetSubdivideToLength() { this->SetSubdivide(VTK_SUBDIVIDE_LENGTH); }
vtkSetClampMacro(Subdivide, int, VTK_SUBDIVIDE_SPECIFIED, VTK_SUBDIVIDE_LENGTH);
vtkGetMacro(Subdivide, int);
vtkSetClampMacro(Length, double, 0.0000001, VTK_DOUBLE_MAX);
vtkGetMacro(Length, double);
const char* GetSubdivideAsString();
SetNumberOfSubdivisions:设置为多段线创建的细分数目。只有将Subdivisions设置为SetSubdivisionsToSpecify()时,此方法才有效。
SetSubdivideToSpecified:指定如何确定细分数目。
SetLength基于绝对长度控制为多段线创建的细分数目。样条曲线的长度除以此长度以确定细分的数目。只有将Subdivisions设置为SetSubdivideToLength()时,此方法才有效。
SetMaximumNumberOfSubdivisions:设置为每条多段线创建的最大细分数。
插值类型对象
vtkSpline是样条插值的样条抽象类,SetSpline设置了vtkSplineFilter使用的vtkSpline的派生类对象,可以使用vtkCardinalSpline/vtkKochanekSpline/vtkSCurveSpline;
virtual void SetSpline(vtkSpline*);
vtkGetObjectMacro(Spline, vtkSpline);
纹理坐标
纹理坐标可以通过三种方式生成:归一化(0,1)生成;基于长度(除以纹理长度);通过使用输入的标量值。
宏 | 意义 |
---|---|
VTK_TCOORDS_FROM_NORMALIZED_LENGTH | 归一化方式 |
VTK_TCOORDS_FROM_LENGTH | 基于长度方式 |
VTK_TCOORDS_FROM_SCALARS | 基于输入的标量值方式 |
vtkSetClampMacro(GenerateTCoords, int, VTK_TCOORDS_OFF, VTK_TCOORDS_FROM_SCALARS);
vtkGetMacro(GenerateTCoords, int);
void SetGenerateTCoordsToOff() { this->SetGenerateTCoords(VTK_TCOORDS_OFF); }
void SetGenerateTCoordsToNormalizedLength(){this->SetGenerateTCoords(VTK_TCOORDS_FROM_NORMALIZED_LENGTH);}
void SetGenerateTCoordsToUseLength() { this->SetGenerateTCoords(VTK_TCOORDS_FROM_LENGTH); }
void SetGenerateTCoordsToUseScalars() { this->SetGenerateTCoords(VTK_TCOORDS_FROM_SCALARS); }
const char* GetGenerateTCoordsAsString();
vtkSetClampMacro(TextureLength, double, 0.000001, VTK_INT_MAX);
vtkGetMacro(TextureLength, double);
vtkSpline
vtkSpline插值一组数据点(插值意味着样条曲线通过这些点)。
vtkSpline是一个抽象类:它的子类vtkCardinalSpline、vtkKochanekSpline和vtkSCurveSpline实现vtkSpline提供的接口功能。请注意,此样条曲线将1D参数化坐标t映射为单个值x。因此,如果要使用样条曲线插值点(即x[3]),则必须为每个x-y-z坐标创建三条样条曲线。vtkParametricSpline类可以将离散点拟合成一条样条曲线。通常,样条曲线是通过添加一系列参数坐标/数据(t,x)值,然后使用求值函数(例如,vtkCardinalSpline::Evaluate())来使用的。因为这些样条曲线是一维的,所以这里的一个点是独立/因变量对。样条曲线也可以设置为关闭或打开。闭合样条曲线以连续函数和导数值从最后一点延续到第一点(无需复制第一个点即可闭合样条曲线,只需设置ClosedOn即可)。
样条曲线的这种实现不使用规范化的参数化坐标。如果样条曲线打开,则参数空间为(tMin<=t<=tMax),其中tMin和tMax是执行AddPoint()时看到的最小和最大参数值。如果样条曲线是闭合的,那么参数空间是(tMin<=t<=(tMax+1)),其中tMin和tMax是执行AddPoint()时看到的最小和最大参数值。但是,请注意,可以通过显式设置ParametricRange(tMin,tMax)来更改此行为。如果设置,则参数空间保持不变(tMin<=t<=tMax),除非参数值超出此范围的数据的添加被限制在此范围内。
样条:通过一组指定点集生成平滑曲线的柔型带;
样条曲线:计算机图形学中,样条曲线指多项式曲线段连接而成的曲线;
样条(Spline) 可以理解为 多段 Cubic Curve 的拼接,不过样条在 Cubic Curve 拼接的时候,提出了特殊要求:两段 Cubic Curve 在拼接的位置,需要能够“平滑衔接”,以保证整个样条的曲线平滑。这种“平滑衔接”,是通过导数和曲线的连续性来实现的。
需要实现的两个接口
virtual void Compute() = 0;
virtual double Evaluate(double t) = 0;
参数空间
void SetParametricRange(double tMin, double tMax);
void SetParametricRange(double tRange[2]) { this->SetParametricRange(tRange[0], tRange[1]); }
void GetParametricRange(double tRange[2]) const;
SetParametricRange设置参数空间范围;如果未设置,则通过跟踪t的(min,max)参数值隐式确定范围。如果设置了,AddPoint()方法会将t值钳制在指定的范围内。
样条曲线拟合的点
void AddPoint(double t, double x);
void RemovePoint(double t);
void RemoveAllPoints();
int GetNumberOfPoints();
AddPoint添加一对要与样条曲线拟合的点。
RemovePoint移除一个坐标对应的点;
RemoveAllPoints移除所有点;
夹逼模式
vtkSetMacro(ClampValue, vtkTypeBool);
vtkGetMacro(ClampValue, vtkTypeBool);
vtkBooleanMacro(ClampValue, vtkTypeBool);
SetClampValue设置插值夹逼类型,如果启用,插值结果将被钳制为输入数据的最小值/最大值。
样条区间是否打开
vtkSetMacro(Closed, vtkTypeBool);
vtkGetMacro(Closed, vtkTypeBool);
vtkBooleanMacro(Closed, vtkTypeBool);
SetClosed()设置是否开启闭合样条曲线,闭合样条曲线形成一个连续的循环:第一个点和最后一个点是相同的,导数是连续的。
vtkSetClampMacro(LeftConstraint, int, 0, 3);
vtkGetMacro(LeftConstraint, int);
vtkSetClampMacro(RightConstraint, int, 0, 3);
vtkGetMacro(RightConstraint, int);
vtkSetMacro(LeftValue, double);
vtkGetMacro(LeftValue, double);
vtkSetMacro(RightValue, double);
vtkGetMacro(RightValue, double);
SetLeftConstraint和SetRightConstraint设置左(右)端点的约束类型。
有四个约束:
值 | 含义 |
---|---|
0 | 最左(右)点的一阶导数由前(后)两点定义的直线确定 |
1 | 最左(右)点的一阶导数设为左(右)值 |
2 | 最左(右)点的二阶导数设为左(右)值 |
3 | 最左(右)点的二阶导数是左(右)值乘以第一个内点的二阶导数 |
SetLeftValue和SetRightValue用来设置左右两边导数的值。
vtkCardinalSpline
在Cardinal样条中,一个控制点的一阶导数值可以由两个相邻控制点的坐标进行计算。
一个Cardinal样条完全由四个连续控制点给出,中间两个控制点是曲线端点,另外两个点用于计算端点斜率。
如图14.12所示,设P(u)是两控制点Pk和Pk+1间的参数三次函数式,则从Pk-1到Pk+1间的四个控制点用于建立cardinal样条段的边界条件:
P(0) = pk;
P(1) = pk+1;
P’(0) =1/2*(1-t)(pk+1-pk-1);
P’(1) =1/2(1-t)*(pk+2-pk);
控制点Pk和Pk+1处的一阶导数(斜率)分别和弦Pk-1Pk+1和PkPk+2成正比。参数t称为张量(tension)参数,因为t控制cardinal样条与输入控制点之间的松紧程度。图14.14说明了张量t取很大和很小值时cardinal曲线的形状。当t=0时,这样的曲线称为Catmull-Rom样条或Overhauser样条。
接口
在这里插入代码片
vtkKochanekSpline
vtkKochanekSpline提供三个参数来控制花键的形状:
张力:更改切线向量的长度;
偏差:主要改变切向量的方向;
连续性:改变切线之间变化的清晰度;
给出了四个连续控制点,记为Pk-1、Pk、Pk+1和Pk+2,在Pk和Pk+1间的Konchanek-Bartels曲线段中的边界条件定义为:
P(0) = pk;
P(1) = pk+1;
P’(0)in =1/2*(1-t*)[(1+b)(1-c)*(pk-pk-1)+(1-b)+(1-b)*(1+c)*(pk+1-pk)];
P’(1)out 1/2*(1-t*)[(1+b)(1+c)*(pk+1-pk)+(1-b)+(1-b)*(1-c)*(pk+2-pk+1)];
其中t是张量(tension)参数,b是偏离(bias)参数,c是连续性(continuity)参数。在Konchanek-Bartels公式中,导数在曲线段边界处不一定是连续。
张量参数t具有cardinal样条公式中同样的解释,即该参数控制曲线段的松紧程度。偏离参数b用来调整曲线段在端点处弯曲的数值,因此曲线段可以偏向一个端点或另一个端点(参见图14.19)。参数c控制切向量在曲线段边界处的连续性。若c取非零值,则曲线在曲线段边界处的斜率上具有不连续性。
#include <vtkActor.h>
#include <vtkGlyph3DMapper.h>
#include <vtkKochanekSpline.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkParametricFunctionSource.h>
#include <vtkParametricSpline.h>
#include <vtkPointSource.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSphereSource.h>
#include "vtkAutoInit.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
using namespace std;
int main()
{
vtkNew<vtkNamedColors> colors;
int numberOfPoints = 10;
vtkNew<vtkPointSource> pointSource;
pointSource->SetNumberOfPoints(numberOfPoints);
pointSource->Update();
vtkPoints* points = pointSource->GetOutput()->GetPoints();
vtkNew<vtkKochanekSpline> xSpline;
vtkNew<vtkKochanekSpline> ySpline;
vtkNew<vtkKochanekSpline> zSpline;
vtkNew<vtkParametricSpline> spline;
spline->SetXSpline(xSpline);
spline->SetYSpline(ySpline);
spline->SetZSpline(zSpline);
spline->SetPoints(points);
vtkNew<vtkParametricFunctionSource> functionSource;
functionSource->SetParametricFunction(spline);
functionSource->SetUResolution(50 * numberOfPoints);
functionSource->SetVResolution(50 * numberOfPoints);
functionSource->SetWResolution(50 * numberOfPoints);
functionSource->Update();
// Setup actor and mapper
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(functionSource->GetOutputPort());
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
actor->GetProperty()->SetColor(colors->GetColor3d("DarkSlateGrey").GetData());
actor->GetProperty()->SetLineWidth(3.0);
// Glyph the points
vtkNew<vtkSphereSource> sphere;
sphere->SetPhiResolution(21);
sphere->SetThetaResolution(21);
sphere->SetRadius(.02);
// Create a polydata to store everything in
vtkNew<vtkPolyData> polyData;
polyData->SetPoints(points);
vtkNew<vtkGlyph3DMapper> pointMapper;
pointMapper->SetInputData(polyData);
pointMapper->SetSourceConnection(sphere->GetOutputPort());
vtkNew<vtkActor> pointActor;
pointActor->SetMapper(pointMapper);
pointActor->GetProperty()->SetColor(colors->GetColor3d("Peacock").GetData());
// Setup render window, renderer, and interactor
vtkNew<vtkRenderer> renderer;
vtkNew<vtkRenderWindow> renderWindow;
renderWindow->AddRenderer(renderer);
renderWindow->SetWindowName("KochanekSpline");
vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
renderWindowInteractor->SetRenderWindow(renderWindow);
renderer->AddActor(actor);
renderer->AddActor(pointActor);
renderer->SetBackground(colors->GetColor3d("Silver").GetData());
renderWindow->Render();
renderWindowInteractor->Start();
return 0;
}
vtkSCurveSpline
vtkSCurveSpline 使用曲线基计算插值样条曲线。使用方法同vtkKochanekSpline;
示例
1.生成8条线段;
float x[8][3] = {
{0,0,0},{1,0,0},{1,1,0},{0,1,0},
{0,0,1},{1,0,1},{1,1,1},{0,1,1}
};
vtkNew<vtkPolyData> geometry;
vtkNew<vtkPoints> points;
for (size_t i = 0; i < 8; i++) {
points->InsertPoint(i, x[i]);
}
vtkNew<vtkCellArray> polys;
polys->InsertNextCell(8);
for (size_t i = 0; i < 8; i++) {
polys->InsertCellPoint(i);
}
geometry->SetPoints(points);
geometry->SetLines(polys);
vtkNew<vtkPolyDataMapper> geometryMapper;
geometryMapper->SetInputData(geometry);
vtkNew<vtkActor> geometryActor;
geometryActor->SetMapper(geometryMapper);
vtkNew<vtkRenderer> renderer;
renderer->AddActor(geometryActor);
renderer->ResetCamera();
renderer->SetBackground(0, 0, 0);
vtkNew<vtkRenderWindow> renWin;
renWin->AddRenderer(renderer);
renWin->SetSize(300, 300);
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin);
renWin->Render();
iren->Start();
2.使用样条插值后
float x[8][3] = {
{0,0,0},{1,0,0},{1,1,0},{0,1,0},
{0,0,1},{1,0,1},{1,1,1},{0,1,1}
};
vtkNew<vtkPolyData> geometry;
vtkNew<vtkPoints> points;
for (size_t i = 0; i < 8; i++) {
points->InsertPoint(i, x[i]);
}
vtkNew<vtkCellArray> polys;
polys->InsertNextCell(8);
for (size_t i = 0; i < 8; i++) {
polys->InsertCellPoint(i);
}
geometry->SetPoints(points);
geometry->SetLines(polys);
vtkNew<vtkCardinalSpline> spline;
spline->SetLeftConstraint(2);
spline->SetLeftValue(0.0);
spline->SetRightConstraint(2);
spline->SetRightValue(0.0);
vtkNew<vtkSplineFilter> filter;
filter->SetInputData(geometry);
filter->SetNumberOfSubdivisions(100);
filter->SetSpline(spline);
filter->Update();
vtkNew<vtkPolyDataMapper> geometryMapper;
geometryMapper->SetInputData(filter->GetOutput());
vtkNew<vtkActor> geometryActor;
geometryActor->SetMapper(geometryMapper);
vtkNew<vtkRenderer> renderer;
renderer->AddActor(geometryActor);
renderer->ResetCamera();
renderer->SetBackground(0, 0, 0);
vtkNew<vtkRenderWindow> renWin;
renWin->AddRenderer(renderer);
renWin->SetSize(300, 300);
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin);
renWin->Render();
iren->Start();
参考文献
1.vtkSpline
2.vtkCardinalSpline
3.vtkKochanekSpline
4.vtkSCurveSpline