2.3.1. Architecture

Instancing, Methods, and Fields

Even though you do not need to be a professional C++ developer, you do need to know a little about C++ to use this library. Here are some concepts you must know before going any further. Before using any MeshViz nodes, as in Open Inventor, you must create an instance of the node. Because each of the new nodes is going to be stored in a scene graph, these nodes must exist for as long as they are used in the scene graph. This means that you cannot allocate a new node the scope of which does not fit the scene graph life-time. For instance, the following function is wrong since the scope of the myRectangle node is only within the CreateRectangle function. The reference to this node will no longer exist outside this function:

CreateRectangle(SoNode *root) { 
    PoRectangle myRectangle; 
    root->addChild(&myRectangle);
    }

A good habit to adopt with MeshViz nodes, as with Open Inventor nodes, is to create a dynamic instance of the node each time you need a new one, using the C++ new operator.

You must not use the C++ delete operator because Open Inventor does the deletion when necessary (see the section called “Reference Counting” in The Inventor Mentor for a complete explanation of reference counting). A correct function to create and add a rectangle to the scene graph should look like this:

CreateRectangle(SoNode *root) { 
    PoRectangle *myRectangle = new PoRectangle; 
    root->addChild(myRectangle);
    }

Once you have created a node, you may want to initialize or set the parameters that define the node. For a rectangle, for example, you may want to specify the two points that define the diagonally opposite corners of rectangle. There are several ways to do this. Some of the parameters may be initialized within the constructor of the class:

PoRectangle *myRectangle = new PoRectangle(Sbvec2f(0.,0.), SbVec2f(10.,10.));

Using this method is not mandatory. To set any of the fields of a node, you can use any of the standard Open Inventor methods setValue , setValues , set1Value or the = operator, depending on the field type.

PoRectangle *myRectangle = new PoRectangle; 
myRectangle->p.setValue(SbVec2f(0.,0.)); 
myRectangle->q.setValue(SbVec2f(10.,10.));

Most of the GraphMaster and 3DdataMaster nodes are actually Open Inventor node kits. A node kit defines a collection of nodes that are automatically created by the kit on an as-needed basis. See Chapter 16, Node Kits in The Inventor Mentor for an explanation of node kits.

Property and Visualization Classes

Differences between the two classes

As with Open Inventor, we distinguish property classes and visualization (or shape) classes. Property classes do not draw anything but are used by visualization classes. Visualization classes realize the drawing using property classes.

Property classes

As described before, visualization classes depend on property classes for their appearance, to retrieve data, and so forth.

There are two ways to define these properties:

  • Using Pbxxxclasses . Pbxxx classes are not stored in the scene graph. Visualization classes have methods with a Pbxxx class as an argument to refer to their properties.

  • Using property nodes . These nodes are stored in the scene graph and their use is similar to Open Inventor property nodes (SoMaterial SoMaterial SoMaterial , SoDrawStyle SoDrawStyle SoDrawStyle , etc.).

Generally we recommend using property nodes (Poxxx) because their values are written out along with the rest of the scene graph when you apply a WriteAction. On the other hand, Pbxxx classes might be better for a large data set that you do not want written out with the scene graph.

Throughout this manual, you will see the preprocessor definition “USE_PB” used in the source code of tutorials to show the use of these two types of property classes.

#ifdef USE_PB
<source code using Pbxxx property classes> 
#else 
<source code using property node classes> 
#endif
Using Pb xxx

All basic Pbxxx types in GraphMaster and 3DdataMaster are derived from the PbBase PbBase PbBase class. This class handles a connection mechanism that allows visualization Poxxx objects to be informed of any change happening in one of their fields, the type of which is derived from PbBase PbBase PbBase . This mechanism allows for the automatic updating of the visualization when any of the objects derived from PbBase PbBase PbBase is changed. For instance, PbMiscTextAttr PbMiscTextAttr PbMiscTextAttr basic types allow for the definition of miscellaneous text attributes, such as the text font. PoAxis PoAxis PoAxis is the basic class for all axis nodes. The following example creates an angular axis and sets the font to be used to draw a text string associated with the axis:

