I had tried to show display coordinate bounds of the 3D model in the article
VTK: Show Display Bounds.
But the result is not good enough because the process that transforms the world coordinate point to display the coordinate point can generate error as we rotate the camera.
So I explore a new way to change the final effect.
I project all points to the view plane which is defined by the cone’s center and direction of the camera’s projection. Then convert the projected points to the new points in the display coordinate system.
Finally, compute the hull of these display coordinate points and show it by a red rectangle.
All implementation details are here.
main.cpp
#include <iostream>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkActor.h>
#include <vtkConeSource.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkPolyDataMapper.h>
#include <vtkXMLPolyDataReader.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkHull.h>
#include <vtkProperty.h>
#include <vtkCamera.h>
#include <vtkPlane.h>
#include <vtkSTLReader.h>
#include "Point.hpp"
#include "CustomIteractorStyle.h"
#define vtkSPtr vtkSmartPointer
#define vtkSPtrNew(Var, Type) vtkSPtr<Type> Var = vtkSPtr<Type>::New();
using namespace std;
vtkSmartPointer<vtkRenderer> renderer;
vtkSmartPointer<vtkCamera> camera;
int main()
{
vtkSPtrNew( reader, vtkXMLPolyDataReader );
reader->SetFileName( "simpleArch.vtp" );
reader->Update();
vtkSPtrNew( source, vtkConeSource );
source->Update();
vtkPolyData *polyData = source->GetOutput();
vtkSPtrNew( mapper, vtkPolyDataMapper );
mapper->SetInputData( polyData );
vtkSPtrNew( actor, vtkActor );
actor->SetMapper( mapper );
camera = vtkSmartPointer<vtkCamera>::New();
renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->SetActiveCamera( camera );
renderer->AddActor( actor );
renderer->SetBackground( 0, 0, 0 );
vtkSPtrNew( renderWindow, vtkRenderWindow );
renderWindow->AddRenderer( renderer );
vtkSPtrNew( renderWindowInteractor, vtkRenderWindowInteractor );
renderWindowInteractor->SetRenderWindow( renderWindow );
vtkSPtrNew( iteractorStyle, CustomIteractorStyle );
iteractorStyle->Setm_ConeActor( actor );
iteractorStyle->Setm_Renderer( renderer );
renderWindowInteractor->SetInteractorStyle( iteractorStyle );
renderer->ResetCamera();
renderWindow->Render();
renderWindowInteractor->Start();
return 0;
}
CustomIteractorStyle.h
#pragma once
#include <iostream>
#include <vtkSmartPointer.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkActor.h>
#include <vtkActor2D.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkTransform.h>
#include <vtkSphereSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkXMLPolyDataReader.h>
#include <vtkHull.h>
#include <vtkProperty.h>
#include <vtkCamera.h>
#include <vtkPlane.h>
#include "Point.hpp"
#define CPP_SET_MACRO(name,type) \
void Set##name(type _arg) \
{ \
if (this->name != _arg) \
{ \
this->name = _arg; \
} \
}
#define vtkSPtr vtkSmartPointer
#define vtkSPtrNew(Var, Type) vtkSPtr<Type> Var = vtkSPtr<Type>::New();
class CustomIteractorStyle: public vtkInteractorStyleTrackballCamera
{
public:
static CustomIteractorStyle *New(){ return new CustomIteractorStyle(); }
void OnMouseWheelForward() override;
void OnMouseWheelBackward() override;
void OnMouseMove() override;
CPP_SET_MACRO( m_ConeActor, vtkActor *)
CPP_SET_MACRO( m_Renderer, vtkRenderer* )
protected:
void ShowPoint( Point pt );
Point GetProjectPtOnDir(Point pt, Point dir, Point startPt);
void ProjectPointBasedOnCamera( vtkPolyData *polyData, Point center, vtkSmartPointer<vtkPoints> points );
void FetchFeatureEdge();
void ComputeDisplayBds(double *resultBounds, vtkSmartPointer<vtkPoints> points);
void UpdateDisplayRectangle();
CustomIteractorStyle();
~CustomIteractorStyle() override;
vtkSmartPointer<vtkActor2D> m_Actor2DDebug;
vtkActor *m_ConeActor;
vtkRenderer *m_Renderer;
vtkSmartPointer<vtkActorCollection> m_DebugActors;
};
CustomIteractorStyle.cpp
#include "CustomIteractorStyle.h"
#include <vtkCoordinate.h>
#include <vtkPolyDataMapper2D.h>
#include <vtkPoints.h>
#include <vtkCellArray.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty2D.h>
#include <vtkFeatureEdges.h>
void CustomIteractorStyle::OnMouseWheelForward()
{
vtkInteractorStyleTrackballCamera::OnMouseWheelForward();
UpdateDisplayRectangle();
}
void CustomIteractorStyle::OnMouseWheelBackward()
{
vtkInteractorStyleTrackballCamera::OnMouseWheelBackward();
UpdateDisplayRectangle();
}
void CustomIteractorStyle::OnMouseMove()
{
vtkInteractorStyleTrackballCamera::OnMouseMove();
UpdateDisplayRectangle();
}
void CustomIteractorStyle::ShowPoint(Point pt)
{
vtkSmartPointer<vtkSphereSource> sphere =
vtkSmartPointer<vtkSphereSource>::New();
sphere->SetThetaResolution( 100 );
sphere->SetPhiResolution( 50 );
sphere->SetRadius( 0.1 );
sphere->SetCenter( pt.point );
sphere->Update();
vtkSmartPointer<vtkPolyDataMapper> sphereMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
sphereMapper->SetInputData( sphere->GetOutput() );
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper( sphereMapper );
actor->GetProperty()->SetColor( 1, 0, 0 );
m_DebugActors->AddItem( actor );
m_Renderer->AddActor( actor );
}
Point CustomIteractorStyle::GetProjectPtOnDir(Point pt, Point dir, Point startPt)
{
dir.Unit();
Point ptVector = pt - startPt;
double cosTheta = ptVector.Dot( dir );
Point resultPt = startPt + dir * cosTheta;
return resultPt;
}
void CustomIteractorStyle::ProjectPointBasedOnCamera(vtkPolyData *polyData, Point center, vtkSmartPointer<vtkPoints> points)
{
vtkCamera *camera = m_Renderer->GetActiveCamera();
Point focal( camera->GetFocalPoint() );
Point pos( camera->GetPosition() );
Point viewDir = focal - pos; // same as camera->GetDirectionOfProjection()
viewDir.Unit();
vtkSPtrNew( plane, vtkPlane );
plane->SetOrigin( center.point );
plane->SetNormal( viewDir.point );
m_DebugActors->InitTraversal();
auto *item = m_DebugActors->GetNextItem();
while( item )
{
m_Renderer->RemoveActor( item );
item = m_DebugActors->GetNextItem();
}
points->Initialize();
for( int i = 0; i < polyData->GetNumberOfPoints(); ++i )
{
Point pt( polyData->GetPoint( i ) );
plane->ProjectPoint( pt.point, pt.point );
ShowPoint( pt );
points->InsertNextPoint( pt.point );
}
}
void CustomIteractorStyle::FetchFeatureEdge()
{
vtkPolyData *polyData = (vtkPolyData *)( m_ConeActor->GetMapper()->GetInput() );
vtkSmartPointer<vtkFeatureEdges> featureEdges = vtkSmartPointer<vtkFeatureEdges>::New();
featureEdges->SetInputData( polyData );
featureEdges->BoundaryEdgesOn();
featureEdges->FeatureEdgesOn();
featureEdges->ManifoldEdgesOff();
featureEdges->NonManifoldEdgesOff();
featureEdges->Update();
vtkPoints *points = featureEdges->GetOutput()->GetPoints();
for( int i = 0; i < points->GetNumberOfPoints(); ++i )
{
Point pt( points->GetPoint( i ) );
ShowPoint( pt );
}
}
void CustomIteractorStyle::ComputeDisplayBds(double *resultBounds, vtkSmartPointer<vtkPoints> points)
{
double displayBounds[6] = { VTK_DOUBLE_MAX, VTK_DOUBLE_MIN, VTK_DOUBLE_MAX,
VTK_DOUBLE_MIN, VTK_DOUBLE_MAX, VTK_DOUBLE_MIN };
for (int i = 0; i < points->GetNumberOfPoints(); i++)
{
Point coordinate( points->GetPoint( i ) );
double tmp[4] = { coordinate[0], coordinate[1], coordinate[2], 0 };
m_Renderer->SetWorldPoint( tmp );
m_Renderer->WorldToDisplay();
double displayPoint[3];
m_Renderer->GetDisplayPoint( displayPoint );
if (displayPoint[0] < displayBounds[0])
displayBounds[0] = displayPoint[0];
if (displayPoint[0] > displayBounds[1])
displayBounds[1] = displayPoint[0];
if (displayPoint[1] < displayBounds[2])
displayBounds[2] = displayPoint[1];
if (displayPoint[1] > displayBounds[3])
displayBounds[3] = displayPoint[1];
if (displayPoint[2] < displayBounds[4])
displayBounds[4] = displayPoint[2];
if (displayPoint[2] > displayBounds[5])
displayBounds[5] = displayPoint[2];
}
for( int i = 0; i < 6; ++i )
{
resultBounds[i] = displayBounds[i];
}
// -------------- debug ----------------
// ==== start to draw line ====
vtkSmartPointer<vtkPoints> linePoints =
vtkSmartPointer<vtkPoints>::New();
linePoints->InsertNextPoint( displayBounds[0], displayBounds[2], 0 );
linePoints->InsertNextPoint( displayBounds[1], displayBounds[2], 0 );
linePoints->InsertNextPoint( displayBounds[1], displayBounds[3], 0 );
linePoints->InsertNextPoint( displayBounds[0], displayBounds[3], 0 );
vtkSmartPointer<vtkCellArray> cells =
vtkSmartPointer<vtkCellArray>::New();
vtkIdType line0[2] = {0, 1};
cells->InsertNextCell(2, line0);
vtkIdType line1[2] = {1, 2};
cells->InsertNextCell(2, line1);
vtkIdType line2[2] = {2, 3};
cells->InsertNextCell(2, line2);
vtkIdType line3[2] = {3, 0};
cells->InsertNextCell(2, line3);
vtkSmartPointer<vtkPolyData> polydata =
vtkSmartPointer<vtkPolyData>::New();
polydata->SetPoints(linePoints);
polydata->SetLines(cells);
polydata->Modified();
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputData(polydata);
mapper->Update();
vtkSmartPointer<vtkCoordinate> coordinateIns =
vtkSmartPointer<vtkCoordinate>::New();
coordinateIns->SetCoordinateSystemToDisplay();
vtkSmartPointer<vtkPolyDataMapper2D> mapper2D =
vtkSmartPointer<vtkPolyDataMapper2D>::New();
mapper2D->SetInputData(polydata);
mapper2D->SetTransformCoordinate(coordinateIns);
mapper2D->Update();
m_Actor2DDebug->SetMapper( mapper2D );
m_Actor2DDebug->GetProperty()->SetColor( 1, 0, 0 );
m_Renderer->AddActor2D( m_Actor2DDebug );
// -------------- finish: debug ----------------
}
void CustomIteractorStyle::UpdateDisplayRectangle()
{
Point center( m_ConeActor->GetCenter() );
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
ProjectPointBasedOnCamera( (vtkPolyData *)(m_ConeActor->GetMapper()->GetInput()), center, points );
double bds[6];
ComputeDisplayBds( bds, points );
//FetchFeatureEdge();
}
CustomIteractorStyle::CustomIteractorStyle()
{
m_ConeActor = nullptr;
m_Renderer = nullptr;
m_Actor2DDebug = vtkSmartPointer<vtkActor2D>::New();
m_DebugActors = vtkSmartPointer<vtkActorCollection>::New();
}
CustomIteractorStyle::~CustomIteractorStyle()
{
m_ConeActor = nullptr;
}
Result:
The result is better than the old experiment:
VTK: Show Display Bounds. But the red rectangle can be bigger or smaller than the actual model’s boundary as we rotate the camera.
The error is still there because the step converts the point in the world coordinate system to the point in the display coordinate system can’t be omitted.
[…] have two old posts about draw display bounding box, VTK: Show Display Bounds After Project Points and VTK: Show Display Bounds. But they didn’t give me a statisfy result. In this post, I find […]