//--------------------------------------------------------------------------- // // 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 . // //--------------------------------------------------------------------------- // Some rules to the inclusion of headers: // * Ordering: // * C Headers // * C++ Standard headers // * External Lib headers (like OSG or Boost headers) // * headers of other classes inside OpenWalnut // * your own header file #define _USE_MATH_DEFINES #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/kernel/WKernel.h" #include "core/common/WColor.h" #include "core/common/WPathHelper.h" #include "core/common/WPropertyHelper.h" #include "core/common/math/WMatrix.h" #include "core/common/math/linearAlgebra/WMatrixFixed.h" #include "core/common/math/linearAlgebra/WVectorFixed.h" #include "core/common/WItemSelection.h" #include "core/common/WItemSelectionItem.h" #include "core/common/WItemSelectionItemTyped.h" #include "core/common/WItemSelector.h" #include "core/graphicsEngine/WGEUtils.h" #include "core/graphicsEngine/WGERequirement.h" #include "../interaction/WViewEventHandler.h" #include "../interaction/WGetMatrixCallback.h" #include "WMTransferCalc.xpm" #include "WMTransferCalc.h" WMTransferCalc::WMTransferCalc(): WModule() { // In the constructor, you can initialize your members and all this stuff. You must not initialize connectors or properties here! You also // should avoid doing computationally expensive stuff, since every module has its own thread which is intended to be used for such calculations. // Please keep in mind, that every member initialized here is also initialized in the prototype, which may be a problem if the member is large, // and therefore, wasting a lot of memory in your module's prototype instance. } WMTransferCalc::~WMTransferCalc() { // Cleanup! } boost::shared_ptr< WModule > WMTransferCalc::factory() const { return boost::shared_ptr< WModule >( new WMTransferCalc() ); } const char** WMTransferCalc::getXPMIcon() const { return WMTransferCalc_xpm; } const std::string WMTransferCalc::getName() const { return "Transfer Function Calculation"; } const std::string WMTransferCalc::getDescription() const { return "Calculates optimal settings for the Transfer Function after klicking " \ "by casting rays through the given data and analysing their profiles, " \ "e. g. with regard to value, gradient or gradient weight."; } void WMTransferCalc::connectors() { m_inputData = WModuleInputData< WDataSetScalar >::createAndAdd( shared_from_this(), "Compulsory Data", "Data which will be checked for displaying." ); m_inputDerivation = WModuleInputData< WDataSetVector >::createAndAdd( shared_from_this(), "Derivated Data", "Derivated data by using the module 'Spatial Derivative'." ); m_inputFA = WModuleInputData< WDataSetScalar >::createAndAdd( shared_from_this(), "Fractional Anisotropy Data (optional)", "Input for additional data concerning fractional anisotropy of a dataset." ); // output data m_curveMeanOut = WModuleOutputData< WDataSetScalar >::createAndAdd( shared_from_this(), "(a+b)/2", "The calculated curvature dataset in mean form." ); m_curveGaussOut = WModuleOutputData< WDataSetScalar >::createAndAdd( shared_from_this(), "a*b", "The calculated curvature dataset in Gauss form." ); // call WModules initialization WModule::connectors(); } void WMTransferCalc::properties() { m_propCondition = boost::shared_ptr< WCondition >( new WCondition() ); m_color = m_properties->addProperty( "Color", "Color of the ray.", WColor( 1.0, 0.0, 0.0, 1.0 ), m_propCondition ); m_interval = m_properties->addProperty( "Interval", "Interval for sampling rays.", 0.30, m_propCondition ); m_rayNumber = m_properties->addProperty( "# of Rays", "Number of rays being casted with one click.", 10, m_propCondition ); m_radius = m_properties->addProperty( "Radius", "Radius for circular vicinity in which the rays shall be casted.", 2, m_propCondition ); m_plotDataSelection = WItemSelection::SPtr( new WItemSelection() ); m_plotDataSelection->addItem( MyItemType::create( 3, "Plot value.", "Chooses 'value' data for plotting." ) ); m_plotDataSelection->addItem( MyItemType::create( 7, "Plot gradient weight.", "Chooses 'gradient weight' data for plotting." ) ); m_plotDataSelection->addItem( MyItemType::create( 8, "Plot FA value.", "Chooses 'fractional anisotropy' data for plotting." ) ); m_plotDataSelection->addItem( MyItemType::create( 9, "Plot angle.", "Chooses 'angle between ray direction and gradient' for plotting." ) ); m_plotDataSelection->addItem( MyItemType::create( 10, "Plot mean curvature.", "Chooses 'mean curvature' data for plotting." ) ); m_plotDataSelection->addItem( MyItemType::create( 11, "Plot gaussian curvature.", "Chooses 'Gaussian curvature' data for plotting." ) ); m_multiPlotDataSelection = m_properties->addProperty( "Choose plot data", "Choose one of these for data plotting.", m_plotDataSelection->getSelectorFirst(), m_propCondition ); m_RaySaveFilePath = m_properties->addProperty( "Save RayProfile to Folder:", "Savefile for RayProfile.", WPathHelper::getAppPath(), m_propCondition ); m_saveTrigger = m_properties->addProperty( "Save", "Save RayProfile to given directory.", WPVBaseTypes::PV_TRIGGER_READY, m_propCondition ); WPropertyHelper::PC_PATHEXISTS::addTo( m_RaySaveFilePath ); WPropertyHelper::PC_ISDIRECTORY::addTo( m_RaySaveFilePath ); WPropertyHelper::PC_NOTEMPTY::addTo( m_multiPlotDataSelection ); WModule::properties(); } void WMTransferCalc::requirements() { m_requirements.push_back( new WGERequirement() ); } bool WMTransferCalc::saveRayProfileTo( WPropFilename path, std::string filename, WRayProfile *profile ) { WRayProfile *prof; if( profile == NULL ) { // if no profile is given, the main profile will be used as default prof = &m_mainProfile; } else { // use given profile prof = profile; } //( profile == NULL ) ? prof = &m_mainProfile : prof = profile; if( prof->size() == 0 ) { return false; // empty profiles will not be saved } // compose context of the soon-to-be-saved file which will be saved in given path with given filename std::ofstream savefile( ( path->get( true ).string() + "/" + filename ).c_str() /*, ios::app */ ); // heading savefile << "# RayProfile data for OpenWalnut TransferCalcModule" << std::endl; savefile << "# ID \t dist \t value \t grad_x \t grad_y \t grad_z \t gradW \t FA \t angle \t mean \t gauss" << std::endl; // save data of each sample point for( size_t sample = 0; sample < prof->size(); sample++ ) { savefile << sample + 1 << "\t"; // print id savefile << (*prof)[sample].distance() << "\t"; // print t savefile << (*prof)[sample].value() << "\t"; // print value savefile << (*prof)[sample].gradient()[0] << "\t"; // print gradient x savefile << (*prof)[sample].gradient()[1] << "\t"; // print gradient y savefile << (*prof)[sample].gradient()[2] << "\t"; // print gradient z savefile << (*prof)[sample].gradWeight() << "\t"; // print weight of gradient savefile << (*prof)[sample].fracA() << "\t"; // print fractional anisotropy savefile << (*prof)[sample].angle() << "\t"; // print angle between gradient and ray savefile << (*prof)[sample].meanCurv() << "\t"; // print mean curvature savefile << (*prof)[sample].gaussCurv() << "\t"; // print gauss curvature savefile << std::endl; } savefile.close(); return true; } void WMTransferCalc::moduleMain() { debugLog() << "moduleMain started..."; // init observation of properties m_moduleState.setResetable( true, true ); m_moduleState.add( m_inputData->getDataChangedCondition() ); m_moduleState.add( m_inputDerivation->getDataChangedCondition() ); m_moduleState.add( m_inputFA->getDataChangedCondition() ); m_moduleState.add( m_propCondition ); ready(); debugLog() << "Module is now ready."; m_rootNode = new WGEManagedGroupNode( m_active ); //m_rootNode->addUpdateCallback( new TranslateCallback( this ) ); WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_rootNode ); // Create and add an event handler for the main view as we are interested in click events WViewEventHandler::RefPtr handler( new WViewEventHandler() ); WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->getView()->addEventHandler( handler ); handler->onLeftClick( boost::bind( &WMTransferCalc::onClick, this, _1 ) ); // as we need the projection and view matrices: add a callback which queries this m_matrixCallback = new WGetMatrixCallback(); m_rootNode->addCullCallback( m_matrixCallback ); debugLog() << "Entering main loop"; while( !m_shutdownFlag() ) { debugLog() << "Waiting ..."; m_moduleState.wait(); debugLog() << "New Data Incoming ..."; // woke up since the module is requested to finish if( m_shutdownFlag() ) { debugLog() << "Closing module..."; break; } // activates if user pushes save button to save current RayProfile if( m_saveTrigger->get( true ) == WPVBaseTypes::PV_TRIGGER_TRIGGERED ) { debugLog() << "User saves RayProfile(s)."; // start progress for GUI boost::shared_ptr< WProgress > saving = boost::shared_ptr< WProgress >( new WProgress( "Saving calculated RayProfiles." ) ); m_progress->addSubProgress( saving ); // save data and plot file for every profile unsigned int nrProfiles = m_profiles.size(); for( unsigned int id = 0; id < nrProfiles; id++ ) { // generate header // first a general header for setting default options std::stringstream prepGNUPlotScript; prepGNUPlotScript << "# Measured RayProfile data for Profile "<< id + 1 << std::endl << "#===========================================" << std::endl << std::endl << "set title 'RayProfile " << id + 1 << "'" << std::endl << "set pointsize 0.5" << std::endl << "set grid" << std::endl << "set xlabel 'uniform distance on ray'" << std::endl; // generate filename of data file std::stringstream prepFilename; prepFilename << "RayProfile" << id + 1 << ".dat"; std::string filename( prepFilename.str() ); // save data which will be plotted with the plot script with this method bool success = saveRayProfileTo( m_RaySaveFilePath, filename, &m_profiles[id] ); if( !success ) { errorLog() << "RayProfile " << id + 1 << " was empty or could not be saved."; } else // saving successful { // check selector for the chosen data to plot WItemSelector s = m_multiPlotDataSelection->get( true ); unsigned int count = s.size(); prepGNUPlotScript << "plot "; // each selected option gets an individual command line in the later script for( size_t i = 0; i < s.size(); ++i ) { // infoLog() << "The user chose " << s.at( i )->getName() // << " with the value " << s.at( i )->getAs< MyItemType >()->getValue(); // switch works with an enumeration switch( s.at( i )->getAs< MyItemType >()->getValue() ) { case VALUE: prepGNUPlotScript << "'" << filename << "' using 2:3 title 'value' with linespoints"; if( count - 1 != 0 ) { prepGNUPlotScript << ", \\" << std::endl; } count--; break; case WEIGHT: prepGNUPlotScript << "'" << filename << "' using 2:7 title 'gradient weight' with linespoints"; if( count - 1 != 0 ) { prepGNUPlotScript << ", \\" << std::endl; } count--; break; case FA: prepGNUPlotScript << "'" << filename << "' using 2:($8*100) title 'FA value' with linespoints"; if( count - 1 != 0 ) { prepGNUPlotScript << ", \\" << std::endl; } count--; break; case ANGLE: prepGNUPlotScript << "'" << filename << "' using 2:($9*(180/3.14159265)) title 'angle' with linespoints"; if( count - 1 != 0 ) { prepGNUPlotScript << ", \\" << std::endl; } count--; break; case MEAN: prepGNUPlotScript << "'" << filename << "' using 2:($10*100) title 'mean curvature' with linespoints"; if( count - 1 != 0 ) { prepGNUPlotScript << ", \\" << std::endl; } count--; break; case GAUSS: prepGNUPlotScript << "'" << filename << "' using 2:($11*100) title 'gaussian curvature' with linespoints"; if( count - 1 != 0 ) { prepGNUPlotScript << ", \\" << std::endl; } count--; break; default: errorLog() << "No properties selected!"; } } // generate filename for plot script std::stringstream prepPlotFilename; prepPlotFilename << "/plot" << id + 1 << ".gsc"; std::string plotfilename( prepPlotFilename.str() ); // save plot script std::ofstream plotFile( ( m_RaySaveFilePath->get( true ).string() + plotfilename ).c_str() ); plotFile << prepGNUPlotScript.str(); plotFile.close(); } } // end progress saving->finish(); m_progress->removeSubProgress( saving ); // reactivate trigger m_saveTrigger->set( WPVBaseTypes::PV_TRIGGER_READY, false ); } // check input connectors for new datasets bool dataChanged = ( m_inputData->getData() != m_dataSet ); bool additionalData = ( m_inputFA->getData() != m_FAdataSet ); bool newDerivative = ( m_inputDerivation->getData() != m_deriDataSet ); bool dataValid = static_cast< bool >( m_dataSet ); m_DeriIsValid = static_cast< bool >( m_deriDataSet ); m_FAisValid = static_cast< bool >( m_FAdataSet ); if( dataChanged ) // main data, compulsory { m_dataSet = m_inputData->getData(); dataValid = static_cast< bool >( m_dataSet ); debugLog() << "New Data!"; if( !dataValid ) { debugLog() << "No valid data anymore. Cleaning up."; m_rootNode->clear(); m_grid.reset(); } else { // grab the grid m_grid = boost::dynamic_pointer_cast< WGridRegular3D >( m_dataSet->getGrid() ); if( !m_grid ) { errorLog() << "The dataset does not provide a regular grid. Ignoring dataset."; dataValid = false; } } } if( newDerivative ) // optional: dataset with derivated data { m_deriDataSet = m_inputDerivation->getData(); m_DeriIsValid = static_cast< bool >( m_deriDataSet ); debugLog() << "New Derivated Data!"; if( !m_DeriIsValid ) { debugLog() << "No valid derivated data anymore. Cleaning up."; m_rootNode->clear(); m_deriGrid.reset(); m_curveMeanOut->reset(); m_curveGaussOut->reset(); } else { // grab the grid m_deriGrid = boost::dynamic_pointer_cast< WGridRegular3D >( m_deriDataSet->getGrid() ); if( !m_deriGrid ) { errorLog() << "The derivated dataset does not provide a regular grid. Ignoring dataset."; m_DeriIsValid = false; } } if( m_DeriIsValid ) { // reset old calculations m_meanCurvDataSet.reset(); m_gaussCurvDataSet.reset(); // this may take some time, depending on the data calculateCurvature(); } } if( additionalData ) // optional: fractional anisotropy data { m_FAdataSet = m_inputFA->getData(); m_FAisValid = static_cast< bool >( m_FAdataSet ); debugLog() << "New FA Data!"; if( !m_FAisValid ) { debugLog() << "No valid FA data anymore. Cleaning up."; m_rootNode->clear(); m_FAgrid.reset(); } else { // grab the grid m_FAgrid = boost::dynamic_pointer_cast< WGridRegular3D >( m_FAdataSet->getGrid() ); if( !m_FAgrid ) { errorLog() << "The FA dataset does not provide a regular grid. Ignoring dataset."; m_FAisValid = false; } } } bool propsChanged = m_interval->changed() || m_rayNumber->changed() || m_radius->changed(); // observes properties and connectors // main calculation of the module starts in onClick function if( ( propsChanged || dataChanged ) && dataValid ) { debugLog() << "Data or properties changed."; // grid scale double x_scale = m_grid->getNbCoordsX(); double y_scale = m_grid->getNbCoordsY(); double z_scale = m_grid->getNbCoordsZ(); // voxel scale // double dist_x = m_grid->getOffsetX(); // double dist_y = m_grid->getOffsetY(); // double dist_z = m_grid->getOffsetZ(); debugLog() << "x = " << x_scale << ", y = " << y_scale << ", z = " << z_scale; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Calculating BoundingBox and OuterBounding //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if( dataChanged ) { std::vector< WVector4d > bounding_box( 8 ); // BoundingBox of the dataset bounding_box[0] = WVector4d( 0, 0, 0, 1 ); bounding_box[1] = WVector4d( x_scale, 0, 0, 1 ); bounding_box[2] = WVector4d( 0, y_scale, 0, 1 ); bounding_box[3] = WVector4d( x_scale, y_scale, 0, 1 ); bounding_box[4] = WVector4d( 0, 0, z_scale, 1 ); bounding_box[5] = WVector4d( x_scale, 0, z_scale, 1 ); bounding_box[6] = WVector4d( 0, y_scale, z_scale, 1 ); bounding_box[7] = WVector4d( x_scale, y_scale, z_scale, 1 ); // BoundingBox parallel to the screen (just min and max) m_outer_bounding.resize( 2 ); m_outer_bounding[0] = bounding_box[0]; m_outer_bounding[1] = bounding_box[0]; for( unsigned int k = 0; k < 8; k++ ) { // minimal point in BoundingBox m_outer_bounding[0][0] = std::min( bounding_box[k][0], m_outer_bounding[0][0] ); // x-min m_outer_bounding[0][1] = std::min( bounding_box[k][1], m_outer_bounding[0][1] ); // y-min m_outer_bounding[0][2] = std::min( bounding_box[k][2], m_outer_bounding[0][2] ); // z-min m_outer_bounding[0][3] = 1; // debugLog() << "min-vec:" << m_outer_bounding[0]; // maximal point in BoundingBox m_outer_bounding[1][0] = std::max( bounding_box[k][0], m_outer_bounding[1][0] ); // x-max m_outer_bounding[1][1] = std::max( bounding_box[k][1], m_outer_bounding[1][1] ); // y-max m_outer_bounding[1][2] = std::max( bounding_box[k][2], m_outer_bounding[1][2] ); // z-max m_outer_bounding[1][3] = 1; // debugLog() << "max-vec:" << m_outer_bounding[1]; } } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Getting Data and setting Properties for GUI //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ m_interval->setMin( 0.0 ); m_interval->setMax( 1.0 ); //m_interval->setMax( length( WVector4d( dist_x, dist_y, dist_z, 0.0 ) ) ); m_interval->setRecommendedValue( 0.30 ); m_rayNumber->setMin( 1 ); m_rayNumber->setRecommendedValue( 1 ); m_radius->setMin( 1 ); m_radius->setRecommendedValue( 2 ); //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Drawing current position of the future ray from properties (debugging) //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if( false ) //just for debugging { osg::ref_ptr< osg::Geode > rayGeode = new osg::Geode(); rayGeode->addUpdateCallback( new SafeUpdateCallback( this ) ); WVector4d cylStart = WVector4d( 0.5 * x_scale, 0.5 * y_scale, 0.0, 1.0 ); osg::Cylinder* cylinder = new osg::Cylinder( getAs3D( cylStart + WVector4d( 0.0, 0.0, 0.5 * z_scale, 0.0 ) ), 0.5f, static_cast( z_scale ) ); osg::Quat rot; rot.makeRotate( osg::Vec3d( 0.0, 0.0, 1.0 ), osg::Vec3d( 1.0, 0.0, 0.0 ) // <-- ray direction vector ); cylinder->setRotation( rot ); osg::Cone* cone = new osg::Cone( getAs3D( cylStart + WVector4d( 0.0, 0.0, z_scale, 0.0 ) ), 1.0f, 5.0f ); cone->setRotation( rot ); rayGeode->addDrawable( new osg::ShapeDrawable( cylinder ) ); rayGeode->addDrawable( new osg::ShapeDrawable( cone ) ); m_rootNode->clear(); m_rootNode->insert( rayGeode ); } } } // we technically need to remove the event handler too but there is no method available to do this ... // WKernel::getRunningKernel()->getGraphicsEngine()->getViewer()->removeEventHandler( handler ); WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_rootNode ); } void WMTransferCalc::onClick( WVector2i mousePos ) { /* debugLog() << "Left Click at " << mousePos; debugLog() << "Projection Matrix: " << m_matrixCallback->getProjectionMatrix()->get(); debugLog() << "ModelView Matrix: " << m_matrixCallback->getModelViewMatrix()->get(); debugLog() << "Viewport: " << m_matrixCallback->getViewportX()->get() << ", " << m_matrixCallback->getViewportY()->get() << ", " << m_matrixCallback->getViewportWidth()->get() << ", " << m_matrixCallback->getViewportHeight()->get(); */ // get both matrices inverted WMatrix4d projectionMatrixInverted = invert( m_matrixCallback->getProjectionMatrix()->get() ); WMatrix4d modelviewMatrixInverted = invert( m_matrixCallback->getModelViewMatrix()->get() ); // get the clip-space coordinate: WVector4d pInClipSpace( ( 2.0 * mousePos.x() / m_matrixCallback->getViewportWidth()->get() ) - 1.0, ( 2.0 * mousePos.y() / m_matrixCallback->getViewportHeight()->get() ) - 1.0, 0.0, 1.0 ); WVector4d dirInClipSpace( 0.0, 0.0, 1.0, 0.0 ); // unproject the clip-space vectors to the world space WVector4d pInWorldSpace = projectionMatrixInverted * pInClipSpace; WVector4d dirInWorldSpace = projectionMatrixInverted * dirInClipSpace; // get back to model-space WVector4d pInObjectSpace = modelviewMatrixInverted * pInWorldSpace; WVector4d dirInObjectSpace = modelviewMatrixInverted * dirInWorldSpace; // for multilication with a vertex //WMatrix4d totalTransform = modelviewMatrixInverted * projectionMatrixInverted; // debugLog() << pInWorldSpace << " --- " << pInObjectSpace; // debugLog() << dirInWorldSpace << " --- " << dirInObjectSpace; // debug "vector" osg::ref_ptr< osg::Geode > debugGeode = new osg::Geode(); debugGeode->addUpdateCallback( new SafeUpdateCallback( this ) ); debugGeode->addDrawable( new osg::ShapeDrawable( new osg::Sphere( getAs3D( pInObjectSpace, true ), 2.5f ) ) ); debugGeode->addDrawable( new osg::ShapeDrawable( new osg::Sphere( getAs3D( pInObjectSpace + dirInObjectSpace, true ), 5.0f ) ) ); m_rootNode->clear(); m_rootNode->insert( debugGeode ); //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Ray Casting //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // ray object calculated from click position: ray = start + t * direction WRay ray( pInObjectSpace, dirInObjectSpace ); // delete old profiles m_profiles.clear(); // get user defined properties double interval = m_interval->get( true ); unsigned int samplesInVicinity = static_cast( m_rayNumber->get( true ) ); unsigned int radius = static_cast( m_radius->get( true ) ); osg::ref_ptr< osg::Geode > rayGeode = new osg::Geode(); rayGeode->addUpdateCallback( new SafeUpdateCallback( this ) ); // seed for random std::srand( time( 0 ) ); // cast additional rays if user defined more than one ray in properties for( unsigned int n = 0; n < samplesInVicinity; n++ ) { if( n == 0 ) // the initial profile { m_mainProfile = castRay( ray, interval, rayGeode ); m_profiles.push_back( m_mainProfile ); } else // random profiles in vicinity { // calculate random angle in [0,2pi] double angle = 2 * M_PI * rand() / ( static_cast< double >( RAND_MAX ) ); // calculate random radius in [0,1] double random_rad = rand() / ( static_cast< double >( RAND_MAX ) ); // create orthogonal plane to ray for calculating rays in vicinity // (the ray gets transformed and scaled before this step, // so we have to calculate this plane to get a uniform scale for the radius) double coord = std::max( ray.direction().x(), std::max( ray.direction().y(), ray.direction().z() ) ); WVector3d helper( coord, coord, coord ); WVector3d ax1 = cross( getAs3D( ray.direction(), true ), helper ); WVector3d ax2 = cross( getAs3D( ray.direction(), true ), ax1 ); ax1 = normalize( ax1 ); ax2 = normalize( ax2 ); // base vectors of the calculated plane WVector4d ax1_4( ax1.x(), ax1.y(), ax1.z(), 0.0 ); WVector4d ax2_4( ax2.x(), ax2.y(), ax2.z(), 0.0 ); // calculate random ray WVector4d vicPoint = ray.start() + ( ax1_4 * sqrt( random_rad ) * std::cos( angle ) * radius ) + ( ax2_4 * sqrt( random_rad ) * std::sin( angle ) * radius ); WRay vicinity( vicPoint, ray.direction() ); m_profiles.push_back( castRay( vicinity, interval, rayGeode ) ); } } m_rootNode->clear(); m_rootNode->insert( rayGeode ); } WRayProfile WMTransferCalc::castRay( WRay ray, double interval, osg::ref_ptr< osg::Geode > rayGeode ) { //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Calculating a single profile with all samples //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // start progress for GUI boost::shared_ptr< WProgress > prog = boost::shared_ptr< WProgress >( new WProgress( "Casting ray." ) ); m_progress->addSubProgress( prog ); //TODO(fjacob): the not set RaySamples may be deleted after collecting all profiles size_t max_nbSamples = ceil( length( m_outer_bounding[1] - m_outer_bounding[0] ) / interval ); WRayProfile curProfile( max_nbSamples ); // debugLog() << "Max samples: " << max_nbSamples; // BoxIntersecParameter contains two values, the first and second intersection parameter with the grid struct BoxIntersecParameter bounds = rayIntersectsBox( ray ); if( bounds.isNull ) //no intersection with data grid { prog->finish(); m_progress->removeSubProgress( prog ); return curProfile; //empty profile } double start_t = bounds.minimum_t; double end_t = bounds.maximum_t; // debugLog() << "Start:" << start_t; // debugLog() << "End :" << end_t; // helpers for visualization WVector3d geodeStartVec, geodeEndVec, cylinderVec; size_t sampleCount = 0; for( double sample_t = start_t; sample_t <= end_t; sample_t += interval ) { WVector4d current = ray.getSpot( sample_t ); WVector3d cur3D = getAs3D( current ); // do not calculate anything for vectors outside of the data grid // (should not be neccessary anymore, though) if( !m_grid->encloses( cur3D ) ) { //debugLog() << "continue..."; continue; } // save position of the sample point in profile double vox_x = static_cast< double >( m_grid->getVoxelNum( cur3D ) ); double vox_y = static_cast< double >( m_grid->getVoxelNum( cur3D ) ); double vox_z = static_cast< double >( m_grid->getVoxelNum( cur3D ) ); curProfile[sampleCount].position() = WVector4d( vox_x, vox_y, vox_z, 1 ); // set start vector for drawing the ray if( sampleCount == 0 ) { geodeStartVec = cur3D; } // get interpolated value at current sample point double val = interpolate( current, m_grid, m_dataSet ); // save value curProfile[sampleCount].value() = val; // save distance t curProfile[sampleCount].distance() = sample_t; // if there is any FA data available, save it if( m_FAisValid ) { if( m_FAgrid->encloses( cur3D ) ) { // save fractional anisotropy curProfile[sampleCount].fracA() = interpolate( current, m_FAgrid, m_FAdataSet ); } } // if there is any derivated data available, save calculated curvature if( m_DeriIsValid ) { if( m_deriGrid->encloses( cur3D ) ) { if( m_meanCurvDataSet ) { curProfile[sampleCount].meanCurv() = interpolate( current, m_deriGrid, m_meanCurvDataSet ); } if( m_gaussCurvDataSet ) { curProfile[sampleCount].gaussCurv() = interpolate( current, m_deriGrid, m_gaussCurvDataSet ); } } } // calculate gradient WVector4d grad = getGradient( current ); double weight = length( grad ); // save gradient weight curProfile[sampleCount].gradWeight() = weight; // normalize if( weight != 0 ) { // save gradient curProfile[sampleCount].gradient() = grad * ( 1 / weight ); } // calculate angle between gradient and ray // gradVec and rayVec are already normalized Eigen::Vector3d gradVec( curProfile[sampleCount].gradient()[0], curProfile[sampleCount].gradient()[1], curProfile[sampleCount].gradient()[2] ); Eigen::Vector3d rayVec( ray.direction()[0], ray.direction()[1], ray.direction()[2] ); // curProfile[sampleCount].angle() = std::min( std::acos( gradVec.dot( rayVec ) ), // std::acos( gradVec.dot( rayVec * -1 ) ) ); curProfile[sampleCount].angle() = std::acos( gradVec.dot( rayVec * -1 ) ); // set helper for visualization geodeEndVec = cur3D; sampleCount++; } // draw ray (a cylinder and a cone) // (it is a little tricky because osg does not support // native rotation of its drawables on initialization) cylinderVec = geodeEndVec - geodeStartVec; // set cylinder center in the middle of the ray (will be rotation center) osg::Cylinder* cylinder = new osg::Cylinder( geodeStartVec + ( 0.5 * cylinderVec ), 0.5f, length( cylinderVec ) ); osg::Cone* cone = new osg::Cone( geodeEndVec, 1.0f, 5.0f ); // let osg calculate the neccessary rotation matrix and rotate the shapes osg::Quat rotation; rotation.makeRotate( osg::Vec3d( 0.0, 0.0, 1.0 ), osg::Vec3d( ray.direction().x(), ray.direction().y(), ray.direction().z() ) ); cylinder->setRotation( rotation ); cone->setRotation( rotation ); // draw it rayGeode->addDrawable( new osg::ShapeDrawable( cylinder ) ); rayGeode->addDrawable( new osg::ShapeDrawable( cone ) ); // end progress prog->finish(); m_progress->removeSubProgress( prog ); return curProfile; } struct BoxIntersecParameter WMTransferCalc::rayIntersectsBox( WRay ray ) { // ray = start + t * dir double tnear = - std::numeric_limits::max(); double tfar = std::numeric_limits::max(); double t0, t1; struct BoxIntersecParameter result; result.isNull = true; for( unsigned int coord = 0; coord < 3; coord++ ) // for x,y,z { if( ray.direction()[coord] == 0 ) // ray parallel to the min/max coordinate planes { if( ray.start()[coord] < m_outer_bounding[0][coord] || ray.start()[coord] > m_outer_bounding[1][coord] ) { return result; // ray not within min and max values of current coordinate } } else // ray not parallel, so calculate intersections { // time at which ray intersects min plane t0 = ( m_outer_bounding[0][coord] - ray.start()[coord] ) / ray.direction()[coord]; // time at which ray intersects max plane t1 = ( m_outer_bounding[1][coord] - ray.start()[coord] ) / ray.direction()[coord]; if( t0 > t1 ) { std::swap( t0, t1 ); } if( t0 > tnear ) { tnear = t0; } if( t1 < tfar ) { tfar = t1; } if( tnear > tfar ) { return result; // cube is missed } /*if( tfar < 0 ) { return result; // cube behind ray - possible case here (because of unknown click position) }*/ } } result.minimum_t = tnear; result.maximum_t = tfar; result.isNull = false; return result; } WVector3d WMTransferCalc::getAs3D( const WVector4d& vec, bool disregardW ) { if( vec[3] == 0 || vec[3] == 1 || disregardW ) { return WVector3d( vec[0], vec[1], vec[2] ); } else { return WVector3d( vec[0] / vec[3], vec[1] / vec[3], vec[2] / vec[3] ); } } WVector4d WMTransferCalc::getGradient( const WVector4d& position ) { // if( m_DeriIsValid ) // { // // not usable yet (has to be interpolated) // return static_cast< WVector3d >( m_deriDataSet->getValueSet()->getWValueDouble( getAs3D( position ) ) ); // } // voxel distance in each direction (usually 1) double dist_x = m_grid->getOffsetX(); double dist_y = m_grid->getOffsetY(); double dist_z = m_grid->getOffsetZ(); double x_val, y_val, z_val; // central difference quotient in each direction x_val = ( ( interpolate( position + WVector4d( dist_x, 0.0, 0.0, 0.0 ), m_grid, m_dataSet ) ) - ( interpolate( position - WVector4d( dist_x, 0.0, 0.0, 0.0 ), m_grid, m_dataSet ) ) ) / 2 * dist_x; y_val = ( ( interpolate( position + WVector4d( 0.0, dist_y, 0.0, 0.0 ), m_grid, m_dataSet ) ) - ( interpolate( position - WVector4d( 0.0, dist_y, 0.0, 0.0 ), m_grid, m_dataSet ) ) ) / 2 * dist_y; z_val = ( ( interpolate( position + WVector4d( 0.0, 0.0, dist_z, 0.0 ), m_grid, m_dataSet ) ) - ( interpolate( position - WVector4d( 0.0, 0.0, dist_z, 0.0 ), m_grid, m_dataSet ) ) ) / 2 * dist_z; return WVector4d( x_val, y_val, z_val, 0 ); } double WMTransferCalc::interpolate( const WVector4d& position, boost::shared_ptr< WGridRegular3D > inter_grid, boost::shared_ptr< WDataSetScalar > inter_dataSet ) { // determine center of hit voxel double vox_x = static_cast< double >( inter_grid->getXVoxelCoord( getAs3D( position ) ) ); double vox_y = static_cast< double >( inter_grid->getYVoxelCoord( getAs3D( position ) ) ); double vox_z = static_cast< double >( inter_grid->getZVoxelCoord( getAs3D( position ) ) ); WVector4d voxel_center( vox_x, vox_y, vox_z, 1 ); // distance between voxels in each direction (usually 1) double x_offset = inter_grid->getOffsetX(); double y_offset = inter_grid->getOffsetY(); double z_offset = inter_grid->getOffsetZ(); // debugLog() << "Offsets: " << x_offset << ", " << y_offset << ", " << z_offset; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Calculating all relevant neighbours for the interpolation //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // vector with all 8 direct neighbours std::vector< WVector4d > neighbours( 8 ); // vectors with min / max coordinates of neighbours { min_x, min_y, min_z } std::vector< double > min_val( 3 ); std::vector< double > max_val( 3 ); // determine quadrant for trilinear interpolation to choose right neighbours // positive x direction if( position[0] >= voxel_center[0] ) { // positive y direction if( position[1] >= voxel_center[1] ) { // positive z direction if( position[2] >= voxel_center[2] ) { // Quadrant I neighbours[0] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] , 1 ); neighbours[1] = WVector4d( voxel_center[0] + x_offset, voxel_center[1], voxel_center[2] , 1 ); neighbours[2] = WVector4d( voxel_center[0], voxel_center[1] + y_offset, voxel_center[2] , 1 ); neighbours[3] = WVector4d( voxel_center[0] + x_offset, voxel_center[1] + y_offset, voxel_center[2] , 1 ); neighbours[4] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] + z_offset, 1 ); neighbours[5] = WVector4d( voxel_center[0] + x_offset, voxel_center[1], voxel_center[2] + z_offset, 1 ); neighbours[6] = WVector4d( voxel_center[0], voxel_center[1] + y_offset, voxel_center[2] + z_offset, 1 ); neighbours[7] = WVector4d( voxel_center[0] + x_offset, voxel_center[1] + y_offset, voxel_center[2] + z_offset, 1 ); min_val[0] = voxel_center[0]; min_val[1] = voxel_center[1]; min_val[2] = voxel_center[2]; max_val[0] = voxel_center[0] + x_offset; max_val[1] = voxel_center[1] + y_offset; max_val[2] = voxel_center[2] + z_offset; } else // negative z direction { // Quadrant V neighbours[0] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] - z_offset, 1 ); neighbours[1] = WVector4d( voxel_center[0] + x_offset, voxel_center[1], voxel_center[2] - z_offset, 1 ); neighbours[2] = WVector4d( voxel_center[0], voxel_center[1] + y_offset, voxel_center[2] - z_offset, 1 ); neighbours[3] = WVector4d( voxel_center[0] + x_offset, voxel_center[1] + y_offset, voxel_center[2] - z_offset, 1 ); neighbours[4] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] , 1 ); neighbours[5] = WVector4d( voxel_center[0] + x_offset, voxel_center[1], voxel_center[2] , 1 ); neighbours[6] = WVector4d( voxel_center[0], voxel_center[1] + y_offset, voxel_center[2] , 1 ); neighbours[7] = WVector4d( voxel_center[0] + x_offset, voxel_center[1] + y_offset, voxel_center[2] , 1 ); min_val[0] = voxel_center[0]; min_val[1] = voxel_center[1]; min_val[2] = voxel_center[2] - z_offset; max_val[0] = voxel_center[0] + x_offset; max_val[1] = voxel_center[1] + y_offset; max_val[2] = voxel_center[2]; } } else // negative y direction { // positive z direction if( position[2] >= voxel_center[2] ) { // Quadrant IV neighbours[0] = WVector4d( voxel_center[0], voxel_center[1] - y_offset, voxel_center[2] , 1 ); neighbours[1] = WVector4d( voxel_center[0] + x_offset, voxel_center[1] - y_offset, voxel_center[2] , 1 ); neighbours[2] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] , 1 ); neighbours[3] = WVector4d( voxel_center[0] + x_offset, voxel_center[1], voxel_center[2] , 1 ); neighbours[4] = WVector4d( voxel_center[0], voxel_center[1] - y_offset, voxel_center[2] + z_offset, 1 ); neighbours[5] = WVector4d( voxel_center[0] + x_offset, voxel_center[1] - y_offset, voxel_center[2] + z_offset, 1 ); neighbours[6] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] + z_offset, 1 ); neighbours[7] = WVector4d( voxel_center[0] + x_offset, voxel_center[1], voxel_center[2] + z_offset, 1 ); min_val[0] = voxel_center[0]; min_val[1] = voxel_center[1] - y_offset; min_val[2] = voxel_center[2]; max_val[0] = voxel_center[0] + x_offset; max_val[1] = voxel_center[1]; max_val[2] = voxel_center[2] + z_offset; } else // negative z direction { // Quadrant VIII neighbours[0] = WVector4d( voxel_center[0], voxel_center[1] - y_offset, voxel_center[2] - z_offset, 1 ); neighbours[1] = WVector4d( voxel_center[0] + x_offset, voxel_center[1] - y_offset, voxel_center[2] - z_offset, 1 ); neighbours[2] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] - z_offset, 1 ); neighbours[3] = WVector4d( voxel_center[0] + x_offset, voxel_center[1], voxel_center[2] - z_offset, 1 ); neighbours[4] = WVector4d( voxel_center[0], voxel_center[1] - y_offset, voxel_center[2] , 1 ); neighbours[5] = WVector4d( voxel_center[0] + x_offset, voxel_center[1] - y_offset, voxel_center[2] , 1 ); neighbours[6] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] , 1 ); neighbours[7] = WVector4d( voxel_center[0] + x_offset, voxel_center[1], voxel_center[2] , 1 ); min_val[0] = voxel_center[0]; min_val[1] = voxel_center[1] - y_offset; min_val[2] = voxel_center[2] - z_offset; max_val[0] = voxel_center[0] + x_offset; max_val[1] = voxel_center[1]; max_val[2] = voxel_center[2]; } } } else // negative x direction { // positive y direction if( position[1] >= voxel_center[1] ) { // positive z direction if( position[2] >= voxel_center[2] ) { // Quadrant II neighbours[0] = WVector4d( voxel_center[0] - x_offset, voxel_center[1], voxel_center[2] , 1 ); neighbours[1] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] , 1 ); neighbours[2] = WVector4d( voxel_center[0] - x_offset, voxel_center[1] + y_offset, voxel_center[2] , 1 ); neighbours[3] = WVector4d( voxel_center[0], voxel_center[1] + y_offset, voxel_center[2] , 1 ); neighbours[4] = WVector4d( voxel_center[0] - x_offset, voxel_center[1], voxel_center[2] + z_offset, 1 ); neighbours[5] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] + z_offset, 1 ); neighbours[6] = WVector4d( voxel_center[0] - x_offset, voxel_center[1] + y_offset, voxel_center[2] + z_offset, 1 ); neighbours[7] = WVector4d( voxel_center[0], voxel_center[1] + y_offset, voxel_center[2] + z_offset, 1 ); min_val[0] = voxel_center[0] - x_offset; min_val[1] = voxel_center[1]; min_val[2] = voxel_center[2]; max_val[0] = voxel_center[0]; max_val[1] = voxel_center[1] + y_offset; max_val[2] = voxel_center[2] + z_offset; } else // negative z direction { // Quadrant VI neighbours[0] = WVector4d( voxel_center[0] - x_offset, voxel_center[1], voxel_center[2] - z_offset, 1 ); neighbours[1] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] - z_offset, 1 ); neighbours[2] = WVector4d( voxel_center[0] - x_offset, voxel_center[1] + y_offset, voxel_center[2] - z_offset, 1 ); neighbours[3] = WVector4d( voxel_center[0], voxel_center[1] + y_offset, voxel_center[2] - z_offset, 1 ); neighbours[4] = WVector4d( voxel_center[0] - x_offset, voxel_center[1], voxel_center[2] , 1 ); neighbours[5] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] , 1 ); neighbours[6] = WVector4d( voxel_center[0] - x_offset, voxel_center[1] + y_offset, voxel_center[2] , 1 ); neighbours[7] = WVector4d( voxel_center[0], voxel_center[1] + y_offset, voxel_center[2] , 1 ); min_val[0] = voxel_center[0] - x_offset; min_val[1] = voxel_center[1]; min_val[2] = voxel_center[2] - z_offset; max_val[0] = voxel_center[0]; max_val[1] = voxel_center[1] + y_offset; max_val[2] = voxel_center[2]; } } else // negative y direction { // positive z direction if( position[2] >= voxel_center[2] ) { // Quadrant III neighbours[0] = WVector4d( voxel_center[0] - x_offset, voxel_center[1] - y_offset, voxel_center[2] , 1 ); neighbours[1] = WVector4d( voxel_center[0], voxel_center[1] - y_offset, voxel_center[2] , 1 ); neighbours[2] = WVector4d( voxel_center[0] - x_offset, voxel_center[1], voxel_center[2] , 1 ); neighbours[3] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] , 1 ); neighbours[4] = WVector4d( voxel_center[0] - x_offset, voxel_center[1] - y_offset, voxel_center[2] + z_offset, 1 ); neighbours[5] = WVector4d( voxel_center[0], voxel_center[1] - y_offset, voxel_center[2] + z_offset, 1 ); neighbours[6] = WVector4d( voxel_center[0] - x_offset, voxel_center[1], voxel_center[2] + z_offset, 1 ); neighbours[7] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] + z_offset, 1 ); min_val[0] = voxel_center[0] - x_offset; min_val[1] = voxel_center[1] - y_offset; min_val[2] = voxel_center[2]; max_val[0] = voxel_center[0]; max_val[1] = voxel_center[1]; max_val[2] = voxel_center[2] + z_offset; } else // negative z direction { // Quadrant VII neighbours[0] = WVector4d( voxel_center[0] - x_offset, voxel_center[1] - y_offset, voxel_center[2] - z_offset, 1 ); neighbours[1] = WVector4d( voxel_center[0], voxel_center[1] - y_offset, voxel_center[2] - z_offset, 1 ); neighbours[2] = WVector4d( voxel_center[0] - x_offset, voxel_center[1], voxel_center[2] - z_offset, 1 ); neighbours[3] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] - z_offset, 1 ); neighbours[4] = WVector4d( voxel_center[0] - x_offset, voxel_center[1] - y_offset, voxel_center[2] , 1 ); neighbours[5] = WVector4d( voxel_center[0], voxel_center[1] - y_offset, voxel_center[2] , 1 ); neighbours[6] = WVector4d( voxel_center[0] - x_offset, voxel_center[1], voxel_center[2] , 1 ); neighbours[7] = WVector4d( voxel_center[0], voxel_center[1], voxel_center[2] , 1 ); min_val[0] = voxel_center[0] - x_offset; min_val[1] = voxel_center[1] - y_offset; min_val[2] = voxel_center[2] - z_offset; max_val[0] = voxel_center[0]; max_val[1] = voxel_center[1]; max_val[2] = voxel_center[2]; } } } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Interpolation //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // for storing the values of the 8 neighbours std::vector< double > values( 8 ); // check, if all neighbours are inside the grid and get the corresponding value for( unsigned int i = 0; i < neighbours.size(); i++) { if( !inter_grid->encloses( getAs3D( neighbours[i] ) ) ) { values[i] = 0; // temporary solution } else { values[i] = inter_dataSet->getValueSet()->getScalarDouble( inter_grid->getVoxelNum( getAs3D( neighbours[i] ) ) ); } } // trilinear interpolation from wikipedia double x_dif = ( position[0] - min_val[0] ) / ( max_val[0] - min_val[0] ); double y_dif = ( position[1] - min_val[1] ) / ( max_val[1] - min_val[1] ); double z_dif = ( position[2] - min_val[2] ) / ( max_val[2] - min_val[2] ); double c_00 = values[0] * ( 1 - x_dif ) + values[1] * x_dif; double c_10 = values[2] * ( 1 - x_dif ) + values[3] * x_dif; double c_01 = values[4] * ( 1 - x_dif ) + values[5] * x_dif; double c_11 = values[6] * ( 1 - x_dif ) + values[7] * x_dif; double c_0 = c_00 * ( 1 - y_dif ) + c_10 * y_dif; double c_1 = c_01 * ( 1 - y_dif ) + c_11 * y_dif; return c_0 * ( 1 - z_dif ) + c_1 * z_dif; } void WMTransferCalc::calculateCurvature() { // additional data for derivation must be available if( !m_DeriIsValid ) { return; } // init progress for GUI boost::shared_ptr< WProgress > calcCurvProg = boost::shared_ptr< WProgress >( new WProgress( "Calculating curvature values." ) ); m_progress->addSubProgress( calcCurvProg ); // scales of the current grid double x_scale = m_deriGrid->getNbCoordsX(); double y_scale = m_deriGrid->getNbCoordsY(); double z_scale = m_deriGrid->getNbCoordsZ(); // voxel dimensions (usually 1 in each direction) double dist_x = m_deriGrid->getOffsetX(); double dist_y = m_deriGrid->getOffsetY(); double dist_z = m_deriGrid->getOffsetZ(); // vector for creating dataset with the calculated mean curvature values boost::shared_ptr< std::vector< double > > mean_values = boost::shared_ptr< std::vector< double > >( new std::vector< double >( m_deriDataSet->getValueSet()->size(), 0.0 ) ); // vector for creating dataset with the calculated gaussian curvature values boost::shared_ptr< std::vector< double > > gauss_values = boost::shared_ptr< std::vector< double > >( new std::vector< double >( m_deriDataSet->getValueSet()->size(), 0.0 ) ); // eigenvalues which will be calculated double k1, k2, k3; // index of current position in the grid to adopt the same position in the new datagrid size_t id = 0; // vectors for all neighbour positions for derivation (in x/y/z direction) std::vector< WVector3d > derivDirection( 6 ); // calculated position in the current grid (negative, if outside grid) std::vector< int > derivIds( 6 ); //debugLog() << "Test " << x_scale << " - " << y_scale << " - " << z_scale ; //debugLog() << "Size: " << m_deriDataSet->getValueSet()->size(); //debugLog() << "Rawsize: " << m_deriDataSet->getValueSet()->rawSize(); //debugLog() << "Distances: " << dist_x << "-" << dist_x << "-" << dist_x; // traverse dataset/grid for( unsigned int zdir = 0; zdir < z_scale; zdir++ ) { for( unsigned int ydir = 0; ydir < y_scale; ydir++ ) { for( unsigned int xdir = 0; xdir < x_scale; xdir++ ) { WVector3d position( xdir, ydir, zdir ); // get gradient of current position WVector3d curGrad = static_cast< WVector3d >( m_deriDataSet->getValueSet()->getWValueDouble( id ) ); // skip grid point, if there is no gradient if( length( curGrad ) == 0 ) { id++; continue; } // calculate grid ids of neighbours (will be -1 if outside of the grid) derivIds[0] = m_deriGrid->getVoxelNum( position + WVector3d( -dist_x, 0, 0 ) ); derivIds[1] = m_deriGrid->getVoxelNum( position + WVector3d( dist_x, 0, 0 ) ); derivIds[2] = m_deriGrid->getVoxelNum( position + WVector3d( 0, -dist_y, 0 ) ); derivIds[3] = m_deriGrid->getVoxelNum( position + WVector3d( 0, dist_y, 0 ) ); derivIds[4] = m_deriGrid->getVoxelNum( position + WVector3d( 0, 0, -dist_z ) ); derivIds[5] = m_deriGrid->getVoxelNum( position + WVector3d( 0, 0, dist_z ) ); // get gradient vectors for each neighbour if this neighbour is inside the grid derivDirection[0] = static_cast< WVector3d >( m_deriDataSet->getValueSet()->getWValueDouble( ( derivIds[0] == -1 ) ? id : derivIds[0] ) ); derivDirection[1] = static_cast< WVector3d >( m_deriDataSet->getValueSet()->getWValueDouble( ( derivIds[1] == -1 ) ? id : derivIds[1] ) ); derivDirection[2] = static_cast< WVector3d >( m_deriDataSet->getValueSet()->getWValueDouble( ( derivIds[2] == -1 ) ? id : derivIds[2] ) ); derivDirection[3] = static_cast< WVector3d >( m_deriDataSet->getValueSet()->getWValueDouble( ( derivIds[3] == -1 ) ? id : derivIds[3] ) ); derivDirection[4] = static_cast< WVector3d >( m_deriDataSet->getValueSet()->getWValueDouble( ( derivIds[4] == -1 ) ? id : derivIds[4] ) ); derivDirection[5] = static_cast< WVector3d >( m_deriDataSet->getValueSet()->getWValueDouble( ( derivIds[5] == -1 ) ? id : derivIds[5] ) ); // derivate in each direction again (central differential quotient) to get hessian matrix Eigen::Matrix3d hesse; // x-values form x,y,z direction hesse << ( derivDirection[1][0] - derivDirection[0][0] ) / 2.0 * dist_x, ( derivDirection[3][0] - derivDirection[2][0] ) / 2.0 * dist_y, ( derivDirection[5][0] - derivDirection[4][0] ) / 2.0 * dist_z, // y-values form x,y,z direction ( derivDirection[1][1] - derivDirection[0][1] ) / 2.0 * dist_x, ( derivDirection[3][1] - derivDirection[2][1] ) / 2.0 * dist_y, ( derivDirection[5][1] - derivDirection[4][1] ) / 2.0 * dist_z, // z-values form x,y,z direction ( derivDirection[1][2] - derivDirection[0][2] ) / 2.0 * dist_x, ( derivDirection[3][2] - derivDirection[2][2] ) / 2.0 * dist_y, ( derivDirection[5][2] - derivDirection[4][2] ) / 2.0 * dist_z; //debugLog() << hesse; // method of the paper // we need: // current gradient g // normal n = -g/|g| // P = E - nn^T // nabla-n^T = -PH / |g| (where H is the hessian matrix) WVector3d g = static_cast< WVector3d >( m_deriDataSet->getValueSet()->getWValueDouble( m_deriGrid->getVoxelNum( position ) ) ); double g_weight = length( g ); WVector3d n = ( g * -1 ) / g_weight; Eigen::Matrix3d E, nnT, nabla_nT; E << 1, 0, 0, 0, 1, 0, 0, 0, 1; nnT << n[0] * n[0], n[0] * n[1], n[0] * n[2], n[1] * n[0], n[1] * n[1], n[1] * n[2], n[2] * n[0], n[2] * n[1], n[2] * n[2]; nabla_nT = ( ( E - nnT ) * hesse ) * ( -1 / g_weight ); Eigen::EigenSolver< Eigen::Matrix3d > eigenResults( nabla_nT, false ); k1 = eigenResults.eigenvalues()[0].real(); k2 = eigenResults.eigenvalues()[1].real(); k3 = eigenResults.eigenvalues()[2].real(); // sort absolute eigenvalues ( because one will be 0 ) if( std::abs( k1 ) <= std::abs( k2 ) ) std::swap( k1, k2 ); if( std::abs( k2 ) <= std::abs( k3 ) ) std::swap( k2, k3 ); if( std::abs( k1 ) <= std::abs( k2 ) ) std::swap( k1, k2 ); // calculate mean and gaussian curvature (*mean_values)[id] = ( k1 + k2 ) * 0.5; (*gauss_values)[id] = k1 * k2; id++; } } } // corresponding valueset to calculated list of values for mean curvature boost::shared_ptr< WValueSet< double > > mean_valueset = boost::shared_ptr< WValueSet< double > >( new WValueSet< double >( 0, 1, mean_values, W_DT_DOUBLE ) ); // corresponding valueset to calculated list of values for gaussian curvature boost::shared_ptr< WValueSet< double > > gauss_valueset = boost::shared_ptr< WValueSet< double > >( new WValueSet< double >( 0, 1, gauss_values, W_DT_DOUBLE ) ); // create mean curvature output data with calculated valueset m_meanCurvDataSet = boost::shared_ptr< WDataSetScalar >( new WDataSetScalar( mean_valueset, m_deriGrid ) ); m_curveMeanOut->updateData( m_meanCurvDataSet ); // create gaussian curvature output data with calculated valueset m_gaussCurvDataSet = boost::shared_ptr< WDataSetScalar >( new WDataSetScalar( gauss_valueset, m_deriGrid ) ); m_curveGaussOut->updateData( m_gaussCurvDataSet ); // end progress calcCurvProg->finish(); m_progress->removeSubProgress( calcCurvProg ); } // not in use right now, but maybe later void WMTransferCalc::SafeUpdateCallback::operator()( osg::Node* node, osg::NodeVisitor* nv ) { // One note about m_aColor: As you might have notices, changing one of the properties, which recreate the OSG node, causes the material to be // gray again. This is simply caused by m_aColor->changed() is still false! To resolve this problem, use some m_osgNeedsUpdate boolean which // gets set in your thread main and checked here or, as done in this module, by checking whether the callback is called the first time. if( m_module->m_color->changed() || m_initialUpdate ) { // Set the diffuse color and material: osg::ref_ptr< osg::Material > mat = new osg::Material(); mat->setDiffuse( osg::Material::FRONT, m_module->m_color->get( true ) ); node->getOrCreateStateSet()->setAttribute( mat, osg::StateAttribute::ON ); } traverse( node, nv ); }