Chapter 6. Text

Chapter Objectives

After reading this chapter, you'll be able to do the following:

This chapter describes the use of 2D and 3D text. Inventor's 2D text provides you with a simple, quick method for annotating your graphics. For greater embellishment and flexibility, use 3D text, which offers you a wide range of possibilities for shaping the profiles of 3D fonts. Key concepts introduced in this chapter include justification, spacing, font type and size, and profiles. Although the topic of NURBS curves and surfaces is mentioned, that subject is explained fully in Chapter 8.

The first part of this chapter focuses on 2D text and introduces certain concepts common to both 2D and 3D text, such as justification, spacing, and font type and size. The second part of the chapter describes the use of 3D text. The main additional concept in the use of 3D text is defining the cross-sectional profile for the text. You can create profiles that are straight, curved, or a combination of the two.

Two-Dimensional Text

The text node, SoText2, defines text strings that are rendered as 2D screen-aligned text. Just as other shape nodes cause their shape to be drawn when encountered during rendering traversal, an SoText2 node causes text to be drawn, using the current values for font and color. Text attributes used by SoText2 are specified in the SoFont node. These attributes include font type and point size. Two-dimensional text does not scale in size according to changes in distance from the camera.

SoText2 has the following fields:

string (SoMFString) 


the text string or strings to display. You can specify multiple strings.

spacing (SoSFFloat) 


the spacing between lines of text. The default interval is 1.0. For a multiple-string field, the vertical distance from the top of one line to the top of the next line is equal to spacing times the font size.

justification (SoSFEnum) 


alignment of the text strings relative to the text origin. Justification can be LEFT (the default), RIGHT, or CENTER.

The text origin is positioned at (0, 0, 0), transformed by the current geometric transformation. Text is drawn relative to the text origin, according to the specified justification. For example, if you specify RIGHT justification, the right side of the text aligns with the text origin.

Font Type and Size

Use the SoFont node to specify a font type and size for subsequent text nodes (both 2D and 3D) in the scene graph. This node contains the following fields:

name (SoSFName) 


font name. Check your release documentation for a list of font types that are supported on your system.

size (SoSFFloat)  


for SoText2, the point size in printer's points. For SoText3, the size in object space units (default = 10.0).

For example, to specify 140-point Courier bold italic:

SoFont *font = new SoFont;
font->name.setValue("Courier-BoldOblique");
font->size.setValue(140);

Using 2D Text

Example 6-1 renders a globe and uses 2D text to label the continents Africa and Asia. The SoFont node specifies 24-point Times Roman as the current font. Figure 6-1 shows the scene graph for this example. “Simple Text” shows the image produced by this program.

Figure 6-1. 2D Text Example


Example 6-1. Using 2D Text


#include <Inventor/nodes/SoFont.h>
#include <Inventor/nodes/SoGroup.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSphere.h>
#include <Inventor/nodes/SoText2.h>
#include <Inventor/nodes/SoTexture2.h>
#include <Inventor/nodes/SoTranslation.h>

#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>

