1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif // HAVE_CONFIG_H
36 
37 #ifdef HAVE_NODEKITS
38 
39 #include <Inventor/annex/Profiler/nodekits/SoNodeVisualize.h>
40 
41 #include <map>
42 #include <string>
43 
44 #include <boost/scoped_ptr.hpp>
45 
46 #include <Inventor/SoDB.h>
47 #include <Inventor/SoInput.h>
48 #include <Inventor/SoPickedPoint.h>
49 #include <Inventor/actions/SoHandleEventAction.h>
50 #include <Inventor/annex/Profiler/nodes/SoProfilerStats.h>
51 #include <Inventor/events/SoMouseButtonEvent.h>
52 #include <Inventor/misc/SoChildList.h>
53 #include <Inventor/nodes/SoIndexedLineSet.h>
54 #include <Inventor/nodes/SoMaterial.h>
55 #include <Inventor/nodes/SoRotation.h>
56 #include <Inventor/nodes/SoSeparator.h>
57 #include <Inventor/nodes/SoShape.h>
58 #include <Inventor/nodes/SoSphere.h>
59 #include <Inventor/nodes/SoSwitch.h>
60 #include <Inventor/nodes/SoTexture2.h>
61 #include <Inventor/nodes/SoTexture2Transform.h>
62 #include <Inventor/nodes/SoTranslation.h>
63 #include <Inventor/nodes/SoVertexProperty.h>
64 
65 struct TextureImageData {
66   int width;
67   int height;
68   int numcomps;
69   const unsigned char * pixels;
70 };
71 
72 static const unsigned char material_1_data[] = {
73   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
74 };
75 
76 // texture maps with node type symbols
77 #include "inventormaps.icc"
78 
79 static TextureImageData NodeKit = {
80   125, 200, 1, NodeKit_data
81 };
82 
83 static TextureImageData Switch = {
84   125, 200, 1, Switch_data
85 };
86 
87 static TextureImageData geometry_root = {
88   125, 200, 1, geometry_root_data
89 };
90 
91 static TextureImageData material_1 = {
92   1, 8, 1, material_1_data
93 };
94 
95 static TextureImageData shape = {
96   125, 200, 1, shape_data
97 };
98 
99 static TextureImageData top_transform = {
100   125, 200, 1, top_transform_data
101 };
102 
103 // *************************************************************************
104 
105 // for debugging
106 
107 #define PRINT_FUNCTION()
108 
109 // *************************************************************************
110 
111 namespace {
112   class TextureDict {
113   public:
TextureDict()114     TextureDict()
115     {
116     }
117 
~TextureDict()118     ~TextureDict()
119     {
120       //This code should only be called from a static context, so we
121       //cannot do any unreffing of Coin-nodes
122 
123       assert(this->nodemap.size()==0);
124 #if 0
125       clear();
126 #endif
127     }
128 
createTexture(const TextureImageData * data)129     SoTexture2 * createTexture(const TextureImageData * data)
130     {
131       SoTexture2 * texnode = new SoTexture2;
132       texnode->image.setValue(SbVec2s(data->width, data->height), data->numcomps, data->pixels);
133       return texnode;
134     }
135 
clear()136     void clear() {
137       std::map<const TextureImageData *, SoTexture2 *>::iterator it, end;
138       for (it = this->nodemap.begin(), end = this->nodemap.end();
139            it != end; ++it) {
140         it->second->unref();
141         it->second = NULL;
142       }
143       this->nodemap.clear();
144     }
145 
operator [](const TextureImageData & data)146     SoTexture2 * operator[](const TextureImageData & data)
147     {
148       std::map<const TextureImageData *, SoTexture2 *>::iterator e;
149       e = this->nodemap.find(&data);
150       if (e == this->nodemap.end()) {
151         // not found, create
152         SoTexture2 * node = TextureDict::createTexture(&data);
153         node->ref();
154         this->nodemap[&data] = node;
155         return node;
156       } else {
157         return e->second;
158       }
159     }
160 
161 
162   private:
163     std::map<const TextureImageData *, SoTexture2 *> nodemap;
164   };
165 }
166 
167 // *************************************************************************
168 
169 class SoNodeVisualizeP {
170 public:
SoNodeVisualizeP(void)171   SoNodeVisualizeP(void) {
172   }
173 
174   static TextureDict textures;
175 };
176 
177 TextureDict SoNodeVisualizeP::textures;
178 
179 // *************************************************************************
180 
181 SO_KIT_SOURCE(SoNodeVisualize);
182 
183 // *************************************************************************
184 
185 /*!
186    Constructor
187 
188    Note, this constructor is not public, as it sets up the class in an
189    internally inconsistent way, this also ensures that we do not try
190    to analyze ourself, which may do strange things to our state.
191 
192    //FIXME: Should perhaps make a public constructor which takes the
193    //same parameters as visualize tree
194 */
SoNodeVisualize(void)195 SoNodeVisualize::SoNodeVisualize(void)
196 {
197   SO_KIT_CONSTRUCTOR(SoNodeVisualize);
198 
199   SO_KIT_ADD_CATALOG_ENTRY(topSeparator, SoSeparator, FALSE, this, "", FALSE);
200   SO_KIT_ADD_CATALOG_ENTRY(childrenVisible, SoSwitch, FALSE, topSeparator, shape, FALSE);
201 
202   //NOTE: This setup also rotates our own geometry, which wasn't by
203   //design, but I'm not totally unhappy with the behaviour.
204   SO_KIT_ADD_CATALOG_ENTRY(rotSwitch, SoSwitch, FALSE, childrenVisible, lineSep, FALSE);
205   SO_KIT_ADD_CATALOG_ENTRY(rotation, SoRotation, FALSE, rotSwitch, "", FALSE);
206   SO_KIT_ADD_CATALOG_ENTRY(lineSep, SoSeparator, FALSE, childrenVisible, childGeometry, FALSE);
207   SO_KIT_ADD_CATALOG_ENTRY(lines, SoIndexedLineSet, FALSE, lineSep, "", FALSE);
208   SO_KIT_ADD_CATALOG_ENTRY(childGeometry, SoSeparator, FALSE, childrenVisible, color, FALSE);
209   SO_KIT_ADD_CATALOG_ENTRY(color, SoMaterial, FALSE, childrenVisible, "", FALSE);
210 
211   SO_KIT_ADD_CATALOG_ENTRY(texture, SoTexture2, TRUE, topSeparator, shape, FALSE);
212   SO_KIT_ADD_CATALOG_ENTRY(textureTransform, SoTexture2Transform, FALSE, topSeparator, shape, FALSE);
213   SO_KIT_ADD_CATALOG_ENTRY(shape, SoSphere, FALSE, topSeparator, "", FALSE);
214 
215   SO_KIT_INIT_INSTANCE();
216 
217   //Setup some default stuff
218   SoTexture2Transform *tt=static_cast<SoTexture2Transform*>(this->getAnyPart("textureTransform",FALSE));
219   tt->scaleFactor.setValue(2, 1);
220   tt->center.setValue(.5,0);
221 
222   SoSwitch* sw=static_cast<SoSwitch*>(this->getAnyPart("childrenVisible",TRUE));
223   sw->whichChild=SO_SWITCH_ALL;
224 
225   SoMaterial *color=static_cast<SoMaterial*>(this->getAnyPart("color",TRUE));
226   color->diffuseColor=SbVec3f(1,1,0);
227 
228   sw=static_cast<SoSwitch*>(this->getAnyPart("rotSwitch",TRUE));
229   sw->whichChild=SO_SWITCH_NONE;
230 
231   /*
232     ISSUE: Currently we are making all rotations in one direction,
233     meaning that the concept of right sibling will have changed by 180
234     degrees if we descend two levels with alternate on.
235    */
236   //FIXME: We should reuse this node, instead of creating it for every single node.
237   SoRotation *rot=static_cast<SoRotation*>(this->getAnyPart("rotation",TRUE));
238   rot->rotation.setValue(SbVec3f(0, 1, 0), 1.5707963f);
239 
240   this->parent=NULL;
241   this->dirty=true;
242   this->node=NULL;
243 
244 }
245 
246 SoNodeVisualize*
visualize(SoNode * node)247 SoNodeVisualize::visualize(SoNode * node) {
248   this->node=node;
249   if (node!=NULL) {
250     SoType type = node->getTypeId();
251     //FIXME: Exhange all the named textures, with images
252     //FIXME: Make the images better fitting for its shape
253     //FIXME: Check if cubes are better options for some of the nodetypes
254     //FIXME: Make smaller textures
255     if(type.isDerivedFrom(SoMaterial::getClassTypeId())) {
256       this->setAnyPart("texture", SoNodeVisualizeP::textures[material_1]);
257       SoTexture2Transform * tt =
258        static_cast<SoTexture2Transform*>(this->getAnyPart("textureTransform",FALSE));
259       tt->scaleFactor.setValue(1,5);
260 
261     } else if (type.isDerivedFrom(SoShape::getClassTypeId())) {
262       // FIXME 20071107 rolvs: Currently, this->shape is a SoMFField, and
263       // ::shape is a const * char. This will be 'resolved' when we change
264       // ::shape to Shape. And hem... maybe add some prefix to the texture
265       // names in the staticglobalfile.
266       this->setAnyPart("texture", SoNodeVisualizeP::textures[::shape]);
267 
268     } else if (type.isDerivedFrom(SoSeparator::getClassTypeId())) {
269       this->setAnyPart("texture", SoNodeVisualizeP::textures[geometry_root]);
270 
271     } else if (type.isDerivedFrom(SoBaseKit::getClassTypeId())) {
272       this->setAnyPart("texture", SoNodeVisualizeP::textures[NodeKit]);
273 
274     } else if (type.isDerivedFrom(SoSwitch::getClassTypeId())) {
275       this->setAnyPart("texture", SoNodeVisualizeP::textures[Switch]);
276 
277     } else if (type.isDerivedFrom(SoTransformation::getClassTypeId())) {
278       this->setAnyPart("texture", SoNodeVisualizeP::textures[top_transform]);
279 
280     } else if (type.isDerivedFrom(SoTexture::getClassTypeId())){
281       this->setAnyPart("texture", node);
282     }
283   }
284   return this;
285 }
286 
287 void
setupChildCatalog(SoNode * node,int depth)288 SoNodeVisualize::setupChildCatalog(SoNode * node,int depth) {
289   SoSeparator * sep=static_cast<SoSeparator*>(this->getAnyPart("childGeometry",TRUE));
290   SoNodeList * children=node->getChildren();
291   unsigned int numNodeChildren=this->nodeNumChildren();
292 
293   --depth;
294 
295   unsigned int i;
296   for (i=0;i<numNodeChildren;++i) {
297     sep->addChild(new SoTranslation());
298     SoNodeVisualize * nv=visualizeTree((*children)[i],depth);
299     nv->parent=this;
300     sep->addChild(nv);
301   }
302 
303   //Construct the indexes of the lineset
304   SoIndexedLineSet * lineS=static_cast<SoIndexedLineSet*>(this->getAnyPart("lines",TRUE));
305   lineS->coordIndex.setNum((numNodeChildren+1)*3);
306 
307   int32_t * ind = lineS->coordIndex.startEditing();
308   for (i=1;i<=numNodeChildren;++i) {
309     *ind++=0;
310     *ind++=i;
311     *ind++=-1;
312   }
313   lineS->coordIndex.finishEditing();
314 
315   //The lineSet should not have any vertex property yet
316   assert(lineS->vertexProperty.getValue()==NULL);
317 
318   //Lets assign a vertex property with the proper length
319   SoVertexProperty *vp=new SoVertexProperty();
320   vp->vertex.setNum(numNodeChildren+1);
321   lineS->vertexProperty=vp;
322 
323 }
324 
325 SoNodeVisualize *
visualizeTree(SoNode * node,int depth)326 SoNodeVisualize::visualizeTree(SoNode * node,int depth) {
327   SoNodeVisualize * nv=new SoNodeVisualize();
328   nv->visualizeSubTree(node,depth);
329   nv->recalculate();
330   return nv;
331 }
332 
333 void
recalculate()334 SoNodeVisualize::recalculate() {
335   //At this point we must shall have a node
336   assert(node);
337 
338   //FIXME: Perhaps not the right place to set dirty
339   this->dirty = true;
340   SoNodeList * geometryChildren = getChildGeometry();
341 
342   //No need to do anything if we have no children
343   if (geometryChildren->getLength()) {
344 
345     //Before we do any calculation on our own, we recalculate all our children
346     SoNodeList * nodeChildren=node->getChildren();
347     int i;
348     for (i = 0; i < nodeChildren->getLength(); ++i)
349       static_cast<SoNodeVisualize *>((*geometryChildren)[2 * i + 1])->recalculate();
350 
351     //The child geometry should contain exactly one translation and
352     //one SoNodeVisualize node per nodeChildren
353     int numNodeChildren = this->nodeNumChildren();
354     assert((numNodeChildren * 2) == geometryChildren->getLength());
355 
356     //First we set up the Y translation, to the next level and the right X translation
357     //If we are alternating, we get rotated values for our own
358     //boundingbox, we need to pick the Z component for the X
359     //translation
360     SoTranslation * trans = static_cast<SoTranslation *>((*geometryChildren)[0]);
361     trans->translation.setValue(1.5f * this->getWidth()[this->isAlternating()?1:0],
362                                 -3.0f, 0.0f);
363 
364     for (i = 0; i < nodeChildren->getLength(); ++i) {
365       //We translate this node left for half the boundingbox's X component
366       SoNodeVisualize * nv = static_cast<SoNodeVisualize*>((*geometryChildren)[2*i+1]);
367       trans->translation =
368         trans->translation.getValue() + SbVec3f(-1.5f * nv->getWidth()[0],
369                                                 0.0f,
370                                                 0.0f);
371       //If this is not the last node
372       if (i < (numNodeChildren - 1)) {
373         //we translate the subsequent node for half of this ones X component
374         trans = static_cast<SoTranslation *>((*geometryChildren)[2 * i + 2]);
375         trans->translation = SbVec3f(-1.5f * nv->getWidth()[0], 0.0f, 0.0f);
376       }
377     }
378 
379     SoIndexedLineSet * lineS =
380       static_cast<SoIndexedLineSet*>(this->getAnyPart("lines",FALSE));
381 
382     assert(lineS->vertexProperty.getValue()!=NULL &&
383            "No vertexProperty available from setupChildCatalog");
384 
385     //Sets up the vertices of the lines between the nodes
386     SoVertexProperty * vp=
387       static_cast<SoVertexProperty*>(lineS->vertexProperty.getValue());
388 
389     SbVec3f * dst = vp->vertex.startEditing();
390     *dst = SbVec3f(0,0,0);
391     for (i = 0; i < numNodeChildren; ++i)
392       dst[i + 1] = dst[i] +
393         static_cast<SoTranslation *>((*geometryChildren)[2 * i])->translation.getValue();
394     vp->vertex.finishEditing();
395   }
396 }
397 
398 /*!
399    Visualizes a subtree, stops at depth 1
400 
401    If no argument given, visualizes everything
402 
403    WARNING: Allthough the default is to visualize everything, it is
404    recommended to set a limit on how deep you want to traverse. If you
405    go to deep, the memory requirements, and recalculation may become
406    so heavy it grinds to a halt. Additionally it is difficult to see,
407    when there is too much information on the screen at once.
408 */
409 void
visualizeSubTree(SoNode * node,int depth)410 SoNodeVisualize::visualizeSubTree(SoNode * node,int depth) {
411   if (!this->node)
412     this->visualize(node);
413   //We have reached the max visualization depth, don't expand anymore
414   if (depth == 1) {
415     //Always display a node without children as expanded
416     if (this->nodeNumChildren())
417       static_cast<SoSwitch*>(this->getAnyPart("childrenVisible",TRUE))->whichChild =
418         SO_SWITCH_NONE;
419     return;
420   }
421 
422   //If we have children set up their catalog as well
423   if (this->nodeNumChildren()) {
424     SoNodeList *children = this->getChildGeometry();
425 
426     //Only set up the catalog if we haven't done it before
427     if((this->nodeNumChildren() * 2) != (unsigned int)children->getLength())
428       this->setupChildCatalog(node, depth);
429 
430     assert((this->nodeNumChildren() * 2) == (unsigned int)children->getLength());
431   }
432 }
433 
434 void
traverse(SoProfilerStats * stats)435 SoNodeVisualize::traverse(SoProfilerStats * stats)
436 {
437   // FIXME 20071109 rolvs: This is just crammed in here, during the last hours of the hackathon.
438   // And I'm quite sure that a better way to do this alltogeher might be
439   // conjured. its also more than possible that I duplicate some functionality somewhere here...
440   // And phew. Cut and paste programming. Darn.
441 
442   // check if this->node is a cache-separator. In that case, find it's material and set it.
443   //this->getChildrenGeometry()
444   assert(stats && "Stats not set.");
445   SoMaterial * material = static_cast<SoMaterial *>(this->color.getValue());
446   if (material == NULL) {
447     material = new SoMaterial;
448     material->diffuseColor = SbVec3f(1.0f, 1.0f, 0.0f);
449     material->transparency = 0.0f;
450     this->setAnyPart("color", material);
451   }
452 
453   assert(material->diffuseColor.getNum() == 1);
454   assert(material->transparency.getNum() == 1);
455   SbColor color = material->diffuseColor[0];
456   float transparency = material->transparency[0];
457 
458   float green = 0.0f;
459   if ((parent != NULL) && (this->node != NULL)) {
460     const unsigned long CRITICAL = 10;
461 
462     /*SoNode * parent = */this->parent->node;
463     // FIXME: larsa
464     unsigned long msec = 0; // stats->getTotalProfilingTime(parent, this->node).getMsecValue();
465     msec = SbMax<unsigned long>(CRITICAL, msec);
466     green = 1.0f - (float)msec / (float)CRITICAL;
467   }
468 
469   if(this->node->isOfType(SoSeparator::getClassTypeId()) &&
470      0 // FIXME: larsa
471      //stats->hasGLCache((SoSeparator *)this->node)
472      ) {
473     // FIXME All children are cached. Make them inherit material
474     color = SbVec3f(0.0f, green, 1.0f);
475     transparency = 0.0f;
476   }
477 
478   if (this->node->getTypeId().getName() == SbName("ScenarioSimulator"))
479     color = SbVec3f(1.0f, green, 0.0f);
480 
481   /*  else if(stats->separatorsCullRoots.findNode(this->node)!=-1){
482     // FIXME All children are culled. Make them inherit material
483     printf("Culled: %p / %s\n", this->node, this->node->getName().getString());
484 
485     color->transparency = 0.7;
486   }
487   */
488 
489   if (material->diffuseColor[0] != color)
490     material->diffuseColor = color;
491 
492   if (material->transparency[0] != transparency)
493     material->transparency = transparency;
494 
495   SoNodeList * geometryChildren = this->getChildGeometry();
496   int numGeometryChildren = geometryChildren->getLength();
497   if( numGeometryChildren == 0 ||
498     static_cast<SoSwitch*>(this->getAnyPart("childrenVisible",FALSE))->whichChild.getValue() == SO_SWITCH_NONE)
499     return;
500 
501   for (int i = 1; i < numGeometryChildren; i += 2) {
502     // FIXME 20071109 using index value to iterate over this geometry is kind of... errh.
503     // sucky.
504     SoNodeVisualize * viz = static_cast<SoNodeVisualize*>((*geometryChildren)[i]);
505     viz->traverse(stats);
506   }
507 }
508 /*!
509    Calculates the size of a bounding box for this subtree
510 */
511 SbVec2s
recalculateWidth()512 SoNodeVisualize::recalculateWidth() {
513   PRINT_FUNCTION();
514 
515   SoNodeList * geometryChildren=this->getChildGeometry();
516 
517   int numGeometryChildren = geometryChildren->getLength();
518   // check that numGeometryChildren is a even number
519   assert(!(numGeometryChildren & 0x1));
520 
521   SoSwitch * childrenswitch = static_cast<SoSwitch *>(this->childrenVisible.getValue());
522 
523   //If we have no childgeometry, or it is invisible, we occupy a 1x1 box
524   if ((numGeometryChildren == 0) ||
525       (childrenswitch == NULL) ||
526       (childrenswitch->whichChild.getValue() == SO_SWITCH_NONE))
527     return SbVec2s(1,1);
528 
529   //If we have children, we occupy the maximum of all our children in the z direction
530   //and the sum of our children in the x direction
531   short accum = 0;
532   short max = 0;
533 
534   for (int i = 1; i < numGeometryChildren; i += 2) {
535     SbVec2s tmp = static_cast<SoNodeVisualize*>((*geometryChildren)[i])->getWidth();
536     accum += tmp[0];
537     if (tmp[1] > max)
538       max = tmp[1];
539   }
540 
541   return SbVec2s(accum, max);
542 
543   //If we are alternating, the box should be rotated 90 degrees, before returned
544                                                       //  return (!this->isAlternating()?
545                                                       //          SbVec2s(accum, max):
546                                                       //          SbVec2s(max, accum));
547 }
548 
549 //This should usually always be turned on
550 //turning it off should only be useful for debugging
551 //#define CACHING
552 
553 /*!
554    Returns the size of a bounding box for this subtree
555 */
556 SbVec2s
getWidth()557 SoNodeVisualize::getWidth() {
558   PRINT_FUNCTION();
559 #ifdef CACHING
560   if (this->dirty) {
561 #endif
562     this->width=recalculateWidth();
563 #ifdef CACHING
564     this->dirty=false;
565   }
566 #endif
567   return width;
568 }
569 
570 /*!
571    Destructor
572 */
~SoNodeVisualize()573 SoNodeVisualize::~SoNodeVisualize()
574 {
575 }
576 
577 /*!
578    Standard intialization for node catalog
579 */
580 void
initClass(void)581 SoNodeVisualize::initClass(void)
582 {
583   if(getClassTypeId() == SoType::badType()) {
584     SO_KIT_INIT_CLASS(SoNodeVisualize, SoBaseKit, "BaseKit");
585 
586     cc_coin_atexit(cleanClass);
587   }
588 }
589 
590 /*!
591    Static cleanup
592 */
593 void
cleanClass(void)594 SoNodeVisualize::cleanClass(void)
595 {
596   SoNodeVisualizeP::textures.clear();
597 }
598 
599 /*!
600    Changes the state of a node with children
601 */
602 bool
clicked()603 SoNodeVisualize::clicked() {
604   PRINT_FUNCTION();
605   SoSwitch* sw=static_cast<SoSwitch*>(this->getAnyPart("childrenVisible",TRUE));
606   assert(sw->whichChild.getValue()==SO_SWITCH_NONE||sw->whichChild.getValue()==SO_SWITCH_ALL);
607 
608   //If we have no children, we always want the open state
609   //Don't allow any changes
610   if (!this->nodeHasChildren())
611     return true;
612 
613   //First if we are at on already
614   if (sw->whichChild.getValue()==SO_SWITCH_ALL) {
615     sw->whichChild=SO_SWITCH_NONE;
616     //We only need to recalculate if we have a node above us
617     if (parent)
618       getSoNodeVisualizeRoot()->recalculate();
619     return false;
620   }
621 
622   //If we are off now
623   sw->whichChild=SO_SWITCH_ALL;
624   SoNodeList *children=this->getChildGeometry();
625 
626   //Lazy instantiation
627   if (children && this->nodeNumChildren()!=(unsigned int)children->getLength()) {
628     this->visualizeSubTree(node, 2);
629     this->setAlternate(this->isAlternating());
630   }
631 
632   getSoNodeVisualizeRoot()->recalculate();
633 
634   return true;
635 }
636 
637 /*!
638    Checks for intersection with the shape part of the node
639 */
640 void
handleEvent(SoHandleEventAction * action)641 SoNodeVisualize::handleEvent(SoHandleEventAction * action)
642 {
643   inherited::handleEvent(action);
644 
645   if (!SO_MOUSE_PRESS_EVENT(action->getEvent(), BUTTON1)) { return; }
646 
647   const SoPickedPoint * pp = action->getPickedPoint();
648   if (!pp) { return; }
649 
650   SoFullPath * path = static_cast<SoFullPath*>(pp->getPath());
651   SoShape* shape = static_cast<SoShape*>(this->getAnyPart("shape",TRUE));
652 
653   //REVIEW: BFG - Not sure what I'm doing here, the getDetail is
654   //from an example source
655   if (path->containsNode(shape) && pp->getDetail(shape) == NULL) {
656     SbVec3f point = pp->getPoint();
657     this->clicked();
658   }
659 }
660 
661 /*!
662    Returns the uppermost connected parent
663 */
664 SoNodeVisualize*
getSoNodeVisualizeRoot()665 SoNodeVisualize::getSoNodeVisualizeRoot() {
666   SoNodeVisualize *nv;
667   for (nv=this;nv->parent;nv=nv->parent) {}
668   assert(nv);
669   return nv;
670 }
671 
672 /*!
673    Turns on the alternating direction state for all children
674 */
675 void
setAlternate(bool alternate)676 SoNodeVisualize::setAlternate(bool alternate) {
677   this->internalAlternating(alternate,0);
678   this->getSoNodeVisualizeRoot()->recalculate();
679 }
680 
681 /*!
682    Traverses all nodes, and turns on the alternating state,
683 
684    May need a recalculation afterwards
685 */
686 void
internalAlternating(bool alternate,int direction)687 SoNodeVisualize::internalAlternating(bool alternate,int direction) {
688   PRINT_FUNCTION();
689 
690   static_cast<SoSwitch*>(this->getAnyPart("rotSwitch",TRUE))
691     ->whichChild=(alternate)?SO_SWITCH_ALL:SO_SWITCH_NONE;
692 
693   SoNodeList * children=this->getChildGeometry();
694   int l;
695   if (!children ||
696       (l=children->getLength())==0 ||
697       static_cast<SoSwitch*>(this->getAnyPart("childrenVisible",FALSE))->whichChild.getValue()==SO_SWITCH_NONE)
698     return;
699 
700   ++direction;
701   for (int i=1;i<l;i+=2)
702     static_cast<SoNodeVisualize*> ((*children)[i])->internalAlternating(alternate,direction);
703 }
704 
705 /*!
706    Does the associated node have any children?
707 */
708 bool
nodeHasChildren()709 SoNodeVisualize::nodeHasChildren() {
710   return (nodeNumChildren() > 0);
711 }
712 
713 /*!
714    Number of children of the associated node
715  */
716 unsigned int
nodeNumChildren()717 SoNodeVisualize::nodeNumChildren() {
718   assert(this->node);
719   SoNodeList * children=node->getChildren();
720   if (!children) return 0;
721   return children->getLength();
722 }
723 
724 /*!
725   Resets the datastructure in anticipation of a reanalysis
726 
727   This puts the structure in the same inconsistent structure as the empty constructor
728 */
729 void
reset()730 SoNodeVisualize::reset() {
731   this->node=NULL;
732   SoSeparator * sep=static_cast<SoSeparator*>(this->getAnyPart("childGeometry",TRUE));
733   sep->removeAllChildren();
734 }
735 
736 bool
isAlternating() const737 SoNodeVisualize::isAlternating() const {
738   return static_cast<SoSwitch*>(
739            const_cast<SoNodeVisualize*>(this)
740            ->getAnyPart("rotSwitch",FALSE)
741            )->whichChild.getValue()==SO_SWITCH_ALL;
742 }
743 
744 SoNodeList *
getChildGeometry()745 SoNodeVisualize::getChildGeometry() {
746   SoNodeList * childgeometry=
747     static_cast<SoSeparator*>(this->getAnyPart("childGeometry",FALSE))
748     ->getChildren();
749   assert(childgeometry);
750   return childgeometry;
751 }
752 
753 #ifdef CACHING
754 #undef CACHING
755 #endif
756 #undef PRINT_FUNCTION
757 
758 #endif // HAVE_NODEKITS
759