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