main(int argc, char **argv)
{
   Widget myWindow = SoXt::init(argv[0]);
   if(myWindow == NULL) exit(1);

   SoGroup *root = new SoGroup;
   root->ref();

   // Choose a font.
   SoFont *myFont = new SoFont;
   myFont->name.setValue("Times-Roman");
   myFont->size.setValue(24.0);
   root->addChild(myFont);

   // Add the globe, a sphere with a texture map.
   // Put it within a separator.
   SoSeparator *sphereSep = new SoSeparator;
   SoTexture2  *myTexture2 = new SoTexture2;
   root->addChild(sphereSep);
   sphereSep->addChild(myTexture2);
   sphereSep->addChild(new SoSphere);
   myTexture2->filename = "globe.rgb";

   // Add Text2 for AFRICA, translated to proper location.
   SoSeparator *africaSep = new SoSeparator;
   SoTranslation *africaTranslate = new SoTranslation;
   SoText2 *africaText = new SoText2;
   africaTranslate->translation.setValue(.25,.0,1.25);
   africaText->string = "AFRICA";
   root->addChild(africaSep);
   africaSep->addChild(africaTranslate);
   africaSep->addChild(africaText);


   // Add Text2 for ASIA, translated to proper location.
   SoSeparator *asiaSep = new SoSeparator;
   SoTranslation *asiaTranslate = new SoTranslation;
   SoText2 *asiaText = new SoText2;
   asiaTranslate->translation.setValue(.8,.8,0);
   asiaText->string = "ASIA";
   root->addChild(asiaSep);
   asiaSep->addChild(asiaTranslate);
   asiaSep->addChild(asiaText);

   SoXtExaminerViewer *myViewer = 
            new SoXtExaminerViewer(myWindow);
   myViewer->setSceneGraph(root);
   myViewer->setTitle("2D Text");
   myViewer->setBackgroundColor(SbColor(0.35, 0.35, 0.35));
   myViewer->show();
   myViewer->viewAll();
   
   SoXt::show(myWindow);
   SoXt::mainLoop();
}

Three-Dimensional Text

In contrast to 2D text, 3D text scales in size according to changes in distance from the camera and does not always stay parallel to the screen. Three-dimensional text has depth. The face of a 3D letter can join its sides at right angles (the default). Or you can bevel the edges of the letter by specifying your own text profile, as shown at the right of Figure 6-3, which shows a beveled letter A.

The chief advantages of 2D text are that it is faster than 3D text and, because it remains parallel to the screen, is always readable. Advantages of 3D text are that it can be scaled and is generally prettier than 2D text.

Figure 6-2. Defining a Customized Profile for 3D Text


SoText3 has the following fields:

string (SoMFString) 


the text string or strings to display. You can specify multiple strings.

spacing (SoSFFloat) 


the spacing between lines of text. The default interval is 1.0. For a multiple-string field, the vertical distance from the top of one line to the top of the next line is equal to spacing times the font size.

justification (SoSFEnum) 


alignment of the text strings relative to the text origin. Justification can be LEFT (the default), RIGHT, or CENTER. LEFT means that the bottom-left front of the first character in the first line is at (0.0, 0.0, 0.0). Successive lines start under the first character. RIGHT means that the bottom-right of the last character is at (0.0, 0.0, 0.0). Successive lines end under the last character of the first line. CENTER means that the center of each line is at (0.0, 0.0, 0.0).

parts (SoSFBitMask) 


visible parts of the text (FRONT, SIDES, BACK, or ALL). The default is FRONT.

Parts of 3D Text

Three-dimensional text has three parts: front, sides, and back. Text uses the current material. If material binding is specified as PER_PART, the front uses the first material, the sides use the second material, and the back uses the third material.


Tip: Be aware that when you turn on SIDES and BACK of 3D text, you draw three times more polygons than with FRONT only, so performance is slower.


Profile

The profile describes the cross-section of the letter, as shown in Figure 6-3. The profile is drawn in its own 2D plane. This plane is perpendicular to the face of the text, as shown in Figure 6-4. The origin of this plane is at the edge of the letter. In this coordinate system, capital letters are one unit high. The profile coordinates thus need to be in the range of 0.0 to about 0.3 or 0.4 times the size of the font.

Figure 6-3. 2D Plane for Drawing a Text Profile


Linear Profiles

Profiles are constructed from the current profile coordinates. If the profile is a collection of connected straight-line segments, use the SoLinearProfile node to specify how the coordinates are connected. The profile coordinates are specified in an SoProfileCoordinate2 node, which precedes the SoLinearProfile node in the scene graph (see Example 6-3).

Curved Profiles

If the profile is curved, use the SoNurbsProfile node to specify how the coordinates are used. If you are interested in creating curved profiles, first read Chapter 8 for detailed conceptual information on NURBS curves. The coordinates themselves are specified in the SoProfileCoordinate2 node or the SoProfileCoordinate3 node, depending on whether the curve is nonrational or rational. (The terms nonrational and rational are also explained in Chapter 8.)

