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

[MERGE]

parents 37740482 d02fbe27
......@@ -40,6 +40,7 @@
#include "WCondition.h"
#include "WPropertyGroupBase.h"
#include "WPropertyBase.h"
#include "WPropertyTypes.h"
#include "exceptions/WPropertyUnknown.h"
/**
......@@ -97,6 +98,76 @@ namespace WPropertyStructHelper
*/
typedef boost::mpl::na NOTYPE;
/**
* Comfortable template to create a property instance and add it to the group. This is needed in \ref WPropertyStruct to create instances for each
* of WPropertyStruct's types while automatically handling the NOTYPE defaults.
*
* \tparam PropertyType the property type to create. It is assumed that this is a shared_ptr< WPropertyXYZ >.
*/
template< typename PropertyType >
struct InstanceCreator
{
/**
* The type of the initial value.
*/
typedef typename PropertyType::element_type::ValueType ValueType;
/**
* Actually does the work and adds a new property with the given name, description and other parameters to the specified group.
*
* \param group the group to add the new property to
* \param name the name of the new property
* \param description the description of the new property
* \param initial initial value
*/
static void createAndAdd( WPropertyGroupBase* group, std::string name, std::string description, const ValueType& initial = ValueType() )
{
group->addArbitraryProperty(
PropertyType(
new typename PropertyType::element_type( name, description, initial )
)
);
}
};
/**
* Specialization which does nothing for the NOTYPE default template parameters of \ref WPropertyStruct.
*/
template<>
struct InstanceCreator< NOTYPE >
{
/**
* The type of the initial value.
*/
typedef NOTYPE ValueType;
/**
* Dummy method which does nothing for NOTYPE types.
*
* \param WPropertyGroupBase not used
* \param std::string not used
* \param std::string not used
* \param ValueType not used.
*/
static void createAndAdd( WPropertyGroupBase*, std::string, std::string, const ValueType& )
{
// NOTYPE will not cause any property creation.
}
/**
* Dummy method which does nothing for NOTYPE types.
*
* \param WPropertyGroupBase not used
* \param std::string not used
* \param std::string not used
*/
static void createAndAdd( WPropertyGroupBase*, std::string, std::string )
{
// NOTYPE will not cause any property creation.
}
};
/**
* Convert a list of template parameters to a boost::mpl::vector. This is currently done using the boost::mpl no-type type. This might get a
* problem some day?!
......@@ -140,6 +211,7 @@ namespace WPropertyStructHelper
* property is only ONE object and not a group of multiple objects.
*
* \note the limitation to 10 types is due to the boost::tuple. If you need more, you need to replace the tuple type as storage-backend.
* \note if we use C++11 some day, we could use variadic templates here.
*
* \tparam T0 first type. Mandatory.
* \tparam T1 additional type. Optional.
......@@ -167,6 +239,7 @@ template<
class WPropertyStruct: public WPropertyGroupBase
{
friend class WPropertyStructTest;
template< typename T > friend class WPropertyStructHelper::InstanceCreator;
public:
typedef WPropertyStruct< BOOST_PP_ENUM_PARAMS( 10, T ) > WPropertyStructType;
......@@ -200,6 +273,17 @@ public:
WPropertyStruct( std::string name, std::string description ):
WPropertyGroupBase( name, description )
{
// now create the property instances
WPropertyStructHelper::InstanceCreator< T0 >::createAndAdd( this, "0", "0" );
WPropertyStructHelper::InstanceCreator< T1 >::createAndAdd( this, "1", "1" );
WPropertyStructHelper::InstanceCreator< T2 >::createAndAdd( this, "2", "2" );
WPropertyStructHelper::InstanceCreator< T3 >::createAndAdd( this, "3", "3" );
WPropertyStructHelper::InstanceCreator< T4 >::createAndAdd( this, "4", "4" );
WPropertyStructHelper::InstanceCreator< T5 >::createAndAdd( this, "5", "5" );
WPropertyStructHelper::InstanceCreator< T6 >::createAndAdd( this, "6", "6" );
WPropertyStructHelper::InstanceCreator< T7 >::createAndAdd( this, "7", "7" );
WPropertyStructHelper::InstanceCreator< T8 >::createAndAdd( this, "8", "8" );
WPropertyStructHelper::InstanceCreator< T9 >::createAndAdd( this, "9", "9" );
}
/**
......@@ -381,7 +465,7 @@ public:
virtual bool set( boost::shared_ptr< WPropertyBase > value )
{
// is this the same type as we are?
WPropertyStructType::SPtr v = boost::shared_dynamic_cast< WPropertyStructType >( value );
typename WPropertyStructType::SPtr v = boost::shared_dynamic_cast< WPropertyStructType >( value );
if( !v )
{
// it is not a WPropertyStruct with the same type
......
......@@ -22,11 +22,11 @@
//
//---------------------------------------------------------------------------
#include <cassert>
#include <cmath>
#include <iostream>
#include <algorithm>
#include "WAssert.h"
#include "WTransferFunction.h"
......@@ -142,18 +142,18 @@ void WTransferFunction::sample1DTransferFunction( unsigned char*array, int width
{
while ( c2 != colors.end() && iso > c2->iso )
{
assert( c2 != colors.end() );
WAssert( c2 != colors.end(), "Corruption of internal data structure." );
c1++;
c2++;
assert( c1 != colors.end() );
WAssert( c1 != colors.end(), "Corruption of internal data structure." );
}
while ( a2 != alphas.end() && iso > a2->iso )
{
assert( a2 != alphas.end() );
WAssert( a2 != alphas.end(), "Corruption of internal data structure." );
a1++;
a2++;
assert( a1 != alphas.end() );
WAssert( a1 != alphas.end(), "Corruption of internal data structure." );
}
if ( c2 == colors.end() )
......@@ -231,4 +231,145 @@ void WTransferFunction::addAlpha( double iso, double alpha )
}
}
WTransferFunction WTransferFunction::createFromRGBA( unsigned char const * const rgba, size_t size )
{
// we create a linear match to the transfer funciton given by rgba by scanning the
// alpha and color values in two passes.
// each pass starts at the left of the picture and moves right looking for a good match of
// the line between start point and end point to the given function in between. The error is
// normalized to a per-sample basis. If the maximum error is below MIN_ERROR_THRESHOLD, the
// line is accepted and the next segment is analyzed.
const double MIN_ERROR_THRESHOLD = 5.0;
WTransferFunction rgbatf;
std::vector < float > values( size );
// copy channel
for ( size_t i = 0; i < size; ++i )
{
values[ i ] = static_cast<double>( rgba[ i*4+3 ] );
}
// add first and last alpha
rgbatf.addAlpha( 0.0, values[ 0 ]/255. );
rgbatf.addAlpha( 1.0, values[ size-1 ]/255. );
std::vector < float > errors( size );
size_t seed = 0;
while ( seed < size-1 )
{
// start at first pixel and fit a line to the data
size_t to = seed+1;
while ( to < size )
{
double error = 0.0;
double incline = ( values[ to ] - values[ seed ] )/( to-seed );
for ( size_t j = seed+1; j < to; ++j )
{
error += std::sqrt( std::pow( values[ j ] - values[ seed ] - incline * ( j-seed ), 2 ) );
}
errors[ to ] = error/( to-seed ); // compute square error per pixel length of line
++to;
}
size_t minElement = size-1;
double minerror = errors[ minElement ];
for ( to = size-1; to > seed; --to )
{
if ( errors[ to ] < minerror )
{
minElement = to;
minerror = errors[ to ];
if ( minerror < MIN_ERROR_THRESHOLD )
{
break;
}
}
}
if ( minElement < size-1 )
{
rgbatf.addAlpha( ( double )minElement/( double )( size-1 ), values[ minElement ]/255. );
}
seed = minElement;
}
// same for color
// add first and last color
rgbatf.addColor( 0.0, WColor( rgba[ 0*4+0 ]/255.f, rgba[ 0*4+1 ]/255.f, rgba[ 0*4+2 ]/255.f, 0.f ) );
rgbatf.addColor( 1.0, WColor( rgba[ ( size-1 )*4+0 ]/255.f, rgba[ ( size-1 )*4+1 ]/255.f, rgba[ ( size-1 )*4+2 ]/255.f, 0.f ) );
// first try of code: use combined RGB errors
seed = 0;
while ( seed < size-1 )
{
// start at first pixel and fit a line to the data
size_t to = seed+1;
while ( to < size )
{
double error = 0.0;
double inclineR = ( rgba[ to*4+0 ] - rgba[ seed*4+0 ] )/( to-seed );
double inclineG = ( rgba[ to*4+1 ] - rgba[ seed*4+1 ] )/( to-seed );
double inclineB = ( rgba[ to*4+2 ] - rgba[ seed*4+2 ] )/( to-seed );
for ( size_t j = seed; j < to; ++j )
{
error += std::sqrt(
std::pow( rgba[ 4*j+0 ] - rgba[ 4*seed+0 ] - inclineR * ( j-seed ), 2 ) +
std::pow( rgba[ 4*j+1 ] - rgba[ 4*seed+1 ] - inclineG * ( j-seed ), 2 ) +
std::pow( rgba[ 4*j+2 ] - rgba[ 4*seed+2 ] - inclineB * ( j-seed ), 2 )
);
}
errors[ to ] = error/( to-seed ); // compute square error per pixel length of line
++to;
}
size_t minElement = size-1;
double minerror = errors[ size-1 ];
// traverse from back
for ( to = size-2; to > seed; --to )
{
if ( errors[ to ] < minerror )
{
minElement = to;
minerror = errors[ to ];
}
if ( minerror < MIN_ERROR_THRESHOLD*2.0 ) //! the threshold here is larger than for alpha, becuase we compare all colors at once
{
break;
}
}
if ( minElement < size-1 )
{
rgbatf.addColor( ( double )minElement/( double )( size-1 ),
WColor( rgba[ minElement*4+0 ]/255.f, rgba[ minElement*4+1 ]/255.f, rgba[ minElement*4+2 ]/255.f, 0.f ) );
}
seed = minElement;
}
std::cout << "New Transfer Function: " << rgbatf << "." << std::endl;
return rgbatf;
}
std::ostream& operator << ( std::ostream& out, const WTransferFunction& tf )
{
size_t numColors = tf.numColors();
for ( size_t i = 0; i < numColors; ++i )
{
double iso = tf.getColorIsovalue( i );
WColor c = tf.getColor( i );
out << "c:" << iso << ":" << c[ 0 ] << ":" << c[ 1 ] << ":" << c[ 2 ] << ";";
}
size_t numAlphas = tf.numAlphas();
for ( size_t i = 0; i < numAlphas; ++i )
{
double iso = tf.getAlphaIsovalue( i );
double alpha = tf.getAlpha( i );
out << "a:" << iso << ":" << alpha;
if ( i != numAlphas-1 )
{
out << ";";
}
}
return out;
}
......@@ -25,6 +25,7 @@
#ifndef WTRANSFERFUNCTION_H
#define WTRANSFERFUNCTION_H
#include <iosfwd>
#include <vector>
#include "WColor.h"
......@@ -249,6 +250,14 @@ public:
* \post array contains the sampled data
*/
void sample1DTransferFunction( unsigned char*array, int width, double min, double max ) const;
/**
* Try to estimate a transfer function from an RGBA image.
* \param rgba: values between 0 and 255 representing the red, green, and blue channel
* \param size: number of color entries in rgba
* \returns approximated transfer function
*/
static WTransferFunction createFromRGBA( unsigned char const * const rgba, size_t size );
private:
std::vector<ColorEntry> colors; //< sorted list of colors
......@@ -259,6 +268,17 @@ private:
std::vector< double > histogram; //< store a histogram. This is used for property-handling only
// and does not change the transfer function at all.
friend std::ostream& operator << ( std::ostream& out, const WTransferFunction& tf );
};
/**
* Default output operator. Currently stores values the same way as it is done in the properties.
* This code should only be used for debugging and you should not realy on the interface.
* \param tf The transfer function to output
* \param out The stream to which we write
* \returns reference to \param out
*/
std::ostream& operator << ( std::ostream& out, const WTransferFunction& tf );
#endif // WTRANSFERFUNCTION_H
......@@ -49,6 +49,6 @@ bool WGERequirement::isComplied() const
std::string WGERequirement::getDescription() const
{
return "Module needs an running graphics engine.";
return "Module needs a running graphics engine.";
}
......@@ -185,12 +185,15 @@ bool WGraphicsEngine::waitForStartupComplete()
{
if( !m_instance )
{
WLogger::getLogger()->addLogMessage( "Not Graphics Engine running", "GE", LL_INFO );
return false;
}
WLogger::getLogger()->addLogMessage( "Blocking for graphics engine startup", "GE", LL_INFO );
// this ensures that the startup is completed if returning.
m_instance->m_startThreadingCondition.wait();
WLogger::getLogger()->addLogMessage( "Done blocking for graphics engine startup, maybe running now", "GE", LL_INFO );
// did something went wrong? Ensure by checking if really running.
return isRunning();
}
......@@ -207,6 +210,9 @@ void WGraphicsEngine::threadMain()
m_viewer->run();
m_viewer->stopThreading();
m_running = false;
#else
//m_startThreadingCondition.wait();
m_running = true; // we have to make sure, that we are "running"
#endif
}
......
......@@ -28,6 +28,7 @@
#include <string>
#include <osg/LightModel>
#include <osg/Geometry>
#include "core/common/WAssert.h"
#include "core/common/WPropertyHelper.h"
......
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-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 <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#include <string>
#include <boost/lexical_cast.hpp>
#include <osg/Projection>
#include <osg/Geode>
#include "core/dataHandler/WDataTexture3D.h"
#include "core/graphicsEngine/WGEColormapping.h"
#include "core/graphicsEngine/callbacks/WGENodeMaskCallback.h"
#include "core/graphicsEngine/callbacks/WGEFunctorCallback.h"
#include "core/graphicsEngine/WGEGeodeUtils.h"
#include "core/graphicsEngine/shaders/WGEShader.h"
#include "core/graphicsEngine/widgets/labeling/WGELabel.h"
#include "core/kernel/WKernel.h"
#include "WMTransferFunctionColorBar.xpm"
#include "WMTransferFunctionColorBar.h"
// This line is needed by the module loader to actually find your module.
W_LOADABLE_MODULE( WMTransferFunctionColorBar )
WMTransferFunctionColorBar::WMTransferFunctionColorBar() :
WModule()
{
// initialize
}
WMTransferFunctionColorBar::~WMTransferFunctionColorBar()
{
// cleanup
removeConnectors();
}
boost::shared_ptr< WModule > WMTransferFunctionColorBar::factory() const
{
return boost::shared_ptr< WModule >( new WMTransferFunctionColorBar() );
}
const char** WMTransferFunctionColorBar::getXPMIcon() const
{
return WMTransferFunctionColorBar_xpm;
}
const std::string WMTransferFunctionColorBar::getName() const
{
return "Transfer Function Color Bar";
}
const std::string WMTransferFunctionColorBar::getDescription() const
{
return "Can use the input as a texture that can be mapped to the navslices and so on.";
}
void WMTransferFunctionColorBar::connectors()
{
m_input = WModuleInputData< WDataSetSingle >::createAndAdd( shared_from_this(), "transfer function", "Input to apply as texture." );
// call WModules initialization
WModule::connectors();
}
void WMTransferFunctionColorBar::properties()
{
m_propCondition = boost::shared_ptr< WCondition >( new WCondition() );
// m_replace = m_properties->addProperty( "Keep position",
// "If true, new texture on the input connector get placed in the list where the old one was.", true );
WPropGroup colorBarGroup = m_properties->addPropertyGroup( "Colorbar", "The colorbar with several properties." );
// m_showColorbar = colorBarGroup->addProperty( "Show Colorbar", "If true, a colorbar is shown for the current colormap.", false );
m_colorBarBorder = colorBarGroup->addProperty( "Show Border", "If true, a thin white border is shown around the colorbar.", true );
m_colorBarName = colorBarGroup->addProperty( "Show Name", "If true, a shortened version of the data name is shown.", true );
m_colorBarLabels = colorBarGroup->addProperty( "Colorbar Labels", "This defines the number of labels.", 10 );
m_colorBarLabels->setMin( 0 );
m_colorBarLabels->setMax( 55 );
WModule::properties();
}
namespace
{
/**
* Formats a given string to have the specified maximum length.
*
* \param str the string
* \param maxLen max length
*
* \return formatted string
*/
std::string format( std::string str, size_t maxLen = 45 )
{
// string will be at least 9 chars long: because of " [...] " + first and last char.
WAssert( maxLen >= 9, "MaxLen has to be 9 or more." );
std::ostringstream ss;
// cut away some stuff
if( str.length() > maxLen )
{
size_t keep = maxLen - 3; // how much chars to keep?
size_t keepFront = keep / 2;
size_t keepEnd = keep - keepFront;
ss << str.substr( 0, keepFront ) << " [...] " << str.substr( str.length() - keepEnd, keepEnd );
}
else
{
ss << str;
}
return ss.str();
}
}
/**
* Formats a number properly to be usable as label
*
* \tparam T the type of value.
* \param number the number to format
*
* \return the formated number
*/
template< typename T >
std::string format( T number )
{
std::ostringstream ss;
ss.precision( 3 );
ss << number;
return ss.str();
}
void WMTransferFunctionColorBar::moduleMain()
{
// let the main loop awake if the data changes or the properties changed.
m_moduleState.setResetable( true, true );
m_moduleState.add( m_input->getDataChangedCondition() );
m_moduleState.add( m_propCondition );
osg::ref_ptr< WGEShader > colormapShader = osg::ref_ptr< WGEShader > ( new WGEShader( "WMTransferFunctionColorBar", m_localPath ) );
// signal ready state
ready();
// loop until the module container requests the module to quit
while( !m_shutdownFlag() )
{
// Now, the moduleState variable comes into play. The module can wait for the condition, which gets fired whenever the input receives data
// or an property changes. The main loop now waits until something happens.
debugLog() << "Waiting ...";
m_moduleState.wait();
// woke up since the module is requested to finish
if( m_shutdownFlag() )
{
break;
}
// has the data changed?
if( m_input->handledUpdate() )
{
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_barProjection );
colormapShader->deactivate( m_colorBar );
boost::shared_ptr< WDataSetSingle > dataSet = m_input->getData();
// add a colorbar
if( dataSet/* && dataSet->isTexture()*/ )
{
// create camera oriented 2d projection
m_barProjection = new osg::Projection();
// m_barProjection->addUpdateCallback( new WGENodeMaskCallback( m_showColorbar ) );
// m_barProjection->addUpdateCallback( new WGENodeMaskCallback( m_active ) );
m_barProjection->setMatrix( osg::Matrix::ortho2D( 0, 1.0, 0, 1.0 ) );
m_barProjection->getOrCreateStateSet()->setRenderBinDetails( 15, "RenderBin" );
m_barProjection->getOrCreateStateSet()->setDataVariance( osg::Object::DYNAMIC );
m_barProjection->getOrCreateStateSet()->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
m_barProjection->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
float borderWidth = 0.001;
// create a colorbar geode