//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV-Leipzig and CNCF-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see .
//
//---------------------------------------------------------------------------
#include
#include
#include
#include "core/common/WPropertyHelper.h"
#include "core/dataHandler/io/WWriterFiberVTK.h"
#include "core/kernel/WKernel.h"
#include "WMWriteTracts.h"
#include "WMWriteTracts.xpm"
W_LOADABLE_MODULE( WMWriteTracts )
WMWriteTracts::WMWriteTracts():
WModule()
{
}
WMWriteTracts::~WMWriteTracts()
{
}
boost::shared_ptr< WModule > WMWriteTracts::factory() const
{
return boost::shared_ptr< WModule >( new WMWriteTracts() );
}
const char** WMWriteTracts::getXPMIcon() const
{
return WMWriteTracts_xpm;
}
const std::string WMWriteTracts::getName() const
{
return "Write Tracts";
}
const std::string WMWriteTracts::getDescription() const
{
return "Writes tracts either from a cluster or from a WDataSetFibers to a file";
}
void WMWriteTracts::connectors()
{
m_clusterIC = WModuleInputData< const WFiberCluster >::createAndAdd( shared_from_this(), "clusterInput", "A the tracts behind the WFiberCluster" ); // NOLINT line length
m_tractIC = WModuleInputData< const WDataSetFibers >::createAndAdd( shared_from_this(), "tractInput", "A dataset of tracts" );
WModule::connectors();
}
void WMWriteTracts::properties()
{
m_savePath = m_properties->addProperty( "Save Path", "Where to save the result", boost::filesystem::path( "/no/such/file" ) );
WPropertyHelper::PC_NOTEMPTY::addTo( m_savePath );
m_run = m_properties->addProperty( "Save", "Start saving", WPVBaseTypes::PV_TRIGGER_READY );
m_fileTypeSelectionsList = boost::shared_ptr< WItemSelection >( new WItemSelection() );
m_fileTypeSelectionsList->addItem( "VTK fib", "Stores the fibers in the VTK line format." );
m_fileTypeSelectionsList->addItem( "json", "" );
m_fileTypeSelectionsList->addItem( "json2", "" );
m_fileTypeSelectionsList->addItem( "json triangles", "" );
m_fileTypeSelectionsList->addItem( "POVRay Cylinders", "Stores the fibers as cylinders in a POVRay SDL file." );
m_fileTypeSelection = m_properties->addProperty( "File type", "file type.", m_fileTypeSelectionsList->getSelectorFirst(),
boost::bind( &WMWriteTracts::fileTypeChanged, this )
);
WPropertyHelper::PC_SELECTONLYONE::addTo( m_fileTypeSelection );
m_povrayOptions = m_properties->addPropertyGroup( "POVRay Options", "Options for the POVRay Exporter." );
m_povrayOptions->setHidden( true );
m_povrayTubeDiameter = m_povrayOptions->addProperty( "Tube Diameter",
"The tube diameter. Each fibers is represented as a tube with spheres as connections between them",
0.25 );
m_povrayTubeDiameter->setMin( 0.001 );
m_povrayTubeDiameter->setMax( 2.0 );
m_povrayRadiosity = m_povrayOptions->addProperty( "Enable Radiosity",
"Enable POVRay's radiosity renderer. Creates more realistic lighting but is very slow.",
false );
m_povraySaveOnlyNth = m_povrayOptions->addProperty( "Save Every n'th", "Option allows thinning the data. This is useful in cases were fast"
" rendering is needed.", 1 );
m_povraySaveOnlyNth->setMin( 1 );
m_povraySaveOnlyNth->setMax( 1000 );
WModule::properties();
}
void WMWriteTracts::moduleMain()
{
m_moduleState.add( m_clusterIC->getDataChangedCondition() );
m_moduleState.add( m_tractIC->getDataChangedCondition() );
m_moduleState.add( m_run->getCondition() );
ready();
while( !m_shutdownFlag() )
{
debugLog() << "Waiting for data ...";
m_moduleState.wait();
if( !m_clusterIC->getData() && !m_tractIC->getData() )
{
continue;
}
if( m_run->get( true ) == WPVBaseTypes::PV_TRIGGER_TRIGGERED )
{
switch( m_fileTypeSelection->get( true ).getItemIndexOfSelected( 0 ) )
{
case 0:
{
WWriterFiberVTK w( m_savePath->get(), true );
if( m_clusterIC->getData() )
{
w.writeFibs( m_clusterIC->getData()->getDataSetReference() );
}
else if( m_tractIC->getData() )
{
w.writeFibs( m_tractIC->getData() );
}
}
break;
case 1:
saveJson();
break;
case 2:
saveJson2();
break;
case 3:
saveJsonTriangles();
break;
case 4:
if( m_tractIC->getData() )
{
savePOVRay( m_tractIC->getData() );
}
break;
default:
debugLog() << "this shouldn't be reached";
break;
}
m_run->set( WPVBaseTypes::PV_TRIGGER_READY, true );
}
}
}
bool WMWriteTracts::saveJson() const
{
boost::shared_ptr< const WDataSetFibers > ds = m_tractIC->getData();
if( !ds )
{
return false;
}
if( ds->getVertices()->size() == 0 )
{
WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 vertices.", "Write Tracts", LL_ERROR );
return false;
}
const char* c_file = m_savePath->get().string().c_str();
std::ofstream dataFile( c_file, std::ios_base::binary );
if( dataFile )
{
WLogger::getLogger()->addLogMessage( "opening file", "Write Tracts", LL_DEBUG );
}
else
{
WLogger::getLogger()->addLogMessage( "open file failed" + m_savePath->get().string() , "Write Tracts", LL_ERROR );
return false;
}
dataFile.setf( std::ios_base::fixed );
dataFile.precision( 3 );
WLogger::getLogger()->addLogMessage( "start writing file", "Write Tracts", LL_DEBUG );
dataFile << ( "{\n" );
dataFile << ( " \"vertices\" : [" );
boost::shared_ptr > verts = ds->getVertices();
float fValue;
for( size_t i = 0; i < (verts->size() - 1 )/ 3; ++i )
{
fValue = verts->at( i * 3 );
dataFile << fValue << ",";
fValue = verts->at( i * 3 + 1 );
dataFile << fValue << ",";
fValue = verts->at( i * 3 + 2 );
dataFile << fValue << ",";
}
fValue = verts->at( verts->size() - 3 );
dataFile << fValue << ",";
fValue = verts->at( verts->size() - 2 );
dataFile << fValue << ",";
fValue = verts->at( verts->size() - 1 );
dataFile << fValue << "],\n";
dataFile << ( " \"normals\" : [" );
boost::shared_ptr > tangents = ds->getTangents();
for( size_t i = 0; i < tangents->size() - 1; ++i )
{
fValue = tangents->at( i );
dataFile << fValue << ",";
}
fValue = tangents->at( tangents->size() - 1 );
dataFile << fValue << "],\n";
dataFile << ( " \"colors\" : [" );
boost::shared_ptr< std::vector< float > > colors = ds->getColorScheme( "Global Color" )->getColor();
for( size_t i = 0; i < colors->size() - 3; i += 3 )
{
fValue = colors->at( i );
dataFile << fValue << ",";
fValue = colors->at( i + 1 );
dataFile << fValue << ",";
fValue = colors->at( i + 2 );
dataFile << fValue << ",1.0,";
}
fValue = colors->at( colors->size() - 3 );
dataFile << fValue << ",";
fValue = colors->at( colors->size() - 2 );
dataFile << fValue << ",";
fValue = colors->at( colors->size() - 1 );
dataFile << fValue << ",1.0],\n";
int iValue;
dataFile << ( " \"indices\" : [" );
boost::shared_ptr > lengths = ds->getLineLengths();
for( size_t i = 0; i < lengths->size() - 1; ++i )
{
iValue = lengths->at( i );
dataFile << iValue << ",";
}
iValue = lengths->at( lengths->size() - 1 );
dataFile << iValue << "]";
dataFile << "\n}";
dataFile.close();
WLogger::getLogger()->addLogMessage( "saving done", "Write Tracts", LL_DEBUG );
return true;
}
bool WMWriteTracts::saveJson2() const
{
boost::shared_ptr< const WDataSetFibers > ds = m_tractIC->getData();
if( !ds )
{
return false;
}
if( ds->getVertices()->size() == 0 )
{
WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 vertices.", "Write Tracts", LL_ERROR );
return false;
}
const char* c_file = m_savePath->get().string().c_str();
std::ofstream dataFile( c_file, std::ios_base::binary );
if( dataFile )
{
WLogger::getLogger()->addLogMessage( "opening file", "Write Tracts", LL_DEBUG );
}
else
{
WLogger::getLogger()->addLogMessage( "open file failed" + m_savePath->get().string() , "Write Tracts", LL_ERROR );
return false;
}
dataFile.setf( std::ios_base::fixed );
dataFile.precision( 3 );
//**************************************************************************************
// create arrays;
std::vector nVertices;
std::vector nNormals;
std::vector nColors;
std::vector nIndices;
boost::shared_ptr > starts = ds->getLineStartIndexes();
boost::shared_ptr > lengths = ds->getLineLengths();
boost::shared_ptr > verts = ds->getVertices();
boost::shared_ptr > tangents = ds->getTangents();
boost::shared_ptr< std::vector< float > > colors = ds->getColorScheme( "Global Color" )->getColor();
for( size_t k = 0; k < lengths->size(); ++k )
{
size_t newLength = 0;
for( size_t i = starts->at( k ); i < ( starts->at( k ) + lengths->at( k ) ); ++i )
{
if( i % 2 == 0 )
{
nVertices.push_back( verts->at( i * 3 ) );
nVertices.push_back( verts->at( i * 3 + 1 ) );
nVertices.push_back( verts->at( i * 3 + 2 ) );
nNormals.push_back( - tangents->at( i * 3 ) );
nNormals.push_back( - tangents->at( i * 3 + 1 ) );
nNormals.push_back( tangents->at( i * 3 + 2 ) );
nColors.push_back( colors->at( i * 3 ) );
nColors.push_back( colors->at( i * 3 + 1 ) );
nColors.push_back( colors->at( i * 3 + 2 ) );
nColors.push_back( 1.0 );
++newLength;
}
}
nIndices.push_back( newLength );
}
WLogger::getLogger()->addLogMessage( "start writing file", "Write Tracts", LL_DEBUG );
dataFile << ( "{\n" );
dataFile << ( " \"vertices\" : [" );
float fValue;
for( size_t i = 0; i < nVertices.size() - 1 ; ++i )
{
fValue = nVertices[i];
dataFile << fValue << ",";
}
fValue = verts->at( verts->size() - 1 );
dataFile << fValue << "],\n";
dataFile << ( " \"normals\" : [" );
for( size_t i = 0; i < nNormals.size() - 1; ++i )
{
fValue = nNormals[i];
dataFile << fValue << ",";
}
fValue = nNormals[nNormals.size() - 1];
dataFile << fValue << "],\n";
dataFile << ( " \"colors\" : [" );
for( size_t i = 0; i < nColors.size()- 1; ++i )
{
fValue = nColors[i];
dataFile << fValue << ",";
}
fValue = nColors[nColors.size() - 1];
dataFile << fValue << ",1.0],\n";
int iValue;
dataFile << ( " \"indices\" : [" );
for( size_t i = 0; i < nIndices.size() - 1; ++i )
{
iValue = nIndices[i];
dataFile << iValue << ",";
}
iValue = nIndices[nIndices.size() - 1];
dataFile << iValue << "]";
dataFile << "\n}";
dataFile.close();
WLogger::getLogger()->addLogMessage( "saving done", "Write Tracts", LL_DEBUG );
return true;
}
bool WMWriteTracts::saveJsonTriangles() const
{
boost::shared_ptr< const WDataSetFibers > ds = m_tractIC->getData();
if( !ds )
{
return false;
}
if( ds->getVertices()->size() == 0 )
{
WLogger::getLogger()->addLogMessage( "Will not write file that contains 0 vertices.", "Write Tracts", LL_ERROR );
return false;
}
const char* c_file = m_savePath->get().string().c_str();
std::ofstream dataFile( c_file, std::ios_base::binary );
if( dataFile )
{
WLogger::getLogger()->addLogMessage( "opening file", "Write Tracts", LL_DEBUG );
}
else
{
WLogger::getLogger()->addLogMessage( "open file failed" + m_savePath->get().string() , "Write Tracts", LL_ERROR );
return false;
}
dataFile.precision( 7 );
WLogger::getLogger()->addLogMessage( "start writing file", "Write Tracts", LL_DEBUG );
dataFile << ( "{\n" );
dataFile << ( " \"vertices\" : [" );
boost::shared_ptr > verts = ds->getVertices();
float fValue0;
float fValue1;
float fValue2;
for( size_t i = 0; i < verts->size() - 3; ++i )
{
fValue0 = verts->at( i );
i += 1;
fValue1 = verts->at( i );
i += 1;
fValue2 = verts->at( i );
dataFile << fValue0 << "," << fValue1 << "," << fValue2 << ",";
dataFile << fValue0 << "," << fValue1 << "," << fValue2 << ",";
}
fValue0 = verts->at( verts->size() - 3 );
fValue1 = verts->at( verts->size() - 2 );
fValue2 = verts->at( verts->size() - 1 );
dataFile << fValue0 << "," << fValue1 << "," << fValue2 << ",";
dataFile << fValue0 << "," << fValue1 << "," << fValue2 << "],\n";
dataFile << ( " \"indices\" : [" );
boost::shared_ptr > starts = ds->getLineStartIndexes();
boost::shared_ptr > lengths = ds->getLineLengths();
int counter = 0;
for( size_t i = 0; i < lengths->size(); ++i )
{
for( size_t k = 0; k < lengths->at( i ); ++k )
{
dataFile << counter << "," << counter + 1 << "," << counter + 2 << ",";
dataFile << counter + 1 << "," << counter + 3 << "," << counter + 2 << ",";
}
}
dataFile << "\n}";
dataFile.close();
WLogger::getLogger()->addLogMessage( "saving done", "Write Tracts", LL_DEBUG );
return true;
}
bool WMWriteTracts::savePOVRay( boost::shared_ptr< const WDataSetFibers > fibers ) const
{
// open file
boost::filesystem::path meshFile( m_savePath->get() );
std::string fnPath = meshFile.parent_path().string();
std::string fnBase = meshFile.stem().string();
std::string fnExt = meshFile.extension().string();
// construct the filenames
// the meshfile
std::string fnMesh = fnBase + ".mesh" + fnExt;
std::string fnScene = fnBase + ".scene" + fnExt;
// absolute paths
std::string fnMeshAbs = fnPath + "/" + fnMesh;
std::string fnSceneAbs = fnPath + "/" + fnScene;
debugLog() << "Opening " << fnMeshAbs << " for writing the mesh data.";
std::ofstream dataFile( fnMeshAbs.c_str(), std::ios_base::binary );
if( !dataFile )
{
errorLog() << "Opening " << fnMeshAbs << " failed.";
return false;
}
// needed arrays for iterating the fibers
WDataSetFibers::IndexArray fibStart = fibers->getLineStartIndexes();
WDataSetFibers::LengthArray fibLen = fibers->getLineLengths();
WDataSetFibers::VertexArray fibVerts = fibers->getVertices();
WDataSetFibers::TangentArray fibTangents = fibers->getTangents();
// get current color scheme - the mode is important as it defines the number of floats in the color array per vertex.
WDataSetFibers::ColorScheme::ColorMode fibColorMode = fibers->getColorScheme()->getMode();
debugLog() << "Color mode is " << fibColorMode << ".";
WDataSetFibers::ColorArray fibColors = fibers->getColorScheme()->getColor();
// for each fiber:
debugLog() << "Iterating over all fibers.";
// find min and max
double minX = wlimits::MAX_DOUBLE;
double minY = wlimits::MAX_DOUBLE;
double minZ = wlimits::MAX_DOUBLE;
double maxX = wlimits::MIN_DOUBLE;
double maxY = wlimits::MIN_DOUBLE;
double maxZ = wlimits::MIN_DOUBLE;
size_t currentStart = 0;
size_t increment = m_povraySaveOnlyNth->get();
boost::shared_ptr< WProgress > progress1 = boost::shared_ptr< WProgress >( new WProgress( "Converting fibers", fibStart->size() / increment ) );
m_progress->addSubProgress( progress1 );
for( size_t fidx = 0; fidx < fibStart->size(); fidx += increment )
{
++*progress1;
// the start vertex index
size_t sidx = fibStart->at( fidx ) * 3;
size_t csidx = fibStart->at( fidx ) * fibColorMode;
// the length of the fiber
size_t len = fibLen->at( fidx );
// walk along the fiber
WVector3d lastvert( fibVerts->at( sidx ),
fibVerts->at( sidx + 1 ),
fibVerts->at( sidx + 2 ) );
for( size_t k = 1; k < len; ++k )
{
// grab vector and color
WVector3d vert( fibVerts->at( ( 3 * k ) + sidx ),
fibVerts->at( ( 3 * k ) + sidx + 1 ),
fibVerts->at( ( 3 * k ) + sidx + 2 ) );
WColor color( fibColors->at( ( fibColorMode * k ) + csidx + ( 0 % fibColorMode ) ),
fibColors->at( ( fibColorMode * k ) + csidx + ( 1 % fibColorMode ) ),
fibColors->at( ( fibColorMode * k ) + csidx + ( 2 % fibColorMode ) ),
( fibColorMode == WDataSetFibers::ColorScheme::RGBA ) ?
fibColors->at( ( fibColorMode * k ) + csidx + ( 3 % fibColorMode ) ) : 1.0 );
if( vert.x() > maxX )
maxX = vert.x();
if( vert.y() > maxY )
maxY = vert.y();
if( vert.z() > maxZ )
maxZ = vert.z();
if( vert.x() < minX )
minX = vert.x();
if( vert.y() < minY )
minY = vert.y();
if( vert.z() < minZ )
minZ = vert.z();
// write it in POVRay style
dataFile << "cylinder" << std::endl <<
"{" << std::endl <<
" <" << lastvert.x() << "," << lastvert.y() << "," << lastvert.z() << ">," <<
"<" << vert.x() << "," << vert.y() << "," << vert.z() << ">,Diameter" << std::endl <<
" pigment{color rgb <" << color.x() << "," << color.y() << "," << color.z() << ">}" << std::endl <<
" transform MoveToCenter" << std::endl <<
"}" << std::endl;
dataFile << "sphere {" << std::endl <<
" <" << vert.x() << "," << vert.y() << "," << vert.z() << ">,Diameter" << std::endl <<
" pigment{ color rgb <" << color.x() << "," << color.y() << "," << color.z() << ">}" << std::endl <<
" transform MoveToCenter" << std::endl <<
"}" << std::endl;
lastvert = vert;
}
currentStart += len;
}
double sizeX = maxX - minX;
double sizeY = maxY - minY;
double sizeZ = maxZ - minZ;
double mX = minX + ( sizeX / 2.0 );
double mY = minY + ( sizeY / 2.0 );
double mZ = minZ + ( sizeZ / 2.0 );
// done writing mesh. Close
infoLog() << "Done. Closing " << fnMesh << ".";
dataFile.close();
progress1->finish();
debugLog() << "Opening " << fnSceneAbs << " for writing.";
std::ofstream dataFileScene( fnSceneAbs.c_str(), std::ios_base::binary );
if( !dataFileScene )
{
errorLog() << "Opening " << fnSceneAbs << " failed.";
return false;
}
// write some head data
dataFileScene << "#version 3.6;" << std::endl << std::endl;
dataFileScene << "// run with povray -w800 -h600 -Q0 fibsLarge.scene.pov " << std::endl <<
"// * this creates a fast preview of the scene with a resolution of 800x600." << std::endl <<
"// * the Q parameter defines the quality Q0 means plain colors. Q11 is best, including radiosity." << std::endl << std::endl;
if( m_povrayRadiosity->get() )
{
dataFileScene << "global_settings {" << std::endl <<
" ambient_light 0" << std::endl << std::endl <<
" radiosity {" << std::endl <<
" pretrace_start 0.08" << std::endl <<
" pretrace_end 0.005" << std::endl <<
" count 350" << std::endl <<
" error_bound 0.15" << std::endl <<
" recursion_limit 2" << std::endl <<
" }" << std::endl <<
"}" << std::endl << std::endl;
}
dataFileScene << "// Enable Phong lighting for all the geometry" << std::endl <<
dataFileScene << "#default{" << std::endl <<
" finish{" << std::endl <<
" ambient 0" << std::endl <<
" phong 1" << std::endl <<
" // reflection 0.9 " << std::endl <<
" }" << std::endl <<
"}" << std::endl << std::endl;
// save camera and add a light
double camX = 0;
double camY = sizeY;
double camZ = 0;
dataFileScene << "#declare MoveToCenter = transform{ translate < " << -mX << ", " << -mY << ", " << -mZ << " > };" << std::endl;
dataFileScene << "#declare CamPosition = < " << camX << ", " << camY << ", " << camZ << " >;" << std::endl << std::endl;
dataFileScene << "// Tube diameter" << std::endl;
dataFileScene << "#declare Diameter = " << m_povrayTubeDiameter->get() << ";" << std::endl << std::endl;
// this camera should produce a direct front view. The user surely needs to modify the camera
dataFileScene << "camera {" << std::endl <<
" orthographic angle 45" << std::endl <<
" location CamPosition" << std::endl <<
" right 1.33*x" << std::endl <<
" // use this with 1280x1024" << std::endl <<
" // right 1.25*x" << std::endl <<
" up y " << std::endl <<
" look_at < 0, 0, 0 >" << std::endl <<
"}" << std::endl << std::endl;
// headlight
dataFileScene << "light_source {" << std::endl <<
" CamPosition" << std::endl <<
" color rgb <1.0, 1.0, 1.0>" << std::endl <<
"}" << std::endl << std::endl;
// do not forget the mesh
dataFileScene << "#include \"" << fnMesh << "\"" << std::endl;
// done. Close
infoLog() << "Done.";
dataFileScene.close();
return true;
}
void WMWriteTracts::fileTypeChanged()
{
if( m_fileTypeSelection->get().getItemIndexOfSelected( 0 ) == 4 )
{
m_povrayOptions->setHidden( false );
}
else
{
m_povrayOptions->setHidden( true );
}
}