OpenWalnut  1.5.0dev
WMTeemGlyphs.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
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 <algorithm>
26 #include <memory>
27 #include <sstream>
28 #include <string>
29 
30 #include <osg/Geometry>
31 #include <osg/LightModel>
32 #include <teem/elf.h> // NOLINT false positive C system header
33 
34 #include "WMTeemGlyphs.h"
35 #include "WMTeemGlyphs.xpm"
36 #include "core/common/WAssert.h"
37 #include "core/common/WConditionOneShot.h"
38 #include "core/common/WLimits.h"
39 #include "core/common/WPropertyHelper.h"
40 #include "core/common/WStringUtils.h"
41 #include "core/common/WThreadedFunction.h"
42 #include "core/kernel/WKernel.h"
43 
44 // This line is needed by the module loader to actually find your module. Do not remove. Do NOT add a ";" here.
45 W_LOADABLE_MODULE( WMTeemGlyphs )
46 
48  WModule(),
49  m_recompute( std::shared_ptr< WCondition >( new WCondition() ) )
50 {
51 }
52 
53 namespace
54 {
55 // Helper routine to estimate normals in a triangle soup in which antipodal
56 // vertices are subsequent
57 // taken from teem (glyphElf.c)
58 void estimateNormalsAntipodal( limnPolyData *glyph, const char normalize )
59 {
60  unsigned int faceno = glyph->indxNum/3;
61  unsigned int *faces = glyph->indx;
62  unsigned int f;
63  memset( glyph->norm, 0, sizeof( float )*3*glyph->xyzwNum );
64  for( f = 0; f < faceno/2; f++ )
65  {
66  float diff1[3];
67  float diff2[3];
68  float cross[3];
69  ELL_3V_SUB( diff1, glyph->xyzw+4*faces[3*f+1],
70  glyph->xyzw+4*faces[3*f] );
71  ELL_3V_SUB( diff2, glyph->xyzw+4*faces[3*f+2],
72  glyph->xyzw+4*faces[3*f] );
73  ELL_3V_CROSS( cross, diff1, diff2 );
74  ELL_3V_INCR( glyph->norm+3*faces[3*f], cross );
75  ELL_3V_INCR( glyph->norm+3*faces[3*f+1], cross );
76  ELL_3V_INCR( glyph->norm+3*faces[3*f+2], cross );
77  /* same for anti-face */
78  if( faces[3*f]%2 == 0 )
79  {
80  ELL_3V_SUB( glyph->norm+3*faces[3*f]+3, glyph->norm+3*faces[3*f]+3, cross );
81  }
82  else
83  {
84  ELL_3V_SUB( glyph->norm+3*faces[3*f]-3, glyph->norm+3*faces[3*f]-3, cross );
85  }
86  if( faces[3*f+1]%2 == 0 )
87  {
88  ELL_3V_SUB( glyph->norm+3*faces[3*f+1]+3, glyph->norm+3*faces[3*f+1]+3, cross );
89  }
90  else
91  {
92  ELL_3V_SUB( glyph->norm+3*faces[3*f+1]-3, glyph->norm+3*faces[3*f+1]-3, cross );
93  }
94  if( faces[3*f+2]%2 == 0 )
95  {
96  ELL_3V_SUB( glyph->norm+3*faces[3*f+2]+3, glyph->norm+3*faces[3*f+2]+3, cross );
97  }
98  else
99  {
100  ELL_3V_SUB( glyph->norm+3*faces[3*f+2]-3, glyph->norm+3*faces[3*f+2]-3, cross );
101  }
102  }
103  if( normalize )
104  {
105  float len;
106  unsigned int i;
107  for( i = 0; i < glyph->normNum; i++ )
108  {
109  ELL_3V_NORM_TT( glyph->norm + 3*i, float, glyph->norm + 3*i, len );
110  }
111  }
112 }
113 }
114 
116 {
117 }
118 
119 std::shared_ptr< WModule > WMTeemGlyphs::factory() const
120 {
121  return std::shared_ptr< WModule >( new WMTeemGlyphs() );
122 }
123 
124 const char** WMTeemGlyphs::getXPMIcon() const
125 {
126  return teemGlyphs_xpm;
127 }
128 
129 
130 const std::string WMTeemGlyphs::getName() const
131 {
132  return "Teem Glyphs";
133 }
134 
135 const std::string WMTeemGlyphs::getDescription() const
136 {
137  return "Higher-Order Tensor Glyphs as described at http://www.ci.uchicago.edu/~schultz/sphinx/home-glyph.html";
138 }
139 
141 {
142  m_input = std::shared_ptr< WModuleInputData < WDataSetSphericalHarmonics > >(
143  new WModuleInputData< WDataSetSphericalHarmonics >( shared_from_this(), "in", "The input dataset." ) );
145 
146  m_inputGFA = std::shared_ptr< WModuleInputData< WDataSetScalar > >( new WModuleInputData< WDataSetScalar >( shared_from_this(),
147  "inGFA", "Generalized fractional anisotropy." )
148  );
150 
151 
152  // call WModules initialization
154 }
155 
157 {
158  m_exceptionCondition = std::shared_ptr< WCondition >( new WCondition() );
159 
160  m_sliceOrientations = std::shared_ptr< WItemSelection >( new WItemSelection() );
161  m_sliceOrientations->addItem( "x", "x-slice" );
162  m_sliceOrientations->addItem( "y", "y-slice" );
163  m_sliceOrientations->addItem( "z", "z-slice" );
164  m_sliceOrientationSelectionProp = m_properties->addProperty( "Slice orientation",
165  "Which slice will be shown?",
166  m_sliceOrientations->getSelector( 1 ), m_recompute );
168 
169  m_sliceIdProp = m_properties->addProperty( "Slice ID", "Number of the slice to display", 100, m_recompute );
170  m_sliceIdProp->setMin( 0 );
171  m_sliceIdProp->setMax( 128 );
172 
173  m_orders = std::shared_ptr< WItemSelection >( new WItemSelection() );
174  m_orders->addItem( "2", "Order 2" );
175  m_orders->addItem( "4", "Order 4" );
176  m_orders->addItem( "6", "Order 6" );
177 
178  m_orderProp = m_properties->addProperty( "Order",
179  "Order of the displayed spherical harmonics."
180  " If actual order is higer, the additional coefficients are ignored.",
181  m_orders->getSelector( 1 ),
182  m_recompute );
184 
185  m_GFAThresholdProp = m_properties->addProperty( "GFA threshold", "Show only glyphs at voxels above the given generalized fractional"
186  " anisotropy (GFA) threshold"
187  " (if GFA data is present at input connector).",
188  0.0,
189  m_recompute );
190  m_GFAThresholdProp->setMin( 0.0 );
191  m_GFAThresholdProp->setMax( 1.0 );
192 
193  m_glyphSizeProp = m_properties->addProperty( "Glyph size", "Size of the displayed glyphs.", 1.0, m_recompute );
194  m_glyphSizeProp->setMin( 0 );
195  m_glyphSizeProp->setMax( 100. );
196 
197 
198  m_moduloProp = m_properties->addProperty( "Modulo", "Shows only every Modulo-th glyph in the two slice directions", 2, m_recompute );
199  m_moduloProp->setMin( 1 );
200  m_moduloProp->setMax( 10 );
201 
202  m_subdivisionLevelProp = m_properties->addProperty( "Subdivision level",
203  "Determines the glyph resolution. Subdivision level of"
204  " icosahadra use as sphere approximations.",
205  2,
206  m_recompute );
207  m_subdivisionLevelProp->setMin( 0 );
208  m_subdivisionLevelProp->setMax( 5 );
209 
210  m_usePolarPlotProp = m_properties->addProperty( "Use polar plot", "Use polar plot for glyph instead of HOME?", true, m_recompute );
211  m_useNormalizationProp = m_properties->addProperty( "Min-max normalization", "Scale the radius of each glyph to be in [0,1]."
212  " Do <b>not</b> use with \"Hide negative lobes\"!",
213  true,
214  m_recompute );
215  m_useRadiusNormalizationProp = m_properties->addProperty( "Radius normalization", "Make all glyphs have similar size.",
216  true,
217  m_recompute );
218  m_hideNegativeLobesProp = m_properties->addProperty( "Hide negative lobes", "Hide glyph lobes that have negative radius."
219  " Do <b>not</b> use with \"Min-max normalization\"!",
220  false,
221  m_recompute );
222 
224 }
225 
227 {
228  m_lastException = std::shared_ptr< WException >( new WException( e ) );
229  m_exceptionCondition->notify();
230 }
231 
233 {
234  m_moduleState.add( m_input->getDataChangedCondition() );
237 
238  ready();
239 
240  // loop until the module container requests the module to quit
241  while( !m_shutdownFlag() )
242  {
243  if( !m_input->getData().get() )
244  {
245  // OK, the output has not yet sent data
246  debugLog() << "Waiting for data ...";
248  continue;
249  }
250  if( m_input->getData().get() )
251  {
252  bool dataChanged = false;
253  if( m_dataSet != m_input->getData() )
254  {
255  // acquire data from the input connector
256  m_dataSet = m_input->getData();
257  dataChanged = true;
258  }
259 
260  std::shared_ptr< WGridRegular3D > gridReg = std::dynamic_pointer_cast< WGridRegular3D >( m_input->getData().get()->getGrid() );
261  switch( m_sliceOrientationSelectionProp->get( true ).getItemIndexOfSelected( 0 ) )
262  {
263  case 0:
264  m_sliceIdProp->setMax( gridReg->getNbCoordsX() - 1 );
265  break;
266  case 1:
267  m_sliceIdProp->setMax( gridReg->getNbCoordsY() - 1 );
268  break;
269  case 2:
270  m_sliceIdProp->setMax( gridReg->getNbCoordsZ() - 1 );
271  break;
272  }
273 
274  if( dataChanged )
275  {
276  m_sliceIdProp->set( m_sliceIdProp->getMax()->getMax() / 2 );
277  }
278 
279  std::shared_ptr< WDataSetScalar > gfa = m_inputGFA->getData();
280  if( gfa )
281  {
282  m_GFAThresholdProp->setMax( gfa->getMax() );
283  m_GFAThresholdProp->setMin( gfa->getMin() );
284  }
285 
286  renderSlice( m_sliceIdProp->get( true ) );
287  }
288 
289  if( !( m_sliceIdProp->changed()
290  || m_sliceOrientationSelectionProp->changed()
291  || m_orderProp->changed()
292  || m_GFAThresholdProp->changed()
293  || m_glyphSizeProp->changed()
294  || m_subdivisionLevelProp->changed()
295  || m_moduloProp->changed()
296  || m_usePolarPlotProp->changed()
297  || m_useNormalizationProp->changed()
298  || m_useRadiusNormalizationProp->changed()
299  || m_hideNegativeLobesProp->changed()
300  )
301  )
302  {
304  }
305  }
306 
307  {
308  std::unique_lock< boost::mutex > lock( m_moduleNodeLock );
309 
310  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_moduleNode );
311 
312  lock.unlock();
313  }
314 }
315 
316 void WMTeemGlyphs::renderSlice( size_t sliceId )
317 {
318  // Please look here http://www.ci.uchicago.edu/~schultz/sphinx/home-glyph.htm
319 
320  debugLog() << "Rendering Slice " << sliceId;
321  std::shared_ptr< WProgress > progress;
322  progress = std::shared_ptr< WProgress >( new WProgress( "Glyph Generation", 2 ) );
323  m_progress->addSubProgress( progress );
324 
325  size_t sliceType = m_sliceOrientationSelectionProp->get( true ).getItemIndexOfSelected( 0 );
326  size_t order =
327  string_utils::fromString< float >( m_orders->getSelector( m_orderProp->get( true ).getItemIndexOfSelected( 0 ) ) .at( 0 )->getName() );
328 
329  {
330  std::unique_lock< boost::mutex > lock( m_moduleNodeLock );
331 
332  if( m_moduleNode )
333  {
334  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_moduleNode );
335  }
336 
337  lock.unlock();
338  }
339 
340  //-----------------------------------------------------------
341  // run through the positions in the slice and draw the glyphs
342  std::shared_ptr< GlyphGeneration > generator;
343  generator = std::shared_ptr< GlyphGeneration >(
344  new GlyphGeneration( std::dynamic_pointer_cast< WDataSetSphericalHarmonics >( m_input->getData() ),
345  std::dynamic_pointer_cast< WDataSetScalar >( m_inputGFA->getData() ),
346  m_GFAThresholdProp->get( true ),
347  sliceId,
348  order,
349  m_subdivisionLevelProp->get( true ),
350  m_moduloProp->get( true ),
351  sliceType,
352  m_usePolarPlotProp->get( true ),
353  m_glyphSizeProp->get( true ),
354  m_useNormalizationProp->get( true ),
355  m_useRadiusNormalizationProp->get( true ),
356  m_hideNegativeLobesProp->get( true ) ) );
357  WThreadedFunction< GlyphGeneration > generatorThreaded( W_AUTOMATIC_NB_THREADS, generator );
358  generatorThreaded.run();
359  generatorThreaded.wait();
360 
361  ++*progress;
362 
363  {
364  std::unique_lock< boost::mutex > lock( m_moduleNodeLock );
365 
366  m_moduleNode = osg::ref_ptr< WGEGroupNode >( new WGEGroupNode() );
367  osg::ref_ptr< osg::Geode > glyphsGeode = generator->getGraphics();
368  m_moduleNode->insert( glyphsGeode );
369  m_moduleNode->setName( "teem glyphs module node" );
370 
371  m_shader = osg::ref_ptr< WGEShader > ( new WGEShader( "WMTeemGlyphs", m_localPath ) );
372  m_shader->apply( glyphsGeode );
373 
374  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_moduleNode );
375 
376  lock.unlock();
377 
378  activate(); // sets active state of moduleNode correctly
379  }
380 
381  progress->finish();
382 }
383 
384 
386 {
387  std::unique_lock< boost::mutex > lock( m_moduleNodeLock );
388 
389  if( m_moduleNode )
390  {
391  if( m_active->get() )
392  {
393  m_moduleNode->setNodeMask( 0xFFFFFFFF );
394  }
395  else
396  {
397  m_moduleNode->setNodeMask( 0x0 );
398  }
399  }
400 
401  lock.unlock();
402 
404 }
405 
406 void WMTeemGlyphs::GlyphGeneration::minMaxNormalization( limnPolyData *glyph, const size_t& nbVertCoords )
407 {
408  // double min = wlimits::MAX_DOUBLE;
409  // double max = -wlimits::MAX_DOUBLE;
410  double min = 1e15;
411  double max = -1e15;
412  for( size_t vertID = 0; vertID < glyph->xyzwNum; ++vertID )
413  {
414  WPosition pos( glyph->xyzw[nbVertCoords*vertID], glyph->xyzw[nbVertCoords*vertID+1], glyph->xyzw[nbVertCoords*vertID+2] );
415  double norm = length( pos );
416 
417  if( norm < min )
418  {
419  min = norm;
420  }
421  if( norm > max )
422  {
423  max = norm;
424  }
425  }
426  double dist = max - min;
427 
428  if( dist != 0.0 )
429  {
430  WAssert( dist > 0.0, "Max has to be larger than min." );
431 
432  for( size_t i = 0; i < glyph->xyzwNum; ++i )
433  {
434  size_t coordIdBase = nbVertCoords * i;
435  WPosition pos( glyph->xyzw[coordIdBase], glyph->xyzw[coordIdBase+1], glyph->xyzw[coordIdBase+2] );
436  double norm = length( pos );
437  const double epsilon = 1e-9;
438  WPosition newPos;
439 // newPos = ( ( ( norm - min ) / dist ) + epsilon ) * normalize( pos );
440  newPos = ( ( ( norm - min ) / dist ) + epsilon ) * pos / norm;
441  glyph->xyzw[coordIdBase] = newPos[0];
442  glyph->xyzw[coordIdBase+1] = newPos[1];
443  glyph->xyzw[coordIdBase+2] = newPos[2];
444  }
445  }
446  // else do nothing because all values are equal.
447 }
448 
449 WMTeemGlyphs::GlyphGeneration::GlyphGeneration( std::shared_ptr< WDataSetSphericalHarmonics > dataSet,
450  std::shared_ptr< WDataSetScalar > dataGFA,
451  double thresholdGFA,
452  const size_t& sliceId,
453  const size_t& order,
454  const size_t& subdivisionLevel,
455  const size_t& modulo,
456  const size_t& sliceType,
457  const bool& usePolar,
458  const float& scale,
459  const bool& useNormalization,
460  const bool& useRadiusNormalization,
461  const bool& hideNegativeLobes ) :
462  m_dataSet( dataSet ),
463  m_dataGFA( dataGFA ),
464  m_grid( std::dynamic_pointer_cast< WGridRegular3D >( dataSet->getGrid() ) ),
465  m_thresholdGFA( thresholdGFA ),
466  m_order( order ),
467  m_sliceType( sliceType ),
468  m_subdivisionLevel( subdivisionLevel ),
469  m_modulo( modulo ),
470  m_usePolar( usePolar ),
471  m_scale( scale ),
472  m_useNormalization( useNormalization ),
473  m_useRadiusNormalization( useRadiusNormalization ),
474  m_hideNegativeLobes( hideNegativeLobes )
475 {
476  enum sliceTypeEnum
477  {
478  xSlice = 0,
479  ySlice,
480  zSlice
481  };
482 
483  // convenience names for grid dimensions
484  m_nX = m_grid->getNbCoordsX();
485  m_nY = m_grid->getNbCoordsY();
486  m_nZ = m_grid->getNbCoordsZ();
487  m_sliceId = sliceId;
488 
489  switch( sliceType )
490  {
491  case xSlice:
492  m_nA = m_nY;
493  m_nB = m_nZ;
494  break;
495  case ySlice:
496  m_nA = m_nX;
497  m_nB = m_nZ;
498  break;
499  case zSlice:
500  m_nA = m_nX;
501  m_nB = m_nY;
502  break;
503  }
504  size_t nbGlyphs = ( ( m_nA + ( m_modulo - 1 ) ) / m_modulo ) * ( ( m_nB + ( m_modulo - 1 ) ) / m_modulo );
505 
506  const unsigned int level = m_subdivisionLevel; // subdivision level of sphere
507  unsigned int infoBitFlag = ( 1 << limnPolyDataInfoNorm ) | ( 1 << limnPolyDataInfoRGBA );
508 
509  m_sphere = limnPolyDataNew();
510  limnPolyDataIcoSphere( m_sphere, infoBitFlag, level );
511  size_t nbVerts = m_sphere->xyzwNum;
512 
513  m_vertArray = osg::ref_ptr< osg::Vec3Array >( new osg::Vec3Array() );
514  m_normals = osg::ref_ptr< osg::Vec3Array >( new osg::Vec3Array() );
515  m_colors = osg::ref_ptr< osg::Vec4Array >( new osg::Vec4Array() );
516  m_vertArray->resize( nbVerts * nbGlyphs );
517  m_normals->resize( nbVerts * nbGlyphs );
518  m_colors->resize( nbVerts * nbGlyphs, osg::Vec4( 0, 0, 0, 1.0 ) );
519 
520  m_glyphElements = osg::ref_ptr< osg::DrawElementsUInt >( new osg::DrawElementsUInt( osg::PrimitiveSet::TRIANGLES ) );
521  m_glyphElements->resize( m_sphere->indxNum * nbGlyphs );
522 }
523 
525 {
526  // free memory
527  m_sphere = limnPolyDataNix( m_sphere );
528 }
529 
530 void WMTeemGlyphs::GlyphGeneration::operator()( size_t id, size_t numThreads, WBoolFlag& /*b*/ )
531 {
532  const size_t nbVertCoords = 4; //The teem limn data structure has 4 values for a coordinate: x, y, z, w.
533  limnPolyData *localSphere = limnPolyDataNew();
534  limnPolyDataCopy( localSphere, m_sphere );
535  enum sliceTypeEnum
536  {
537  xSlice = 0,
538  ySlice,
539  zSlice
540  };
541 
542  WAssert( m_sphere->xyzwNum == m_sphere->normNum, "Wrong size of arrays." );
543  WAssert( m_sphere->xyzwNum == m_sphere->rgbaNum, "Wrong size of arrays." );
544  size_t nbVerts = m_sphere->xyzwNum;
545 
546  const tijk_type *type = 0; // Initialized to quiet compiler
547  const tijk_type *typeOrder2 = tijk_2o3d_sym;
548  const tijk_type *typeOrder4 = tijk_4o3d_sym;
549  const tijk_type *typeOrder6 = tijk_6o3d_sym;
550 
551  // memory for the tensor and spherical harmonics data.
552  float* ten = new float[ typeOrder6->num ];
553  float* res = new float[ typeOrder6->num ];
554  float* esh = new float[ typeOrder6->num ];
555 
556 
557  size_t chunkSize = m_nA / ( numThreads - 1 );
558  size_t first = id * chunkSize;
559 
560  size_t lastPlusOne = ( id + 1 ) * chunkSize;
561 
562  if( id == numThreads - 1 )
563  {
564  lastPlusOne = m_nA;
565  }
566 
567  std::stringstream ss;
568  ss << id << "/" << numThreads <<" (" << first << " ... " << lastPlusOne - 1 << ")[" << chunkSize << "/" << m_nA << "]" << std::endl;
569  WLogger::getLogger()->addLogMessage( ss.str(), "______", LL_DEBUG );
570 
571  for( size_t aId = first; aId < lastPlusOne; ++aId )
572  {
573  for( size_t bId = 0; bId < m_nB; ++bId )
574  {
575  if( ( aId % m_modulo != 0) || ( bId % m_modulo != 0 ) )
576  {
577  continue;
578  }
579  size_t glyphId = ( aId / m_modulo ) * ( ( m_nB + ( m_modulo - 1 ) ) / m_modulo ) + ( bId / m_modulo );
580 
581  size_t vertsUpToCurrentIteration = glyphId * nbVerts;
582  size_t idsUpToCurrentIteration = glyphId * m_sphere->indxNum;
583 
584  size_t posId = 0; // initialized to quiet compiler
585  switch( m_sliceType )
586  {
587  case xSlice:
588  posId = m_sliceId + aId * m_nX + bId * m_nX * m_nY;
589  break;
590  case ySlice:
591  posId = aId + m_sliceId * m_nX + bId * m_nX * m_nY;
592  break;
593  case zSlice:
594  posId = aId + bId * m_nX + m_sliceId * m_nX * m_nY;
595  break;
596  }
597 
598  //-------------------------------
599  // vertex indices
600  // We have to set them also if we do not draw the glyph because otherwise we would leave their
601  // to be zero. If many indices are zero, they block each other because of synchronized
602  // memory access to the same memory address.
603  for( unsigned int vertId = 0; vertId < localSphere->indxNum; ++vertId )
604  {
605  ( *m_glyphElements )[idsUpToCurrentIteration+vertId] = ( vertsUpToCurrentIteration + localSphere->indx[vertId] );
606  }
607 
608  // do not compute positions of vertices if GFA below threshold
609  if( m_dataGFA && std::static_pointer_cast< WDataSetSingle >( m_dataGFA )->getValueAt( posId ) < m_thresholdGFA )
610  {
611  continue;
612  }
613 
614  WValue<double> coeffs( m_dataSet->getSphericalHarmonicAt( posId ).getCoefficients() );
615  int countOfCoeffsToUse = 0;
616  switch( m_order )
617  {
618  case 2:
619  countOfCoeffsToUse = 6;
620  break;
621  case 4:
622  countOfCoeffsToUse = 15;
623  break;
624  case 6:
625  countOfCoeffsToUse = 28;
626  break;
627  default:
628  WAssert( false, "order above 6 not supported yet." );
629  }
630  countOfCoeffsToUse = std::min( static_cast< int >( coeffs.size() ), countOfCoeffsToUse );
631 
632  // determine order to use
633  int useOrder = 0;
634  switch( countOfCoeffsToUse )
635  {
636  case 6:
637  useOrder = 2;
638  type = typeOrder2;
639  break;
640  case 15:
641  useOrder = 4;
642  type = typeOrder4;
643  break;
644  case 28:
645  useOrder = 6;
646  type = typeOrder6;
647  break;
648  default:
649  WAssert( false, "order above 6 not supported yet." );
650  }
651 
652  for( int coeffId = 0; coeffId < countOfCoeffsToUse; ++coeffId )
653  {
654  esh[ coeffId ] = coeffs[ coeffId ];
655  }
656 
657  // change Descoteaux basis to Schulz/teem basis
658  // Descoteaux basis: see his PhD thesis page 66
659  // Schultz basis: see PDF "Real Spherical Harmonic basis" - Luke Bloy 9. December 2009
660  size_t k = 0;
661  for( int l = 0; l <= useOrder; l += 2 )
662  {
663  for( int m = -l; m <= l; ++m )
664  {
665  if( m < 0 && ( ( -m ) % 2 == 1 ) )
666  {
667  esh[k] *= -1.0;
668  }
669  else if( m > 0 && ( m % 2 == 0 ) )
670  {
671  esh[k] *= -1.0;
672  }
673  ++k;
674  }
675  }
676 
677  // convert even-order spherical harmonics to higher-order tensor
678  tijk_esh_to_3d_sym_f( ten, esh, useOrder );
679 
680  const char normalize = 0;
681 
682  limnPolyData *glyph = limnPolyDataNew();
683  limnPolyDataCopy( glyph, localSphere );
684 
685  double radius = 1.0; // some initialization
686  if( m_usePolar )
687  {
688  char isdef = 3; // some initialization
689  radius = elfGlyphPolar( glyph, 1, ten, type, &isdef, m_hideNegativeLobes, normalize, NULL, NULL );
690  }
691  else
692  {
693  // create positive approximation of the tensor
694  tijk_refine_rankk_parm *parm = tijk_refine_rankk_parm_new();
695  parm->pos = 1;
696  int ret = tijk_approx_rankk_3d_f( NULL, NULL, res, ten, type, 6, parm );
697  WAssert( ret == 0, "Error condition in call to tijk_approx_rankk_3d_f." );
698  parm = tijk_refine_rankk_parm_nix( parm );
699  tijk_sub_f( ten, ten, res, type );
700 
701  radius = elfGlyphHOME( glyph, 1, ten, type, NULL, normalize );
702  }
703 
704  // -------------------------------------------------------------------------------------------------------
705  // One can insert per-peak coloring here (see http://www.ci.uchicago.edu/~schultz/sphinx/home-glyph.html )
706  // -------------------------------------------------------------------------------------------------------
707 
708  float scale = m_scale;
709 
710  if( m_useNormalization )
711  {
712  minMaxNormalization( glyph, nbVertCoords );
713  if( !m_useRadiusNormalization )
714  {
715  scale = m_scale * radius;
716  }
717  }
718  else
719  {
720  if( m_useRadiusNormalization )
721  {
722  if( radius != 0 )
723  {
724  scale = m_scale / radius;
725  }
726  else
727  {
728  scale = 0.0;
729  }
730  }
731  }
732  estimateNormalsAntipodal( glyph, normalize );
733 
734  WPosition glyphPos = m_grid->getPosition( posId );
735 
736 
737  for( unsigned int vertId = 0; vertId < glyph->xyzwNum; ++vertId )
738  {
739  size_t globalVertexId = vertsUpToCurrentIteration + vertId;
740  //-------------------------------
741  // vertices
742  ( *m_vertArray )[globalVertexId][0] = glyph->xyzw[nbVertCoords*vertId ] * scale + glyphPos[0];
743  ( *m_vertArray )[globalVertexId][1] = glyph->xyzw[nbVertCoords*vertId+1] * scale + glyphPos[1];
744  ( *m_vertArray )[globalVertexId][2] = glyph->xyzw[nbVertCoords*vertId+2] * scale + glyphPos[2];
745 
746  // ------------------------------------------------
747  // normals
748  ( *m_normals )[globalVertexId][0] = glyph->norm[3*vertId];
749  ( *m_normals )[globalVertexId][1] = glyph->norm[3*vertId+1];
750  ( *m_normals )[globalVertexId][2] = glyph->norm[3*vertId+2];
751  ( *m_normals )[globalVertexId].normalize();
752 
753  // ------------------------------------------------
754  // colors
755  const size_t nbColCoords = 4;
756  ( *m_colors )[globalVertexId][0] = glyph->rgba[nbColCoords*vertId] / 255.0;
757  ( *m_colors )[globalVertexId][1] = glyph->rgba[nbColCoords*vertId+1] / 255.0;
758  ( *m_colors )[globalVertexId][2] = glyph->rgba[nbColCoords*vertId+2] / 255.0;
759  ( *m_colors )[globalVertexId][3] = glyph->rgba[nbColCoords*vertId+3] / 255.0;
760  }
761 
762  // free memory
763  glyph = limnPolyDataNix( glyph );
764  }
765  }
766 
767  // free memory
768  localSphere = limnPolyDataNix( localSphere );
769 
770  delete[] ten;
771  delete[] res;
772  delete[] esh;
773 }
774 
775 osg::ref_ptr< osg::Geode > WMTeemGlyphs::GlyphGeneration::getGraphics()
776 {
777  osg::ref_ptr< osg::Geometry > glyphGeometry = new osg::Geometry();
778  glyphGeometry->setVertexArray( m_vertArray );
779  glyphGeometry->addPrimitiveSet( m_glyphElements );
780  glyphGeometry->setNormalArray( m_normals );
781  glyphGeometry->setNormalBinding( osg::Geometry::BIND_PER_VERTEX );
782  glyphGeometry->setColorArray( m_colors );
783  glyphGeometry->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
784 
785 
786  osg::ref_ptr< osg::Geode > glyphsGeode;
787  glyphsGeode = osg::ref_ptr< osg::Geode >( new osg::Geode );
788  glyphsGeode->setName( "teem glyphs" );
789  osg::StateSet* state = glyphsGeode->getOrCreateStateSet();
790  state->setMode( GL_BLEND, osg::StateAttribute::ON );
791 
792  glyphsGeode->addDrawable( glyphGeometry );
793  return glyphsGeode;
794 }
virtual void wait() const
Wait for the condition.
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
Basic exception handler.
Definition: WException.h:39
Class to wrap around the osg Group node and providing a thread safe add/removal mechanism.
Definition: WGEGroupNode.h:48
Class encapsulating the OSG Program class for a more convenient way of adding and modifying shader.
Definition: WGEShader.h:48
A grid that has parallelepiped cells which all have the same proportion.
A class containing a list of named items.
static WKernel * getRunningKernel()
Returns pointer to the currently running kernel.
Definition: WKernel.cpp:117
std::shared_ptr< WGraphicsEngine > getGraphicsEngine() const
Returns pointer to currently running instance of graphics engine.
Definition: WKernel.cpp:122
void addLogMessage(std::string message, std::string source="", LogLevel level=LL_DEBUG)
Appends a log message to the logging queue.
Definition: WLogger.cpp:84
static WLogger * getLogger()
Returns pointer to the currently running logger instance.
Definition: WLogger.cpp:64
This class actually generated the glyph geometry.
Definition: WMTeemGlyphs.h:174
~GlyphGeneration()
Destructor freeing the data.
size_t m_sliceId
Stores option from property.
Definition: WMTeemGlyphs.h:249
osg::ref_ptr< osg::Vec3Array > m_normals
Normals of the vertices of the glyphs.
Definition: WMTeemGlyphs.h:244
size_t m_nB
Internal variable holding the number of glyphs in the second direction of the slice.
Definition: WMTeemGlyphs.h:236
size_t m_nY
Number of voxels in y direction.
Definition: WMTeemGlyphs.h:238
osg::ref_ptr< osg::DrawElementsUInt > m_glyphElements
Indices of the vertices of the triangles of the glyphs.
Definition: WMTeemGlyphs.h:246
size_t m_nZ
Number of voxels in z direction.
Definition: WMTeemGlyphs.h:239
osg::ref_ptr< osg::Geode > getGraphics()
Get the geode of the computed glyphs.
limnPolyData * m_sphere
The geometry of the subdivided icosahedron.
Definition: WMTeemGlyphs.h:260
osg::ref_ptr< osg::Vec4Array > m_colors
Colors of the vertices of the glyphs.
Definition: WMTeemGlyphs.h:245
size_t m_nA
Internal variable holding the number of glyphs in the first direction of the slice.
Definition: WMTeemGlyphs.h:235
std::shared_ptr< WGridRegular3D > m_grid
Pointer to the grid of the treated data set.
Definition: WMTeemGlyphs.h:242
size_t m_subdivisionLevel
Store option from property.
Definition: WMTeemGlyphs.h:252
GlyphGeneration(std::shared_ptr< WDataSetSphericalHarmonics > dataSet, std::shared_ptr< WDataSetScalar > dataGFA, double thresholdGFA, const size_t &sliceId, const size_t &order, const size_t &subdivisionLevel, const size_t &modulo, const size_t &sliceType, const bool &usePolar, const float &scale, const bool &useNormalization, const bool &useRadiusNormalization, const bool &hideNegativeLobes)
Constructor setting the data pointers and the properties from the module.
void minMaxNormalization(limnPolyData *glyph, const size_t &nbVertCoords)
Makes the radii of the glyph be distributed between [0,1].
size_t m_nX
Number of voxels in x direction.
Definition: WMTeemGlyphs.h:237
size_t m_modulo
Store option from property.
Definition: WMTeemGlyphs.h:253
void operator()(size_t id, size_t numThreads, WBoolFlag &b)
Computes the glyphs.
osg::ref_ptr< osg::Vec3Array > m_vertArray
Vertices of the triangles of the glyphs.
Definition: WMTeemGlyphs.h:243
Spherical harmonics glyphs using teem (http://teem.sourceforge.net/).
Definition: WMTeemGlyphs.h:51
std::shared_ptr< WCondition > m_exceptionCondition
condition indicating if any exception was thrown.
Definition: WMTeemGlyphs.h:167
virtual const std::string getName() const
Gives back the name of this module.
WPropSelection m_sliceOrientationSelectionProp
To choose whether to x, y or z slice.
Definition: WMTeemGlyphs.h:145
WPropInt m_subdivisionLevelProp
Property holding information on the subdivision level of the spheres (resolution).
Definition: WMTeemGlyphs.h:157
void handleException(WException const &e)
Handle an exception that was thrown by the threaded function in any worker thread.
osg::ref_ptr< WGEGroupNode > m_moduleNode
Pointer to the modules group node.
Definition: WMTeemGlyphs.h:160
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
std::shared_ptr< WException > m_lastException
The last exception thrown by any worker thread.
Definition: WMTeemGlyphs.h:163
std::shared_ptr< WItemSelection > m_orders
A list of the selectable orders.
Definition: WMTeemGlyphs.h:153
std::shared_ptr< WModuleInputData< WDataSetSphericalHarmonics > > m_input
An input connector that accepts spherical harmonics datasets.
Definition: WMTeemGlyphs.h:116
virtual void connectors()
Initialize the connectors this module is using.
WPropBool m_hideNegativeLobesProp
Indicates whether to hide negativ radius lobes of glyphs.
Definition: WMTeemGlyphs.h:149
virtual const std::string getDescription() const
Gives back a description of this module.
std::shared_ptr< WModuleInputData< WDataSetScalar > > m_inputGFA
The input for the GFA.
Definition: WMTeemGlyphs.h:142
WPropSelection m_orderProp
Property holding the order of the SH to show.
Definition: WMTeemGlyphs.h:154
std::shared_ptr< WItemSelection > m_sliceOrientations
A list of the selectable slice orientations, i.e x, y and z.
Definition: WMTeemGlyphs.h:144
virtual void properties()
Initialize the properties for this module.
std::shared_ptr< WCondition > m_recompute
This condition denotes whether we need to recompute the surface.
Definition: WMTeemGlyphs.h:121
void activate()
Gets signaled from the properties object when something was changed.
WPropInt m_moduloProp
Property holding information on how many glyphs will be omited between two glyphs (modulo-1).
Definition: WMTeemGlyphs.h:156
osg::ref_ptr< WGEShader > m_shader
The shader used for the glyph surfaces.
Definition: WMTeemGlyphs.h:143
WPropDouble m_GFAThresholdProp
Property holding the threshold of GFA above which glyphs should be drawn.
Definition: WMTeemGlyphs.h:150
WMTeemGlyphs()
Nothing special with this constructor.
WPropDouble m_glyphSizeProp
Property holding the size of the displayed glyphs.
Definition: WMTeemGlyphs.h:151
WPropBool m_useRadiusNormalizationProp
Indicates whether to use radius normalization.
Definition: WMTeemGlyphs.h:148
boost::mutex m_moduleNodeLock
Lock to prevent concurrent threads trying access m_moduleNode.
Definition: WMTeemGlyphs.h:164
void renderSlice(size_t sliceId)
Renders all glyphs for the given slice.
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...
WPropBool m_usePolarPlotProp
Property indicating whether to use polar plot instead of HOME glyph.
Definition: WMTeemGlyphs.h:146
WPropBool m_useNormalizationProp
Indicates whether to use min max normalization.
Definition: WMTeemGlyphs.h:147
virtual void moduleMain()
Entry point after loading the module.
virtual ~WMTeemGlyphs()
Nothing special with this constructor.
std::shared_ptr< WDataSetSphericalHarmonics > m_dataSet
Pointer to the treated data set.
Definition: WMTeemGlyphs.h:140
WPropInt m_sliceIdProp
Property holding the slice ID.
Definition: WMTeemGlyphs.h:152
Class offering an instantiate-able data connection between modules.
Class representing a single module of OpenWalnut.
Definition: WModule.h:72
boost::filesystem::path m_localPath
The path where the module binary resides in.
Definition: WModule.h:734
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
void addConnector(std::shared_ptr< WModuleInputConnector > con)
Adds the specified connector to the list of inputs.
Definition: WModule.cpp:108
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
virtual void activate()
Callback for m_active.
Definition: WModule.cpp:220
WPropBool m_active
True whenever the module should be active.
Definition: WModule.h:723
std::shared_ptr< WProgressCombiner > m_progress
Progress indicator used as parent for all progress' of this module.
Definition: WModule.h:652
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
Creates threads that computes a function in a multithreaded fashion.
virtual void wait()
Wait for all threads to stop.
virtual void run()
Starts the threads.
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
Base class for all higher level values like tensors, vectors, matrices and so on.
Definition: WValue.h:41
size_t size() const
Get number of components the value consists of.
Definition: WValue.h:116
void addTo(WPropSelection prop)
Add the PC_SELECTONLYONE constraint to the property.