Commit 368e2658 authored by A. Betz D. Gerlicher (HS Coburg)'s avatar A. Betz D. Gerlicher (HS Coburg)
Browse files

[ADD #396] first implementation of picking WYSIWYP module

parent b1023aa6
#include <string>
#include <osgViewer/View>
#include <osg/ShapeDrawable>
#include <osg/Geometry>
#include <osg/CullFace>
#include "core/dataHandler/WDataSetScalar.h"
#include "core/graphicsEngine/callbacks/WGELinearTranslationCallback.h"
#include "core/graphicsEngine/shaders/WGEPropertyUniform.h"
#include "core/graphicsEngine/shaders/WGEShader.h"
#include "core/graphicsEngine/WGEGeodeUtils.h"
#include "core/graphicsEngine/WGEColormapping.h"
#include "core/graphicsEngine/WGEManagedGroupNode.h"
#include "core/graphicsEngine/WGraphicsEngine.h"
#include "core/graphicsEngine/WPickHandler.h"
#include "core/graphicsEngine/WPickInfo.h"
#include "core/kernel/WKernel.h"
#include "core/kernel/WModuleInputData.h"
#include "core/kernel/WModuleOutputData.h"
#include "core/graphicsEngine/geodes/WGEGridNode.h"
#include "WMEDUPicking.xpm"
#include "WMEDUPicking.h"
#include "WMEDUPickingHelper.h"
// This line is needed by the module loader to actually find your module.
W_LOADABLE_MODULE( WMEDUPicking )
WMEDUPicking::WMEDUPicking():
WModule(),
m_propCondition( new WCondition() )
{
this->m_bIntersected = false;
this->m_posStart = osg::Vec3f(0.0, 0.0, 0.0);
this->m_posEnd = osg::Vec3f(0.0, 0.0, 0.0);
}
WMEDUPicking::~WMEDUPicking()
{
}
boost::shared_ptr< WModule > WMEDUPicking::factory() const
{
return boost::shared_ptr< WModule >( new WMEDUPicking() );
}
const char** WMEDUPicking::getXPMIcon() const
{
return WMEDUPicking_xpm;
}
const std::string WMEDUPicking::getName() const
{
return "[EDU] Picking";
}
const std::string WMEDUPicking::getDescription() const
{
return "Picks a 3D Position from the dataset";
}
void WMEDUPicking::connectors()
{
// The transfer function for our DVR
m_transferFunction = WModuleInputData< WDataSetSingle >::createAndAdd( shared_from_this(), "transfer function", "The 1D transfer function." );
//Scalar field
m_scalarIC = WModuleInputData< WDataSetScalar >::createAndAdd( shared_from_this(), "scalarData", "Scalar data." );
WModule::connectors();
}
void WMEDUPicking::properties()
{
//Color Property
m_color = m_properties->addProperty( "Crosshair color", "Crosshair Color", WColor( 0.5f, 0.5f, 0.5f, 1.0f ), m_propCondition );
// ---> Put the code for your properties here. See "src/modules/template/" for an extensively documented example.
m_sampleRate = m_properties->addProperty("Samples - Steps", "Sample - value", 60, m_propCondition);
m_sampleRate->setMin(0);
m_sampleRate->setMax(10000);
m_alphaThreshold = m_properties->addProperty("Alpha Threshold %", "Alpha Value Threshold", 0.5, m_propCondition);
m_alphaThreshold->setMin(0.0);
m_alphaThreshold->setMax(1.0);
m_crossThickness = m_properties->addProperty("Crosshair Thickness", "Crosshair Thickness", 0.5, m_propCondition);
m_crossThickness->setMin(0.001);
m_crossThickness->setMax(1.0);
m_crossSize = m_properties->addProperty("Crosshair Size", "Crosshair Size", 100.0, m_propCondition);
m_crossSize->setMin(0.001);
m_crossSize->setMax(1000.0);
m_pickingCritereaList = boost::shared_ptr< WItemSelection >( new WItemSelection() );
m_pickingCritereaList->addItem( WMEDU_PICKING_MAX_INT, WMEDU_PICKING_MAX_INT);
m_pickingCritereaList->addItem( WMEDU_PICKING_FIRST_HIT, WMEDU_PICKING_FIRST_HIT);
m_pickingCritereaList->addItem( WMEDU_PICKING_THRESHOLD, WMEDU_PICKING_THRESHOLD);
m_pickingCritereaList->addItem( WMEDU_PICKING_MOST_CONTRIBUTING, WMEDU_PICKING_MOST_CONTRIBUTING);
m_pickingCritereaList->addItem( WMEDU_PICKING_WYSIWYP, WMEDU_PICKING_WYSIWYP);
m_pickingCritereaCur = m_properties->addProperty( "Picking method", "Select a picking method", m_pickingCritereaList->getSelectorFirst(), m_propCondition );
WPropertyHelper::PC_SELECTONLYONE::addTo( m_pickingCritereaCur );
WPropertyHelper::PC_NOTEMPTY::addTo( m_pickingCritereaCur );
WModule::properties();
}
void WMEDUPicking::requirements()
{
}
void WMEDUPicking::moduleMain()
{
// get notified about data changes
m_moduleState.setResetable( true, true );
m_moduleState.add( m_scalarIC->getDataChangedCondition() );
m_moduleState.add( m_transferFunction->getDataChangedCondition() );
m_moduleState.add( m_propCondition );
ready();
// graphics setup
m_rootNode = osg::ref_ptr< WGEManagedGroupNode >( new WGEManagedGroupNode( m_active ) );
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_rootNode );
//Shader
m_shader = new WGEShader( "WGELighting", m_localPath );
m_shader->setDefine( "USE_MATERIAL_DIFFUSE" );
//Get Camera and Register the callback
boost::shared_ptr< WGraphicsEngine > graphicsEngine = WGraphicsEngine::getGraphicsEngine();
boost::shared_ptr< WGEViewer > mainView = graphicsEngine->getViewerByName( "Main View" );
osg::ref_ptr<WGECamera> camera = mainView->getCamera();
//Register Pickhandler
mainView->getPickHandler()->getPickSignal()->connect( boost::bind( &WMEDUPicking::pickHandler, this, _1 ) );
// main loop
while( !m_shutdownFlag() )
{
debugLog() << "Waiting ...";
m_moduleState.wait();
// woke up since the module is requested to finish?
if( m_shutdownFlag() )
{
break;
}
//Valid Positions
if(this->m_bIntersected)
{
//Position Variables
osg::Vec3f posStart = this->m_posStart;
osg::Vec3f posEnd = this->m_posEnd;
osg::Vec3f posSample = posStart;
osg::Vec3f posPicking = posStart;
osg::Vec3f vecDir = posEnd - posStart;
//Picking Variable
double dMax = 0.0;
double dMin = 0.0;
double dCurrentAlpha = 0.0;
double dAccAlpha = 0.0;
double dPickedAlpha = 0.0;
double dMaxValue = 0.0;
bool bPickedPos = false;
//Derivative Variables
std::vector<double> vecFirstDerivative;
std::vector<double> vecSecondDerivative;
std::vector<double> vecAlphaAcc;
//Calculate Step
if(this->m_sampleRate->get() > 0)
{
vecDir = vecDir / this->m_sampleRate->get();
}
//Get Scalar Field
boost::shared_ptr< WDataSetScalar > scalarData = m_scalarIC->getData();
if(!scalarData)
{
errorLog()<< "[Invalid scalar field]";
continue;
}
//Get Transferfunction Data
boost::shared_ptr< WDataSetSingle > transferFunctionData = m_transferFunction->getData();
if(!transferFunctionData)
{
errorLog()<< "[Invalid transferfunction data]";
continue;
}
//Get Transferfunction Values
boost::shared_ptr< WValueSetBase > transferFunctionValues = transferFunctionData->getValueSet();
dMax = scalarData->getMax();
dMin = scalarData->getMin();
dMaxValue = dMin;
//Get Picking Mode
WItemSelector selector = m_pickingCritereaCur->get( true );
std::string strRenderMode = selector.at( 0 )->getName();
//Sampling
for(int i = 0; i < this->m_sampleRate->get(); i++)
{
//Scalarfield Values
bool bSuccess = false;
double dValue = scalarData->interpolate(posSample, &bSuccess);
//Add Step
posSample = posSample + vecDir;
if(!bSuccess)
{
continue;
}
//Classification Variables
double dNominator = dValue - dMin;
double dDenominator = dMax - dMin;
if(dDenominator == 0.0)
{
dDenominator = 0.0001;
}
//Classification: Convert Scalar to Color
double dScalarPercentage = dNominator / dDenominator;
int iColorIdx = dScalarPercentage * transferFunctionValues->size();
//color
WMEDUColor<double> color;
//Get Color from transferfunction
color.setRed( transferFunctionData->getValueAt(iColorIdx * 4 + 0) );
color.setGreen( transferFunctionData->getValueAt(iColorIdx * 4 + 1) );
color.setBlue( transferFunctionData->getValueAt(iColorIdx * 4 + 2) );
color.setAlpha( transferFunctionData->getValueAt(iColorIdx * 4 + 3) );
color.normalize();
//gamma = alpha + beta - alpha * beta == currentAlpha + accedAlpha - currentAlpa * accedAlpha
//alpha = currentAlpha
//beta = accedAlpha
dCurrentAlpha = color.getAlpha();
dAccAlpha = dCurrentAlpha + (dAccAlpha - dCurrentAlpha * dAccAlpha);
if(strRenderMode == WMEDU_PICKING_MAX_INT)
{
//Maximum Intensity: maximal scalar value
if(dValue > dMaxValue)
{
dMaxValue = dValue;
posPicking = posSample;
bPickedPos = true;
}
}
else if(strRenderMode == WMEDU_PICKING_FIRST_HIT)
{
//First Hit: first alpha value > 0.0
if(dCurrentAlpha > 0.0)
{
dPickedAlpha = dCurrentAlpha;
posPicking = posSample;
bPickedPos = true;
break;
}
}
else if(strRenderMode == WMEDU_PICKING_THRESHOLD)
{
//Threshold: accumulated alpha value > threshold
if(dAccAlpha > this->m_alphaThreshold->get())
{
dPickedAlpha = dCurrentAlpha;
posPicking = posSample;
bPickedPos = true;
break;
}
}
else if (strRenderMode == WMEDU_PICKING_MOST_CONTRIBUTING)
{
//Most Contributing: maximal alpha value
if(dCurrentAlpha > dPickedAlpha)
{
dPickedAlpha = dCurrentAlpha;
posPicking = posSample;
bPickedPos = true;
}
}
else if (strRenderMode == WMEDU_PICKING_WYSIWYP)
{
//WYSIWYP: Save all the accumulated alpha values
vecAlphaAcc.push_back(dAccAlpha);
}
}
//WYSIWYP: Calculate the largest interval
if(strRenderMode == WMEDU_PICKING_WYSIWYP)
{
//Fourth Order Finite Differencing by Daniel Gerlicher
unsigned int n = vecAlphaAcc.size();
double dDeriv = 0.0;
double dCoeff = 1.0 / 12.0;
//Calculate first derivative
for(unsigned int j = 0; j < n; j++)
{
//Forward Diff
if(j == 0)
{
dDeriv = dCoeff * (-25*vecAlphaAcc[j] +48*vecAlphaAcc[j+1] -36*vecAlphaAcc[j+2] +16*vecAlphaAcc[j+3] -3*vecAlphaAcc[j+4]);
}
else if(j == 1)
{
dDeriv = dCoeff * (-3*vecAlphaAcc[j-1] -10*vecAlphaAcc[j] +18*vecAlphaAcc[j+1] -6*vecAlphaAcc[j+2] +1*vecAlphaAcc[j+3]);
}
//Backward Diff
else if(j == n - 1)
{
dDeriv = dCoeff * (25*vecAlphaAcc[j] -48*vecAlphaAcc[j-1] +36*vecAlphaAcc[j-2] -16*vecAlphaAcc[j-3] +3*vecAlphaAcc[j-4]);
}
else if(j == n - 2)
{
dDeriv = dCoeff * (+3*vecAlphaAcc[j+1] +10*vecAlphaAcc[j] -18*vecAlphaAcc[j-1] +6*vecAlphaAcc[j-2] -1*vecAlphaAcc[j-3]);
}
//Center
else
{
dDeriv = dCoeff * (-1*vecAlphaAcc[j+2] +8*vecAlphaAcc[j+1] -8*vecAlphaAcc[j-1] +1*vecAlphaAcc[j-2]);
}
vecFirstDerivative.push_back(dDeriv);
}
//Calculate Second derivative, by applying the first derivative
for(unsigned int j = 0; j < n; j++)
{
//Forward Diff
if(j == 0)
{
dDeriv = dCoeff * (-25*vecFirstDerivative[j] +48*vecFirstDerivative[j+1] -36*vecFirstDerivative[j+2] +16*vecFirstDerivative[j+3] -3*vecFirstDerivative[j+4]);
}
else if(j == 1)
{
dDeriv = dCoeff * (-3*vecFirstDerivative[j-1] -10*vecFirstDerivative[j] +18*vecFirstDerivative[j+1] -6*vecFirstDerivative[j+2] +1*vecFirstDerivative[j+3]);
}
//Backward Diff
else if(j == n - 1)
{
dDeriv = dCoeff * (25*vecFirstDerivative[j] -48*vecFirstDerivative[j-1] +36*vecFirstDerivative[j-2] -16*vecFirstDerivative[j-3] +3*vecFirstDerivative[j-4]);
}
else if(j == n - 2)
{
dDeriv = dCoeff * (+3*vecFirstDerivative[j+1] +10*vecFirstDerivative[j] -18*vecFirstDerivative[j-1] +6*vecFirstDerivative[j-2] -1*vecFirstDerivative[j-3]);
}
//Center
else
{
dDeriv = dCoeff * (-1*vecFirstDerivative[j+2] +8*vecFirstDerivative[j+1] -8*vecFirstDerivative[j-1] +1*vecFirstDerivative[j-2]);
}
vecSecondDerivative.push_back(dDeriv);
}
//Create Intervals
std::vector<int> vecIndicesLowerBounds;
std::vector<int> vecIndicesUpperBounds;
//Calculate Interval Boundaries
double dOldDerivative = vecSecondDerivative[0];
for(unsigned int j = 1; j < n; j++)
{
if(dOldDerivative <= 0.0 && vecSecondDerivative[j] > 0.0)
{
vecIndicesLowerBounds.push_back(j);
}
if(dOldDerivative < 0.0 && vecSecondDerivative[j] >= 0.0)
{
vecIndicesUpperBounds.push_back(j);
}
dOldDerivative = vecSecondDerivative[j];
}
//Calculate Max Diff
double dDiff = 0.0;
double dMaxDiff = 0.0;
int iSample = -1;
for(unsigned int j = 0; j < std::min(vecIndicesLowerBounds.size(), vecIndicesUpperBounds.size()); j++)
{
//Calculate Diff
dDiff = vecAlphaAcc[vecIndicesUpperBounds[j]] - vecAlphaAcc[vecIndicesLowerBounds[j]];
//Is Max Diff
if(dDiff > dMaxDiff)
{
dMaxDiff = dDiff;
iSample = vecIndicesLowerBounds[j];
}
}
//Calculate Position
if(iSample >= 0)
{
//Calculate pick position
posPicking = posStart + vecDir * iSample;
bPickedPos = true;
}
}
//Rendering: update only if picked
if(bPickedPos)
{
#ifdef WMEDU_PICKING_DEBUG
debugLog() << "[dPickedAlpha = " << dPickedAlpha << "][posPicking][X = " << posPicking.x() << " ][Y = " << posPicking.y() << " ][Z = " << posPicking.z() << "]";
#endif
osg::ref_ptr< osg::Geometry > geometry;
osg::ref_ptr< osg::Geode > geode( new osg::Geode );
double dSize = m_crossSize->get();
double dThickness = m_crossThickness->get();
osg::ShapeDrawable* pBoxX = new osg::ShapeDrawable( new osg::Box( posPicking, dSize, dThickness, dThickness ) );
osg::ShapeDrawable* pBoxY = new osg::ShapeDrawable( new osg::Box( posPicking, dThickness, dSize, dThickness ) );
osg::ShapeDrawable* pBoxZ = new osg::ShapeDrawable( new osg::Box( posPicking, dThickness, dThickness, dSize ) );
pBoxX->setColor(m_color->get());
pBoxY->setColor(m_color->get());
pBoxZ->setColor(m_color->get());
geode->addDrawable(pBoxX);
geode->addDrawable(pBoxY);
geode->addDrawable(pBoxZ);
m_rootNode->remove(m_geode);
m_geode = geode;
m_shader->apply(m_geode);
m_rootNode->clear();
m_rootNode->insert( geode );
}
}
}
WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_rootNode );
}
void WMEDUPicking::pickHandler( WPickInfo pickInfo )
{
//Not Right Mouse
if(pickInfo.getMouseButton() != 2)
{
return;
}
//Intersect with 3D
boost::shared_ptr< WGraphicsEngine > graphicsEngine = WGraphicsEngine::getGraphicsEngine();
boost::shared_ptr< WGEViewer > mainView = graphicsEngine->getViewerByName( "Main View" );
osg::ref_ptr<osgViewer::Viewer> view = mainView->getView();
osgUtil::LineSegmentIntersector::Intersections intersections;
float fPosX = pickInfo.getPickPixel().x();
float fPosY = pickInfo.getPickPixel().y();
//Intersect
bool bIntersected = view->computeIntersections(fPosX, fPosY, intersections, 0xFFFFFFFF);
if(bIntersected)
{
osgUtil::LineSegmentIntersector::Intersection start= *intersections.begin();
osgUtil::LineSegmentIntersector::Intersection end = *intersections.rbegin();
this->m_posStart = start.getWorldIntersectPoint();
this->m_posEnd = end.getWorldIntersectPoint();
this->m_bIntersected = true;
//Notify Main
this->m_propCondition->notify();
}
}
#ifndef WMEDUPICKING_H
#define WMEDUPICKING_H
#include <string>
#include "core/kernel/WModule.h"
//Debug Switch
//#define WMEDU_PICKING_DEBUG
//Module Defines
#define WMEDU_PICKING_MAX_INT "Picking - Maximum Intensity"
#define WMEDU_PICKING_FIRST_HIT "Picking - First Hit"
#define WMEDU_PICKING_THRESHOLD "Picking - Threshold"
#define WMEDU_PICKING_MOST_CONTRIBUTING "Picking - Most Contributing"
#define WMEDU_PICKING_WYSIWYP "Picking - WYSIWYP"
// forward declarations to reduce compile dependencies
template< class T > class WModuleInputData;
class WDataSetScalar;
class WGEManagedGroupNode;
/**
* Computes contour lines (aka isolines) for the given data and render them on a 2D plane.
* \ingroup modules
*/
class WMEDUPicking: public WModule
{
public:
/**
* Creates the module for drawing contour lines.
*/
WMEDUPicking();
/**
* Destroys this module.
*/
virtual ~WMEDUPicking();
/**
* Gives back the name of this module.
* \return the module's name.
*/
virtual const std::string getName() const;
/**
* Gives back a description of this module.
* \return description to module.
*/
virtual const std::string getDescription() const;
/**
* Due to the prototype design pattern used to build modules, this method returns a new instance of this method. NOTE: it
* should never be initialized or modified in some other way. A simple new instance is required.
*
* \return the prototype used to create every module in OpenWalnut.
*/
virtual boost::shared_ptr< WModule > factory() const;
/**
* Get the icon for this module in XPM format.
*
* \return The icon.
*/
virtual const char** getXPMIcon() const;
protected:
/**
* Entry point after loading the module. Runs in separate thread.
*/
virtual void moduleMain();
/**
* Initialize the connectors this module is using.
*/
virtual void connectors();
/**
* Initialize the properties for this module.
*/
virtual void properties();
/**
* Initialize requirements for this module.
*/
virtual void requirements();
private:
/**
* Handles picking and calculates ray start/end-position
*/
void pickHandler( WPickInfo pickInfo );
/**
* Input connector for scalar data.
*/
boost::shared_ptr< WModuleInputData< WDataSetScalar > > m_scalarIC;
/**
* The transfer function as an input data set
*/
boost::shared_ptr< WModuleInputData< WDataSetSingle > > m_transferFunction;
/**
* Output connector for Triangle Grid
*/
boost::shared_ptr< WModuleOutputData< WTriangleMesh > > m_gridOutput;
/**