PbMiscTextAttr textAttr; textAttr.setFontName("Courier");
PoAngularAxis *myAxis = new PoAngularAxis(.5,.0,2.5,1.,.0); 
myAxis->setMiscTextAttr(&textAttr);

Anytime later, the font can be changed by calling the setFontName() method and the myAxis object will be informed automatically of this change and will update its visualization. The following call will change the display of the axis to use the Helvetica font instead of the Courier font:

textAttr.setFontName("Helvetica");

This connection mechanism is activated by default. You can turn it on and off by calling the enableConnection() method inherited from the PbBase PbBase PbBase class. For instance, the following line will deactivate the default connection mechanism for the PbMiscTextAttr PbMiscTextAttr PbMiscTextAttr object; connected objects will not be informed of any changes to it:

textAttr.enableConnection(FALSE);

Updating of connected objects can be forced by calling the touch() method inherited from the PbBase PbBase PbBase class. The following code will force all objects connected to the PbMiscTextAttr PbMiscTextAttr PbMiscTextAttr object to be updated:

textAttr.touch();

The isConnectionEnabled() method can be used to find out if the connection mechanism is active for a specific instance of a PbBase PbBase PbBase or derived object. You must be careful with the use of local variables. Consider the following example:

void buildAxis(PoAngular *axis)
{
PbMiscTextAttr textAttr;
  textAttr.setFontName("Courier");
  axis = new PoAngularAxis(.5,.0,2.5,1.,.0);
  myAxis->setMiscTextAttr(&textAttr);
}

Note that MeshViz node kits are built when it is necessary, that is, when an action occurs. Furthermore a Pbxxx object informs connected Poxxx objects of any change if the connection mechanism is active. However, even if the connection mechanism is inactive, Poxxx objects are informed of the delete of a Pbxxx object which is connected to them in order to maintain the integrity of data. In the previous example, the variable textAttr will be deleted at the end of the function buildAxis, so the axis object will be informed of this delete and, when it is built, the Utopia-Regular font (default font) will be used instead of the Courier font. (When a Poxxx object is not connected to a Pbxxx object, the values of the default constructor of the Pbxxx class are used.)

In the previous example, there are three solutions to solve this problem:

  • Declare the object textAttr static to the function,

  • Declare the object textAttr global to the program,

  • Create an instance of the object textAttr by the operator new .

The copy constructor and assignment operator of any Pbxxx object do not copy the reference to Poxxx objects. This restriction prevents several Pbxxx objects (of the same class) from referencing the same Poxxx object.

The advantage of using Pbxxx classes is non-duplication of data for classes derived from PbMesh PbMesh PbMesh . The main disadvantage is that these properties are not written into .iv files and these objects must be deleted when they are no longer needed (unlike property nodes).

Using property nodes

MeshViz property node classes (available since version 3.5) must be used as Open Inventor property nodes classes (SoMaterial SoMaterial SoMaterial , SoDrawStyle SoDrawStyle SoDrawStyle , SoCoordinate3 SoCoordinate3 SoCoordinate3 , etc.). These nodes are placed in the scene graph and when they are traversed, they replace the values in a corresponding element of the traversal state with their own values.

For instance, PoMiscTextAttr PoMiscTextAttr PoMiscTextAttr basic types allow for the definition of miscellaneous text attributes, such as the text font. PoAxis PoAxis PoAxis is the basic class for all axis nodes. The following example creates an angular axis and sets the font to be used to draw a text string associated with the axis:

Example 2.1. Use of PoMiscTextAttr property node

PoMiscTextAttr *textAttr = new PoMiscTextAttr; 
textAttr->fontName = "Courier";

SoGroup *axisGroup = new SoGroup; 
PoAngularAxis *myAxis = new PoAngularAxis(0.5, 0.0, 2.5, 1, 0);

axisGroup->addChild(textAttr); axisGroup->addChild(myAxis);


Visualization classes

