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 /*!
34   \class SoSwitch SoSwitch.h Inventor/nodes/SoSwitch.h
35   \brief The SoSwitch class is a group node which selects one child subgraph for traversal.
36 
37   \ingroup nodes
38 
39   Which child to traverse is controlled by the application programmer
40   by using the SoSwitch::whichChild field. In addition to picking out
41   a single child for traversal, it is also possible to flip all
42   children on or off for traversal.
43 
44   This node is very useful for conditionally turning on or off parts
45   of the scenegraph based on the current application processing mode,
46   visualizing mode, or whatever else the application can do.
47 
48   <b>FILE FORMAT/DEFAULTS:</b>
49   \code
50     Switch {
51         whichChild -1
52     }
53   \endcode
54 */
55 
56 // *************************************************************************
57 
58 #include <Inventor/nodes/SoSwitch.h>
59 
60 #include <Inventor/misc/SoChildList.h>
61 #include <Inventor/actions/SoGetBoundingBoxAction.h>
62 #include <Inventor/actions/SoSearchAction.h>
63 #include <Inventor/actions/SoGetMatrixAction.h>
64 #include <Inventor/actions/SoGLRenderAction.h>
65 #include <Inventor/actions/SoPickAction.h>
66 #include <Inventor/actions/SoHandleEventAction.h>
67 #include <Inventor/actions/SoAudioRenderAction.h>
68 #include <Inventor/elements/SoSoundElement.h>
69 #include <Inventor/elements/SoSwitchElement.h>
70 #include <Inventor/actions/SoCallbackAction.h>
71 #include <Inventor/actions/SoGetPrimitiveCountAction.h>
72 #include <Inventor/actions/SoWriteAction.h>
73 #include <Inventor/SoOutput.h>
74 #include <Inventor/errors/SoDebugError.h>
75 
76 #include "nodes/SoSubNodeP.h"
77 #include "coindefs.h" // COIN_OBSOLETED()
78 #include "io/SoWriterefCounter.h"
79 
80 // *************************************************************************
81 
82 /*!
83   \var SoSFInt32 SoSwitch::whichChild
84 
85   Selects which child to traverse during rendering (and some other)
86   actions.
87 
88   The value should be either \c SO_SWITCH_NONE (for traversing no
89   children, like it was an empty SoGroup node), \c SO_SWITCH_ALL (for
90   traversing like if we were an SoGroup node), \c SO_SWITCH_INHERIT
91   (for traversing the same child as the last SoSwitch node), or an
92   index value for a child.
93 
94   When using \c SO_SWITCH_INHERIT, it is important to understand how
95   the SoSwitch-node is affected by other SoSwitch-nodes. If you have
96   several switches in the scenegraph, the last switch with its \c
97   whichChild field set to anything but \c SO_SWITCH_INHERIT will be
98   used. The switch does not only inherit from its parent switch node,
99   but also from its siblings, located anywhere before it in the
100   scenegraph. An example will help clarify this:
101 
102   \code
103   #Inventor V2.1 ascii
104 
105   Separator {
106     Switch {
107       whichChild 0
108 
109       Group {
110         Switch {
111           whichChild 1
112           BaseColor { rgb 1 0 0 } # red
113           BaseColor { rgb 1 1 0 } # yellow
114         }
115         Switch {
116           whichChild -2 # SO_SWITCH_INHERIT
117           BaseColor { rgb 0 1 0 } # green
118           BaseColor { rgb 0 0 1 } # blue
119         }
120         Cube { }
121       }
122     }
123   }
124   \endcode
125 
126   This results in a blue cube on the screen. The reason being that the
127   value of the previous \c whichChild field was inherited by the final
128   switch, making it select child 1 - the blue BaseColor.
129 
130   When constructing ascii Inventor files, the integer values for the
131   keywords must be used instead of their names.  They are -1 for
132   \c SO_SWITCH_NONE, -2 for \c SO_SWITCH_INHERIT, and -3 for
133   \c SO_SWITCH_ALL.
134 
135   Default value for the field is \c SO_SWITCH_NONE.
136 */
137 
138 // *************************************************************************
139 
140 
141 #include "SoSoundElementHelper.h"
142 
143 class SoSwitchP : public SoSoundElementHelper
144 {
145 public:
SoSwitchP(SoSwitch * master)146   SoSwitchP(SoSwitch * master) : master(master) {};
147   SoSwitch *master;
148 };
149 
150 #define PRIVATE(p) ((p)->pimpl)
151 #define PUBLIC(p) ((p)->master)
152 
153 SO_NODE_SOURCE(SoSwitch);
154 
155 /*!
156   Default constructor.
157 */
SoSwitch(void)158 SoSwitch::SoSwitch(void)
159 {
160   this->commonConstructor();
161 }
162 
163 /*!
164   Constructor.
165 
166   The argument should be the approximate number of children which is
167   expected to be inserted below this node. The number need not be
168   exact, as it is only used as a hint for better memory resource
169   allocation.
170 */
SoSwitch(int numchildren)171 SoSwitch::SoSwitch(int numchildren)
172   : inherited(numchildren)
173 {
174   this->commonConstructor();
175 }
176 
177 void
commonConstructor(void)178 SoSwitch::commonConstructor(void)
179 {
180   PRIVATE(this) = new SoSwitchP(this);
181 
182   SO_NODE_INTERNAL_CONSTRUCTOR(SoSwitch);
183   SO_NODE_ADD_FIELD(whichChild, (SO_SWITCH_NONE));
184 }
185 
186 /*!
187   Destructor.
188 */
~SoSwitch()189 SoSwitch::~SoSwitch()
190 {
191   delete PRIVATE(this);
192 }
193 
194 // doc in super
195 void
initClass(void)196 SoSwitch::initClass(void)
197 {
198   SO_NODE_INTERNAL_INIT_CLASS(SoSwitch, SO_FROM_INVENTOR_1|SoNode::VRML1);
199 
200   SO_ENABLE(SoGetBoundingBoxAction, SoSwitchElement);
201   SO_ENABLE(SoSearchAction, SoSwitchElement);
202   SO_ENABLE(SoGetMatrixAction, SoSwitchElement);
203   SO_ENABLE(SoGLRenderAction, SoSwitchElement);
204   SO_ENABLE(SoPickAction, SoSwitchElement);
205 
206   SO_ENABLE(SoCallbackAction, SoSwitchElement);
207   SO_ENABLE(SoGetPrimitiveCountAction, SoSwitchElement);
208   SO_ENABLE(SoHandleEventAction, SoSwitchElement);
209 }
210 
211 // Documented in superclass.
212 void
GLRender(SoGLRenderAction * action)213 SoSwitch::GLRender(SoGLRenderAction * action)
214 {
215   this->doAction(action);
216 }
217 
218 // Documented in superclass.
219 void
getBoundingBox(SoGetBoundingBoxAction * action)220 SoSwitch::getBoundingBox(SoGetBoundingBoxAction * action)
221 {
222   SoSwitch::doAction(action);
223 }
224 
225 // Documented in superclass.
226 void
search(SoSearchAction * action)227 SoSwitch::search(SoSearchAction * action)
228 {
229   // This method must be overridden in SoSwitch nodes to take into
230   // account if the search involves every single node, or just the
231   // nodes involved in normal graph traversal.
232 
233   // Include this node in the search.
234   SoNode::search(action);
235   if (action->isFound()) return;
236 
237   if (action->isSearchingAll()) {
238     this->children->traverse(action);
239   }
240   else {
241     SoSwitch::doAction(action);
242   }
243 }
244 
245 // Documented in superclass.
246 void
doAction(SoAction * action)247 SoSwitch::doAction(SoAction * action)
248 {
249   SoState * state = action->getState();
250   int idx = this->whichChild.isIgnored() ?
251     SO_SWITCH_NONE : this->whichChild.getValue();
252   if (idx == SO_SWITCH_INHERIT) {
253     idx = SoSwitchElement::get(action->getState());
254     // when we inherit, idx might be out of range. Use modulo.
255     if (idx >= this->getNumChildren()) idx %= this->getNumChildren();
256   }
257   else {
258     SoSwitchElement::set(state, idx);
259   }
260 
261   int numindices;
262   const int * indices;
263   SoAction::PathCode pathcode = action->getPathCode(numindices, indices);
264 
265   if (idx == SO_SWITCH_ALL ||
266       (action->isOfType(SoCallbackAction::getClassTypeId()) &&
267        ((SoCallbackAction *)action)->isCallbackAll())) {
268     if (action->isOfType(SoGetBoundingBoxAction::getClassTypeId())) {
269       SoGroup::getBoundingBox((SoGetBoundingBoxAction*) action);
270     }
271     else { // not a getBoundingBoxAction
272       if (pathcode == SoAction::IN_PATH) {
273         this->children->traverseInPath(action, numindices, indices);
274       }
275       else {
276         this->children->traverse(action);
277       }
278     }
279   }
280   else {
281     if (idx >= 0) { // should only traverse one child
282       if (pathcode == SoAction::IN_PATH) {
283         // traverse only if one path matches idx
284         for (int i = 0; i < numindices; i++) {
285           if (indices[i] == idx) {
286             this->children->traverse(action, idx);
287             break;
288           }
289         }
290       }
291       else { // off, below or no path traversal
292         // be robust for index out of range
293         if (idx >= this->getNumChildren()) {
294 #if COIN_DEBUG
295           static SbBool first = TRUE;
296           if (first) {
297             first = FALSE;
298             SbString s("(warning will be printed once, but there might be more cases of this problem).");
299             int lastidx = this->getNumChildren()-1;
300             if (lastidx >= 0) {
301               SoDebugError::post("SoSwitch::doAction",
302                                  "whichChild %d out of range [0, %d] %s",
303                                  idx, lastidx, s.getString());
304             }
305             else {
306               SoDebugError::post("SoSwitch::doAction",
307                                  "whichChild %d out of range -- "
308                                  "switch node has no children! %s",
309                                  idx, s.getString());
310             }
311           }
312 #endif // COIN_DEBUG
313         }
314         else {
315           this->children->traverse(action, idx);
316         }
317       }
318     }
319     PRIVATE(this)->traverseInactiveChildren(this, action, idx, pathcode,
320                                             this->getNumChildren(),
321                                             this->getChildren());
322   }
323 }
324 
325 // Documented in superclass.
326 SbBool
affectsState(void) const327 SoSwitch::affectsState(void) const
328 {
329   // Overridden because when this function is called we don't know
330   // which "mode" the traversing action is in. If it's an
331   // SoSearchAction with isSearchingAll() set to TRUE, we should
332   // behave as if whichChild == SO_SWITCH_ALL, for instance.
333   //
334   // (To handle this exact case, SGI and TGS Inventor seems to use a
335   // global static flag SoSearchAction::duringSearchAll. We find this
336   // to be an utterly crap idea, though.)
337   //
338   // So to be safe, we _always_ behave as if whichChild is set to
339   // traverse all children. The worst that can happen is that we get a
340   // "false positive", ie TRUE when it should be FALSE. That means the
341   // action needs to traverse one level further down onto one of our
342   // children -- which will just take a miniscule amount of additional
343   // processing time.
344 
345   int n = this->getNumChildren();
346   for (int i=0; i < n; i++) {
347     if (this->getChild(i)->affectsState()) { return TRUE; }
348   }
349   return FALSE;
350 }
351 
352 // Documented in superclass.
353 void
callback(SoCallbackAction * action)354 SoSwitch::callback(SoCallbackAction *action)
355 {
356   SoSwitch::doAction(action);
357 }
358 
359 // Documented in superclass.
360 void
audioRender(SoAudioRenderAction * action)361 SoSwitch::audioRender(SoAudioRenderAction * action)
362 {
363   PRIVATE(this)->preAudioRender(this, action);
364 
365   SoSwitch::doAction((SoAction*)action);
366 
367   PRIVATE(this)->postAudioRender(this, action);
368 }
369 
370 // Documented in superclass.
371 void
pick(SoPickAction * action)372 SoSwitch::pick(SoPickAction *action)
373 {
374   SoSwitch::doAction((SoAction*)action);
375 }
376 
377 // Documented in superclass.
378 void
handleEvent(SoHandleEventAction * action)379 SoSwitch::handleEvent(SoHandleEventAction *action)
380 {
381   SoSwitch::doAction(action);
382 }
383 
384 // Documented in superclass.
385 void
getMatrix(SoGetMatrixAction * action)386 SoSwitch::getMatrix(SoGetMatrixAction *action)
387 {
388   switch (action->getCurPathCode()) {
389   case SoAction::OFF_PATH:
390   case SoAction::IN_PATH:
391     SoSwitch::doAction((SoAction*)action);
392     break;
393   default:
394     break;
395   }
396 }
397 
398 // Documented in superclass.
399 void
write(SoWriteAction * action)400 SoSwitch::write(SoWriteAction * action)
401 {
402   // to keep child numbering, always write out all children for a
403   // switch
404 
405   SoOutput * out = action->getOutput();
406   if (out->getStage() == SoOutput::COUNT_REFS) {
407     this->addWriteReference(out, FALSE);
408     // No need to traverse children as SoGroup::addWriteReference() already
409     // handles write references of children.
410   }
411   else if (out->getStage() == SoOutput::WRITE) {
412     if (this->writeHeader(out, TRUE, FALSE)) return;
413     this->getFieldData()->write(out, this);
414     if (out->isBinary()) out->write(this->getNumChildren());
415     this->getChildren()->traverse(action);
416     this->writeFooter(out);
417   }
418   else assert(0 && "unknown stage");
419 }
420 
421 // Documented in superclass.
422 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)423 SoSwitch::getPrimitiveCount(SoGetPrimitiveCountAction *action)
424 {
425   SoSwitch::doAction((SoAction*)action);
426 }
427 
428 /*!
429   This function was part of the original SGI Inventor API, but it is
430   not supported in Coin, as it looks like it should probably have been
431   private in Inventor.
432 */
433 void
traverseChildren(SoAction * COIN_UNUSED_ARG (action))434 SoSwitch::traverseChildren(SoAction * COIN_UNUSED_ARG(action))
435 {
436   COIN_OBSOLETED();
437 }
438 
439 // Doc from superclass.
440 void
notify(SoNotList * nl)441 SoSwitch::notify(SoNotList * nl)
442 {
443   SoNotRec * rec = nl->getLastRec();
444 
445   // If whichChild is set to a specific child and we get a
446   // notification from some other child, ignore it to avoid redraws
447   // and invalidated caches because of inactive parts of the scene
448   // graph. This fixes cases like these:
449 
450   // DEF Switch {
451   //   whichChild 1
452   //   Separator {
453   //     Rotor {
454   //       on TRUE
455   //       speed 1
456   //     }
457   //   }
458   //   Separator {
459   //     Cube { }
460   //   }
461   // }
462 
463   SbBool ignoreit = FALSE;
464 
465   // if getBase() == this, the notification is from a field under this
466   // node, and should _not_ be ignored
467   if (rec && (rec->getBase() != (SoBase*) this)) {
468     int which = this->whichChild.getValue();
469     if (which == -1) ignoreit = TRUE; // also ignore if no children are traversed
470     else if (which >= 0) {
471       int fromchild = this->findChild((SoNode*) rec->getBase());
472       if (fromchild >= 0 && fromchild != which) ignoreit = TRUE;
473     }
474   }
475 
476   if (!ignoreit) {
477     inherited::notify(nl);
478     PRIVATE(this)->notifyCalled();
479   }
480 }
481 
482 #undef PRIVATE
483 #undef PUBLIC
484