The article shows a way to find the bounding shell of the 3D model in its local coordinate system.
Let’s create a cone and an axes actor which displays three axes in the world coordinate system.
#include <iostream>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkActor.h>
#include <vtkConeSource.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkPolyDataMapper.h>
#include <vtkAxesActor.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkTransform.h>
#define vtkSPtr vtkSmartPointer
#define vtkSPtrNew(Var, Type) vtkSPtr<Type> Var = vtkSPtr<Type>::New();
using namespace std;
int main()
{
vtkSPtrNew( cone, vtkConeSource );
vtkSPtrNew( mapper, vtkPolyDataMapper );
mapper->SetInputConnection( cone->GetOutputPort() );
vtkSPtrNew( actor, vtkActor );
actor->SetMapper( mapper );
vtkSPtrNew( trans, vtkTransform );
trans->RotateZ( 45 );
trans->Update();
actor->SetUserTransform( trans );
vtkSPtrNew( axesActor, vtkAxesActor );
vtkSPtrNew( renderer, vtkRenderer );
renderer->AddActor( actor );
renderer->AddActor( axesActor );
renderer->SetBackground( 0, 0, 0 );
vtkSPtrNew( renderWindow, vtkRenderWindow );
renderWindow->AddRenderer( renderer );
vtkSPtrNew( renderWindowInteractor, vtkRenderWindowInteractor );
renderWindowInteractor->SetRenderWindow( renderWindow );
renderer->ResetCamera();
renderWindow->Render();
renderWindowInteractor->Start();
return 0;
}
I assume that we know the direction of the three-axis and origin, the polydata of the cone is really like the above image.
Now let’s start to find the six corners and generate a bounding shell in the local coordinate system.
Steps:
1. Create a clip plane whose origin is (0, 0, 0) and normal is axes vector in the local coordinate system.
2. Find the positive farthest point and the negative farthest point from the clip plane.
3. Use the found six points to generate corner points on the bounding shell.
4. Connect these corner points and generate the shell’s polydata.
All implementation details are showed in the following code snippet.
#include <iostream>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkActor.h>
#include <vtkConeSource.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkPolyDataMapper.h>
#include <vtkAxesActor.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkTransform.h>
#include <vtkPlane.h>
#include <vtkProperty.h>
#include <vtkCylinderSource.h>
#include <vtkTransformFilter.h>
#include "../share/tool.h"
#define vtkSPtr vtkSmartPointer
#define vtkSPtrNew(Var, Type) vtkSPtr<Type> Var = vtkSPtr<Type>::New();
using namespace std;
vtkSPtr<vtkPolyData> meshData;
PointStruct origin( 0, 0, 0 );
PointStruct xDir( 1, 0, 0 );
PointStruct yDir( 0, 1, 0 );
PointStruct zDir( 0, 0, 1 );
vtkSPtr<vtkActor> shellActor;
vtkSPtr<vtkRenderer> renderer;
void FindFarPtsFromPlane(vtkSmartPointer<vtkPlane> plane, PointStruct &posPt, PointStruct &negPt)
{
double maxValue[2] = { VTK_DOUBLE_MIN, VTK_DOUBLE_MIN };
for( int i = 0; i < meshData->GetNumberOfPoints(); ++i )
{
PointStruct tmp( meshData->GetPoint( i ) );
double value = plane->EvaluateFunction( tmp.point );
if( value > 0 && value > maxValue[0] )
{
maxValue[0] = value;
posPt = tmp;
}
if( value < 0 && fabs(value) > maxValue[1] )
{
maxValue[1] = fabs(value);
negPt = tmp;
}
}
}
PointStruct GetProjectPtOnDir(PointStruct pt, PointStruct dir, PointStruct startPt)
{
dir.Unit();
PointStruct ptVector = pt - startPt;
double cosTheta = ptVector.Dot( dir );
PointStruct resultPt = startPt + dir * cosTheta;
return resultPt;
}
void ShowPoint( PointStruct pt )
{
vtkSPtrNew( sphere, vtkSphereSource );
sphere->SetCenter( pt.point );
sphere->SetRadius( 0.1 );
sphere->Update();
vtkSPtrNew( mapper, vtkPolyDataMapper );
mapper->SetInputData( sphere->GetOutput() );
vtkSPtrNew( actor, vtkActor );
actor->SetMapper( mapper );
actor->GetProperty()->SetColor( 1, 0, 0 );
renderer->AddActor( actor );
}
void FindAllCorners(vtkSmartPointer<vtkPoints> points)
{
vtkSPtrNew( xPlane, vtkPlane );
xPlane->SetOrigin( origin.point );
xPlane->SetNormal( xDir.point );
PointStruct xPts[2];
FindFarPtsFromPlane( xPlane, xPts[0], xPts[1] );
vtkSPtrNew( yPlane, vtkPlane );
yPlane->SetOrigin( origin.point );
yPlane->SetNormal( yDir.point );
PointStruct yPts[2];
FindFarPtsFromPlane( yPlane, yPts[0], yPts[1] );
//ShowPoint( yPts[0] ); ShowPoint( yPts[1] );
vtkSPtrNew( zPlane, vtkPlane );
zPlane->SetOrigin( origin.point );
zPlane->SetNormal( zDir.point );
PointStruct zPts[2];
FindFarPtsFromPlane( zPlane, zPts[0], zPts[1] );
xPts[0] = GetProjectPtOnDir( xPts[0], xDir, origin );
xPts[1] = GetProjectPtOnDir( xPts[1], xDir, origin );
yPts[0] = GetProjectPtOnDir( yPts[0], yDir, origin );
yPts[1] = GetProjectPtOnDir( yPts[1], yDir, origin );
zPts[0] = GetProjectPtOnDir( zPts[0], zDir, origin );
zPts[1] = GetProjectPtOnDir( zPts[1], zDir, origin );
PointStruct xVec[2] = { xPts[0] - origin, xPts[1] - origin };
PointStruct yVec[2] = { yPts[0] - origin, yPts[1] - origin };
PointStruct zVec[2] = { zPts[0] - origin, zPts[1] - origin };
PointStruct corners[8] = {
xVec[1] + yVec[1] + zVec[0] + origin,
xVec[0] + yVec[1] + zVec[0] + origin,
xVec[0] + yVec[1] + zVec[1] + origin,
xVec[1] + yVec[1] + zVec[1] + origin,
xVec[1] + yVec[0] + zVec[0] + origin,
xVec[0] + yVec[0] + zVec[0] + origin,
xVec[0] + yVec[0] + zVec[1] + origin,
xVec[1] + yVec[0] + zVec[1] + origin,
};
for( int i = 0; i < 8; ++i )
{
points->InsertNextPoint( corners[i].point );
}
}
void InsertCell( vtkSmartPointer<vtkCellArray> cell, vtkIdType *index )
{
vtkIdType pts1[3] = { index[0], index[2], index[1] };
cell->InsertNextCell( 3, pts1 );
vtkIdType pts2[3] = { index[2], index[0], index[3] };
cell->InsertNextCell( 3, pts2 );
}
void GenerateShell()
{
vtkSPtrNew( pts, vtkPoints );
FindAllCorners( pts );
vtkSPtrNew( pd, vtkPolyData );
pd->SetPoints( pts );
vtkSPtrNew( cells, vtkCellArray );
vtkIdType index0[4] = { 0, 1, 2, 3 }; // down
InsertCell( cells, index0 );
vtkIdType index1[4] = { 4, 7, 6, 5 }; // up
InsertCell( cells, index1 );
vtkIdType index2[4] = { 0, 4, 5, 1 }; // front
InsertCell( cells, index2 );
vtkIdType index3[4] = { 3, 2, 6, 7 }; // back
InsertCell( cells, index3 );
vtkIdType index4[4] = { 0, 3, 7, 4 }; // left
InsertCell( cells, index4 );
vtkIdType index5[4] = { 1, 5, 6, 2 }; // right
InsertCell( cells, index5 );
pd->SetPolys( cells );
shellActor->GetMapper()->SetInputDataObject( pd );
renderer->AddActor( shellActor );
}
int main()
{
vtkSPtrNew( trans, vtkTransform );
trans->RotateZ( 45 );
trans->Update();
vtkSPtrNew( cone, vtkConeSource );
cone->SetResolution( 20 );
cone->Update();
meshData = cone->GetOutput();
vtkSPtrNew( transF, vtkTransformFilter );
transF->SetInputData( meshData );
transF->SetTransform( trans );
transF->Update();
meshData->DeepCopy( transF->GetPolyDataOutput() );
vtkSPtrNew( mapper, vtkPolyDataMapper );
mapper->SetInputData( meshData );
vtkSPtrNew( actor, vtkActor );
actor->SetMapper( mapper );
trans->TransformVector( xDir.point, xDir.point );
trans->TransformVector( yDir.point, yDir.point );
trans->TransformVector( zDir.point, zDir.point );
vtkSPtrNew( axesActor, vtkAxesActor );
shellActor = vtkSPtr<vtkActor>::New();
vtkSPtrNew( shellMapper, vtkPolyDataMapper );
shellActor->SetMapper( shellMapper );
renderer = vtkSPtr<vtkRenderer>::New();
renderer->AddActor( actor );
renderer->AddActor( axesActor );
renderer->SetBackground( 0, 0, 0 );
vtkSPtrNew( renderWindow, vtkRenderWindow );
renderWindow->AddRenderer( renderer );
vtkSPtrNew( renderWindowInteractor, vtkRenderWindowInteractor );
renderWindowInteractor->SetRenderWindow( renderWindow );
GenerateShell();
renderer->ResetCamera();
renderWindow->Render();
renderWindowInteractor->Start();
return 0;
}
[…] Relevant link: VTK: Find Bounding Shell In Local Coordinate System […]