GraphMaster and 3DdataMaster visualization classes are implemented using the Open Inventor node kit facility, that is they all derive from the node kit base class SoBaseKit SoBaseKit SoBaseKit . This means that each new MeshViz node is a collection of Open Inventor nodes. The reference manual gives you a description of the parts (catalog parts ) for each node kit. You can access these parts to change the appearance of a node or the appearance of a part of the node kit. In this way, you can, for example, control the color of the arrow at the end of an axis or change the way you want to draw a rectangle, by drawing only the edges in red or applying a texture to it. The following example creates a transparent red-filled rectangle by changing the values of the appearance part of the PoRectangle PoRectangle PoRectangle node kit:

Example 2.2.  Use of PoRectangle visualization class

PoRectangle *myRectangle = new PoRectangle; 
myRectangle->p.setValue(0.,0.); 
myRectangle->q.setValue(10.,20.); 
myRectangle->set("appearance.drawStyle", "style FILLED"); 
myRectangle->set("appearance.material", "transparency 0.9"); 
myRectangle->set("appearance.material", "diffuseColor 1 0 0");



It is also important to know that all MeshViz node kits are built only when required, that is when any action causes them to be traversed. For instance, in the previous example, the catalog part containing the shape node used for the rectangle is updated (the part rectangle is updated; before the first rebuild this part is NULL) when a render action traverses this node (that is, just the node is drawn), and also when a bounding box action, callback action, etc. traverses it.

In the following chapters, we will use the words “visualization node,” “visualization class,” and “representation class,” as generic terms for nodes and node kits, but always keeping in mind that all GraphMaster and 3DdataMaster extensions are, in fact, node kits.

Master Database

Initialization of the database

All MeshViz nodes are derived from the PoBase PoBase PoBase class. The init() static method of PoMeshViz::init() must be called before any other call to MeshViz. This method initializes the visualization database, registering all node kits and starting the connection mechanism. You must have the following line in all of your MeshViz applications:

PoMeshViz::init();
File output

The setNodeWriteFormat() static method selects the way node kits are stored on disk when using the Open Inventor SoWriteAction SoWriteAction SoWriteAction . Folded or unfolded storage of the node kit can be selected. Unfold format is required if you want to be able to share this output with a standard .iv browser or if you want to convert it to VRML. Fold format is the default. The following code specifies the unfolded output format:

PoBase::setNodeWriteMethod(PoBase::UNFOLD_NODE_WRITE_FORMAT);

The following GraphMaster program will output two different .iv files (listed after the program) depending on whether UNFOLD or FOLD is specified.

Example 2.3. Generate an output file rectangle.iv with unfolded format

#include <Inventor/SoDB.h> 
#include <Inventor/nodekits/SoNodeKit.h> 
#include <Inventor/nodes/SoSeparator.h> 
#include <Inventor/actions/SoWriteAction.h> 
#include <MeshViz/graph/PoRectangle.h> 

int
main()
 {
 SoDB::init();
   SoNodeKit::init();
   PoMeshViz::init();

 // Remove the following line to output using UNFOLD format
   PoBase::setNodeWriteFormat(PoBase::UNFOLD_NODE_WRITE_FORMAT);

   SoSeparator *root = new SoSeparator;
   root->ref();
   PoRectangle *myRectangle = new PoRectangle;
   root->addChild(myRectangle);

   SoWriteAction myAction;
   myAction.getOutput()->openFile("rectangle.iv");
   myAction.getOutput()->setBinary(FALSE);
   myAction.apply(root);
   myAction.getOutput()->closeFile();
   return 0;
 }


Example 2.4. Output file rectangle.iv with unfolded output

#Inventor V2.1 ascii
Separator {
   PoRectangle {
     fields [ SFNode catchActionCallback, SFNode appearance,
       SFNode rectangle, SFNode alternateRep ]
       catchActionCallback Callback {
     }
     appearance DEF +0 AppearanceKit {
       lightModel LightModel {
         model BASE_COLOR
         }
     }
     rectangle DEF +1 Group {
       Coordinate3 {
         point [0 0 0,
               1 0 0,
               1 1 0,
               0 1 0]
       }
       FaceSet  {
         numVertices 4
       }
     }
     alternateRep Separator { 
       USE +0 
       USE +1 
     } 
   }
}


