Commit ae99d37c authored by Sebastian Eichelbaum's avatar Sebastian Eichelbaum
Browse files

[CHANGE] - made Camera writing/reading work

[ADD] - added multiple options menu for Save Project button
[ADD] - added foundation for ROI saving/reading
parent 62971b94
......@@ -46,12 +46,67 @@ WGEProjectFileIO::~WGEProjectFileIO()
// cleanup
}
/**
* Interpret a string as a ";"- separated sequence of doubles.
*
* \param seq the sequence
* \param size the number of doubles needed
*
* \return the values
*/
double* parseDoubleSequence( std::string seq, unsigned int size )
{
// parse the string
// -> tokenize it and fill pointer appropriately
typedef boost::tokenizer<boost::char_separator< char > > tokenizer;
boost::char_separator< char > sep( ";" ); // separate by ;
tokenizer tok( seq, sep );
// each value must be stored at the proper position
double* values = new double[ size ];
unsigned int i = 0;
for ( tokenizer::iterator it = tok.begin(); ( it != tok.end() ) && ( i < size ); ++it )
{
values[ i ] = boost::lexical_cast< double >( ( *it ) );
++i;
}
// finally, set the values
return values;
}
/**
* Parse a string and interpret it as matrix. It creates a array of 16 values representing a 4x4 matrix in OSG notation.
*
* \param matrix the matrix as string.
*
* \return the parsed values.
*/
double* parseMatrix( std::string matrix )
{
return parseDoubleSequence( matrix, 16 );
}
/**
* Parse a string and interpret it as vector. It creates an array of 3 values representing the vector in OSG notation-
*
* \param vec the string to parse
*
* \return the values.
*/
double* parseVector( std::string vec )
{
return parseDoubleSequence( vec, 3 );
}
bool WGEProjectFileIO::parse( std::string line, unsigned int lineNumber )
{
// regular expressions to parse the line
static const boost::regex camRe( "^ *CAMERA:([0-9]*):(.*)$" );
static const boost::regex pmatrixRe( "^ *PROJECTIONMATRIX:\\(([0-9]*),(.*)\\)=(.*)$" );
static const boost::regex mmatrixRe( "^ *MODELVIEWMATRIX:\\(([0-9]*),(.*)\\)=(.*)$" );
static const boost::regex matrixRe( "^ *MANIPULATOR:\\(([0-9]*),Matrix\\)=(.*)$" );
static const boost::regex homeEyeRe( "^ *MANIPULATOR:\\(([0-9]*),HomeEye\\)=(.*)$" );
static const boost::regex homeCenterRe( "^ *MANIPULATOR:\\(([0-9]*),HomeCenter\\)=(.*)$" );
static const boost::regex homeUpRe( "^ *MANIPULATOR:\\(([0-9]*),HomeUp\\)=(.*)$" );
// use regex to parse it
boost::smatch matches; // the list of matches
......@@ -67,36 +122,58 @@ bool WGEProjectFileIO::parse( std::string line, unsigned int lineNumber )
return true;
}
else if ( boost::regex_match( line, matches, mmatrixRe ) )
else if ( boost::regex_match( line, matches, matrixRe ) )
{
// it is a camera modelview matrix line
// matches[1] is the ID of the camera
// matches[2] is the type of matrix definition (currently only "Matrix", later there might be "Eye", "Center", "Up".
// matches[3] is the matrix line
wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Camera Modelview Matrix \"" << matches[2]
<< "\" with ID " << matches[1];
// valid?
if ( ( matches[2] != "Matrix" ) )
{
wlog::error( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Camera Matrix definition type unknown: \"" << matches[2]
<< "\" for camera with ID " << matches[1];
return true;
}
// matches[2] is the matrix line
wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Camera Manipulator Matrix with ID " << matches[1];
// is there already a matrix for this camera? -> we do not care :-). Overwrite it.
m_viewMatrices[ boost::lexical_cast< unsigned int >( matches[1] ) ] = osg::Matrixd::identity();
m_manipulatorMatrices[ boost::lexical_cast< unsigned int >( matches[1] ) ] =
osg::Matrixd( parseMatrix( boost::lexical_cast< std::string >( matches[2] ) ) );
return true;
}
else if ( boost::regex_match( line, matches, homeEyeRe ) )
{
// it is a eye point of the manipulator
// parse the matrix -> tokenize it and fill pointer appropriately
typedef boost::tokenizer<boost::char_separator< char > > tokenizer;
boost::char_separator< char > sep( ";" ); // separate by ;
tokenizer tok( boost::lexical_cast< std::string >( matches[3] ), sep );
// matches[1] is the ID of the camera
// matches[2] is the eye vector
wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Camera Manipulator Home Eye Point with ID " << matches[1];
// each value must be stored at the proper position
//double* values = new
for ( tokenizer::iterator it = tok.begin(); it != tok.end(); ++it )
{
}
// is there already a vector set? -> ignore.
double* vals = parseVector( boost::lexical_cast< std::string >( matches[2] ) );
m_homeEyeVectors[ boost::lexical_cast< unsigned int >( matches[1] ) ] = osg::Vec3d( vals[0], vals[1], vals[2] );
return true;
}
else if ( boost::regex_match( line, matches, homeCenterRe ) )
{
// it is a center point of the manipulator
// matches[1] is the ID of the camera
// matches[2] is the eye vector
wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Camera Manipulator Home Center Point with ID " << matches[1];
// is there already a vector set? -> ignore.
double* vals = parseVector( boost::lexical_cast< std::string >( matches[2] ) );
m_homeCenterVectors[ boost::lexical_cast< unsigned int >( matches[1] ) ] = osg::Vec3d( vals[0], vals[1], vals[2] );
return true;
}
else if ( boost::regex_match( line, matches, homeUpRe ) )
{
// it is a up vector of the manipulator
// matches[1] is the ID of the camera
// matches[2] is the eye vector
wlog::debug( "Project Loader [Parser]" ) << "Line " << lineNumber << ": Camera Manipulator Home Up Point with ID " << matches[1];
// is there already a vector set? -> ignore.
double* vals = parseVector( boost::lexical_cast< std::string >( matches[2] ) );
m_homeUpVectors[ boost::lexical_cast< unsigned int >( matches[1] ) ] = osg::Vec3d( vals[0], vals[1], vals[2] );
return true;
}
......@@ -107,6 +184,44 @@ bool WGEProjectFileIO::parse( std::string line, unsigned int lineNumber )
void WGEProjectFileIO::done()
{
// apply camera here
for ( CameraList::const_iterator iter = m_cameras.begin(); iter != m_cameras.end(); ++iter )
{
// does the corresponding view exists?
boost::shared_ptr< WGEViewer > view = WGraphicsEngine::getGraphicsEngine()->getViewerByName( ( *iter ).second );
if ( !view )
{
wlog::warn( "Project Loader" ) << "Project file contained a camera \"" << ( *iter ).second << "\" but the corresponding view does " <<
"not exist. Ignoring.";
}
// did we have a matrix?
if ( !m_manipulatorMatrices.count( ( *iter ).first ) )
{
wlog::warn( "Project Loader" ) << "Project file contained a camera \"" << ( *iter ).second << "\" but no proper manipulator matrix. " <<
"Leaving current matrix untouched.";
}
else
{
view->getCameraManipulator()->setByMatrix( m_manipulatorMatrices[ ( *iter ).first ] );
}
// home position found?
if ( ( !m_homeEyeVectors.count( ( *iter ).first ) ) ||
( !m_homeCenterVectors.count( ( *iter ).first ) ) ||
( !m_homeUpVectors.count( ( *iter ).first ) )
)
{
wlog::warn( "Project Loader" ) << "Project file contained a camera \"" << ( *iter ).second << "\" but no proper manipulator home " <<
"position. Leaving current home untouched.";
}
else
{
view->getCameraManipulator()->setHomePosition( m_homeEyeVectors[ ( *iter ).first ],
m_homeCenterVectors[ ( *iter ).first ],
m_homeUpVectors[ ( *iter ).first ]
);
}
}
}
void WGEProjectFileIO::save( std::ostream& output ) // NOLINT
......@@ -117,25 +232,46 @@ void WGEProjectFileIO::save( std::ostream& output ) // NOLINT
std::endl;
// grab the main camera
osg::ref_ptr<osg::Camera> cam = WGraphicsEngine::getGraphicsEngine()->getViewer()->getCamera();
// TODO(ebaum): extend to all views
osg::ref_ptr< osg::Camera > cam = WGraphicsEngine::getGraphicsEngine()->getViewer()->getCamera();
osg::ref_ptr< osgGA::MatrixManipulator > mani = WGraphicsEngine::getGraphicsEngine()->getViewer()->getCameraManipulator();
std::string name = WGraphicsEngine::getGraphicsEngine()->getViewer()->getName();
output << "CAMERA:0:" << name << std::endl;
// NOTE: OSG uses manipulators to modify the camera's view and projection matrix every frame. -> it is useless to store view and projection
// matrix as these get overwritten every frame -> we need to store the manipulator settings here.
// get the matrices
osg::Matrixd view = cam->getViewMatrix();
osg::Matrixd proj = cam->getProjectionMatrix();
osg::Matrixd view = mani->getMatrix();
output << "//Camera Matrices: \"" << name << "\"" << std::endl;
output << " MODELVIEWMATRIX:(0,Matrix)=";
output << view( 0, 0 ) << ";" << view( 0, 1 ) << ";" << view( 0, 2 ) << ";" << view( 0, 3 ) << ";"
<< view( 1, 0 ) << ";" << view( 1, 1 ) << ";" << view( 1, 2 ) << ";" << view( 1, 3 ) << ";"
<< view( 2, 0 ) << ";" << view( 2, 1 ) << ";" << view( 2, 2 ) << ";" << view( 2, 3 ) << ";"
<< view( 3, 0 ) << ";" << view( 3, 1 ) << ";" << view( 3, 2 ) << ";" << view( 3, 3 ) << std::endl;
output << " PROJECTIONMATRIX:(0,Matrix)=";
output << proj( 0, 0 ) << ";" << proj( 0, 1 ) << ";" << proj( 0, 2 ) << ";" << proj( 0, 3 ) << ";"
<< proj( 1, 0 ) << ";" << proj( 1, 1 ) << ";" << proj( 1, 2 ) << ";" << proj( 1, 3 ) << ";"
<< proj( 2, 0 ) << ";" << proj( 2, 1 ) << ";" << proj( 2, 2 ) << ";" << proj( 2, 3 ) << ";"
<< proj( 3, 0 ) << ";" << proj( 3, 1 ) << ";" << proj( 3, 2 ) << ";" << proj( 3, 3 ) << std::endl;
output << " MANIPULATOR:(0,Matrix)=";
for ( unsigned int i = 0; i < 16; ++i )
{
output << view.ptr()[i];
if ( i < 15 )
{
output << ";";
}
}
output << std::endl;
// get home position
osg::Vec3d eye;
osg::Vec3d center;
osg::Vec3d up;
mani->getHomePosition( eye, center, up );
// write them
// NOTE: vec3d already provides a << operator but it writes the vector in a space separated style -> this is not conform to our common style
// -> we manually print them
output << " MANIPULATOR:(0,HomeEye)=";
output << eye.x() << ";" << eye.y() << ";" << eye.z() << std::endl;
output << " MANIPULATOR:(0,HomeCenter)=";
output << center.x() << ";" << center.y() << ";" << center.z() << std::endl;
output << " MANIPULATOR:(0,HomeUp)=";
output << up.x() << ";" << up.y() << ";" << up.z() << std::endl;
output << "//Camera Matrices END: \"" << name << "\"" << std::endl;
}
......@@ -33,7 +33,8 @@
#include "../common/WProjectFileIO.h"
/**
* IO class for writing the graphics engine state to a project file. Currently it only writes the camera settings.
* IO class for writing the graphics engine state to a project file. Currently it only writes the camera settings for the main view with the
* WGEZoomTrackbalManipulator.
*/
class WGEProjectFileIO: public WProjectFileIO
{
......@@ -77,17 +78,32 @@ protected:
/**
* All Cameras parsed.
*/
std::map< unsigned int, std::string > m_cameras;
typedef std::map< unsigned int, std::string > CameraList;
/**
* All camera view matrices.
* Camera map.
*/
std::map< unsigned int, osg::Matrixd > m_viewMatrices;
CameraList m_cameras;
/**
* All camera projection matrices.
* All view's manipulator matrices.
*/
std::map< unsigned int, osg::Matrixd > m_projMatrices;
std::map< unsigned int, osg::Matrixd > m_manipulatorMatrices;
/**
* The home position eye point.
*/
std::map< unsigned int, osg::Vec3d > m_homeEyeVectors;
/**
* The home position center point.
*/
std::map< unsigned int, osg::Vec3d > m_homeCenterVectors;
/**
* The home position up vector.
*/
std::map< unsigned int, osg::Vec3d > m_homeUpVectors;
private:
};
......
......@@ -37,7 +37,9 @@ WGEZoomTrackballManipulator::WGEZoomTrackballManipulator():
void WGEZoomTrackballManipulator::setByMatrix( const osg::Matrixd& matrix )
{
m_zoom = 1.0 / matrix.getScale()[0];
TrackballManipulator::setByMatrix( matrix );
// The zoom needs to be undone before forwarding the matrix.
TrackballManipulator::setByMatrix( osg::Matrixd::inverse( osg::Matrixd::scale( 1.0 / m_zoom, 1.0 / m_zoom, 1.0 ) ) * matrix );
}
osg::Matrixd WGEZoomTrackballManipulator::getMatrix() const
......
......@@ -178,10 +178,18 @@ void WMainWindow::setupPermanentToolBar()
WQtPushButton* projectLoadButton = new WQtPushButton( m_iconManager.getIcon( "loadProject" ), "loadProject", m_permanentToolBar );
WQtPushButton* projectSaveButton = new WQtPushButton( m_iconManager.getIcon( "saveProject" ), "saveProject", m_permanentToolBar );
// setup save button
QMenu* saveMenu = new QMenu( "Save Project", projectSaveButton );
saveMenu->addAction( "Save Project", this, SLOT( projectSaveAll() ) );
saveMenu->addAction( "Save Camera", this, SLOT( projectSaveCameraOnly() ) );
saveMenu->addAction( "Save ROIs", this, SLOT( projectSaveROIOnly() ) );
projectSaveButton->setPopupMode( QToolButton::MenuButtonPopup );
projectSaveButton->setMenu( saveMenu );
connect( loadButton, SIGNAL( pressed() ), this, SLOT( openLoadDialog() ) );
connect( roiButton, SIGNAL( pressed() ), this, SLOT( newRoi() ) );
connect( projectLoadButton, SIGNAL( pressed() ), this, SLOT( projectLoad() ) );
connect( projectSaveButton, SIGNAL( pressed() ), this, SLOT( projectSave() ) );
connect( projectSaveButton, SIGNAL( pressed() ), this, SLOT( projectSaveAll() ) );
loadButton->setToolTip( "Load Data" );
roiButton->setToolTip( "Create New ROI" );
......@@ -379,7 +387,7 @@ WQtToolBar* WMainWindow::getCompatiblesToolBar()
return m_compatiblesToolBar;
}
void WMainWindow::projectSave()
void WMainWindow::projectSave( const std::vector< boost::shared_ptr< WProjectFileIO > >& writer )
{
QFileDialog fd;
fd.setWindowTitle( "Save Project as" );
......@@ -406,7 +414,14 @@ void WMainWindow::projectSave()
try
{
// This call is synchronous.
proj->save();
if ( writer.empty() )
{
proj->save();
}
else
{
proj->save( writer );
}
}
catch( const std::exception& e )
{
......@@ -418,6 +433,27 @@ void WMainWindow::projectSave()
}
}
void WMainWindow::projectSaveAll()
{
std::vector< boost::shared_ptr< WProjectFileIO > > w;
// an empty list equals "all"
projectSave( w );
}
void WMainWindow::projectSaveCameraOnly()
{
std::vector< boost::shared_ptr< WProjectFileIO > > w;
w.push_back( WProjectFile::getCameraWriter() );
projectSave( w );
}
void WMainWindow::projectSaveROIOnly()
{
std::vector< boost::shared_ptr< WProjectFileIO > > w;
w.push_back( WProjectFile::getROIWriter() );
projectSave( w );
}
void WMainWindow::projectLoad()
{
QFileDialog fd;
......
......@@ -49,6 +49,7 @@
#include "datasetbrowser/WQtDatasetBrowser.h"
#include "../../kernel/WModule.h"
#include "../../common/WProjectFileIO.h"
// forward declarations
class QMenuBar;
......@@ -152,6 +153,13 @@ protected:
*/
virtual bool event( QEvent* event );
/**
* Called for each project save request.
*
* \param writer the list of writers to use.
*/
virtual void projectSave( const std::vector< boost::shared_ptr< WProjectFileIO > >& writer );
public slots:
/**
* gets called when menu option or toolbar button load is activated
......@@ -176,7 +184,17 @@ public slots:
/**
* Gets called whenever the user presses the project save button.
*/
void projectSave();
void projectSaveAll();
/**
* Gets called by the save menu to only save the camera settings
*/
void projectSaveCameraOnly();
/**
* Gets called by the save menu to only save the ROI settings
*/
void projectSaveROIOnly();
/**
* Sets that a fiber data set has already been loaded. Thi shelps to prevent multiple fiber data sets to be loaded.
......
......@@ -27,8 +27,12 @@
#include "WQtPushButton.h"
WQtPushButton::WQtPushButton( QIcon icon, QString name, QWidget* parent, QString label )
: QPushButton( icon, label, parent )
: QToolButton( parent )
{
setText( label );
setIcon( icon );
setAutoRaise( false );
m_name = name;
// we need to use released signal here, as the pushed signal also gets emitted on newly created buttons which are under the mouse pointer with
// pressed left button.
......
......@@ -28,11 +28,12 @@
#include <string>
#include <QtGui/QPushButton>
#include <QtGui/QToolButton>
/**
* Implements a QPushButton with a boost signal for convenience.
*/
class WQtPushButton : public QPushButton
class WQtPushButton : public QToolButton
{
Q_OBJECT
......
......@@ -30,6 +30,7 @@
#include "WKernel.h"
#include "combiner/WModuleProjectFileCombiner.h"
#include "../modules/fiberDisplay/WRoiProjectFileIO.h"
#include "../graphicsEngine/WGEProjectFileIO.h"
#include "../common/exceptions/WFileNotFound.h"
#include "../common/exceptions/WFileOpenFailed.h"
......@@ -44,25 +45,36 @@ WProjectFile::WProjectFile( boost::filesystem::path project ):
// initialize members
// The module graph parser
m_parsers.push_back( new WModuleProjectFileCombiner() );
m_parsers.push_back( boost::shared_ptr< WProjectFileIO >( new WModuleProjectFileCombiner() ) );
// The ROI parser
// m_parsers.push_back( new W???() );
m_parsers.push_back( boost::shared_ptr< WProjectFileIO >( new WRoiProjectFileIO() ) );
// The Camera parser
m_parsers.push_back( new WGEProjectFileIO() );
m_parsers.push_back( boost::shared_ptr< WProjectFileIO >( new WGEProjectFileIO() ) );
}
WProjectFile::~WProjectFile()
{
// cleanup
for ( std::vector< WProjectFileIO* >::iterator iter = m_parsers.begin(); iter != m_parsers.end(); ++iter )
{
delete( *iter );
}
m_parsers.clear();
}
boost::shared_ptr< WProjectFileIO > WProjectFile::getCameraWriter()
{
return boost::shared_ptr< WProjectFileIO >( new WGEProjectFileIO() );
}
boost::shared_ptr< WProjectFileIO > WProjectFile::getModuleWriter()
{
return boost::shared_ptr< WProjectFileIO >( new WModuleProjectFileCombiner() );
}
boost::shared_ptr< WProjectFileIO > WProjectFile::getROIWriter()
{
return boost::shared_ptr< WProjectFileIO >( new WRoiProjectFileIO() );
}
void WProjectFile::load()
{
// the instance needs to be added here, as it else could be freed before the thread finishes ( remember: it is a shared_ptr ).
......@@ -72,7 +84,7 @@ void WProjectFile::load()
run();
}
void WProjectFile::save()
void WProjectFile::save( const std::vector< boost::shared_ptr< WProjectFileIO > >& writer )
{
wlog::info( "Project File" ) << "Saving project file \"" << m_project.file_string() << "\".";
......@@ -84,7 +96,7 @@ void WProjectFile::save()
}
// allow each parser to handle save request
for ( std::vector< WProjectFileIO* >::const_iterator iter = m_parsers.begin(); iter != m_parsers.end(); ++iter )
for ( std::vector< boost::shared_ptr< WProjectFileIO > >::const_iterator iter = writer.begin(); iter != writer.end(); ++iter )
{
( *iter )->save( output );
output << std::endl;
......@@ -93,6 +105,11 @@ void WProjectFile::save()
output.close();
}
void WProjectFile::save()
{
save( m_parsers );
}
void WProjectFile::threadMain()
{
try
......@@ -122,13 +139,20 @@ void WProjectFile::threadMain()
match = false;
// allow each parser to handle the line.
for ( std::vector< WProjectFileIO* >::const_iterator iter = m_parsers.begin(); iter != m_parsers.end(); ++iter )
for ( std::vector< boost::shared_ptr< WProjectFileIO > >::const_iterator iter = m_parsers.begin(); iter != m_parsers.end(); ++iter )
{
if ( ( *iter )->parse( line, i ) )
try
{
if ( ( *iter )->parse( line, i ) )
{
match = true;
// the first parser matching this line -> next line
break;
}
}
catch( const std::exception& e )
{
match = true;
// the first parser matching this line -> next line
break;
wlog::error( "Project Loader" ) << "Line " << i << ": Parsing caused an exception. Line Malformed? Skipping.";
}
}
......@@ -143,7 +167,7 @@ void WProjectFile::threadMain()
input.close();
// finally, let every one know that we have finished
for ( std::vector< WProjectFileIO* >::const_iterator iter = m_parsers.begin(); iter != m_parsers.end(); ++iter )
for ( std::vector< boost::shared_ptr< WProjectFileIO > >::const_iterator iter = m_parsers.begin(); iter != m_parsers.end(); ++iter )
{
( *iter )->done();
}
......
......@@ -64,6 +64,35 @@ public:
*/
virtual void save();
/**
* Saves the current state to the file specified in the constructor. This also supports a custom list of writers. This is useful to only
* write some parts of the state.
*
* \param writer the list of writers to use.
*/
virtual void save( const std::vector< boost::shared_ptr< WProjectFileIO > >& writer );
/**
* Returns an instance of the Camera writer.
*
* \return the writer able to output the camera configuration to a stream.
*/
static boost::shared_ptr< WProjectFileIO > getCameraWriter();
/**
* Returns an instance of the module writer.
*
* \return the writer able to output the module configuration to a stream.
*/
static boost::shared_ptr< WProjectFileIO > getModuleWriter();