Sometimes the 3D model and 2D object will be overlapped as the above scene.
I want to move the text to the top for avoiding overlap situation.
My solution has these steps:
1. Calculate the bounding box of the sphere and convert it to a rectangle which wraps the image of it in the vtk display coordinate system.
2. Calculate the wrapping rectangle of text actor in the vtk display coordinate system.
3. Judge whether both rectangles are overlapped.
But how to judge whether they are overlapped? It's complicated to get the right method which contains all possible overlapping situations, so we can think about this problem in an opponent view, how are they not overlapped?
Write code to make our idea works.
logprinter.hpp:
/**********************************************************
*
* @brief This source file provides a way to print log into
* local file in QT development environment.
*
* @author theArcticOcean
***********************************************************/
#ifndef LOGPRINTER_HPP
#define LOGPRINTER_HPP
#include <unistd.h>
#include <QString>
#include <QDir>
#include <QMessageBox>
#include <QCoreApplication>
// QtGlobal has many basic function such as qCritical()
#include <QtGlobal>
#define BUFFER_SIZE 1024*10
static QString logFilePath;
/*
* Create log file, truncate the file if it exsits.
*/
void LogInit()
{
logFilePath = QCoreApplication::applicationDirPath();
logFilePath = logFilePath + "/log.txt";
if( 0 == access( logFilePath.toStdString().c_str(), F_OK )
&& truncate( logFilePath.toStdString().c_str(), 0 ) )
{
QString prefix = "[logInit] ";
prefix = prefix + logFilePath;
perror( prefix.toStdString().c_str() );
}
}
/*
* The function is provided for qInstallMessageHandler.
* It redirects output messages to log file.
*/
void LogBase
(
QtMsgType type,
const QMessageLogContext &context = QMessageLogContext( __FILE__, __LINE__, __FUNCTION__, NULL ),
const QString &msg = QString("")
)
{
FILE *fp;
int offset;
char buffer[ BUFFER_SIZE ] = {0};
QByteArray localMsg = msg.toLocal8Bit();
fp = NULL;
switch( type )
{
case QtDebugMsg:
offset = sprintf( buffer, "[Debug ");
break;
case QtInfoMsg:
offset = sprintf( buffer, "[Info ");
break;
case QtWarningMsg:
offset = sprintf( buffer, "[Warning " );
break;
case QtCriticalMsg:
offset = sprintf( buffer, "[Critical " );
break;
case QtFatalMsg:
offset = sprintf( buffer, "[Fatal " );
break;
}
sprintf( buffer + offset, "(%s, %u, %s)]\n%s\n\n", context.file, context.line, context.function, localMsg.constData() );
fp = fopen( logFilePath.toStdString().c_str(), "a" );
if( NULL != fp )
{
fprintf( fp, "%s", buffer );
fclose( fp );
}
}
#endif // LOGPRINTER_HPP
main.cpp:
#include <vtkActor.h>
#include <vtkPolyData.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include <vtkTextActor.h>
#include <vtkSphereSource.h>
#include <vtkPlaneSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkPlane.h>
#include <vtkTextProperty.h>
#include <vtkProperty.h>
#include <vtkPolyDataMapper2D.h>
#include <vtkProperty2D.h>
#include <QApplication>
#include <iostream>
#include <QtDebug>
#include <vector>
#include <QChar>
#include "tool.h"
#include "logprinter.hpp"
vtkSmartPointer<vtkRenderer> renderer;
vtkSmartPointer<vtkTextActor> textActor;
/*
* The interface is used to judge whether two rectangle are overlapping.
*/
bool IsRectangleOverLapping
(
PointStruct leftBottom0,
PointStruct rightUpper0,
PointStruct leftBottom1,
PointStruct rightUpper1
)
{
if( rightUpper0[0] < leftBottom1[0] || // left side
rightUpper0[1] < leftBottom1[1] || // down side
leftBottom0[0] > rightUpper1[0] || // right side
leftBottom0[1] > rightUpper1[1] ) // upper side
{
return false;
}
return true;
}
/*
* To avoid angle actor and other actor overlapped, move the angle actor to upper area.
*/
void RelocateAngleActor(vtkSmartPointer<vtkActor> anotherActor)
{
double *bounds = anotherActor->GetBounds();
PointStruct leftPt( bounds[0], bounds[2], bounds[4] );
PointStruct rightPt( bounds[1], bounds[3], bounds[5] );
PointStruct moveDirForTextActor( 0, 1, 0 );
double leftWorld[4] = { leftPt[0], leftPt[1], leftPt[2], 0 };
renderer->SetWorldPoint( leftWorld );
renderer->WorldToDisplay();
renderer->GetDisplayPoint( leftPt.point );
double rightWorld[4] = { rightPt[0], rightPt[1], rightPt[2], 0 };
renderer->SetWorldPoint( rightWorld );
renderer->WorldToDisplay();
renderer->GetDisplayPoint( rightPt.point );
double *anglePostion = textActor->GetPosition();
double angleSize[2];
textActor->GetSize( renderer, angleSize );
int *rendererSize = renderer->GetSize();
PointStruct angleLeftPt( anglePostion[0], anglePostion[1], 0 );
PointStruct angleRightPt( angleLeftPt );
angleRightPt[0] = angleRightPt[0] + angleSize[0];
angleRightPt[1] = angleRightPt[1] + angleSize[1];
qInfo( "Works, rendererSize[1]: %d", rendererSize[1] );
qInfo( "leftPt: %lf, %lf, %lf", leftPt[0], leftPt[1], leftPt[2] );
qInfo( "rightPt: %lf, %lf, %lf", rightPt[0], rightPt[1], rightPt[2] );
qInfo( "angleLeftPt: %lf, %lf, %lf", angleLeftPt[0], angleLeftPt[1], angleLeftPt[2] );
qInfo( "angleRightPt: %lf, %lf, %lf", angleRightPt[0], angleRightPt[1], angleRightPt[2] );
while( IsRectangleOverLapping( leftPt, rightPt, angleLeftPt, angleRightPt ) && rendererSize[1] > angleRightPt[1] )
{
angleLeftPt = angleLeftPt + moveDirForTextActor * 1;
angleRightPt = angleRightPt + moveDirForTextActor * 1;
}
textActor->SetPosition( angleLeftPt[0], angleLeftPt[1] );
}
int main(int argc, char *argv[])
{
QApplication app( argc, argv );
LogInit();
qInstallMessageHandler( LogBase );
qDebug() << "LogInit finished.";
textActor = vtkSmartPointer<vtkTextActor>::New();
textActor->SetInput( "hello world" );
textActor->GetTextProperty()->SetFontSize( 30 );
textActor->GetTextProperty()->SetColor( 1, 0, 0 );
renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor( textActor );
renderer->SetBackground( 0, 0, 0 );
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer( renderer );
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow( renderWindow );
renderer->ResetCamera();
renderWindow->Render();
int *size = renderer->GetSize();
qDebug() << size[0] << " " << size[1];
int centerY = int(size[1] / 2.0 + 0.5);
int centerX = int(size[0] / 2.0 + 0.5);
double textActorSize[2];
textActor->GetSize( renderer, textActorSize );
textActor->SetPosition( centerX - textActorSize[0] / 2, centerY - textActorSize[1] / 2 );
// ==== add line in 3D world ====
vtkSmartPointer<vtkSphereSource> sphereSource =
vtkSmartPointer<vtkSphereSource>::New();
sphereSource->SetRadius( 2 );
sphereSource->Update();
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputData( sphereSource->GetOutput() );
mapper->Update();
vtkSmartPointer<vtkActor> sphereActor =
vtkSmartPointer<vtkActor>::New();
sphereActor->SetMapper( mapper );
sphereActor->GetProperty()->SetColor( 0, 1, 0 );
renderer->AddActor( sphereActor );
// ==== Finished: add line in 3D world ====
renderer->ResetCamera();
//RelocateAngleActor( sphereActor );
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}