Example 2.5. Output file rectangle.iv with folded output

#Inventor V2.1 ascii
Separator {
    PoRectangle {
        fields [SFNode appearance,] 
        appearance AppearanceKit { 
            lightModel LightModel { 
                model BASE_COLOR 
             } 
        } 
    }
}


Update of the visualization

Inherited from PoBase PoBase PoBase , the setUpdateMethod() allows you to control updating of GraphMaster and 3DdataMaster nodes. As with Open Inventor, a node may be updated each time one of its fields is changed. This may be time-consuming if several fields of a single node have to be edited before getting the expected result. This method lets you specify whether the object should be updated as soon as the node changes, or only when an explicit update is requested. The following code sets the update of the PoRectangle PoRectangle PoRectangle node to be automatic, each time one of its p or q fields is modified (this is not the default):

PoRectangle *myRectangle = new PoRectangle;
myRectangle->setUpdateMethod(PoBase::UPDATE_METHOD_IMMEDIATE);

You can force an object to be updated by using the touchKit method; the isModified method can be called to determine if a node has been modified and not yet updated.

Retrieving Open Inventor nodes built by a MeshViz nodekit

MeshViz nodekits create Open Inventor nodes in order to realize their own representation. For instance, a PoPieChart PoPieChart PoPieChart nodekit (for building a pie chart) creates a list of SoFaceSet SoFaceSet SoFaceSet nodes for each slice of the pie chart and stores them in its catalog (see Section 16.4, “Node-Kit Catalog”, page 363 of The Inventor Mentor).

If you need to consult these nodes, they must be available (i.e. have been created). This normally happens on the first traversal of the nodekit. The callback set by the PoBase PoBase PoBase ::addPostRebuildCallback() method is called as soon as these nodes are available.

For instance, if you want to retrieve the SoFaceSet SoFaceSet SoFaceSet of the first slice you must do the following:

PoPieChart3D *pieChart3D = new PoPieChart3D;
pieChart3D->addPostRebuildCallback(pieChartRebuildCB, NULL);

void pieChartRebuildCB(void *userData, PoBase *base)
{
PoPieChart3D *myPieChart = (PoPieChart3D *) base;

// Retrieve the part named "slice".
  SoGroup *sliceGroup = (SoGroup*)myPieChart->getPart("slice", FALSE);

  if (sliceGroup)
  {
  SoFaceSet *firstSliceFaceSet = NULL;

  // Search the first SoFaceSet if it exists.
    for (int i = 0; i < sliceGroup->getNumChildren(); i++)
    {
    if (sliceGroup->getChild(i)->isOfType(SoFaceSet::getClassTypeId()))
      {
      firstSliceFaceSet = sliceGroup->getChild(i);
        break;
      }
    }

  if (firstSliceFaceSet)
    {
    // You can now work with the face set
    // corresponding to the first slice
    }
  }
}

Domain

All nodes in a scene graph can be transformed, scaled, and translated. They are able to draw their own geometry using the parameters that define it. For instance, an axis is defined by a starting and a finishing point. Suppose you want to create a Cartesian Y axis ranging from 0 to 10 and another one ranging from -π to π, and you want labels and strings to be placed at tick intervals that are 5% of the axis length. If you were to build your axes directly this way, you would have two axes rendered on your screen with different origins, different lengths, and different text heights. Perhaps, instead you want two parallel Y axes displayed with the same length, with the same Y origin, and using the same text height. Domains solve this problem by defining an area or a volume into which the node geometry is transformed before being displayed. By applying this transformation, a heterogeneous domain will be transformed into a homogeneous space. This transformation is realized by an SoTransform SoTransform SoTransform node within the node kit and applies a scale factor on the X, Y, or Z coordinates depending on the type of transformation you choose. Some node fields are defined using the domain coordinates. For instance, the text height of axes or the arrow width at the end of an axis is defined as a percentage of the domain. You can choose the following types of transformations assuming dx, dy, and dz are the differences (xmax-xmin), (ymax-ymin), and (zmax-zmin), and that xmin, ymin, zmin, xmax, ymax, and zmax are the domain limits.

  • TRANSFORM_01: All nodes are scaled and translated to be rendered in a unit cube [0-1] × [0-1] × [0-1].

  • SCALE_X_FIXED: All nodes are transformed using a scale factor of 1 on the X coordinates,dx/dy on Y, and dx/dz on Z.

  • SCALE_Y_FIXED: All nodes are transformed using a scale factor of dy/dx on the X coordinates, 1 on Y, and dy/dz on Z.

  • SCALE_Z_FIXED: All nodes are transformed using a scale factor of dz/dx on the X coordinates, dz/dy on Y, and 1 on Z.

  • SCALE_XYZ_FIXED: All nodes are transformed using one of the SCALE_[X][Y][Z]_FIXED methods depending on the maximum value of dx, dy, and dz.

