vtkBoxWidget2 shows a bounding box outline, the face of the box can be picked and moved to scale the object inside the bounding box.
After rotating, translating, and scaling, we can ensure the inside item change shape and position with the box widget.
It sounds so good that user can widen and narrow the item conveniently by a visible bounding box.
#include <iostream>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkActor.h>
#include <vtkConeSource.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkLight.h>
#include <vtkCamera.h>
#include <vtkActor2D.h>
#include <vtkImplicitPlaneRepresentation.h>
#include <vtkImplicitPlaneWidget2.h>
#include <vtkClipPolyData.h>
#include <vtkPlane.h>
#include <vtkBoxWidget2.h>
#include <vtkBoxRepresentation.h>
#include <vtkTransform.h>
using namespace std;
// Callback for the interaction
class vtkBoxCallback2 : public vtkCommand
{
public:
static vtkBoxCallback2 *New()
{
return new vtkBoxCallback2;
}
virtual void Execute(vtkObject *caller, unsigned long, void*)
{
vtkBoxWidget2 *boxWidget = reinterpret_cast<vtkBoxWidget2*>(caller);
vtkBoxRepresentation *boxRep = reinterpret_cast<vtkBoxRepresentation*>(boxWidget->GetRepresentation());
boxRep->GetTransform( this->Transform );
this->Actor->SetUserTransform( this->Transform );
}
vtkTransform *Transform;
vtkActor *Actor;
};
int main()
{
vtkSmartPointer<vtkConeSource> cone =
vtkSmartPointer<vtkConeSource>::New();
cone->Update();
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection( cone->GetOutputPort() );
vtkSmartPointer<vtkActor> actor =
vtkSmartPointer<vtkActor>::New();
actor->SetMapper( mapper );
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(actor);
renderer->SetBackground( 0, 0, 0 );
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer( renderer );
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow( renderWindow );
vtkBoxRepresentation *boxRep = vtkBoxRepresentation::New();
boxRep->SetPlaceFactor( 1.25 );
boxRep->PlaceWidget(cone->GetOutput()->GetBounds());
vtkTransform *t = vtkTransform::New();
vtkBoxCallback2 *myCallback = vtkBoxCallback2::New();
myCallback->Transform = t;
myCallback->Actor = actor;
vtkBoxWidget2 *boxWidget = vtkBoxWidget2::New();
boxWidget->SetRepresentation( boxRep );
boxWidget->AddObserver(vtkCommand::InteractionEvent,myCallback);
boxWidget->SetInteractor( renderWindowInteractor );
boxWidget->EnabledOn();
renderer->ResetCamera();
renderWindow->Render();
renderWindowInteractor->Start();
t->Delete();
myCallback->Delete();
boxWidget->Delete();
return 0;
}
The algorithm generates a new transform after dragging face of the bounding box can be found in the VTK source.
It uses points on bounding box and center point to calculate the vector to translate and new normal to rotate (construct new matrix), relative zoom in/out rate to scale.
void vtkBoxRepresentation::GetTransform(vtkTransform *t)
{
double *pts =
static_cast<vtkDoubleArray *>(this->Points->GetData())->GetPointer(0);
double *p0 = pts;
double *p1 = pts + 3*1;
double *p3 = pts + 3*3;
double *p4 = pts + 3*4;
double *p14 = pts + 3*14;
double center[3], translate[3], scale[3], scaleVec[3][3];
double InitialCenter[3];
int i;
// The transformation is relative to the initial bounds.
// Initial bounds are set when PlaceWidget() is invoked.
t->Identity();
// Translation
for (i=0; i<3; i++)
{
InitialCenter[i] =
(this->InitialBounds[2*i+1]+this->InitialBounds[2*i]) / 2.0;
center[i] = p14[i] - InitialCenter[i];
}
translate[0] = center[0] + InitialCenter[0];
translate[1] = center[1] + InitialCenter[1];
translate[2] = center[2] + InitialCenter[2];
t->Translate(translate[0], translate[1], translate[2]);
// Orientation
this->Matrix->Identity();
this->PositionHandles();
this->ComputeNormals();
for (i=0; i<3; i++)
{
this->Matrix->SetElement(i,0,this->N[1][i]);
this->Matrix->SetElement(i,1,this->N[3][i]);
this->Matrix->SetElement(i,2,this->N[5][i]);
}
t->Concatenate(this->Matrix);
// Scale
for (i=0; i<3; i++)
{
scaleVec[0][i] = (p1[i] - p0[i]);
scaleVec[1][i] = (p3[i] - p0[i]);
scaleVec[2][i] = (p4[i] - p0[i]);
}
scale[0] = vtkMath::Norm(scaleVec[0]);
if (this->InitialBounds[1] != this->InitialBounds[0])
{
scale[0] = scale[0] / (this->InitialBounds[1]-this->InitialBounds[0]);
}
scale[1] = vtkMath::Norm(scaleVec[1]);
if (this->InitialBounds[3] != this->InitialBounds[2])
{
scale[1] = scale[1] / (this->InitialBounds[3]-this->InitialBounds[2]);
}
scale[2] = vtkMath::Norm(scaleVec[2]);
if (this->InitialBounds[5] != this->InitialBounds[4])
{
scale[2] = scale[2] / (this->InitialBounds[5]-this->InitialBounds[4]);
}
t->Scale(scale[0],scale[1],scale[2]);
// Add back in the contribution due to non-origin center
t->Translate(-InitialCenter[0], -InitialCenter[1], -InitialCenter[2]);
}