Linking Profiles (Advanced)

If your text profile is a combination of linear and curved lines, you can join the linear profile to the curved profile. The base profile class, SoProfile, includes a linkage field that is inherited by both SoLinearProfile and SoNurbsProfile. This field indicates whether the profile is START_FIRST (begin the first profile for the text), START_NEW (begin a new profile; for NURBS trimming only), or ADD_TO_CURRENT (append this profile to the previous one).

Simple Use of 3D Text

Example 6-2 illustrates a simple use of 3D text. It renders a globe and then uses 3D text to label the continents Africa and Asia. The SoFont node specifies Times Roman as the current font. Figure 6-5 shows the scene graph for this example. “Simple 3D Text Example” shows the image produced by this program.

Figure 6-4. Scene Graph for Simple 3D Text Example


Example 6-2. Using 3D Text


#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
#include <Inventor/nodes/SoFont.h>
#include <Inventor/nodes/SoGroup.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoMaterialBinding.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSphere.h>
#include <Inventor/nodes/SoText3.h>
#include <Inventor/nodes/SoTexture2.h>
#include <Inventor/nodes/SoTransform.h>


main(int, char **argv)
{
   Widget myWindow = SoXt::init(argv[0]);
   if(myWindow == NULL) exit(1);

   SoGroup *root = new SoGroup;
   root->ref();

   
   // Choose a font.
   SoFont *myFont = new SoFont;
   myFont->name.setValue("Times-Roman");
   myFont->size.setValue(.2);
   root->addChild(myFont);

   // We'll color the front of the text white, and the sides 
   // dark grey. So use a materialBinding of PER_PART and
   // two diffuseColor values in the material node.
   SoMaterial        *myMaterial = new SoMaterial;
   SoMaterialBinding *myBinding = new SoMaterialBinding;
   myMaterial->diffuseColor.set1Value(0,SbColor(1,1,1));
   myMaterial->diffuseColor.set1Value(1,SbColor(.1,.1,.1));
   myBinding->value = SoMaterialBinding::PER_PART;
   root->addChild(myMaterial);
   root->addChild(myBinding);

   // Create the globe.
   SoSeparator *sphereSep = new SoSeparator;
   SoTexture2  *myTexture2 = new SoTexture2;
   root->addChild(sphereSep);
   sphereSep->addChild(myTexture2);
   sphereSep->addChild(new SoSphere);
   myTexture2->filename = "globe.rgb";
   // Add Text3 for AFRICA, transformed to proper location.
   SoSeparator *africaSep = new SoSeparator;
   SoTransform *africaTransform = new SoTransform;
   SoText3 *africaText = new SoText3;
   africaTransform->rotation.setValue(SbVec3f(0,1,0),.4);
   africaTransform->translation.setValue(.25,.0,1.25);
   africaText->parts = SoText3::ALL;
   africaText->string = "AFRICA";
   root->addChild(africaSep);
   africaSep->addChild(africaTransform);
   africaSep->addChild(africaText);

   // Add Text3 for ASIA, transformed to proper location.
   SoSeparator *asiaSep = new SoSeparator;
   SoTransform *asiaTransform = new SoTransform;
   SoText3 *asiaText = new SoText3;
   asiaTransform->rotation.setValue(SbVec3f(0,1,0),1.5);
   asiaTransform->translation.setValue(.8,.6,.5);
   asiaText->parts = SoText3::ALL;
   asiaText->string = "ASIA";
   root->addChild(asiaSep);
   asiaSep->addChild(asiaTransform);
   asiaSep->addChild(asiaText);

   SoXtExaminerViewer *myViewer = 
            new SoXtExaminerViewer(myWindow);
   myViewer->setSceneGraph(root);
   myViewer->setTitle("3D Text");
   myViewer->setBackgroundColor(SbColor(0.35, 0.35, 0.35));
   myViewer->show();
   myViewer->viewAll();
   
   SoXt::show(myWindow);
   SoXt::mainLoop();
}

Advanced Use of 3D Text (Advanced)

Example 6-3 illustrates additional features available with 3D text. It specifies a beveled cross-section for the text using the SoProfile-Coordinate2 and SoLinearProfile nodes. The text uses two different materials— one for the front of the text, and one for the back and sides. The font node specifies the Times Roman font. Figure 6-7 shows the scene graph for this figure. “Advanced 3D Text Example” shows the rendered image.

Figure 6-5. Scene Graph for Advanced 3D Text Example


Example 6-3. Creating Beveled 3D Text


#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
#include <Inventor/nodes/SoFont.h>
#include <Inventor/nodes/SoGroup.h>
#include <Inventor/nodes/SoLinearProfile.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoMaterialBinding.h>
#include <Inventor/nodes/SoPerspectiveCamera.h>
#include <Inventor/nodes/SoProfileCoordinate2.h>
#include <Inventor/nodes/SoText3.h>

main(int argc, char **argv)
{

   Widget myWindow = SoXt::init(argv[0]);
   if(myWindow == NULL) exit(1);

   SoGroup *root = new SoGroup;
   root->ref();

   // Set up camera. 
   SoPerspectiveCamera	*myCamera = new SoPerspectiveCamera;
   myCamera->position.setValue(0, -(argc - 1) / 2, 10);
   myCamera->nearDistance.setValue(5.0);
   myCamera->farDistance.setValue(15.0);
   root->addChild(myCamera);

   // Let's make the front of the text white, 
   // and the sides and back yellow.
   SoMaterial *myMaterial = new SoMaterial;
   SbColor colors[3];
   // diffuse
   colors[0].setValue(1, 1, 1);
   colors[1].setValue(1, 1, 0);
   colors[2].setValue(1, 1, 0);
   myMaterial->diffuseColor.setValues(0, 3, colors);
   // specular
   colors[0].setValue(1, 1, 1);
   colors[1].setValue(1, 1, 0);
   colors[2].setValue(1, 1, 0);
   myMaterial->specularColor.setValues(0, 3, colors);
   myMaterial->shininess.setValue(.1);
   root->addChild(myMaterial);

   // Choose a font.
   SoFont *myFont = new SoFont;
   myFont->name.setValue("Times-Roman");
   root->addChild(myFont);

   // Specify a beveled cross-section for the text.
   SoProfileCoordinate2 *myProfileCoords = 
            new SoProfileCoordinate2;
   SbVec2f coords[4];
   coords[0].setValue(.00, .00);
   coords[1].setValue(.25, .25);
   coords[2].setValue(1.25, .25);
   coords[3].setValue(1.50, .00);
   myProfileCoords->point.setValues(0, 4, coords);
   root->addChild(myProfileCoords);

   SoLinearProfile *myLinearProfile = new SoLinearProfile;
   long	   index[4] ;
   index[0] = 0;
   index[1] = 1;
   index[2] = 2;
   index[3] = 3;
   myLinearProfile->index.setValues(0, 4, index);
   root->addChild(myLinearProfile);

   // Set the material binding to PER_PART.
   SoMaterialBinding *myMaterialBinding = new SoMaterialBinding;
   myMaterialBinding->
            value.setValue(SoMaterialBinding::PER_PART);
   root->addChild(myMaterialBinding);

   // Add the text.
   SoText3 *myText3 = new SoText3;
   myText3->string.setValue("Beveled Text");
   myText3->justification.setValue(SoText3::CENTER);
   myText3->parts.setValue(SoText3::ALL);
   root->addChild(myText3);

   SoXtExaminerViewer *myViewer = 
            new SoXtExaminerViewer(myWindow);
   myViewer->setSceneGraph(root);
   myViewer->setTitle("Complex 3D Text");
   myViewer->show();
   myViewer->viewAll();
   
   SoXt::show(myWindow);
   SoXt::mainLoop();
}