By default, the domain is [0-1] × [0-1] × [0-1] with the SCALE_X_FIXED method. Thus the transformation is the identity and none of the coordinates are scaled or translated. 2D or 3D domains can be defined.

The setDomain method inherited from PoBase PoBase PoBase class or the PoDomain PoDomain PoDomain property node can be used to define a domain. The following example defines a 2D domain [0-10] × [0-20]. By default the method used is SCALE_X_FIXED, which means that a scale factor of 0.5 (which is 10./20.) will be applied to the Y coordinates. Then the resulting rectangle will be rendered as a square on the screen.

This rectangle problem could have easily been solved using an Open Inventor SoTransform SoTransform SoTransform node without using the setDomain method or PoDomain PoDomain PoDomain node. The purpose here is to explain how a domain transformation acts on a node and how to solve basic “ratio” problems using the domain transformation.

Example 2.6. Set a domain transformation

...
PoRectangle *myRectangle = new 
  PoRectangle(SbVec2f(0.,0.), 
              SbVec2f(10.,20.)); 
SoGroup *group = new SoGroup;

#ifdef USE_PB
      PbDomain *myDomain = 
        new PbDomain(0.,0.,10.,20.);
      myRectangle->setDomain(myDomain);
#else
      PoDomain *myDomain = new PoDomain;
      myDomain->setValues(SbVec2f(0,0),
      SbVec2f(10,20));
      group->addChild(myDomain);
#endif

group->addChild(myRectangle);
...


If we want to render the rectangle with the real ratio on the screen and the correct dimensions, we should have a scale of 1 for each coordinate. Setting a domain to be [0-20] × [0-20] will solve this problem and the following lines will render the rectangle as expected:

Example 2.7. Set a domain to retain the original aspect ratio

... 
PoRectangle *myRectangle =
   new PoRectangle(SbVec2f(0.,0.), 
                  SbVec2f(10.,20.)); 
SoGroup *group = new SoGroup;

#ifdef USE_PB PbDomain *myDomain =
    new PbDomain(0.,0.,20.,20.); 
    myRectangle->setDomain(myDomain); 
#else 
    PoDomain *myDomain = new PoDomain;
    myDomain->setValues(SbVec2f(0,0),
                           SbVec2f(20,20));
    group->addChild(myDomain);
#endif

group->addChild(myRectangle); 
...



Setting a domain may not be important if you only want to use one coordinate system. In the previous example, even if you had not set the domain (so the default domain and transformation method were used instead), your rectangle would have been rendered correctly. But real problems begin when you want to draw different visualizations with different “bounding boxes” and when you want to mix them on the screen.

Example 2.8.  Use of domain to align two rectangles with different locations and dimensions

Now imagine we have two rectangles of different dimensions, and we want them to be rendered with the same size and in the same location on the screen. Here is a possible solution to this problem using domains:

...
// Create the root of our scene graph
  SoSeparator *root = new SoSeparator;
  root->ref();

// Create our first rectangle red // and solid
  PoRectangle *myRec1 = new PoRectangle;
  myRec1->p.setValue(0,0);
  myRec1->q.setValue(10,20);
  myRec1->set("appearance.material", "diffuseColor 1. 0. 0.");

