OpenWalnut  1.5.0dev
WNonBinDendroGeode.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 //---------------------------------------------------------------------------
26 //
27 // Project: hClustering
28 //
29 // Whole-Brain Connectivity-Based Hierarchical Parcellation Project
30 // David Moreno-Dominguez
31 // d.mor.dom@gmail.com
32 // moreno@cbs.mpg.de
33 // www.cbs.mpg.de/~moreno//
34 // This file is also part of OpenWalnut ( http://www.openwalnut.org ).
35 //
36 // hClustering is free software: you can redistribute it and/or modify
37 // it under the terms of the GNU Lesser General Public License as published by
38 // the Free Software Foundation, either version 3 of the License, or
39 // (at your option) any later version.
40 // http://creativecommons.org/licenses/by-nc/3.0
41 //
42 // hClustering is distributed in the hope that it will be useful,
43 // but WITHOUT ANY WARRANTY; without even the implied warranty of
44 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45 // GNU Lesser General Public License for more details.
46 //
47 //---------------------------------------------------------------------------
48 #include <iostream>
49 #include <vector>
50 
51 #include "core/graphicsEngine/WGEUtils.h"
52 
53 #include "WNonBinDendroGeode.h"
54 
55 /**
56  * Class implements a dendrogram as an osg geode
57  */
58 WNonBinDendroGeode::WNonBinDendroGeode( const WHtree& tree, const std::vector< WColor > &displayColors, size_t cluster,
59  float xSize, float ySize, float xOffset, float yOffset, bool useHLevel, bool triangleLeaves, float hozLine ) :
60  osg::Geode(), m_tree( tree ), m_rootCluster( cluster ), m_displayColors( displayColors ), m_xSize( xSize ), m_ySize( ySize ),
61  m_xOff( xOffset ), m_yOff( yOffset ), m_useHLevel( useHLevel ), m_triangleLeaves( triangleLeaves ),
62  m_hozLine( hozLine )
63 
64 {
65  create();
66 } // end dendrogramGeode()
67 
69 {
70 }
71 
73 {
74  m_lineColors = osg::ref_ptr< osg::Vec4Array >( new osg::Vec4Array );
75 
76  m_vertexArray = new osg::Vec3Array;
77 
78  m_lineArray = new osg::DrawElementsUInt( osg::PrimitiveSet::LINES, 0 );
79 
80  const WHnode& currentRoot( m_tree.getNode( m_rootCluster ) );
81 
82  float subTreeSize( static_cast< float > ( currentRoot.getSize() ) );
83  m_xUnit = m_xSize / subTreeSize;
84 
85  float topLevel( 0 ), topRoot( 0 );
86 
87  if( m_useHLevel )
88  {
89  topLevel = ( currentRoot.getHLevel() );
90  m_yUnit = m_ySize / topLevel;
91 
92  topRoot = topLevel + ( 20 / m_yUnit );
93  }
94  else
95  {
96  topLevel = ( currentRoot.getDistLevel() );
97 
98  if( m_rootCluster == m_tree.getRoot().getID() )
99  {
100  m_yUnit = m_ySize;
101  topRoot = 1;
102  }
103  else
104  {
105  m_yUnit = m_ySize / topLevel;
106  topRoot = topLevel + ( 20 / m_yUnit );
107  }
108  }
109 
110  // first line (trunk), first draw 2 vertices and then indicate that a line goes between them
111  m_vertexArray->push_back( osg::Vec3( subTreeSize / 2.0, topRoot, 0 ) );
112  m_vertexArray->push_back( osg::Vec3( subTreeSize / 2.0, topLevel, 0 ) );
113  m_lineArray->push_back( m_vertexArray->size() - 2 );
114  m_lineArray->push_back( m_vertexArray->size() - 1 );
115  m_lineColors->push_back( WColor( 0.3, 0.3, 0.3, 1 ) );
116  m_lineColors->push_back( WColor( 0.3, 0.3, 0.3, 1 ) );
117 
118  // Draw a red horizontal line if indicated
119  if( m_hozLine != 0 && !m_useHLevel )
120  {
121  m_vertexArray->push_back( osg::Vec3( 0, m_hozLine, 0 ) );
122  m_vertexArray->push_back( osg::Vec3( subTreeSize, m_hozLine, 0 ) );
123  m_lineArray->push_back( m_vertexArray->size() - 2 );
124  m_lineArray->push_back( m_vertexArray->size() - 1 );
125  m_lineColors->push_back( WColor( 1, 0, 0, 1 ) );
126  m_lineColors->push_back( WColor( 1, 0, 0, 1 ) );
127  }
128 
129  // draw rest of the tree
130  layout( currentRoot, 0.0f, static_cast< float > ( subTreeSize ) );
131 
132  for( size_t i = 0; i < m_vertexArray->size(); ++i )
133  {
134  ( *m_vertexArray )[i].x() = ( *m_vertexArray )[i].x() * m_xUnit + m_xOff;
135  ( *m_vertexArray )[i].y() = ( *m_vertexArray )[i].y() * m_yUnit + m_yOff;
136  }
137 
138  osg::ref_ptr< osg::Geometry > geometry = osg::ref_ptr< osg::Geometry >( new osg::Geometry() );
139 
140  geometry->setVertexArray( m_vertexArray );
141 
142  geometry->addPrimitiveSet( m_lineArray );
143 
144  geometry->setColorArray( m_lineColors );
145  geometry->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
146 
147  osg::StateSet* state = geometry->getOrCreateStateSet();
148  state->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
149 
150  addDrawable( geometry );
151 } // end create()
152 
153 void WNonBinDendroGeode::layout( const WHnode& cluster, const float leftLimit, const float rightLimit )
154 {
155  // get height of current branching depending
156  float height( 0 );
157  if( m_useHLevel )
158  {
159  height = cluster.getHLevel();
160  }
161  else
162  {
163  height = cluster.getDistLevel();
164  }
165 
166  float leftStart( leftLimit );
167 
168  // order children nodes by size
169  std::vector< nodeID_t > kids( cluster.getChildren() );
170  m_tree.sortBySize( &kids );
171  bool horizontalNotDone( true );
172  for( unsigned int i = 0; i < kids.size(); ++i )
173  {
174  const WHnode& thisKid( m_tree.getNode( kids[i] ) );
175 
176  // if its a leaf and the triangle leaves option is activated
177  if( thisKid.isLeaf() && m_triangleLeaves && !m_useHLevel )
178  {
179  m_vertexArray->push_back( osg::Vec3( ( leftLimit + rightLimit ) / 2.0, height, 0 ) );
180  m_vertexArray->push_back( osg::Vec3( leftStart + 0.5, 0, 0 ) );
181  m_lineArray->push_back( m_vertexArray->size() - 2 );
182  m_lineArray->push_back( m_vertexArray->size() - 1 );
183  m_lineColors->push_back( m_displayColors[cluster.getID()] );
184  m_lineColors->push_back( m_displayColors[cluster.getID()] );
185 
186  ++leftStart;
187  continue;
188  }
189 
190  // draw the horizontal line
191  if( horizontalNotDone )
192  {
193  float leftHozPoint( leftStart + ( thisKid.getSize() * 0.5 ) );
194  if( leftHozPoint > ( ( leftLimit + rightLimit ) / 2.0 ) )
195  leftHozPoint = ( leftLimit + rightLimit ) / 2.0;
196  const WHnode& lastKid( m_tree.getNode( kids.back() ) );
197 
198  m_vertexArray->push_back( osg::Vec3( leftHozPoint, height, 0 ) );
199  m_vertexArray->push_back( osg::Vec3( rightLimit - ( lastKid.getSize() * 0.5 ), height, 0 ) );
200  m_lineArray->push_back( m_vertexArray->size() - 2 );
201  m_lineArray->push_back( m_vertexArray->size() - 1 );
202  m_lineColors->push_back( m_displayColors[cluster.getID()] );
203  m_lineColors->push_back( m_displayColors[cluster.getID()] );
204 
205  horizontalNotDone = false;
206  }
207 
208  // any other case
209 
210  float kidHeight( 0 );
211  if( m_useHLevel )
212  {
213  kidHeight = thisKid.getHLevel();
214  }
215  else
216  {
217  kidHeight = thisKid.getDistLevel();
218  }
219  m_vertexArray->push_back( osg::Vec3( leftStart + ( thisKid.getSize() * 0.5 ), height, 0 ) );
220  m_vertexArray->push_back( osg::Vec3( leftStart + ( thisKid.getSize() * 0.5 ), kidHeight, 0 ) );
221  m_lineArray->push_back( m_vertexArray->size() - 2 );
222  m_lineArray->push_back( m_vertexArray->size() - 1 );
223  m_lineColors->push_back( m_displayColors[cluster.getID()] );
224  m_lineColors->push_back( m_displayColors[cluster.getID()] );
225 
226  // if its not a leaf continue recursively
227  if( thisKid.isNode() )
228  layout( thisKid, leftStart, leftStart + ( thisKid.getSize() ) );
229 
230  // update undrawn dimensions
231  leftStart += thisKid.getSize();
232  }
233 } // end layout()
234 
235 
236 size_t WNonBinDendroGeode::getClickedCluster( int xClick, int yClick )
237 {
239  // translate real coordinates to scaled coordinates of the dendrogram
240  m_xClicked = ( static_cast< float > ( xClick ) - m_xOff ) / m_xUnit;
241  m_yClicked = ( static_cast< float > ( yClick ) - m_yOff ) / m_yUnit;
242 
243  findClickedCluster( m_rootCluster, 0.0f, static_cast< float > ( m_tree.getNode( m_rootCluster ).getSize() ) );
244 
245  return m_clickedCluster;
246 } // end getClickedCluster()
247 
248 
249 void WNonBinDendroGeode::findClickedCluster( size_t cluster, float leftLimit, float rightLimit )
250 {
251  float diffX( m_xClicked - ( ( leftLimit + rightLimit ) * 0.5 ) );
252  if( diffX < 0 )
253  diffX = -diffX;
254 
255  float diffY( 0 );
256  if( m_useHLevel )
257  {
258  diffY = m_yClicked - ( static_cast< float > ( m_tree.getNode( cluster ).getHLevel() ) );
259  }
260  else
261  {
262  diffY = m_yClicked - ( m_tree.getNode( cluster ).getDistLevel() );
263  }
264  if( diffY < 0 )
265  diffY = -diffY;
266 
267  if( diffX <= ( 5.0 / m_xUnit ) && diffY <= ( 5.0 / m_yUnit ) )
268  {
269  m_clickedCluster = cluster;
270  return;
271  }
272  else if( m_tree.getNode( cluster ).getHLevel() > 0 )
273  {
274  float leftStart( leftLimit );
275 
276  std::vector< nodeID_t > kids( m_tree.getNode( cluster ).getChildren() );
277  m_tree.sortBySize( &kids );
278 
279  for( size_t i = 0; i < kids.size(); ++i )
280  {
281  const WHnode& thisKid( m_tree.getNode( kids[i] ) );
282  if( m_xClicked < leftStart + thisKid.getSize() )
283  {
284  if( thisKid.isNode() )
285  {
286  findClickedCluster( thisKid.getID(), leftStart, leftStart + thisKid.getSize() );
287  return;
288  }
289  else
290  {
291  return;
292  }
293  }
294  else
295  {
296  leftStart += thisKid.getSize();
297  }
298  }
299  }
300  return;
301 } // end findClickedCluster()
302 
303 bool WNonBinDendroGeode::inDendrogramArea( int xClick, int yClick ) const
304 {
305  return ( xClick >= m_xOff && xClick <= m_xOff + m_xSize &&
306  yClick >= m_yOff && yClick <= m_yOff + m_ySize );
307 }
308 
this class implements a hierarchical tree node with several relevant attributes
Definition: WHnode.h:69
size_t getSize() const
returns number of elements contained by the node
Definition: WHnode.h:257
bool isLeaf() const
returns true if object is a leaf
Definition: WHnode.h:232
size_t getHLevel() const
returns maximum number of nodes between the node and a leaf element
Definition: WHnode.h:267
size_t getID() const
returns node/leaf ID
Definition: WHnode.h:242
dist_t getDistLevel() const
returns distance level at which the element was formed
Definition: WHnode.h:262
bool isNode() const
returns true if object is a node
Definition: WHnode.h:227
std::vector< nodeID_t > getChildren() const
returns a vector with the ids of the children of that node
Definition: WHnode.h:272
this class implements a hierarchical tree and provides functions for creation, partitioning,...
Definition: WHtree.h:81
const WHnode & getRoot() const
returns a const reference to the root node
Definition: WHtree.cpp:348
const WHnode & getNode(const nodeID_t &thisNode) const
returns a const pointer to the node indicated by the ID
Definition: WHtree.cpp:307
void sortBySize(std::vector< size_t > *const nodeVector) const
Reorders the nodes in the vector according to their size.
Definition: WHtree.cpp:755
float m_yUnit
helper variable for y position in the node selection recursive function
float m_xOff
x offset for dendrogram drawing in the screen
bool m_useHLevel
flag indicating if the level or the value of a cluster will be used for the height of join
const std::vector< WColor > & m_displayColors
stores a the current colors each node should be displayed, given the current selection
const WHtree m_tree
the tree to work on
bool inDendrogramArea(int xClick, int yClick) const
calculates if the given position is within the view area of the dendrogram
~WNonBinDendroGeode()
destructor
void layout(const WHnode &cluster, const float leftLimit, const float rightLimit)
recursive function that lays out the tree from top to bottom, height of the joins is determined by th...
bool m_triangleLeaves
option to join leaves with straight lines, giving a triangle appearance to the bottom level
void create()
helper function the starts the layout process from the input data in the constructor
size_t m_clickedCluster
ID of the clicked cluster.
float m_xClicked
stores the click x position for use in the node selection recursive function
size_t getClickedCluster(int xClick, int yClick)
calculate which cluster was clicked from given pixel coordinates
float m_hozLine
determines if a horizontal line will be drawn to indicate aprtition level
WNonBinDendroGeode(const WHtree &tree, const std::vector< WColor > &displayColors, size_t cluster, float xSize, float ySize, float xOffset, float yOffset, bool useHLevel=false, bool triangleLeaves=false, float hozLine=0)
constructor
float m_yOff
y offset for dendrogram drawing in the screen
float m_yClicked
stores the click y position for use in the node selection recursive function
size_t m_rootCluster
top cluster to draw the tree from
float m_xUnit
helper variable for x position in the node selection recursive function
osg::Vec3Array * m_vertexArray
vertex array
float m_ySize
y size in pixel of the final dendrogram
osg::ref_ptr< osg::Vec4Array > m_lineColors
stores a the colors of each line that should be drawn
float m_xSize
x size in pixel of the final dendrogram
void findClickedCluster(size_t cluster, float leftLimit, float rightLimit)
recurse function that follows the layout to determine the cluster from pixel coordinates
osg::DrawElementsUInt * m_lineArray
line array