Here are two models cube and axes in the render view.
The axes actor had been rotated by 45 degree around Z asix, the cube had been rotated by 45 degree around Y asix after the same process. The following picture shows the final scene. All implementation details are also displayed.
#include <iostream>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkActor.h>
#include <vtkConeSource.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSTLReader.h>
#include <vtkClipClosedSurface.h>
#include <vtkFillHolesFilter.h>
#include <vtkAxesActor.h>
#include <vtkPlane.h>
#include <vtkTransformFilter.h>
#include <vtkTransform.h>
#include <vtkPlaneCollection.h>
#include <vtkAxesActor.h>
#include <vtkIntersectionPolyDataFilter.h>
#include <vtkProperty.h>
#include <vtkConeSource.h>
#include <vtkBooleanOperationPolyDataFilter.h>
#include <vtkCubeSource.h>
#include <vtkClipPolyData.h>
#include <vtkLineSource.h>
#include "point.hpp"
#define vtkSPtr vtkSmartPointer
#define vtkSPtrNew(Var, Type) vtkSPtr<Type> Var = vtkSPtr<Type>::New();
using namespace std;
int main()
{
vtkSPtrNew( source, vtkCubeSource );
source->Update();
auto *pd = source->GetOutput();
vtkSPtrNew( lineSource, vtkLineSource );
lineSource->SetPoint1( -3, 3, 0 );
lineSource->SetPoint2( 3, -3, 0 );
lineSource->Update();
vtkSPtrNew( lineMapper, vtkPolyDataMapper );
lineMapper->SetInputData( lineSource->GetOutput() );
vtkSPtrNew( lineActor, vtkActor );
lineActor->SetMapper( lineMapper );
lineActor->GetProperty()->SetColor( 1, 0, 0 );
Point vec( 1, -1, 0 );
vec.Unit();
cout << "vec: " << vec[0] << ", " << vec[1] << ", " << vec[2] << endl;
vtkSPtrNew( trans0, vtkTransform );
trans0->RotateZ( -45 );
trans0->RotateY( 45 );
trans0->Update();
vtkSPtrNew( trans1, vtkTransform );
trans1->RotateZ( -45 );
trans1->Update();
vtkSPtrNew( mapper, vtkPolyDataMapper );
mapper->SetInputData( pd );
vtkSPtrNew( actor, vtkActor );
actor->SetMapper( mapper );
actor->SetUserTransform( trans0 );
vtkSPtrNew( axesActor, vtkAxesActor );
axesActor->SetTotalLength( 3, 3, 3 );
axesActor->SetUserTransform( trans1 );
vtkSPtrNew( renderer, vtkRenderer );
renderer->AddActor( actor );
//renderer->AddActor( lineActor );
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;
}
Now I want to scale the cube twice along the rotated axis X which we will represented it as red line in the future.
As we know, the action scale works on the three directions axis X, axis Y and asix Z. So the scaling transform I want to create needs to rotate by 45 degree around asix Z, then apply scale(2, 1, 1). But the new transform will rotate cube surely. How to scale the cube without rotating or translating it?
I marks the red line as target scaling axis , represent the axis X in the world coordinate system as . I found the transform matrx from to , its inverse matrix is . I construct a scale transform finalScaleTrans.
vtkSPtrNew( scaleTrans, vtkTransform );
scaleTrans->Scale(2, 1, 1);
scaleTrans->Update();
vtkSPtrNew( finalScaleTrans, vtkTransform );
finalScaleTrans->Concatenate( M1 );
finalScaleTrans->Concatenate( scaleTrans );
finalScaleTrans->Concatenate( M1_Inv );
finalScaleTrans->Update();
I want it works as the my simple demo.
The following program scale the cube along vector(1, -1, 0).
.
int main()
{
vtkSPtrNew( source, vtkCubeSource );
source->Update();
auto pd = source->GetOutput();
vtkSPtrNew( trans0, vtkTransform );
trans0->RotateZ( -45 );
trans0->Scale( 2, 1, 1 );
trans0->RotateZ( 45 );
trans0->Update();
vtkSPtrNew( transFilter, vtkTransformFilter );
transFilter->SetInputData( pd );
transFilter->SetTransform( trans0 );
transFilter->Update();
vtkSPtrNew( mapper, vtkPolyDataMapper );
mapper->SetInputData( transFilter->GetPolyDataOutput() );
vtkSPtrNew( actor, vtkActor );
actor->SetMapper( mapper );
Point testVec( 1, 1, 0 );
trans0->TransformPoint( testVec.point, testVec.point );
cout << "testVec: " << testVec[0] << ", " << testVec[1] << ", " << testVec[2] << endl;
vtkSPtrNew( lineMapper, vtkPolyDataMapper );
vtkSPtrNew( lineActor, vtkActor );
lineActor->SetMapper( lineMapper );
lineActor->GetProperty()->SetColor( 1, 0, 0 );
vtkSPtrNew( axesActor, vtkAxesActor );
vtkSPtrNew( renderer, vtkRenderer );
renderer->AddActor( actor );
renderer->AddActor( axesActor );
renderer->AddActor( lineActor );
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;
}
Unfortunately the scale axis will change after we set userTransform, so we can’t get the correct result.
I find the acctual scale axis and mark it as red line.
Point vec( 1, -1, 0 );
vec.Unit();
cout << "vec: " << vec[0] << ", " << vec[1] << ", " << vec[2] << endl;
vtkSPtrNew( trans0, vtkTransform );
trans0->RotateZ( -45 );
trans0->RotateY( 45 );
trans0->Update();
vtkSPtrNew( trans1, vtkTransform );
trans1->RotateZ( -45 );
trans1->Update();
Point crossVec = Point(1, 0, 0) ^ vec;
auto dotValue = Point(1, 0, 0).Dot( vec );
auto radius = std::acos( dotValue );
auto degree = vtkMath::DegreesFromRadians( radius );
vtkSPtrNew( rotateTrans, vtkTransform );
rotateTrans->RotateWXYZ( -degree, crossVec.point );
rotateTrans->Update();
vtkSPtrNew( rotateTransInv, vtkTransform );
rotateTransInv->DeepCopy( rotateTrans );
rotateTransInv->Inverse();
rotateTransInv->Update();
vtkSPtrNew( scaleTrans, vtkTransform );
scaleTrans->Scale( 2, 1, 1 );
scaleTrans->Update();
vtkSPtrNew( finalScaleTrans, vtkTransform );
finalScaleTrans->Concatenate( rotateTrans );
finalScaleTrans->Concatenate( scaleTrans );
finalScaleTrans->Concatenate( rotateTransInv );
finalScaleTrans->Update();
vtkSPtrNew( transFilter, vtkTransformFilter );
transFilter->SetInputData( pd );
transFilter->SetTransform( finalScaleTrans );
transFilter->Update();
vtkSPtrNew( mapper, vtkPolyDataMapper );
mapper->SetInputData( transFilter->GetPolyDataOutput() );
vtkSPtrNew( actor, vtkActor );
actor->SetMapper( mapper );
actor->SetUserTransform( trans0 );
vtkSPtrNew( axesActor, vtkAxesActor );
axesActor->SetTotalLength( 3, 3, 3 );
axesActor->SetUserTransform( trans1 );
// =====> to find the scale axis <======
vtkSPtrNew( finalTestTrans, vtkTransform );
finalTestTrans->Concatenate( trans0 );
finalTestTrans->Concatenate( rotateTrans ); //finalScaleTrans );//rotateTrans );
finalTestTrans->Update();
Point testVec( 1, 0, 0 );
finalTestTrans->TransformVector( testVec.point, testVec.point );
testVec.Unit();
Point pt0( (-testVec*3) );
Point pt1( (testVec*3) );
vtkSPtrNew( lineSource, vtkLineSource );
lineSource->SetPoint1( pt0.point );
lineSource->SetPoint2( pt1.point );
lineSource->Update();
vtkSPtrNew( lineMapper, vtkPolyDataMapper );
lineMapper->SetInputData( lineSource->GetOutput() );
vtkSPtrNew( lineActor, vtkActor );
lineActor->SetMapper( lineMapper );
lineActor->GetProperty()->SetColor( 1, 0, 0 );
So I have to make rotateTrans smarter to counteract userTransform trans0’s effect.
We have to convert the vector to a new vector which represent it is in trans0’s inverse matrix.
Point vec( 1, -1, 0 );
vec.Unit();
vtkSPtrNew( trans0Inv, vtkTransform );
trans0Inv->DeepCopy( trans0 );
trans0Inv->Inverse();
trans0Inv->Update();
trans0Inv->TransformVector( vec.point, vec.point );
cout << "vec: " << vec[0] << ", " << vec[1] << ", " << vec[2] << endl;
Point crossVec = Point(1, 0, 0)^vec;
auto dotValue = Point(1, 0, 0).Dot( vec );
auto radius = std::acos( dotValue );
auto degree = vtkMath::DegreesFromRadians( radius );
// ...
The code snippet is here.
int main()
{
vtkSPtrNew( source, vtkCubeSource );
source->Update();
auto *pd = source->GetOutput();
Point center( 2, 2, 1 );
Point vec( 1, -1, 0 );
vec.Unit();
cout << "vec: " << vec[0] << ", " << vec[1] << ", " << vec[2] << endl;
vtkSPtrNew( trans0, vtkTransform );
trans0->Translate( center.point );
trans0->RotateZ( -45 );
trans0->RotateY( 45 );
trans0->Translate( center.point );
trans0->Update();
vtkSPtrNew( trans1, vtkTransform );
trans1->RotateZ( -45 );
trans1->Update();
vtkSPtrNew( trans0Inv, vtkTransform );
trans0Inv->DeepCopy( trans0 );
trans0Inv->Inverse();
trans0Inv->Update();
trans0Inv->TransformVector( vec.point, vec.point );
Point crossVec = Point(1, 0, 0) ^ vec;
auto dotValue = Point(1, 0, 0).Dot( vec );
auto radius = std::acos( dotValue );
auto degree = vtkMath::DegreesFromRadians( radius );
cout << "degree: " << degree << endl;
cout << "crossVec: " << crossVec[0] << ", " << crossVec[1] << ", " << crossVec[2] << endl;
vtkSPtrNew( rotateTrans, vtkTransform );
rotateTrans->RotateWXYZ( -degree, crossVec.point );
rotateTrans->Update();
vtkSPtrNew( rotateTransInv, vtkTransform );
rotateTransInv->DeepCopy( rotateTrans );
rotateTransInv->Inverse();
rotateTransInv->Update();
vtkSPtrNew( scaleTrans, vtkTransform );
scaleTrans->Scale( 2, 1, 1 );
scaleTrans->Update();
vtkSPtrNew( finalScaleTrans, vtkTransform );
finalScaleTrans->Concatenate( rotateTransInv );
finalScaleTrans->Concatenate( scaleTrans );
finalScaleTrans->Concatenate( rotateTrans );
finalScaleTrans->Update();
vtkSPtrNew( finalTrans, vtkTransform );
finalTrans->Concatenate( trans0 );
finalTrans->Concatenate( finalScaleTrans );
finalTrans->Update();
vtkSPtrNew( transFilter, vtkTransformFilter );
transFilter->SetInputData( pd );
transFilter->SetTransform( finalScaleTrans );
transFilter->Update();
vtkSPtrNew( mapper, vtkPolyDataMapper );
mapper->SetInputData( transFilter->GetPolyDataOutput() );
vtkSPtrNew( actor, vtkActor );
actor->SetMapper( mapper );
actor->SetUserTransform( trans0 );
vtkSPtrNew( axesActor, vtkAxesActor );
axesActor->SetTotalLength( 5, 5, 5 );
axesActor->SetUserTransform( trans1 );
vtkSPtrNew( lineSource, vtkLineSource );
Point pt0( -3, 3, 0 ), pt1( 3, -3, 0 ), tmpPt( 0, 0, 0 );
trans0->TransformPoint( tmpPt.point, tmpPt.point );
lineSource->SetPoint1( (pt0 + tmpPt).point );
lineSource->SetPoint2( (pt1 + tmpPt).point );
lineSource->Update();
vtkSPtrNew( lineMapper, vtkPolyDataMapper );
lineMapper->SetInputData( lineSource->GetOutput() );
vtkSPtrNew( lineActor, vtkActor );
lineActor->SetMapper( lineMapper );
lineActor->GetProperty()->SetColor( 1, 0, 0 );
vtkSPtrNew( renderer, vtkRenderer );
renderer->AddActor( actor );
renderer->AddActor( lineActor );
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;
}