// Create our second rectangle green and hollow
  PoRectangle *myRec2 = new PoRectangle;
  myRec2->p.setValue(-20.,-10.);
  myRec2->q.setValue(-10.,40.);
  myRec2->set("appearance.material", "diffuseColor 0. 1. 0.");
  myRec2->set("appearance.drawStyle", "style LINES");

  SoTranslation *myTrans = new SoTranslation;
  myTrans->translation.setValue(20.,2.,0.);

  #ifdef USE_PB
        PbDomain myDom1(0.,0.,10.,20.);
        myRec1->setDomain(&myDom1);
        PbDomain myDom2(-20.,-10.,-10.,40.);
        myRec2->setDomain(&myDom2);
  #else
        PoDomain *myDom1 = new PoDomain;
        myDom1->setValues(SbVec2f(0.,0.), SbVec2f(10., 20.));
        PoDomain *myDom2 = new PoDomain;
        myDom2->setValues(SbVec2f(-20.,-10.), SbVec2f(-10., 40.));
        root->addChild(myDom1);
  #endif

        root->addChild(myRec1);

  #ifndef USE_PB
        root->addChild(myDom2);
  #endif

 root->addChild(myTrans);
 root->addChild(myRec2);
 SoXtExaminerViewer *viewer = new SoXtExaminerViewer(myWindow);
 viewer->setSceneGraph(root);
 ...


Example 2.9. No domain use for both rectangles but SoTranslation node to the second rectangle

...
// Create our first rectangle red and
// solid
  PoRectangle *myRec1 = new PoRectangle;
  myRec1->p.setValue(0.,0.);
  myRec1->q.setValue(10.,20.);
  myRec1->set("appearance.material",
                 "diffuseColor 1. 0. 0.");
  root->addChild(myRec1);

// Create our second rectangle green
// and hollow
  PoRectangle *myRec2 = new PoRectangle;
  myRec2->p.setValue(-20.,-10.);
  myRec2->q.setValue(-10.,40.);
  myRec2->set("appearance.material",
                 "diffuseColor 0. 1. 0.");
  myRec2->set("appearance.drawStyle",
                 "style LINES");
  root->addChild(myRec2);
...


Example 2.10. Setting domains for the rectangles without translating the second one

...
// Create our first rectangle red and
// solid
  PoRectangle *myRec1 = new PoRectangle;
  myRec1->p.setValue(0.,0.);
  myRec1->q.setValue(10.,20.);
  myRec1->set("appearance.material",
  "diffuseColor 1. 0. 0.");
#ifdef USE_PB
  PbDomain myDom1(0.,0.,10.,20.);
  myRec1->setDomain(&myDom1);
#else
  PoDomain *myDom1 = new PoDomain;
  myDom1->setValue(SbVec2f(0.,0.),
  SbVec2f(10.,20.));
  root->addChild(myDom1);
#endif

  root->addChild(myRec1);

// Create our second rectangle green and hollow
  PoRectangle *myRec2 = new PoRectangle;
  myRec2->p.setValue(-20.,-10.);
  myRec2->q.setValue(-10.,40.);
  myRec2->set("appearance.material", 
                 "diffuseColor 0. 1. 0.");
  myRec2->set("appearance.drawStyle", 
                 "style LINES");

#ifdef USE_PB
  PbDomain myDom2(-20.,-10.,-10.,40.);
  myRec2->setDomain(&myDom2);
#else
  PoDomain *myDom2 = new PoDomain;
  myDom2->setValues(SbVec2f(-20.,-10.), 
                       SbVec2f(-10.,40.));
  root->addChild(myDom2);
#endif

  root->addChild(myRec2);
...

You can see here that translation has been computed to move the origin of the second rectangle to the first rectangle’s origin. The value 20 for translation on the X axis is obvious and the Y value is computed as follows:

  • We translate the second rectangle by 10 on the Y axis without a domain transformation to make the Y origin of second rectangle match the Y origin of first one.

  • The domain transformation is SCALE_X_FIXED with dx=10 and dy=50. Thus a 10/50 scale factor is applied to the Y coordinates before rendering.

  • The same ratio is applied to the translation. Thus 10*(10/50)=2.



