OpenWalnut  1.5.0dev
WMPickingDVREvaluation.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2017 OpenWalnut Community
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <memory>
26 #include <string>
27 #include <vector>
28 
29 #include "WMPickingDVREvaluation.h"
30 #include "core/dataHandler/WDataSetScalar.h"
31 #include "core/graphicsEngine/WGERequirement.h"
32 #include "core/kernel/WKernel.h"
33 #include "core/ui/WUIRequirement.h"
34 
35 //Module Defines
36 #define WMPICKINGDVR_MAX_INTENS "Maximum intensity"
37 #define WMPICKINGDVR_FIRST_HIT "First hit "
38 #define WMPICKINGDVR_THRESHOLD "Opacity threshold "
39 #define WMPICKINGDVR_MOST_CONTRIBUTING "Most contributing"
40 #define WMPICKINGDVR_HIGHEST_OPACITY "Highest opacity "
41 #define WMPICKINGDVR_WYSIWYP "WYSIWYP "
42 
43 // This line is needed by the module loader to actually find your module. Do not remove. Do NOT add a ";" here.
44 W_LOADABLE_MODULE( WMPickingDVREvaluation )
45 
47  WModule()
48 {
49  m_bbox = WBoundingBox();
50 }
51 
53 {
54 }
55 
56 std::shared_ptr< WModule > WMPickingDVREvaluation::factory() const
57 {
58  return std::shared_ptr< WModule >( new WMPickingDVREvaluation() );
59 }
60 
61 const std::string WMPickingDVREvaluation::getName() const
62 {
63  return "Picking in DVR Evaluation";
64 }
65 
66 const std::string WMPickingDVREvaluation::getDescription() const
67 {
68  return "Evaluate different picking techniques with regard to certain metrics.";
69 }
70 
72 {
73  // The transfer function for our DVR
74  m_transferFunction = WModuleInputData< WDataSetSingle >::createAndAdd( shared_from_this(), "transfer function", "The 1D transfer function." );
75 
76  // Scalar field
77  m_scalarData = WModuleInputData< WDataSetScalar >::createAndAdd( shared_from_this(), "scalar data", "Scalar data." );
78 
80 }
81 
83 {
84  m_propCondition = std::shared_ptr< WCondition >( new WCondition() );
85  m_viewDirection = m_properties->addProperty( "Viewing direction",
86  "Viewing and thus projection direction for DVR. "
87  "If [0,0,0], a multi-directional sampling will be performed.",
88  WVector3d( 0.0, 0.0, -1.0 ),
90  m_sampleSteps = m_properties->addProperty( "Samples - steps",
91  "Number of samples. Choose this appropriately for the settings used for the DVR itself.",
92  256,
94  m_samplesEval = m_properties->addProperty( "Samples - evaluation",
95  "Number of samples. For evaluating Delta_vi.",
96  101,
98 
99  {
100  m_pickingCriteriaList = std::shared_ptr< WItemSelection >( new WItemSelection() );
101  m_pickingCriteriaList->addItem( WMPICKINGDVR_FIRST_HIT, WMPICKINGDVR_FIRST_HIT );
102  //m_pickingCriteriaList->addItem( WMPICKINGDVR_THRESHOLD, WMPICKINGDVR_THRESHOLD );
103  m_pickingCriteriaList->addItem( WMPICKINGDVR_MOST_CONTRIBUTING, WMPICKINGDVR_MOST_CONTRIBUTING );
104  m_pickingCriteriaList->addItem( WMPICKINGDVR_HIGHEST_OPACITY, WMPICKINGDVR_HIGHEST_OPACITY );
105  //m_pickingCriteriaList->addItem( WMPICKINGDVR_WYSIWYP, WMPICKINGDVR_WYSIWYP );
106  m_pickingCriteriaList->addItem( WMPICKINGDVR_MAX_INTENS, WMPICKINGDVR_MAX_INTENS );
107 
108  m_pickingCriteriaCur = m_properties->addProperty( "Picking method",
109  "Select a picking method",
110  m_pickingCriteriaList->getSelectorFirst(),
111  m_propCondition );
112 
115  }
116 
117  {
118  m_impFuncList = std::shared_ptr< WItemSelection >( new WItemSelection() );
119  m_impFuncList->addItem( "Uniform (=1)" );
120  m_impFuncList->addItem( "Opacity ([0,1])" );
121  m_impFuncList->addItem( "Intensity" );
122  m_importanceFunctionCur = m_properties->addProperty( "Importance function",
123  "Importance function.",
124  m_impFuncList->getSelectorFirst(),
125  m_propCondition );
126 
129  }
130 
132 }
133 
135 {
136  m_requirements.push_back( new WGERequirement() );
137  m_requirements.push_back( new WUIRequirement() );
138 }
139 
140 
141 /** \brief Intersects a ray with a plane and calculates the intersection point
142  * \param planePoint - point on plane
143  * \param v1 - first vector of plane
144  * \param v2 - second vector of plane
145  * \param rayOrigin - point on line
146  * \param rayDir - vector on line (i.e. slope)
147  **/
148 inline bool intersectPlaneWithRay( WPosition* cutpoint,
149  WPosition const& planePoint,
150  WVector3d const& v1,
151  WVector3d const& v2,
152  WVector3d const& rayDir,
153  WPosition const& rayOrigin )
154 {
155  WVector3d n = cross( v1, v2 );
156  n = normalize( n ); // normal vector of the plane
157 
158  double const d = dot( n, planePoint );
159 
160  static const double MY_EPSILON = 1e-9;
161  if( std::abs( dot( n, rayDir ) ) < MY_EPSILON ) // plane and line are parallel
162  {
163  *cutpoint = planePoint; // otherwise it would be undefined
164  return false;
165  }
166 
167  double const t = ( d - dot( n, rayOrigin ) ) / ( dot( n, rayDir ) );
168 
169  *cutpoint = rayOrigin + t * rayDir;
170 
171  return true;
172 }
173 
175 {
176  WPosition result;
177  // FIXME: check order of plane vectors for outward normals
178 
179  WPosition intersectionPoint;
180  bool hit;
181 
182  // min X plane
183  hit = intersectPlaneWithRay( &intersectionPoint,
184  bbox.getMin(),
185  WVector3d( 0.0, 1.0, 0.0 ),
186  WVector3d( 0.0, 0.0, 1.0 ),
187  dir,
188  origin );
189  if( hit
190  && bbox.contains( intersectionPoint )
191  && dot( dir, intersectionPoint - origin ) > 0.0 )
192  {
193  // debugLog() << "HIT minX: " << hit << " " << intersectionPoint;
194  return result = intersectionPoint;
195  }
196 
197  // min Y plane
198  hit = intersectPlaneWithRay( &intersectionPoint,
199  bbox.getMin(),
200  WVector3d( 1.0, 0.0, 0.0 ),
201  WVector3d( 0.0, 0.0, 1.0 ),
202  dir,
203  origin );
204  if( hit
205  && bbox.contains( intersectionPoint )
206  && dot( dir, intersectionPoint - origin ) > 0.0 )
207  {
208  // debugLog() << "HIT minY: " << hit << " " << intersectionPoint;
209  return result = intersectionPoint;
210  }
211 
212  // min Z plane
213  hit = intersectPlaneWithRay( &intersectionPoint,
214  bbox.getMin(),
215  WVector3d( 0.0, 1.0, 0.0 ),
216  WVector3d( 1.0, 0.0, 0.0 ),
217  dir,
218  origin );
219  if( hit
220  && bbox.contains( intersectionPoint )
221  && dot( dir, intersectionPoint - origin ) > 0.0 )
222  {
223  // debugLog() << "HIT minZ: " << hit << " " << intersectionPoint;
224  return result = intersectionPoint;
225  }
226 
227  // max X plane
228  hit = intersectPlaneWithRay( &intersectionPoint,
229  bbox.getMax(),
230  WVector3d( 0.0, 1.0, 0.0 ),
231  WVector3d( 0.0, 0.0, 1.0 ),
232  dir,
233  origin );
234  if( hit
235  && bbox.contains( intersectionPoint )
236  && dot( dir, intersectionPoint - origin ) > 0.0 )
237  {
238  // debugLog() << "HIT minX: " << hit << " " << intersectionPoint;
239  return result = intersectionPoint;
240  }
241 
242  // max Y plane
243  hit = intersectPlaneWithRay( &intersectionPoint,
244  bbox.getMax(),
245  WVector3d( 1.0, 0.0, 0.0 ),
246  WVector3d( 0.0, 0.0, 1.0 ),
247  dir,
248  origin );
249  if( hit
250  && bbox.contains( intersectionPoint )
251  && dot( dir, intersectionPoint - origin ) > 0.0 )
252  {
253  // debugLog() << "HIT minY: " << hit << " " << intersectionPoint;
254  return result = intersectionPoint;
255  }
256 
257  // max Z plane
258  hit = intersectPlaneWithRay( &intersectionPoint,
259  bbox.getMax(),
260  WVector3d( 0.0, 1.0, 0.0 ),
261  WVector3d( 1.0, 0.0, 0.0 ),
262  dir,
263  origin );
264  if( hit
265  && bbox.contains( intersectionPoint )
266  && dot( dir, intersectionPoint - origin ) > 0.0 )
267  {
268  // debugLog() << "HIT minZ: " << hit << " " << intersectionPoint;
269  return result = intersectionPoint;
270  }
271 
272  return result;
273 }
274 
275 double sampleTFOpacity( std::shared_ptr< WDataSetSingle > transferFunctionData,
276  std::shared_ptr< WDataSetScalar > scalarData,
277  double value )
278 {
279  //Get Transferfunction Values
280  std::shared_ptr< WValueSetBase > transferFunctionValues = transferFunctionData->getValueSet();
281 
282  double max = scalarData->getMax();
283  double min = scalarData->getMin();
284 
285  //Classification Variables
286  double nominator = value - min;
287  double denominator = max - min;
288 
289  if( denominator == 0.0 )
290  {
291  denominator = 0.0001;
292  }
293 
294  //Classification: Convert Scalar to Color
295  double scalarPercentage = nominator / denominator;
296  int iColorIdx = scalarPercentage * transferFunctionValues->size();
297 
298  const double normalizationFactor = 255.0; // Unsigned char
299  return transferFunctionData->getSingleRawValue( iColorIdx * 4 + 3 ) / normalizationFactor;
300 }
301 
302 
304 {
305  bool interpolationSuccess = false;
306 
307  double value = m_scalarDataSet->interpolate( pos, &interpolationSuccess );
308  WAssert( interpolationSuccess, "Should not fail. Please file a bug report if it does." );
309 
310  if( m_importanceFunctionCur->get( true ).getItemIndexOfSelected( 0 ) == 0 )
311  {
312  return 1.0;
313  }
314  else if( m_importanceFunctionCur->get( true ).getItemIndexOfSelected( 0 ) == 1 )
315  {
316  double opacity = sampleTFOpacity( m_transferFunctionData, m_scalarDataSet, value );
317  return opacity;
318  }
319  else if( m_importanceFunctionCur->get( true ).getItemIndexOfSelected( 0 ) == 2 )
320  {
321  return value;
322  }
323  else
324  {
325  WAssert( false, "Should not fail. Internal module error or bug if it does." );
326  return 0;
327  }
328 }
329 
331 {
332  WPosition endPos = intersectBoundingBoxWithRay( m_bbox, startPos, viewDir );
333 
334  //Get Picking Mode
335  WItemSelector selector = m_pickingCriteriaCur->get( true );
336  std::string pickModeName = selector.at( 0 )->getName();
337 
338  double maxValue = -wlimits::MAX_DOUBLE;
339 
340  WPosition resultPos;
341  double accAlpha = 0.0;
342  double oldOpacity = 0.0;
343 
344  WVector3d rayStep = ( 1.0 / m_sampleSteps->get( true ) ) * ( endPos - startPos );
345 
346  for( int sampleId = 0; sampleId < m_sampleSteps->get( true ); ++sampleId )
347  {
348  // * 0.9999 to get samples inside grid
349  WPosition samplePos = startPos + sampleId * rayStep *.999999;
350 
351  bool interpolationSuccess = false;
352  double scalar = m_scalarDataSet->interpolate( samplePos, &interpolationSuccess );
353  WAssert( interpolationSuccess, "Should not fail. Please file a bug report if it does." );
354 
355  if( pickModeName == WMPICKINGDVR_MAX_INTENS )
356  {
357  if( scalar > maxValue )
358  {
359  maxValue = scalar;
360  resultPos = samplePos;
361  }
362  }
363  else if( pickModeName == WMPICKINGDVR_FIRST_HIT )
364  {
365  double opacity = sampleTFOpacity( m_transferFunctionData, m_scalarDataSet, scalar );
366  if( opacity > 0.0 )
367  {
368  resultPos = samplePos;
369  break;
370  }
371  }
372  else if( pickModeName == WMPICKINGDVR_HIGHEST_OPACITY )
373  {
374  double opacity = sampleTFOpacity( m_transferFunctionData, m_scalarDataSet, scalar );
375  if( opacity > maxValue )
376  {
377  maxValue = opacity;
378  resultPos = samplePos;
379  }
380  }
381  else if( pickModeName == WMPICKINGDVR_MOST_CONTRIBUTING )
382  {
383  double opacity = sampleTFOpacity( m_transferFunctionData, m_scalarDataSet, scalar );
384  accAlpha = opacity + ( accAlpha - opacity * accAlpha );
385  double contribution = accAlpha - oldOpacity;
386  if( contribution > maxValue )
387  {
388  maxValue = contribution;
389  resultPos = samplePos;
390  }
391  oldOpacity = accAlpha;
392  }
393  else
394  {
395  WAssert( false, "This should not happen. Please file a bug report if it does." );
396  }
397  }
398 
399  return resultPos;
400 }
401 
403 {
404  // -1 because we want to project towards the viewer.
405  // * 0.999999 to get samples inside grid also for border vertices
406  return intersectBoundingBoxWithRay( m_bbox, pos, -1 * viewDir ) * 0.999999;
407 }
408 
410 {
411  // get notified about data changes
412  m_moduleState.setResetable( true, true );
413  m_moduleState.add( m_scalarData->getDataChangedCondition() );
414  m_moduleState.add( m_transferFunction->getDataChangedCondition() );
416 
417  ready();
418 
419  // main loop
420  while( !m_shutdownFlag() )
421  {
422  debugLog() << "Waiting ...";
424  debugLog() << "Processing ...";
425 
426  WDataSet::SPtr dataSet = m_scalarData->getData();
427 
428  WDataSetSingle::SPtr dsSingle = std::dynamic_pointer_cast< WDataSetSingle >( dataSet );
429  if( !dsSingle )
430  {
431  errorLog() << "[Invalid data set]";
432  continue;
433  }
434 
435  //Get scalar field
436  m_scalarDataSet = m_scalarData->getData();
437  if( !m_scalarDataSet )
438  {
439  errorLog() << "[Invalid scalar field]";
440  continue;
441  }
442 
443  //Get transfer function data
446  {
447  errorLog() << "[Invalid transfer function data]";
448  continue;
449  }
450 
451  // Is this a data set with a regular grid?
452  WGridRegular3D::SPtr regGrid;
453  regGrid = std::dynamic_pointer_cast< WGridRegular3D >( dsSingle->getGrid() );
454  if( !regGrid )
455  {
456  errorLog() << "[Invalid data set]";
457  continue;
458  }
459 
460  m_bbox = regGrid->getBoundingBox();
461 
462  double deltaVI = 0;
463 
464  std::vector< WVector3d > viewingDirections;
465  if( m_viewDirection->get( true ) == WVector3d() )
466  {
467  debugLog() << "Found [0,0,0] viewing direction -> performing multi-directional sampling.";
468  for( int i_ID = -1; i_ID <= 1; ++i_ID )
469  {
470  for( int j_ID = -1; j_ID <= 1; ++j_ID )
471  {
472  for( int k_ID = -1; k_ID <= 1; ++k_ID )
473  {
474  const WVector3d dir( i_ID, j_ID, k_ID );
475  if( dir != WVector3d() )
476  {
477  viewingDirections.push_back( normalize( dir ) );
478  }
479  }
480  }
481  }
482  }
483  else
484  {
485  viewingDirections.push_back( m_viewDirection->get( true ) );
486  }
487 
488  std::shared_ptr< WProgress > progress( new WProgress( "Sampling", m_samplesEval->get( true ) * viewingDirections.size() ) );
489  m_progress->addSubProgress( progress );
490 
491 
492  for( size_t viewingDirId = 0; viewingDirId < viewingDirections.size(); ++viewingDirId )
493  {
494  debugLog() << "Sampling in direction " << viewingDirections[viewingDirId];
495 
496  for( int sampleId = 0; sampleId < m_samplesEval->get( true ); ++sampleId )
497  {
498  WAssert( regGrid->getOrigin() == WPosition( 0.0, 0.0, 0.0 ),
499  "0.999999 in the following works only if origin is at zero." );
500 
501  size_t posId = sampleId * ( regGrid->size() / m_samplesEval->get( true ) );
502 
503  // * 0.9999 to get samples inside grid also for border vertices
504  WPosition samplePos = regGrid->getPosition( posId ) * 0.999999;
505  //debugLog() << "SamplePos: " << samplePos;
506 
507  WVector3d vd = viewingDirections[viewingDirId];
508  double distance = length( samplePos - interactionMapping( visualizationMapping( samplePos, vd ), vd ) );
509  deltaVI += importance( samplePos ) * distance;
510  //debugLog() << "Distance: " << distance;
511  ++*progress;
512  }
513  }
514 
515  // Normalization
516  deltaVI /= m_samplesEval->get( true ) * viewingDirections.size();
517 
518  {
519  WItemSelector selector = m_pickingCriteriaCur->get( true );
520  std::string pickModeName = selector.at( 0 )->getName();
521  infoLog() << pickModeName << " deltaVI = " << deltaVI;
522  }
523 
524  progress->finish();
525  }
526 }
const vec_type & getMax() const
Gives the back upper right aka maximum corner.
Definition: WBoundingBox.h:308
const vec_type & getMin() const
Gives the front lower left aka minimum corner.
Definition: WBoundingBox.h:302
virtual void wait() const
Wait for the condition.
void setResetable(bool resetable=true, bool autoReset=true)
Sets the resetable flag.
virtual void add(std::shared_ptr< WCondition > condition)
Adds another condition to the set of conditions to wait for.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
std::shared_ptr< WDataSetSingle > SPtr
Convenience typedef for a std::shared_ptr.
std::shared_ptr< WDataSet > SPtr
Shared pointer abbreviation to a instance of this class.
Definition: WDataSet.h:55
This requirement ensures an up and running WGE.
std::shared_ptr< WGridRegular3DTemplate > SPtr
Convenience typedef for a std::shared_ptr< WGridRegular3DTemplate >.
A class containing a list of named items.
This class represents a subset of a WItemSelection.
Definition: WItemSelector.h:53
virtual const std::shared_ptr< WItemSelectionItem > at(size_t index) const
Gets the selected item with the given index.
Someone should add some documentation here.
std::shared_ptr< WModuleInputData< WDataSetSingle > > m_transferFunction
The transfer function as an input data set.
virtual void moduleMain()
Entry point after loading the module.
std::shared_ptr< WItemSelection > m_pickingCriteriaList
Possible criteria.
virtual std::shared_ptr< WModule > factory() const
Due to the prototype design pattern used to build modules, this method returns a new instance of this...
std::shared_ptr< WDataSetScalar > m_scalarDataSet
Input data as WDataSetScalar.
virtual void requirements()
Initialize requirements for this module.
std::shared_ptr< WModuleInputData< WDataSetScalar > > m_scalarData
Input connector for scalar data.
virtual void connectors()
Initialize the connectors this module is using.
virtual const std::string getName() const
Gives back the name of this module.
WPropInt m_sampleSteps
Number of samples along the ray.
WMPickingDVREvaluation()
A simple constructor.
WPosition visualizationMapping(const WPosition &pos, const WVector3d &viewDir)
viusalization mapping of directness model.
WPosition interactionMapping(const WPosition &startPos, const WVector3d &viewDir)
interaction Map of directness model.
WPosition intersectBoundingBoxWithRay(const WBoundingBox &bbox, const WPosition &origin, const WVector3d &dir)
Get position where a given ray intersects a given axis-aligned bounding box.
WPropSelection m_importanceFunctionCur
Current importance function.
virtual void properties()
Initialize the properties for this module.
WPropInt m_samplesEval
Number of samples for evaluating Delta_vi.
std::shared_ptr< WDataSetSingle > m_transferFunctionData
Transfer function as WDataSetSingle.
virtual ~WMPickingDVREvaluation()
A simple destructor.
virtual const std::string getDescription() const
Gives back a description of this module.
double importance(WPosition pos)
Importance function.
WPropPosition m_viewDirection
The viewing and thus projection direction.
WPropSelection m_pickingCriteriaCur
Current picking method.
WBoundingBox m_bbox
Bounding box of the treated data set.
std::shared_ptr< WItemSelection > m_impFuncList
Possible importance functions.
std::shared_ptr< WCondition > m_propCondition
Needed for recreating the geometry, incase when resolution changes.
static PtrType createAndAdd(std::shared_ptr< WModule > module, std::string name="", std::string description="")
Convenience method to create a new instance of this in data connector with proper type and add it to ...
Class representing a single module of OpenWalnut.
Definition: WModule.h:72
Requirements m_requirements
The list of requirements.
Definition: WModule.h:754
virtual void properties()
Initialize properties in this function.
Definition: WModule.cpp:212
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
Definition: WModule.cpp:575
std::shared_ptr< WProperties > m_properties
The property object for the module.
Definition: WModule.h:640
void ready()
Call this whenever your module is ready and can react on property changes.
Definition: WModule.cpp:505
WConditionSet m_moduleState
The internal state of the module.
Definition: WModule.h:703
wlog::WStreamedLogger errorLog() const
Logger instance for comfortable error logging.
Definition: WModule.cpp:570
std::shared_ptr< WProgressCombiner > m_progress
Progress indicator used as parent for all progress' of this module.
Definition: WModule.h:652
wlog::WStreamedLogger infoLog() const
Logger instance for comfortable info logging.
Definition: WModule.cpp:565
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
This only is a 3d double vector.
Class managing progress inside of modules.
Definition: WProgress.h:42
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
This requirement ensures an up and running UI which properly implements the WUI interface.
void addTo(WPropSelection prop)
Add the PC_NOTEMPTY constraint to the property.
void addTo(WPropSelection prop)
Add the PC_SELECTONLYONE constraint to the property.
const double MAX_DOUBLE
Maximum double value.
Definition: WLimits.cpp:31