执行各种平面计算;
vtkPlane提供了各种平面计算的方法。其中包括将点投影到平面上、计算平面方程和返回平面法线。
vtkPlane是抽象类vtkImplicitFunction的具体实现。
之前学到的知识可以知道,平面可以使用一个点和过这个点与平面垂直的法向量来确定,也可以使用一个点和过这个点不平行的两条直线确定;
vtkPlane中记录了点的坐标Origin和法向量信息Normal,确定了唯一的平面;
double Normal[3];
double Origin[3];
vtkSetVector3Macro(Normal, double);
vtkGetVectorMacro(Normal, double, 3);
vtkSetVector3Macro(Origin, double);
vtkGetVectorMacro(Origin, double, 3);
按指定的距离沿法线方向平移平面。
负值使平面朝相反方向移动。
void Push(double distance);
静态函数ProjectPoint,可以计算给定点x在给定平面上的投影点坐标xproj;
成员函数ProjectPoint,可以计算给定点x在当前平面上的投影点坐标xproj;
GeneralizedProjectPoint同ProjectPoint;
注意:normal不必归一化处理;
static void ProjectPoint(const double x[3], const double origin[3], const double normal[3], double xproj[3]);
void ProjectPoint(const double x[3], double xproj[3]);
static void GeneralizedProjectPoint(const double x[3], const double origin[3],
const double normal[3], double xproj[3]);
void GeneralizedProjectPoint(const double x[3], double xproj[3]);
判断点是否满足平面公式,可以用来判断一个点是否在平面上;Evaluate返回值为0表示x在normal和origin表示的平面上;
inline double vtkPlane::Evaluate(double normal[3], double origin[3], double x[3])
{
return normal[0] * (x[0] - origin[0]) + normal[1] * (x[1] - origin[1]) + normal[2] * (x[2] - origin[2]);
}
返回点x到由n*(x-p0)=0定义的平面的距离。
注意:法线n[3]必须是幅值=1。
inline double vtkPlane::DistanceToPlane(double x[3], double n[3], double p0[3])
{
#define vtkPlaneAbs(x) ((x) < 0 ? -(x) : (x))
return (vtkPlaneAbs(n[0] * (x[0] - p0[0]) + n[1] * (x[1] - p0[1]) + n[2] * (x[2] - p0[2])));
}
过点p1和点p2的直线相交平面(过P0点,平面法向量为n)于点x;IntersectWithLine可以根据直线和平面信息计算出相交点x的坐标,t用来返回结果信息,当0<= t <=1时,直线与平面不相交;当t为VTK_LARGE_DOUBLE时,表示与平面平行,永不相交;
static int IntersectWithLine(const double p1[3], const double p2[3],
double n[3], double p0[3], double& t, double x[3]);
int IntersectWithLine(const double p1[3], const double p2[3], double& t, double x[3]);
#include
#include "vtkAutoInit.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
using namespace std;
int main()
{
vtkNew<vtkPlane> plane;
plane->SetOrigin(0.0, 0.0, 0.0);
plane->SetNormal(0.0, 0.0, 1.0);
double p[3] = { 23.1, 54.6, 9.2 };
double origin[3] = { 0.0, 0.0, 0.0 };
double normal[3] = { 0.0, 0.0, 1.0 };
double projected[3];
//plane->ProjectPoint(p, projected);
plane->ProjectPoint(p, origin, normal, projected);
std::cout << "Projected: " << projected[0] << " " << projected[1] << " "<< projected[2] << std::endl;
system("pause");
return 0;
}
平面凸集的隐函数;
vtkPlanes计算一组平面的隐式函数和函数梯度。这些平面必须定义一个凸空间。
函数值是通过评估每个提供的平面获得的交点(即,最大值)。因此,该值是点到由平面定义的凸区域的最大距离。函数梯度是函数值处的平面法线。请注意,法线必须指向凸区域之外。因此,负函数值意味着点在凸区域内。
有几种方法可以定义平面集。最常用的方法是提供一个vtkPoints实例和一个vtkDataArray实例(点定义平面上的点,法线对应平面法线。)其他两种专门的方法是:
1)提供六个定义摄影机视锥的平面;2)提供边界框;
可以从相机接口 vtkCamera::GetFrustumPlanes()获取视锥体面信息,调用函数SetFrustumPlanes来生成6平面组成的视锥体面组合;
double Planes[24];
void SetFrustumPlanes(double planes[24]);
指定由边界框定义的六个平面的替代方法。
边界框是定义为(xmin,xmax,ymin,ymax,zmin,zmax)的六个向量。它定义了与x-y-z坐标轴正交的六个平面。
double Bounds[6];
void SetBounds(const double bounds[6]);
void SetBounds(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax);
vtkPoints* Points;
vtkDataArray* Normals;
vtkPlane* Plane;
virtual void SetPoints(vtkPoints*);
vtkGetObjectMacro(Points, vtkPoints);
void SetNormals(vtkDataArray* normals);
vtkGetObjectMacro(Normals, vtkDataArray);
int GetNumberOfPlanes();
vtkPlane* GetPlane(int i);
void GetPlane(int i, vtkPlane* plane);
Points表示每个平面过的点的集合;
Normals表示每个平面的法向量集合;
Plane表示每个平面;
可以使用GetNumberOfPlanes获取vtkPlanes中平面个数;
可以使用GetPlane获取平面vtkPlane;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "vtkAutoInit.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkInteractionStyle);
using namespace std;
int main(){
vtkNew<vtkNamedColors> colors;
std::vector<std::string> titles{ "Using frustum planes", "Using bounds" };
std::vector<vtkSmartPointer<vtkPlanes>> planes;
for (auto i = 0; i < titles.size(); ++i) {
planes.push_back(vtkSmartPointer<vtkPlanes>::New());
}
// 1. Using frustum planes.
vtkNew<vtkCamera> camera;
double planesArray[24];
camera->GetFrustumPlanes(1, planesArray);
planes[0]->SetFrustumPlanes(planesArray);
// 2. Using bounds.
vtkNew<vtkSphereSource> sphereSource;
sphereSource->Update();
double bounds[6];
sphereSource->GetOutput()->GetBounds(bounds);
planes[1]->SetBounds(bounds);
// Create a common text property.
vtkNew<vtkTextProperty> textProperty;
textProperty->SetFontSize(16);
textProperty->SetJustificationToCentered();
// Create the render window and interactor.
vtkNew<vtkRenderWindow> renWin;
renWin->SetWindowName("Planes");
vtkNew<vtkRenderWindowInteractor> iRen;
iRen->SetRenderWindow(renWin);
std::vector<vtkSmartPointer<vtkHull>> hulls;
std::vector<vtkSmartPointer<vtkPolyData>> pds;
std::vector<vtkSmartPointer<vtkPolyDataMapper>> mappers;
std::vector<vtkSmartPointer<vtkActor>> actors;
std::vector<vtkSmartPointer<vtkTextMapper>> textMappers;
std::vector<vtkSmartPointer<vtkActor2D>> textActors;
std::vector<vtkSmartPointer<vtkRenderer>> renderers;
for (auto i = 0; i < titles.size(); ++i) {
hulls.push_back(vtkSmartPointer<vtkHull>::New());
hulls[i]->SetPlanes(planes[i]);
pds.push_back(vtkSmartPointer<vtkPolyData>::New());
hulls[i]->GenerateHull(pds[i], -200, 200, -200, 200, -200, 200);
mappers.push_back(vtkSmartPointer<vtkPolyDataMapper>::New());
mappers[i]->SetInputData(pds[i]);
actors.push_back(vtkSmartPointer<vtkActor>::New());
actors[i]->SetMapper(mappers[i]);
actors[i]->GetProperty()->SetColor(colors->GetColor3d("Moccasin").GetData());
actors[i]->GetProperty()->SetSpecular(0.8);
actors[i]->GetProperty()->SetSpecularPower(30);
textMappers.push_back(vtkSmartPointer<vtkTextMapper>::New());
textMappers[i]->SetInput(titles[i].c_str());
textMappers[i]->SetTextProperty(textProperty);
textActors.push_back(vtkSmartPointer<vtkActor2D>::New());
textActors[i]->SetMapper(textMappers[i]);
textActors[i]->SetPosition(120, 16);
renderers.push_back(vtkSmartPointer<vtkRenderer>::New());
renderers[i]->AddActor(actors[i]);
renderers[i]->AddViewProp(textActors[i]);
renWin->AddRenderer(renderers[i]);
}
// Setup the viewports
auto xGridDimensions = 2;
auto yGridDimensions = 1;
auto rendererSize = 300;
renWin->SetSize(rendererSize * xGridDimensions, rendererSize * yGridDimensions);
for (auto row = 0; row < yGridDimensions; ++row){
for (auto col = 0; col < xGridDimensions; ++col){
auto index = row * xGridDimensions + col;
// (xmin, ymin, xmax, ymax)
double viewport[4] = {
static_cast<double>(col) / xGridDimensions,
static_cast<double>(yGridDimensions - (row + 1)) / yGridDimensions,
static_cast<double>(col + 1) / xGridDimensions,
static_cast<double>(yGridDimensions - row) / yGridDimensions };
if (index > (actors.size() - 1)){
// Add a renderer even if there is no actor.
// This makes the render window background all the same color.
vtkSmartPointer<vtkRenderer> ren = vtkSmartPointer<vtkRenderer>::New();
ren->SetBackground(colors->GetColor3d("DarkSlateGray").GetData());
ren->SetViewport(viewport);
renWin->AddRenderer(ren);
continue;
}
renderers[index]->SetViewport(viewport);
renderers[index]->SetBackground(colors->GetColor3d("DarkSlateGray").GetData());
renderers[index]->ResetCamera();
renderers[index]->GetActiveCamera()->Azimuth(30);
renderers[index]->GetActiveCamera()->Elevation(-30);
renderers[index]->ResetCameraClippingRange();
}
}
iRen->Initialize();
renWin->Render();
iRen->Start();
system("pause");
return 0;
}
创建位于平面中的四边形数组;
vtkPlaneSource创建一个由四边形组成的m*n数组,这些四边形排列为平面中的规则平铺。该平面是通过指定一个原点来定义的,然后指定另外两个与原点一起定义平面的两个轴的点。这些轴不必是正交的-所以你可以创建一个平行四边形(轴不能平行。)平面的分辨率(即细分的数量)由X分辨率和Y分辨率控制。
默认情况下,平面以原点为中心并垂直于z轴,宽度和高度的长度为1,分辨率设置为1。
该类中有三个便利的方法(即成员函数),帮助你很容易的移动平面。首先,SetNormal(), 允许你指定平面的法向量(plane normal)。这个方法的效果,就是使平面绕着平面中心转动,指向你所规定的方向。所谓的旋转,就是通过将当前平面的法向量与你所指定的法向量叉乘来实现的。第二,SetCenter(), 可以实现平面平移到你所指定的中心点坐标。第三, Push(), 是你能够将平面沿着法向量方向平移你所指定的距离,这是个非常有用的函数方法。如果Push的参数是负值,平面就沿着法向量的反方向移动。注意, SetNormal(), SetCenter() and Push() 这三个方法,修改了Origin、Point1和Point2实例变量。
注意:SetNormal()、SetCenter()和Push()方法修改了Origin、Point1和/或Point2实例变量。
注意:平面的法线将指向第一个轴(原点->点1)与第二个轴(原点->点2)的叉积方向。这也会影响生成的多边形的法线。
Origin–>Point1构成了X方向;Origin–>Point2构成了Y方向;
平面的范围就是Origin,Point1,Point2,组成的平行四边形;
double Origin[3];
double Point1[3];
double Point2[3];
vtkSetVector3Macro(Origin, double);
vtkGetVectorMacro(Origin, double, 3);
void SetPoint1(double x, double y, double z);
void SetPoint1(double pnt[3]);
vtkGetVectorMacro(Point1, double, 3);
void SetPoint2(double x, double y, double z);
void SetPoint2(double pnt[3]);
vtkGetVectorMacro(Point2, double, 3);
vtkSetMacro(XResolution, int);
vtkGetMacro(XResolution, int);
vtkSetMacro(YResolution, int);
vtkGetMacro(YResolution, int);
void SetResolution(const int xR, const int yR);
void GetResolution(int& xR, int& yR)
{
xR = this->XResolution;
yR = this->YResolution;
}
平面的方向与平面中心一起确定平面。
注意:不要使用此方法定义平面。相反,使用它围绕当前中心点旋转平面。
double Normal[3];
double Center[3];
void SetCenter(double x, double y, double z);
void SetCenter(double center[3]);
vtkGetVectorMacro(Center, double, 3);
void SetNormal(double nx, double ny, double nz);
void SetNormal(double n[3]);
vtkGetVectorMacro(Normal, double, 3);
void Push(double distance);
void Rotate(double angle, double rotationAxis[3]);
设置/获取输出点所需的精度。
vtkAlgorithm::SINGLE_PRECISION - Output single-precision floating point.
vtkAlgorithm::DOUBLE_PRECISION - Output double-precision floating point.
int OutputPointsPrecision;
vtkSetMacro(OutputPointsPrecision, int);
vtkGetMacro(OutputPointsPrecision, int);
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace {
vtkSmartPointer<vtkPolyData> CreateArrow(double& pdLength,std::array<double, 3>& startPoint,
std::array<double, 3>& endPoint)
{
vtkSmartPointer<vtkPolyData> polyData;
// Create an arrow.
vtkNew<vtkArrowSource> arrowSource;
arrowSource->SetShaftRadius(pdLength * .01);
arrowSource->SetShaftResolution(20);
arrowSource->SetTipLength(pdLength * .1);
arrowSource->SetTipRadius(pdLength * .05);
arrowSource->SetTipResolution(20);
// Compute a basis
std::array<double, 3> normalizedX;
std::array<double, 3> normalizedY;
std::array<double, 3> normalizedZ;
// The X axis is a vector from start to end
vtkMath::Subtract(endPoint.data(), startPoint.data(), normalizedX.data());
double length = vtkMath::Norm(normalizedX.data());
vtkMath::Normalize(normalizedX.data());
// The Z axis is an arbitrary vector cross X
vtkNew<vtkMinimalStandardRandomSequence> rng;
rng->SetSeed(8775070); // For testing.
std::array<double, 3> arbitrary;
for (auto i = 0; i < 3; ++i)
{
rng->Next();
arbitrary[i] = rng->GetRangeValue(-10, 10);
}
vtkMath::Cross(normalizedX.data(), arbitrary.data(), normalizedZ.data());
vtkMath::Normalize(normalizedZ.data());
// The Y axis is Z cross X
vtkMath::Cross(normalizedZ.data(), normalizedX.data(), normalizedY.data());
vtkNew<vtkMatrix4x4> matrix;
// Create the direction cosine matrix
matrix->Identity();
for (auto i = 0; i < 3; i++)
{
matrix->SetElement(i, 0, normalizedX[i]);
matrix->SetElement(i, 1, normalizedY[i]);
matrix->SetElement(i, 2, normalizedZ[i]);
}
// Apply the transforms
vtkNew<vtkTransform> transform;
transform->Translate(startPoint.data());
transform->Concatenate(matrix);
transform->Scale(length, length, length);
// Transform the polydata
vtkNew<vtkTransformPolyDataFilter> transformPD;
transformPD->SetTransform(transform);
transformPD->SetInputConnection(arrowSource->GetOutputPort());
transformPD->Update();
polyData = transformPD->GetOutput();
return polyData;
}
}
class Test_PlaneSource
{
public:
static void Test()
{
vtkNew<vtkNamedColors> colors;
vtkColor3d backgroundColor = colors->GetColor3d("DarkSlateGray");
vtkColor3d legendBackgroundColor = colors->GetColor3d("SlateGray");
vtkColor3d originColor = colors->GetColor3d("OrangeRed");
vtkColor3d centerColor = colors->GetColor3d("Gold");
vtkColor3d point1Color = colors->GetColor3d("MediumSpringGreen");
vtkColor3d point2Color = colors->GetColor3d("Brown");
vtkColor3d xAxisColor = colors->GetColor3d("lime");
vtkColor3d yAxisColor = colors->GetColor3d("orange");
vtkColor3d normalColor = colors->GetColor3d("Raspberry");
// Create actors
vtkNew<vtkPlaneSource> planeSource;
planeSource->SetOrigin(0.0, 0.0, 0.0);
planeSource->SetPoint1(1, 0, 0);
planeSource->SetPoint2(0, 1, 0);
planeSource->Update();
std::array<double, 6> bounds;
planeSource->GetOutput()->GetBounds(bounds.data());
double length = std::max(bounds[1] - bounds[0], bounds[3] - bounds[2]);
vtkNew<vtkPolyDataMapper> planeMapper;
planeMapper->SetInputConnection(planeSource->GetOutputPort());
vtkNew<vtkActor> planeActor;
planeActor->SetMapper(planeMapper);
vtkNew<vtkSphereSource> sphereSource;
sphereSource->SetRadius(length * .04);
vtkNew<vtkPolyDataMapper> originMapper;
originMapper->SetInputConnection(sphereSource->GetOutputPort());
vtkNew<vtkActor> originActor;
originActor->SetPosition(planeSource->GetOrigin());
originActor->SetMapper(originMapper);
originActor->GetProperty()->SetDiffuseColor(originColor.GetData());
vtkNew<vtkPolyDataMapper> centerMapper;
centerMapper->SetInputConnection(sphereSource->GetOutputPort());
vtkNew<vtkActor> centerActor;
centerActor->SetPosition(planeSource->GetCenter());
centerActor->SetMapper(centerMapper);
centerActor->GetProperty()->SetDiffuseColor(centerColor.GetData());
vtkNew<vtkPolyDataMapper> point1Mapper;
point1Mapper->SetInputConnection(sphereSource->GetOutputPort());
vtkNew<vtkActor> point1Actor;
point1Actor->SetPosition(planeSource->GetPoint1());
point1Actor->SetMapper(point1Mapper);
point1Actor->GetProperty()->SetDiffuseColor(point1Color.GetData());
vtkNew<vtkPolyDataMapper> point2Mapper;
point2Mapper->SetInputConnection(sphereSource->GetOutputPort());
vtkNew<vtkActor> point2Actor;
point2Actor->SetPosition(planeSource->GetPoint2());
point2Actor->SetMapper(point2Mapper);
point2Actor->GetProperty()->SetDiffuseColor(point2Color.GetData());
std::array<double, 3> center;
std::array<double, 3> origin;
std::array<double, 3> normal;
std::array<double, 3> point1;
std::array<double, 3> point2;
for (auto i = 0; i < 3; ++i)
{
point1[i] = planeSource->GetPoint1()[i];
point2[i] = planeSource->GetPoint2()[i];
origin[i] = planeSource->GetOrigin()[i];
center[i] = planeSource->GetCenter()[i];
normal[i] = planeSource->GetNormal()[i] * length;
}
auto xAxisPolyData = CreateArrow(length, origin, point1);
vtkNew<vtkPolyDataMapper> xAxisMapper;
xAxisMapper->SetInputData(xAxisPolyData);
vtkNew<vtkActor> xAxisActor;
xAxisActor->SetMapper(xAxisMapper);
xAxisActor->GetProperty()->SetDiffuseColor(xAxisColor.GetData());
auto yAxisPolyData = CreateArrow(length, origin, point2);
vtkNew<vtkPolyDataMapper> yAxisMapper;
yAxisMapper->SetInputData(yAxisPolyData);
vtkNew<vtkActor> yAxisActor;
yAxisActor->SetMapper(yAxisMapper);
yAxisActor->GetProperty()->SetDiffuseColor(yAxisColor.GetData());
auto normalPolyData = CreateArrow(length, origin, normal);
vtkNew<vtkPolyDataMapper> normalMapper;
normalMapper->SetInputData(normalPolyData);
vtkNew<vtkActor> normalActor;
normalActor->SetMapper(normalMapper);
normalActor->GetProperty()->SetDiffuseColor(normalColor.GetData());
// Create the RenderWindow, Renderer
vtkNew<vtkRenderer> renderer;
vtkNew<vtkLegendBoxActor> legend;
legend->SetNumberOfEntries(7);
legend->UseBackgroundOn();
legend->SetBackgroundColor(legendBackgroundColor.GetData());
legend->GetPositionCoordinate()->SetValue(.7, .7);
legend->GetPosition2Coordinate()->SetValue(.3, .3);
int entry = 0;
legend->SetEntry(entry++, sphereSource->GetOutput(), "center", centerColor.GetData());
legend->SetEntry(entry++, sphereSource->GetOutput(), "origin", originColor.GetData());
legend->SetEntry(entry++, sphereSource->GetOutput(), "point1", point1Color.GetData());
legend->SetEntry(entry++, sphereSource->GetOutput(), "point2", point2Color.GetData());
legend->SetEntry(entry++, xAxisPolyData, "xAxis", xAxisColor.GetData());
legend->SetEntry(entry++, xAxisPolyData, "yAxis", yAxisColor.GetData());
legend->SetEntry(entry++, xAxisPolyData, "normal", normalColor.GetData());
vtkNew<vtkRenderWindow> renderWindow;
renderWindow->AddRenderer(renderer);
renderWindow->SetWindowName("PlaneSourceDemo");
renderWindow->SetSize(640, 480);
vtkNew<vtkRenderWindowInteractor> interactor;
interactor->SetRenderWindow(renderWindow);
renderer->SetBackground(backgroundColor.GetData());
renderer->AddActor(planeActor);
renderer->AddActor(originActor);
renderer->AddActor(centerActor);
renderer->AddActor(point1Actor);
renderer->AddActor(point2Actor);
renderer->AddActor(xAxisActor);
renderer->AddActor(yAxisActor);
renderer->AddActor(normalActor);
renderer->AddActor(legend);
renderWindow->Render();
renderer->GetActiveCamera()->SetPosition(1, 0, 0);
renderer->GetActiveCamera()->SetFocalPoint(0, 1, 0);
renderer->GetActiveCamera()->SetViewUp(0, 0, 1);
renderer->GetActiveCamera()->Azimuth(30);
renderer->GetActiveCamera()->Elevation(30);
renderer->ResetCamera();
renderWindow->Render();
interactor->Start();
return ;
}
};
数据结果为一个正方型,原点Origin为(0,0,0);Point1为(1,0,0);Point2为(0,1,0);
由Origin指向Point1是X轴;
由Origin指向Point2是Y轴;
当设置Point1为(1,0.5,0);Point2为(0,1,0)时,面的形状时一个平行四边形;
1.vtkPlaneSource 平面
2.PlaneSourceDemo
3.vtkPlane Class Reference