What about mixing MeshViz nodes with Open Inventor nodes when we are using the domain facilities? The setDomain method does not exist for SoBase SoBase SoBase -derived objects, thus MeshViz provides you with a method to retrieve the SoTransform SoTransform SoTransform node used for the domain transformation inside the node kit. Then this transformation can be applied to the Inventor nodes you want to mix with MeshViz.

Example 2.11. Mixing use of PoRectangle and SoSphere

...
// Create the root of our scene graph
  SoSeparator *root = new SoSeparator;
  root->ref();

// Create our rectangle green // and hollow
  PoRectangle *myRec = new PoRectangle;
  myRec->p.setValue(-20.,-10.);
  myRec->q.setValue(-10.,40.);
  myRec->set("appearance.material",
                "diffuseColor 0 1 0");
  myRec->set("appearance.drawStyle",
                "style LINES");
#ifdef USE_PB
  PbDomain *myDom = new
  PbDomain(-20.,-10.,-1.,-10.,40.,1.);
  myRec->setDomain(myDom);
#else
  PoDomain *myDom = new PoDomain;
  myDom->min.setValue(-20.,-10.,-1.);
  myDom->max.setValue(-10.,40.,1);
  root->addChild(myDom);
#endif
  root->addChild(myRec);

//Create our sphere and apply transformations:
  SoSphere *mySphere = new SoSphere;
  mySphere->radius = 4.;
  SoTranslation *myTrans = new SoTranslation;
  myTrans->translation.setValue(-15.,3.,0.);
  root->addChild(myTrans);
  root->addChild(myDom->getTransform());
  root->addChild(mySphere);

  SoXtExaminerViewer *viewer = new SoXtExaminerViewer(myWindow); 
  viewer->setSceneGraph(root);
 ...


Example 2.12. TRANSFORM_01 method to maintain the sphere’s aspect ratio

Using this transform method, the rectangle is projected to a [0,1] × [0,1] × [0,1] space.

...
// Create our rectangle green and
// hollow
    PoRectangle *myRec = new PoRectangle;
    myRec->p.setValue(-20.,-10.);
    myRec->q.setValue(-10.,40.);
    myRec->set("appearance.material",
                 "diffuseColor 0 1 0");
    myRec->set("appearance.drawStyle",
                 "style LINES");

#ifdef USE_PB
    PbDomain myDom(-20.,-10.,-1.,
    10.,40.,1.);

    myDom.setTransformType(PbDomain::TRANSFORM_01);
    myRec->setDomain(&myDom);
#else
    PoDomain *myDom = new PoDomain;
    myDom->setValues(SbVec3f(-20.,-10.,-1.),
                        SbVec3f(-10.,40.,1.));
    myDom->transformType = PoDomain::TRANSFORM_01;
    root->addChild(myDom);
#endif

    root->addChild(myRec);

  //Create our sphere and apply transformations:
    SoSphere *mySphere = new SoSphere;
    mySphere->radius = .4;
    SoTranslation *myTrans = new SoTranslation;
    myTrans->translation.setValue(.5,.5,0.);
    root->addChild(myTrans);
    root->addChild(mySphere);
 ...



Domain facilities provided inside MeshViz are very important for drawing multiple axis systems. Once again, the previous example could have been much more easily implemented without using domain facilities. Just remember that the default domain transformation is the identity matrix and that no scaling or translation are applied to your nodes.

Splitting a window into multiple views

MeshViz provides an elegant way to specify several cameras in the same scene graph. The PoSceneView PoSceneView PoSceneView nodekit offers this capability.

A PoSceneView PoSceneView PoSceneView is defined by a camera, a viewport specified in normalized window coordinates, and a scene graph. This allows each PoSceneView PoSceneView PoSceneView to work in its own world coordinates.

As usual, the viewer will attach to the first camera it finds in the scene graph. You can also switch the viewer from one camera to another by calling SoXtViewer ::setCamera(). There is no automatic mechanism to switch the viewer from one PoSceneView PoSceneView PoSceneView ’s camera to another.

The following figure illustrates the use of PoSceneView PoSceneView PoSceneView :

The associated scene graph is: