[N-World Contents] [Book Contents] [Prev] [Next] [Index]

You can use Lisp functions to create and manipulate N·World objects. You can also create objects interactively in the Geometry window, and manipulate them using Lisp. This chapter focuses on basic concepts, including techniques for:


A Quick Review of Bodies and Objects

Bodies contain geometrical information which defines the basic relationships between the components of an entity, such as a polyhedron.

When you modify a body, you are changing the basic geometrical relationships between the points, vertices, and faces which comprise that body. For example, if you select a body in the geometry window, and then move it, the changes you make affect the underlying definition of that body.

Objects, on the other hand, are constructed of bodies, a transformation matrix, and a body-display-item (bdi). When you modify a body, you are changing the relationship between the components of that body with respect to its local coordinate system. Objects, on the other hand, can be transformed. Transforming means to change the relationship between and objects local coordinate system and the global coordinate system.


Getting Started

Three dimensional objects are created in N·World using N-Geometry. Using Lisp programs, you can expand this interface or delve beneath it to gain greater control over the objects you create.

For the following examples, run N·World from within Xemacs in the manner described in Chapter 3, "Creating N·World Code."

Arrange your desktop windows so that you can see Xemacs and a Geometry window at the same time. When N·World has fully loaded, you'll need to tell Xemacs that you wish to use the GEOMETRY package. Type the following command at the Lisp PROMPT:

USER(6): :pa GEOMETRY

GEOMETRY(7):

The prompt changes to show you that you are now in the GEOMETRY package. The GEOMETRY package (also called the 3D package) contains most of the functions related to creating and selecting objects. Being "in" a package means that you can call functions in that package without special colon syntax.


Creating Simple Objects

To review, simple objects which are viewable in N·World have three major components:

The procedure for creating a simple object using Lisp mirrors this structural hierarchy:

1. Create a body.

2. Use the body to make an object.

3. Make the new object viewable.

Make Functions for Polyhedra

The functions which create bodies are called make-functions. Each geometric primitive has its own make function. All of these functions take at least one argument, usually the radius of the completed body. In our examples, we'll be creating a tetrahedron with a radius of 10.0 units. Some objects, like cylinders and spheres, take additional arguments to define.

To create a new tetrahedron:

1. Create a body with 3D:MAKE-TETRAHEDRON function.

For example, to create a tetrahedron with a radius of 10 units, evaluate the following form:

(3D:MAKE-TETRAHEDRON 10.0)

This function returns a value like:

#<POLYHEDRON 7>

This value is the printed representation of the body you just created. It serves as a sort of label to identify the body, but has no intrinsic significance.

If you check the 3D editor window for your new tetrahedron, you'll notice that it isn't there. It does exist, but only as an abstract data structure, an instance of a certain type of Lisp class which contains the geometric information describing a tetrahedron with a radius of 10.0 units. It is possible to manipulate this tetrahedron using Lisp functions without ever actually seeing it in the geometry window. Usually, though, you'll want to use the geometry window to monitor the results generated by the code you create.

Binding Bodies to Variables

In this example, however, we just called the make function without making any provisions for keeping track of the resulting body afterward. Fortunately, Lisp remembers the last three values returned by user functions. Lisp assigns the most recent value to the variable *, the second-most recent to **, and the third most recent to ***.

1. Bind a new body to a variable.

GEOMETRY(12): (setf mytetra *)

#<POLYHEDRON 7>

Again, Lisp returns the printed representation of the body we created. However, the variable mytetra is now bound to the actual body of the object represented by *, not the printed representation.

You can also bind a new body to a variable when you create it. For example:

GEOMETRY(13): (setf mytetra (make-tetrahedron 10.0))

#<POLYHEDRON 8>

Note that the printed representation is different, indicating we have just created a new body.

Evaluating mytetra will return the same value, namely, the printed representation of the tetrahedron's body. Binding a variable to a body is not the most efficient method for keeping track of it. However, for now it provides a handy way of referring to the bodies and objects we're creating.


Creating Objects

Use 3D:MAKE-OBJECT to create objects which reference bodies:

(3D:MAKE-OBJECT body &OPTIONAL name class)

To create an object which references the tetrahedron we just created:

GEOMETRY(19): (MAKE-OBJECT mytetra)

#<OBJECT "Polyhedron-Object" (dead) @ #x19ad85d2>

To facilitate working with this object later, we'll bind it to a variable:

GEOMETRY(20): (setf mytetraobject (MAKE-OBJECT mytetra))

#<OBJECT "Polyhedron-Object" (dead) @ #x19ae953a>

Naming Objects

Optionally, you can give your object a name at this point. Object names are what the 3D editor shows uses to build menu lists in N-Geometry. You can also search for objects by their names. The following example creates an object with the name "My Tetrahedron."

GEOMETRY(21):(setf mytetraobject

			(make-object mytetra "My Tetrahedron"))

#<OBJECT "My Tetrahedron" (dead) @ #x19b11962>

Viewing Objects

Why is our tetrahedron "dead"? It hasn't yet been added to the view.

1. Use 3D:ADD-OBJECT-TO-VIEW to make the object viewable in the geometry window.

GEOMETRY(22): (3D:ADD-OBJECT-TO-VIEW mytetraobject)

Lisp returns

Adding My Tetrahedron, and inserting bdi.

#<OBJECT "My Tetrahedron" @ #x19b11962>

A quick check of the Geometry window shows a brand new tetrahedron, centered on the origin, and with a radius of 10 units. When you select this new body, you can see that it has the name "My Tetrahedron."

2. If your object is not immediately visible, redraw the scene with 3D:REDRAW-SCENE.

GEOMETRY(23): (3D:REDRAW-SCENE *camera*)

Or simply move the camera.

3D:ADD-OBJECT-TO-VIEW does not return a value, but it does produce two important side-effects:


Selecting Items

Once you've created an object, you'll need to have a means of referring to it before you can manipulate it. You can reference bodies and objects explicitly by binding them to symbols, as we did in the examples above. However, this requires that you remember which symbol you assigned to an object, and type its name every time you wish to manipulate it. You can avoid this tedium by selecting an item interactively in the geometry window.

Selecting Bodies Interactively

Whenever you select an item in the geometry window, the special variable ? is set to that object. In fact, Lisp keeps track of the last three items you've selected. Two question marks (??) refer to the second-to-last item selected, while three (???) refer to the item selected third-to-last. You can use this symbol to "grab" an item and perform some operation on it. For example;

To select the body of the tetrahedron we created in the example above:

1. (click-l) on bodies in the sensitivity element menu bar at the top of the Geometry window.

2. (Click-l) on the body of the tetrahedron in the Geometry window.

3. In Xemacs, evaluate the value of ?.

GEOMETRY(25): ?

#<POLYHEDRON 8>

Lisp returns the printed representation of the body.

4. Select an edge of your polyhedron and evaluate ? again.

GEOMETRY(26): ?

#<EDGE 1>

5. Repeat this process for a face.

GEOMETRY(27): ?

#<FACE 1>

Now, if you evaluate ??, Lisp returns the printed representation of the edge you selected earlier:

GEOMETRY(28): ??

#<EDGE 1>

If you evaluate ???, Lisp returns the body you selected first.

GEOMETRY(29): ???

#<POLYHEDRON 8>

The expressions which Lisp returns in these examples are printed representations of the instance of the item in question. At best, these serve as a sort of label for that item. You cannot use these names in performing operations; you must use a variable bound to the object you wish to manipulate. In other words,

(move ? 10.0 10.0 10.0)

is a valid Lisp command, whereas

