/**************************************************************************\
*
* This file is part of the Coin 3D visualization library.
* Copyright (C) 1998-2007 by Systems in Motion. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* ("GPL") version 2 as published by the Free Software Foundation.
* See the file LICENSE.GPL at the root directory of this source
* distribution for additional information about the GNU GPL.
*
* For using Coin with software that can not be combined with the GNU
* GPL, and for taking advantage of the additional benefits of our
* support services, please contact Systems in Motion about acquiring
* a Coin Professional Edition License.
*
* See http://www.coin3d.org/ for more information.
*
* Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY.
* http://www.sim.no/ sales@sim.no coin-support@coin3d.org
*
\**************************************************************************/
/*!
\class SoArray SoArray.h Inventor/nodes/SoArray.h
\brief The SoArray class is a group node for setting up regular arrays of subgraphs.
\ingroup nodes
SoArray presents a convenient way of duplicating a node (typically a
shape node) or a complete subgraph in 1 to 3 dimensions.
The child node or subgraph can only be translated by regular offsets
for all dimensions.
For more flexible functionality for duplication of geometry, see the
SoMultipleCopy group node, which can do general transformations
(including rotation and scaling) for its child.
FILE FORMAT/DEFAULTS:
\code
Array {
origin FIRST
numElements1 1
numElements2 1
numElements3 1
separation1 1 0 0
separation2 0 1 0
separation3 0 0 1
}
\endcode
\sa SoMultipleCopy
*/
#include
#include
#include // SO_SWITCH_ALL
#include
#include
#include
#include
#include
#include
#include
/*!
\enum SoArray::Origin
The possible settings for the SoArray::origin field.
*/
/*!
\var SoSFEnum SoArray::origin
Where the origin of the array should be set, ie how the array
elements will be distributed from the local origo.
Default value is SoArray::FIRST.
*/
/*!
\var SoSFShort SoArray::numElements1
Number of duplicates for each X axis row. Default 1.
*/
/*!
\var SoSFShort SoArray::numElements2
Number of duplicates for each Y axis row. Default 1.
*/
/*!
\var SoSFShort SoArray::numElements3
Number of duplicates for each Z axis row. Default 1.
*/
/*!
\var SoSFVec3f SoArray::separation1
Distance in current units between the center point of each element
along the X axis. Default [1.0, 0.0, 0.0].
*/
/*!
\var SoSFVec3f SoArray::separation2
Distance in current units between the center point of each element
along the Y axis. Default [0.0, 1.0, 0.0].
*/
/*!
\var SoSFVec3f SoArray::separation3
Distance in current units between the center point of each element
along the Z axis. Default [0.0, 0.0, 1.0].
*/
// *************************************************************************
SO_NODE_SOURCE(SoArray);
/*!
Constructor.
*/
SoArray::SoArray(void)
{
SO_NODE_INTERNAL_CONSTRUCTOR(SoArray);
SO_NODE_ADD_FIELD(origin, (SoArray::FIRST));
SO_NODE_ADD_FIELD(numElements1, (1));
SO_NODE_ADD_FIELD(numElements2, (1));
SO_NODE_ADD_FIELD(numElements3, (1));
SO_NODE_ADD_FIELD(separation1, (SbVec3f(1, 0, 0)));
SO_NODE_ADD_FIELD(separation2, (SbVec3f(0, 1, 0)));
SO_NODE_ADD_FIELD(separation3, (SbVec3f(0, 0, 1)));
SO_NODE_DEFINE_ENUM_VALUE(Origin, FIRST);
SO_NODE_DEFINE_ENUM_VALUE(Origin, CENTER);
SO_NODE_DEFINE_ENUM_VALUE(Origin, LAST);
SO_NODE_SET_SF_ENUM_TYPE(origin, Origin);
}
/*!
Destructor.
*/
SoArray::~SoArray()
{
}
// Doc in superclass.
void
SoArray::initClass(void)
{
SO_NODE_INTERNAL_INIT_CLASS(SoArray, SO_FROM_INVENTOR_1);
}
// Doc in superclass.
void
SoArray::getBoundingBox(SoGetBoundingBoxAction * action)
{
#if 0 // OBSOLETED: mortene's old (buggy ?) code (removed 19990423, pederb)
// store incoming modelmatrix
SbMatrix mat = SoModelMatrixElement::get(action->getState());
// get reference to the box
SbXfBox3f & box = action->getXfBoundingBox();
// store current bbox
SbXfBox3f incomingbox = box;
// accumulation variables
SbVec3f acccenter(0.0f, 0.0f, 0.0f);
int numCenters = 0;
SbXfBox3f totalbox;
for (int i=0; i < numElements3.getValue(); i++) {
for (int j=0; j < numElements2.getValue(); j++) {
for (int k=0; k < numElements1.getValue(); k++) {
float multfactor_i = float(i);
float multfactor_j = float(j);
float multfactor_k = float(k);
switch (origin.getValue()) {
case SoArray::FIRST:
break;
case SoArray::CENTER:
multfactor_i = -float(numElements3.getValue()-1.0f)/2.0f + float(i);
multfactor_j = -float(numElements2.getValue()-1.0f)/2.0f + float(j);
multfactor_k = -float(numElements1.getValue()-1.0f)/2.0f + float(k);
break;
case SoArray::LAST:
multfactor_i = -multfactor_i;
multfactor_j = -multfactor_j;
multfactor_k = -multfactor_k;
break;
default: assert(0); break;
}
SbVec3f instance_pos =
separation3.getValue() * multfactor_i +
separation2.getValue() * multfactor_j +
separation1.getValue() * multfactor_k;
#if 0 // debug
SoDebugError::postInfo("SoArray::getBoundingBox",
"instance_pos: <%f, %f, %f>",
instance_pos[0],
instance_pos[1],
instance_pos[2]);
#endif // debug
SbMatrix mat;
mat.setTranslate(instance_pos);
action->getState()->push();
SoSwitchElement::set(action->getState(),
i * numElements2.getValue() *
numElements1.getValue() +
j * numElements1.getValue() + k);
// make current box empty to calculate bbox of this separator
box.makeEmpty();
box.setTransform(SbMatrix::identity());
// set local matrix to identity
SoBBoxModelMatrixElement::set(action->getState(), this, mat);
// traverse all children, calculate the local bbox
inherited::getBoundingBox(action);
// If center point is set, accumulate center.
if (action->isCenterSet()) {
acccenter += action->getCenter();
numCenters++;
action->resetCenter();
}
// expand box by stored bbox
if (!totalbox.isEmpty()) box.extendBy(totalbox);
totalbox = box;
action->getState()->pop();
}
}
}
// transform the local bbox by stored model matrix
if (!box.isEmpty()) box.transform(mat);
if (!incomingbox.isEmpty()) box.extendBy(incomingbox);
if (numCenters != 0)
action->setCenter(acccenter / numCenters, FALSE);
#else // "new" code, 19990423, pederb
float curri = 0.0f;
float currj = 0.0f;
float currk = 0.0f;
float inci = 1.0f;
float incj = 1.0f;
float inck = 1.0f;
// accumulation variables
SbVec3f acccenter(0.0f, 0.0f, 0.0f);
int numCenters = 0;
switch (origin.getValue()) {
case SoArray::FIRST:
break;
case SoArray::CENTER:
curri = -(numElements3.getValue()-1.0f)/2.0f;
currj = -(numElements2.getValue()-1.0f)/2.0f;
currk = -(numElements1.getValue()-1.0f)/2.0f;
break;
case SoArray::LAST:
inci = -1.0f;
incj = -1.0f;
inck = -1.0f;
break;
default: assert(0); break;
}
float initj = currj;
float initk = currk;
int N = 0;
for (int i=0; i < numElements3.getValue(); i++) {
currj = initj;
for (int j=0; j < numElements2.getValue(); j++) {
currk = initk;
for (int k=0; k < numElements1.getValue(); k++) {
SbVec3f instance_pos =
separation3.getValue() * curri +
separation2.getValue() * currj +
separation1.getValue() * currk;
action->getState()->push();
// translate bbox matrix
SoBBoxModelMatrixElement::translateBy(action->getState(),
this, instance_pos);
SoSwitchElement::set(action->getState(),N++);
inherited::getBoundingBox(action);
// If center point is set, accumulate center.
if (action->isCenterSet()) {
acccenter += action->getCenter();
numCenters++;
action->resetCenter();
}
// pop back to the original bboxmatrix
action->getState()->pop();
currk += inck;
}
currj += incj;
}
curri += inci;
}
if (numCenters != 0)
action->setCenter(acccenter / float(numCenters), FALSE);
#endif // end of new code by pederb
}
// Doc in superclass.
void
SoArray::GLRender(SoGLRenderAction * action)
{
SoArray::doAction(action);
}
// Doc in superclass.
SbBool
SoArray::affectsState(void) const
{
return FALSE; // state is pushed/popped for each traversal
}
// Doc in superclass.
void
SoArray::doAction(SoAction *action)
{
int N = 0;
for (int i=0; i < numElements3.getValue(); i++) {
for (int j=0; j < numElements2.getValue(); j++) {
for (int k=0; k < numElements1.getValue(); k++) {
float multfactor_i = float(i);
float multfactor_j = float(j);
float multfactor_k = float(k);
switch (origin.getValue()) {
case SoArray::FIRST:
break;
case SoArray::CENTER:
multfactor_i = -float(numElements3.getValue()-1.0f)/2.0f + float(i);
multfactor_j = -float(numElements2.getValue()-1.0f)/2.0f + float(j);
multfactor_k = -float(numElements1.getValue()-1.0f)/2.0f + float(k);
break;
case SoArray::LAST:
multfactor_i = -multfactor_i;
multfactor_j = -multfactor_j;
multfactor_k = -multfactor_k;
break;
default: assert(0); break;
}
SbVec3f instance_pos =
separation3.getValue() * multfactor_i +
separation2.getValue() * multfactor_j +
separation1.getValue() * multfactor_k;
action->getState()->push();
SoSwitchElement::set(action->getState(),
N++);
SoModelMatrixElement::translateBy(action->getState(), this,
instance_pos);
inherited::doAction(action);
action->getState()->pop();
}
}
}
}
// Doc in superclass.
void
SoArray::callback(SoCallbackAction *action)
{
SoArray::doAction((SoAction*)action);
}
// Doc in superclass.
void
SoArray::pick(SoPickAction *action)
{
// We came across what we think is a bug in TGS/SGI OIV when
// implementing picking for this node and testing against the
// original Inventor library. The SoPickedPoint class can return the
// object space point, normal and texture coordinates. TGS/SGI OIV
// do not consider the translation inside this node before returning
// the object space data from SoPickedPoint, since the path in
// SoPickedPoint does not say anything about on which copy the pick
// occured.
//
// We solved this simply by extending SoPickedPoint for storing both
// world space and object space data.
SoArray::doAction((SoAction*)action);
}
// Doc in superclass.
void
SoArray::handleEvent(SoHandleEventAction *action)
{
SoNode::handleEvent(action);
inherited::handleEvent(action);
}
// Doc in superclass
void
SoArray::audioRender(SoAudioRenderAction * action)
{
SoArray::doAction((SoAction*)action);
}
// Doc in superclass.
void
SoArray::getMatrix(SoGetMatrixAction *action)
{
// path does not specify which copy to traverse
inherited::getMatrix(action);
}
// Doc in superclass.
void
SoArray::search(SoSearchAction * action)
{
SoState * state = action->getState();
state->push();
// set Switch element so that subgraphs depending on this element
// will traverse all children (it's set during normal traversal in
// doAction()).
SoSwitchElement::set(action->getState(), SO_SWITCH_ALL);
// just use SoGroup::search() to traverse all children.
inherited::search(action);
state->pop();
}
// Doc in superclass.
void
SoArray::getPrimitiveCount(SoGetPrimitiveCountAction *action)
{
SoArray::doAction((SoAction*)action);
}