(move #<EDGE 3> 1 1 1)

will result in an error.

Referencing Elements Symbolically

You can also specify an item by referencing the variable whose value you set to that item. For example, when we created the tetrahedron, we bound the variable mytetra to its body. If you evaluate this variable, Lisp returns the printed representation of the body.

GEOMETRY(32): mytetra

#<POLYHEDRON 8>

Our example tetrahedron also has the variable mytetraobject bound to its object. We can reference the body or the object of our tetrahedron separately if we choose to do so. For example, you can transform the object by moving it with the move command.

(move mytetraobject 10.0 10.0 10.0)

You can also modify the geometry of the body by moving it:

(move mytetra 10.0 10.0 10.0)

Referencing Objects by Name

Binding variables to objects and bodies provides an easy way of refer to these items. However, it fails to take advantage of Lisp's ability to accept the values returned by one function as arguments to another.

We gave our tetrahedron object the name "My Tetrahedron" when we created it. 3D:OBJECT-NAMED returns an object with a given name:

GEOMETRY(53): (3D:OBJECT-NAMED "My Tetrahedron")

#<OBJECT "My Tetrahedron" @ #x1a183a42>

Lisp returns the printed representation of the object.

Selecting Objects From a Menu

Earlier we learned that one of the side-effects of add-object-to-view was to add an object to the list of known objects, bound to the global variable *global-object-list*. 3D:CHOOSE-OBJECT uses this list to build a pop-up menu, from which you can select an object.

GEOMETRY(64): (3D:CHOOSE-OBJECT t)

A choose-object menu like the one in Figure 7.1 appears:

Figure 7.1 The Choose Object menu

6. (CLICK-L) on an object to select it.

7. The function returns the object.

GEOMETRY(175): : (3d:choose-object t)

#<OBJECT "Polyhedron-Object" @ #x19147cc2>

#<OBJECT-MENU-FIELD "Polyhedron-Object" :OFF 3105301062>

#(USER-INTERFACE:MOUSE-BUTTON :LEFT 0

If you move the mouse pointer off of this menu without selecting an item, the menu disappears, and the function returns NIL.


Choosing Multiple Objects

3D:CHOOSE-OBJECTS works much the same way as choose-object, but can return a list of objects. Evaluating this function:

GEOMETRY(189): (3D:CHOOSE-OBJECTS t)

produces a menu like this one:

Figure 7.2 The Choose Objects menu

1. (CLICK-L) on the objects you wish to select

Selected objects are highlighted.

To deselect a highlighted object, (CLICK-L) on it again.

2. (CLICK-L) on Do It to complete your selection.

The function returns a list of the objects you selected.

GEOMETRY(204): (choose-objects t)

(SELECTED #<OBJECT "My Tetrahedron" (multiple) @ #x13d4229a>

 #<OBJECT "Polyhedron-Object" (multiple) @ #x13d4227a>

3. (CLICK-L) on Abort to escape without selecting anything.

If you choose to abort, the function returns NIL.

Working with Lists of Multiple Objects

Unlike the functions we have used until now, the choose-objects function returns a list of objects instead of a single object. As a result, be careful that any functions to which you pass the result of choose-objects are prepared for a list. See "Getting the Objects of a Compound Object," on page 7-20 for examples of traversing lists of objects.


Finding Out about Objects with Describe

There are several powerful tools you can use to find out details about the internal structure of an object. Among the simplest to use is the USER:DESCRIBE function. Use it to examine our sample compound object in more detail.

1. (CLICK-L) on Object in the Sensitivity bar.

2. (CLICK-L) on the compound object you created earlier.

The special variable ? is now bound to the compound object.

3. Type (describe ?) at the Lisp prompt.

Lisp returns detailed information about the object.

GEOMETRY(6): (d ?)

#<OBJECT "Octahedron Group" (multiple) @ #x1a6b1ae2> is an instance of

    #<STANDARD-CLASS OBJECT>:

 The following slots have :INSTANCE allocation:

  PROPERTY-LIST     (:HIGHLIGHTED NIL :MENU-FIELD

                     #<OBJECT-MENU-FIELD "Octahedron Group" :OFF 3234642052>

                     VIEWABLE T)

  NAME              "Octahedron Group"

  REGISTER-P        T

  CACHED-TAG        NIL

  UNIQUE-ID         ("/ngc7/people/bryan/" :OBJECT "Octahedron Group")

  SUPERIOR          NIL

  BODY              (#<OBJECT "Octahedron" @ #x1a61176a>

                     #<OBJECT "Tetrahedron" @ #x1a6025c2>

                     #<OBJECT "Cube" @ #x1a5da90a>)

  ATTRIBUTE-PLIST   NIL

  INIT-MATRIX       NIL

  BODY-MATRIX       #(1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0

                      0.0 0.0 1.0 3.644722e-40)

  BASE-MATRIX       #(1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0

                      0.0 0.0 1.0 3.644722e-40)

#<OBJECT "Octahedron Group" (multiple) @ #x1a6b1ae2>

You can see that the object "Octahedron Group" is an instance of the class OBJECT. You can see the values stored at each of the slots on the object, such as its name ("Octahedron Group"), its superiors (in this case, none), and the components of it's body. In this case, the body of "Octahedron Group" is actually itself composed of three objects.


Traversing Elements of Polyhedra

The elements of a polyhedron are vertices, edges, and faces. These elements are stored in a circular, double-pointed list. These lists are called element-rings. The element rings are ordered in "last-added-first" order. In other words, the last element added to the polyhedron is the first element in the list. When traversing elements, you'll need to decide whether to proceed in this order or in the reverse order, "first-added-first."

The primary tool you'll use for traversing elements of polyhedra are special loop iteration forms developed by Nichimen Graphics, and referred to collectively as LOOP macro forms. These forms are described in detail in Chapter 11, "Traversing Elements,"

Traversing Vertices

To traverse the vertices of a polyhedron in "last-added-first" order, use the VERTEX-RING-ELEMENTS loop iteration path. For example,

(loop for verts being the vertex-ring-elements of-polyhedron (make-tetrahedron 10.0)

                        do

                        (format t "Vertex: ~a~%" verts))

returns printed representations for each vertex:

Vertex: #<VERTEX 4 (  -8.16   -3.33   -4.71)>

Vertex: #<VERTEX 3 (   8.16   -3.33   -4.71)>

Vertex: #<VERTEX 2 (   0.00   -3.33    9.43)>

Vertex: #<VERTEX 1 (   0.00   10.00    0.00)>

NIL

To traverse the elements in first-added-first order, use the VERTEX-REVERSE-ELEMENTS loop iteration path:

				(loop for verts being the vertex-reverse-elements of-polyhedron 

					(make-tetrahedron 10.0)

                        do

                        (format t "Vertex: ~a~%" verts))

Which returns:

Vertex: #<VERTEX 1 (   0.00   10.00    0.00)>

Vertex: #<VERTEX 2 (   0.00   -3.33    9.43)>

Vertex: #<VERTEX 3 (   8.16   -3.33   -4.71)>

Vertex: #<VERTEX 4 (  -8.16   -3.33   -4.71)>

NIL

Obviously, you can perform whatever operation you like on the vertices in the body of the loop. Remember that the variable you assign the vertices to in the body of the macro (verts in this example) is defined only within the body of the macro.

Traversing Faces

You traverse faces of a polyhedron in last-added-first order in much the same way using the face-ring-elements loop iteration path:

(loop for faces being the face-ring-elements of-polyhedron (make-tetrahedron 10.0)

                        do

                        (format t "Face: ~a~%" faces))

returns a printed representation for each face:

Face: #<FACE 4>

Face: #<FACE 3>

Face: #<FACE 2>

Face: #<FACE 1>

NIL

Using the FACE-REVERSE-ELEMENTS loop iteration path produces a list in first-added-first order.

Traversing Elements of a Face

You can traverse through the vertices which make up a face by looping through the faces of a polyhedron, and then through the vertices of each face, e.g.

(loop for faces being the face-ring-elements of-polyhedron 

          (make-tetrahedron 10.0)

          do

          (format t "Face: ~a~%" faces)

			(loop for verts being the component-vertices of-face faces

			do

				(format t "Vert: ~a~%" verts)))

returns:

Face: #<FACE 4>

Vert: #<VERTEX 2 (   0.00   -3.33    9.43)>

Vert: #<VERTEX 3 (   8.16   -3.33   -4.71)>

Vert: #<VERTEX 4 (  -8.16   -3.33   -4.71)>

Face: #<FACE 3>

Vert: #<VERTEX 1 (   0.00   10.00    0.00)>

Vert: #<VERTEX 4 (  -8.16   -3.33   -4.71)>

Vert: #<VERTEX 3 (   8.16   -3.33   -4.71)>

Face: #<FACE 2>

Vert: #<VERTEX 1 (   0.00   10.00    0.00)>

Vert: #<VERTEX 2 (   0.00   -3.33    9.43)>

Vert: #<VERTEX 4 (  -8.16   -3.33   -4.71)>

Face: #<FACE 1>

Vert: #<VERTEX 1 (   0.00   10.00    0.00)>

Vert: #<VERTEX 3 (   8.16   -3.33   -4.71)>

Vert: #<VERTEX 2 (   0.00   -3.33    9.43)>

NIL

You can list the vertices in first-added-first order by using the CCW-COMPONENT-VERTICES path.


Collecting the Elements of Polyhedra

Loop forms such as the ones we've just described are useful for stepping through the components of a polyhedron. Because these are macro forms, however, the data they retrieve is limited in scope to within the body of the macro form. Occasionally, you may want to create more lasting collections of elements in the form of lists. It is correct to use the collect function within the body of a macro to create lists of elements. However, the polyhedron class has built-in generic functions for collecting the elements of polyhedra. These generally take the form COLLECT-MY-{target-element}.

Collecting Faces Given a Body

You can collect the faces of a polyhedron using 3D:COLLECT-MY-FACES:

(setf list-of-faces (3d:collect-my-faces (make-tetrahedron 10.0)))

Which returns a list of printed representations:

(#<FACE 4> #<FACE 3> #<FACE 2> #<FACE 1>)

Collecting Faces GIven an Edge

Given an edge, you can collect the faces to the left and to the right with these two functions:

(GET-LEFT-FACE edge)

(GET-RIGHT-FACE edge)

Collecting Vertices

You can collect the vertices of a face using 3D:COLLECT-MY-POINTS.

(3d:collect-my-points (make-tetrahedron 10.0))

Which returns the list:

(#<VERTEX 4 (  -8.16   -3.33   -4.71)> #<VERTEX 3 (   8.16   -3.33   -4.71)>

 #<VERTEX 2 (   0.00   -3.33    9.43)> #<VERTEX 1 (   0.00   10.00    0.00)>)

To return the vertices which comprise a given face, just pass a face to COLLECT-MY-POINTS.

(3D:COLLECT-MY-POINTS (first (collect-my-faces (make-tetrahedron 10.0))))

Which returns the list:
(#<VERTEX 2 ( 0.00 -3.33 9.43)>
#<VERTEX 3 ( 8.16 -3.33 -4.71)>
#<VERTEX 4 ( -8.16 -3.33 -4.71)>)


Traversing Grids

Grids behave identically to polyhedra for the purposes of collecting elements. All of the loop iteration paths and collect functions specified above will work with grids as well as polyhedra.


Traversing Wireframes and Skeletons

Wireframes are non-hierarchical lists of segments. You can return a list of the segments which make up a wire frame with the 3D:GET-SEGMENT-LIST function:

(3d:get-segment-list

		(3d:make-wire-frame-from-wire (3d:make-wire)))

Which returns the list:

(#<WF-SEGMENT 50> #<WF-SEGMENT 49> #<WF-SEGMENT 48> #<WF-SEGMENT 47>#<WF-SEGMENT 46> #<WF-SEGMENT 45> #<WF-SEGMENT 44> #<WF-SEGMENT 43> #<WF-SEGMENT 42> #<WF-SEGMENT 41> ...)

The same function will return the elements of a skeleton:

(3d:get-segment-list (3d:make-human-skeleton))

Which returns the list:

(#<BONE "Head" 435382370> #<BONE "Neck" 435381146> #<BONE "Chest" 435379922> #<BONE "RightHand" 435378314> #<BONE "RightForeArm" 435377090> #<BONE "RightUpperArm" 435375866> #<BONE "RightCollar" 435374642> #<BONE "RightChest" 435373418> #<BONE "LeftHand" 435371922> #<BONE "LeftForeArm" 435370698> ...)

Placing the function within a loop allows you to traverse the elements:

(loop for bone in (get-segment-list (make-human-skeleton))

                                 do

                     (format t "Bone: ~a~%" bone))

Which returns:

Bone: #<BONE "Head" 435523058>

Bone: #<BONE "Neck" 435521834>

Bone: #<BONE "Chest" 435520610>

Bone: #<BONE "RightHand" 435519002>

Bone: #<BONE "RightForeArm" 435517778>

Bone: #<BONE "RightUpperArm" 435516554>

Bone: #<BONE "RightCollar" 435515330>

Bone: #<BONE "RightChest" 435514106>

Bone: #<BONE "LeftHand" 435512610>

Bone: #<BONE "LeftForeArm" 435511386>

Bone: #<BONE "LeftUpperArm" 435510162>

Bone: #<BONE "LeftCollar" 435508938>

Bone: #<BONE "LeftChest" 435507714>

Bone: #<BONE "TopHip" 435506490>

Bone: #<BONE "RightToes" 435504786>

Bone: #<BONE "RightFootTop" 435503562>

Bone: #<BONE "RightFoot" 435502170>

Bone: #<BONE "RightTibia" 435500946>

Bone: #<BONE "RightFemur" 435499722>

Bone: #<BONE "RightHip" 435498498>

Bone: #<BONE "LeftToes" 435497034>

Bone: #<BONE "LeftFootTop" 435495810>

Bone: #<BONE "LeftFoot" 435494418>

Bone: #<BONE "LeftTibia" 435493194>

Bone: #<BONE "LeftFemur" 435491970>

Bone: #<BONE "LeftHip" 435490746>

NIL

Skeletons are hierarchical objects. You can return the components of skeletons in a way which reflects this internal hierarchy using 3D:GET-BONES-IN-MODIFICATION-ORDER.

(loop for bones in (get-bones-in-modification-order (make-human-skeleton))

                     do

                     (format t "Bone: ~a~%" bones))

Which returns the list:

Bone: #<BONE "TopHip" 436228498>

Bone: #<BONE "Chest" 436242618>

Bone: #<BONE "Neck" 436243842>

Bone: #<BONE "Head" 436245066>

Bone: #<BONE "LeftCollar" 436230946>

Bone: #<BONE "LeftUpperArm" 436232170>

Bone: #<BONE "LeftForeArm" 436233394>

Bone: #<BONE "LeftHand" 436234618>

Bone: #<BONE "RightCollar" 436237338>

Bone: #<BONE "RightUpperArm" 436238562>

Bone: #<BONE "RightForeArm" 436239786>

Bone: #<BONE "RightHand" 436241010>

Bone: #<BONE "LeftFemur" 436213978>

Bone: #<BONE "LeftTibia" 436215202>

Bone: #<BONE "LeftFoot" 436216426>

Bone: #<BONE "LeftToes" 436219042>

Bone: #<BONE "RightFemur" 436221730>

Bone: #<BONE "RightTibia" 436222954>

Bone: #<BONE "RightFoot" 436224178>

Bone: #<BONE "RightToes" 436226794>

NIL

This function returns bones starting from the node and travelling upwards. When it reaches a branch, it travels in the following order

1. to locked bones

2. to Bones for which DOFs have been specified

3. alphabetically (e.g., LeftFemur before RightFemur)

More Ways to Collect Elements

Be sure to review Chapter 11, "Traversing Elements," for a more complete description of loop paths, as well as information about time-saving short forms you can use.


Manipulating Objects

Multiple, or compound, objects have other objects as their bodies instead of polyhedra. Before you can work efficiently with multiple objects, you'll need to know how to find your way around in an object hierarchy. Before we begin, you might want to review Figure 6.5, which shows an example of the structure of a top-level, compound object.

To illustrate these concepts, we've created a simple compound object consisting of a cube, a tetrahedron, and an octahedron. If you like, you can use the following procedure to create a similar object, which you can use in the following examples.

To create the example object:

1. (CLICK-L) on Add New Object in the Geometry menu.

2. (CLICK-L) on Create.

3. (CLICK-L) on Cube.

4. Repeat steps 1-3 to create a cube, a tetrahedron, and an octahedron.

5. (CLICK-L) on Restructure in the Geometry menu.

The restructure menu appears.

Figure 7.1 The Restructure Objects dialog.

6. (CLICK-L) on each of the three objects in the dialog, then (CLICK-L) on Do It.

Another dialog appears with options for restructuring.

Figure 7.2 Restructuring options.

7. (CLICK-L) on New Object

A dialog appears for you to name your new object.

Figure 7.3 Name your new object

8. Type the name of your new object and (CLICK-L) on Restructure.

If you don't type a name, Geometry uses the default name (in this case, "Octahedron Group")

Basic Object Hierarchy Concepts

When you're dealing with object hierarchies, it's helpful to keep a few basic concepts in mind.

Determining Position in a Hierarchy

Before you can work efficiently with compound objects, you'll need to be able to reliably and efficiently determine

To determine whether our example object is a top-level object, bind it to the question mark variable and evaluate 3D:TOP-LEVEL-OBJECT-P on it:

(3D:TOP-LEVEL-OBJECT-P ?)

which returns:

T

Lisp returns "T", because our object is a top level object. Otherwise, the function returns NIL. There is a corresponding function for determining whether a given object resides at the bottom of a hierarchy, 3D:TERMINAL-OBJECT-P.

(3D:TERMINAL-OBJECT-P ?)

NIL

Finding Superior and Inferior Objects

For any given object in a hierarchy which is not a top-level object, superior objects are those which include the object in their body. In our example, the cube, tetrahedron, and icosahedron all have "Octahedron Group" as their superior objects. Use 3D:GET-SUPERIOR function to return the superior object of these objects.

1. (CLICK-L) on Objects in the Geometry sensitivity bar.

2. (CLICK-L) on the Cube.

3. Evaluate 3D:GET-SUPERIORS on ?.

3D:GET-SUPERIORS returns a list of all objects superior to the cube object:

(3D:GET-SUPERIORS ?)

(#<OBJECT "Octahedron Group" (multiple) @ #x13b1a9ea>)

You can find an objects inferiors in much the same way with 3D:GET-INFERIORS. Try get-inferiors with the cube object:

(3d:get-inferiors ?)

which returns:

NIL

If we want to see get-inferiors work, we'll have to set ? to a compound object.

1. (CLICK-L) on Objects in the geometry main menu.

2. (CLICK-L) on "Octahedron Group" to select it.

? is now equivalent to the compound object.

3. Evaluate get-inferiors on ?.

(get-inferiors ?)

which returns:

(#<OBJECT "Octahedron" @ #x13b1a9fa> #<OBJECT "Tetrahedron" @ #x13b1a9da> #<OBJECT "Cube" @ #x13b1a812>)

Lisp returns a list of three objects, all of which comprise the body of "Octahedron Group."

Now try evaluating get-superiors on "Octahedron Group"

(get-superiors ?)

returns:

NIL

Lisp returns NIL, because "Octahedron Group" is a top-level object with no superiors

Finding the Body of an Object

The find-body function returns the body of a terminal object, bdi, body, or element of a body. However, if you pass a compound object to find-body, the function returns NIL. This behavior provides a handy way of determining whether an object is a terminal object or not. For example, if we evaluate find-body on our compound object "Octahedron Group", the function returns NIL.

If you want to find the bodies in a compound object, you'll have to use 3d:collect-bodies. Assuming we've bound ? to our octahedron group, this function returns a flat list of bodies, e.g.

(3d:collect-bodies (object-named "Octahedron_Group"))

Returns the list

(#<POLYHEDRON 11> #<POLYHEDRON 9> #<POLYHEDRON 7>)

So, we can combine both functions to provide an ironclad way of finding bodies of any objects we might pass to it:

(cond			((setq bod (find-body object)))

			((setq bod (collect-bodies object)))

)

Finding Objects of a Body

Bind ? to the tetrahedron in the Geometry window. 3D:FIND-OBJECTS-OF-BODY returns a list of all objects which reference the tetrahedron.

(3D:FIND-OBJECTS-OF-BODY ?)

returns:

(#<OBJECT "Tetrahedron" @ #x13b25612>

#<OBJECT "Tetrahedron" @ #x13b1c3ea>)

Which are a terminal object and a top-level object, respectively.

Finding BDIs of a Body

BDIs can also serve as pointers to objects which reference a given body. 3D:GET-SISTERS returns a list of BDIs which reference a given body. Again, for the tetrahedron:

(3D:GET-SISTERS ?)

which returns a list of the BDIs that reference the tetrahedron:

(#<BDI (Tetrahedron) @ #x13b25452>

#<BDI (Tetrahedron) @ #x13b31082>

Finding the Body Referenced by a BDI

Given a BDI, you can obtain the body that BDI points with 3D:GET-BODY. To use the following example, you must first bind a variable to a BDI.

(setq testbdi (first (3d:get-sisters ?)))

Now, evaluate get-body with the variable testbdi.

(3d:get-body testbdi)

which returns:

#<POLYHEDRON 17>

Finding the Object Referenced by a BDI

Given a BDI, you can obtain the object which that BDI points to (its superior) using 3D:GET-OBJECT function.

(get-object testbdi)

which returns:

#<OBJECT "Tetrahedron" @ #x13b25612>

Finding Top-Level Objects of a BDI

Given a BDI, 3D:GET-TOP-LEVEL-OBJECT returns the top-level object in the hierarchy which references the BDI. For example:

(3D:GET-TOP-LEVEL-OBJECT testbdi)

which returns the top-level object:

#<OBJECT "Octahedron Group" (multiple) @ #x13b20832>


Traversing Object Hierarchies

Loop macros are the key to traversing Object Hierarchies. Using combinations of loop macro forms, or nested loops, you can grab any object, body, or bdi in a given object hierarchy. You can use loop macros to return:

Getting the Objects of a Compound Object

For the following examples, we've bound our sample compound object ("Octahedron object") to ?. We can loop through and print a list of the objects which make up "Octahedron objects" using the following loop macro form:

(loop for objvar of-object ?
do
(format t "Body object:~a~%" objvar))

Which returns a list of the objects that make up "Octahedron Group":

Body object:#<OBJECT "Octahedron Group" (multiple) @ #x13b20832>
Body object:#<OBJECT "Tetrahedron" @ #x13b25612>
Body object:#<OBJECT "Octahedron" @ #x13b25602>
Body object:#<OBJECT "Cube" @ #x13b27a82>
NIL

This particular form includes the compound object itself. You can avoid listing the compound object itself by limiting the loop to the inferior objects of the compound object. You can use get-inferiors to access the inferior objects, but this function returns a list. Fortunately, you can use the preposition in to direct the loop within a list:

(loop for objvar being the objects in (get-inferiors ?)

		do

			(format t "Body object:~a~%"objvar))

Which returns a list of inferior objects:

Body object:#<OBJECT "Cube" @ #x13b27a82>

Body object:#<OBJECT "Octahedron" @ #x13b25602>

Body object:#<OBJECT "Tetrahedron" @ #x13b25612>

NIL

Getting the Terminal Objects of a Hierarchy

Loop macros will also return the terminal objects of a hierarchy. For example

(loop for termobjs being the terminal-objects of-object ?

		do

			(format t "Terminal object:~a~%" termobjs))

Returns the terminal objects:

Terminal object:#<OBJECT "Cube" @ #x13b27a82>

Terminal object:#<OBJECT "Octahedron" @ #x13b25602>

Terminal object:#<OBJECT "Tetrahedron" @ #x13b25612>

NIL

Getting the Bodies of Objects in a Hierarchy

Use the bodies path to return the bodies of objects in an object hierarchy.

(loop for geobodies being the bodies of-object ?

		do

			(format t "Geobody:~a~%" geobodies))

Returns a list of bodies:

Geobody:#<POLYHEDRON 13>

Geobody:#<POLYHEDRON 15>

Geobody:#<POLYHEDRON 17>

NIL

Getting the BDIs in a Hierarchy

Use the bdis path to return the BDIs in a hierarchy.

(loop for bdivar being the bdis of-object ?

			do

				(format t "BDI: ~a~%" bdivar))

Returns:

BDI: #<BDI (Cube) @ #x13b31042>

BDI: #<BDI (Octahedron) @ #x13b30fe2>

BDI: #<BDI (Tetrahedron) @ #x13b25452>

NIL

Loop Paths for Object Hierarchies Summarized

The loop paths we've just presented should give you access to all the components of any object hierarchy. Figure 7.4 summarizes loop macro forms for traversing objects

Figure 7.4 Loop path forms for traversing objects.

Non-Standard Loop Paths

You may occasionally need to traverse hierarchies along paths which are not specified in existing loop macro forms. You'll need to be creative in figuring out how to accomplish these tasks as they arise, but here are a few examples.

Getting Superior Objects of a Body

You can combine the objects path and the preposition in to return the objects which reference a body. For example, if we set ? to the tetrahedron by selecting the tetrahedron in the Geometry frame:

				(loop for Superobjs being the objects in (find-objects-of-body ?)

						do

							(format t "Superior Objects: ~a~%" Superobjs))

Returns the superior objects:

Superior Objects: #<OBJECT "Tetrahedron" @ #x13b1c3ea>

Superior Objects: #<OBJECT "Tetrahedron" @ #x13b25612>

NIL

Getting Top Level Objects of a Body

There are no loop forms for returning top level objects given a body. However, you can use standard loop paths to return top-level objects. The key is to use the get-sisters function to return the BDIs which point to the body in question. Given these BDIs, you can use GET-TOP-LEVEL-OBJECT to return all the top-level objects which reference a given body.

				(loop for bdi in (get-sisters ?) for topobj = (get-top-level-object bdi)

						do

						(format t "~a~%" topobj)

)

Returns the top level objects of ?:

#<OBJECT "Octahedron Group" (multiple) @ #x13b20832>

#<OBJECT "Octahedron Group<2>" (multiple) @ #x13b1c41a>

NIL

You can replace the format section with your own code. You may also wish to return a list of these top-level objects, in which case you replace the do with COLLECT.

(loop for bdi in (get-sisters ?) collect (get-top-level-object (get-object bdi)))

which returns a list:

(#<OBJECT "Octahedron Group" (multiple) @ #x13b20832>
#<OBJECT "Octahedron Group<2>" (multiple) @ #x13b1c41a>)



[N-World Contents] [Book Contents] [Prev] [Next] [Index]

Another fine product from Nichimen documentation!

Copyright © 1996, Nichimen Graphics Corporation. All rights reserved.