oed {path_lhs} [{path_rhs}]
Object Editing - the oed Command
On this page
oed - Basic Syntax and Operations
The syntax of oed is fairly straightforward: [1]
When only path_lhs is specified, it is the complete path to the object being edited. The matrix immediately above its final component is selected, and the center of that object’s bounding box is used as the editing keypoint. When path_rhs is also specified, the two paths are joined conceptually: the matrix between the final component of path_lhs and the first component of path_rhs is selected, and the primitive at the end of path_rhs supplies the keypoint.
oed serves primarily to make working with combinations and regions practical when there are large numbers of primitives involved. It is of course possible to move primitives one by one to desired locations, but the process is extremely tedious for any non-trivial combination of primitives and the result is inflexible - for example,if the modeler wished to return the primitives in a combination to their original position after moving them as primitives the process of moving them would need to be reversed one by one as well. Barring being able to do everything perfectly the first time, individual primitive placement is impractical.
The notion of path in this context refers to the tree of objects that traditionally makes up a CSG model. Primitives will be combined with other primitives to make groups of objects, and the "path" to any individual shape within the combinations is described by the series of nodes in the tree that identify the object of interest. For example, take a simple model of a lollipop:
If we examine the tree structure of this object, we see it resembles a file system - combinations and regions act a bit like "folders" which can hold other folders and files. [2]
mged> tree lollipop lollipop/ u outer-candy-shell.r/R u outer-candy-sphere.c/ u outer-candy-sphere.s - core.s - stick.s u outer-candy-cyl.c/ u outer-candy-cyl.s - outer-candy-sphere.s u stick.r/R u stick.s u core.r/R u core.s - stick.s
In the "file system" of a BRL-CAD object combinations can hold combinations, regions and primitives. Regions can hold combinations and primitives. [3] Primitives are always leaves on the tree - they do not "contain" anything. Combinations, regions, and primitives can have associated with them transformation data (sort of like metadata in a file system) that tells BRL-CAD how to manipulate the combination, region, or primitive in question. It is this data that will be acted upon when the oed command is used. So in the above example the full "path" of the outer-candy-sphere.c combination used in lollipop would be:
/lollipop/outer-candy-shell.r/outer-candy-sphere.c
To operate on this combination without choosing a primitive keypoint, use the complete object path as the single argument:
oed /lollipop/outer-candy-shell.r/outer-candy-sphere.c
This form uses the center of the bounding box of outer-candy-sphere.c as the keypoint. To use a primitive’s default keypoint instead, split the path at the matrix to edit and supply a right hand path ending in that primitive. For example, any of the following selects the matrix between outer-candy-shell.r and outer-candy-sphere.c while obtaining the keypoint from a different primitive:
oed /lollipop/outer-candy-shell.r
outer-candy-sphere.c/outer-candy-sphere.s
or
oed /lollipop/outer-candy-shell.r outer-candy-sphere.c/core.s
or
oed /lollipop/outer-candy-shell.r outer-candy-sphere.c/stick.s
Because they are primitives in the outer-candy-sphere.c combination, outer-candy-sphere.s, core.s and stick.s are all legal reference shapes - there will be illustrations later of the consequences of different primitive choices.
In an empty MGED session, create a new geometry database called oed_examples.g. Enable the model coordinate axes display by entering the MGED command rset ax model_draw 1. Create two primitives: [6]
in sphere.s sph 0 0 0 4
in cube.s arb8 5 5 0 8 5 0 8 5 3 5 5 3 5 8 0 8 8 0 8 8 3 5 8 3
There should now be a sphere and a cube visible on the screen. Next create a combination of these two primitives:
comb object1.c u sphere.s u cube.s
After this command both the primitives and the combination are present in the display, which will result in overlaps. Use the B command to reduce the display to just the combination. [7]
B object1.c
Before beginning the editing process, the l command (a.k.a. the "list" command) can be used to view both the combination and its component primitives:
|
Note
|
Note that the list command uses the lower case letter L (l) and not the numeral one (1). |
mged> l object1.c object1.c: -- u sphere.s u cube.s mged> l sphere.s sphere.s: ellipsoid (ELL) V (0, 0, 0) A (4, 0, 0) mag=4 B (0, 4, 0) mag=4 C (0, 0, 4) mag=4 A direction cosines=(0, 90, 90) A rotation angle=0, fallback angle=0 B direction cosines=(90, 0, 90) B rotation angle=90, fallback angle=0 C direction cosines=(90, 90, 0) C rotation angle=0, fallback angle=90 mged> l cube.s cube.s: ARB8 1 (5, 5, 0) 2 (8, 5, 0) 3 (8, 5, 3) 4 (5, 5, 3) 5 (5, 8, 0) 6 (8, 8, 0) 7 (8, 8, 3) 8 (5, 8, 3)
For the first task the goal is to shift both the sphere and the arb8 5 mm up the z axis. First, select the combination for editing:
oed / object1.c/sphere.s
|
Note
|
Changes made during an object edit are not written to the database file unless and until they are accepted. So if in the course of editing an object you make a mistake, you can type reject to revert to the pre-object-edit-mode values and exit object edit mode. |
The object should turn white, as below:
Once selected, use the translate command to shift the combination up the z axis.
translate 0 0 5
Both the sphere and the cube should have shifted together.
To accept the edit and end object edit mode type accept:
accept
Now, use l to again examine the combination and its component primitives.
mged> l object1.c object1.c: -- u sphere.s [0,0,5] u cube.s [0,0,5] mged> l sphere.s sphere.s: ellipsoid (ELL) V (0, 0, 0) A (4, 0, 0) mag=4 B (0, 4, 0) mag=4 C (0, 0, 4) mag=4 A direction cosines=(0, 90, 90) A rotation angle=0, fallback angle=0 B direction cosines=(90, 0, 90) B rotation angle=90, fallback angle=0 C direction cosines=(90, 90, 0) C rotation angle=0, fallback angle=90 mged> l cube.s cube.s: ARB8 1 (5, 5, 0) 2 (8, 5, 0) 3 (8, 5, 3) 4 (5, 5, 3) 5 (5, 8, 0) 6 (8, 8, 0) 7 (8, 8, 3) 8 (5, 8, 3)
Notice that the primitives have not changed, but the combination now contains additional information. It has recorded that both sphere.s and cube.s have been shifted up the z axis 5 mm. Select object1.c again and apply a rotation as well:
oed / object1.c/sphere.s
rot 0 0 90
accept
Another check with l shows still more information stored at the combination level, but the primitives are again unchanged.
mged> l object1.c object1.c: -- u sphere.s az=90, el=0, [0,0,5] u cube.s az=90, el=0, [0,0,5] mged> l sphere.s sphere.s: ellipsoid (ELL) V (0, 0, 0) A (4, 0, 0) mag=4 B (0, 4, 0) mag=4 C (0, 0, 4) mag=4 A direction cosines=(0, 90, 90) A rotation angle=0, fallback angle=0 B direction cosines=(90, 0, 90) B rotation angle=90, fallback angle=0 C direction cosines=(90, 90, 0) C rotation angle=0, fallback angle=90 mged> l cube.s cube.s: ARB8 1 (5, 5, 0) 2 (8, 5, 0) 3 (8, 5, 3) 4 (5, 5, 3) 5 (5, 8, 0) 6 (8, 8, 0) 7 (8, 8, 3) 8 (5, 8, 3)
What if a modeler DID want the primitives to be the elements changed? There exists a command called push that will perform this operation. (So called because it "pushes" the transformations at the upper levels down to the primitives.) Be warned that this change, once done, is difficult to undo. Witness its effect on this example:
push object1.c
mged> l object1.c object1.c: -- u sphere.s u cube.s mged> l sphere.s sphere.s: ellipsoid (ELL) V (0, 0, 5) A (0, -4, 0) mag=4 B (4, 0, 0) mag=4 C (0, 0, 4) mag=4 A direction cosines=(90, 180, 90) A rotation angle=270, fallback angle=0 B direction cosines=(0, 90, 90) B rotation angle=0, fallback angle=0 C direction cosines=(90, 90, 0) C rotation angle=0, fallback angle=90 mged> l cube.s cube.s: ARB8 1 (5, -5, 5) 2 (5, -8, 5) 3 (5, -8, 8) 4 (5, -5, 8) 5 (8, -5, 5) 6 (8, -8, 5) 7 (8, -8, 8) 8 (8, -5, 8)
Now the primitives have changed, and the combination is back to being just a boolean operation on primitives. These operations COULD be undone, but there is no equivalent "pull" command to automatically undo them. By design, push replaces the stored transformations by their results and removes the transformation information. This means reversing a push requires additional transformations and another push to return things to their original state. Any complex sequence of transformations becomes hard to undo - use push with care!
Right Hand Path Selection - Reasons and Implications
Notice in the previous section that sphere.s was explicitly selected as part of the oed path_rhs. What would the impact have been had cube.s been selected instead?
Using the commands shown below, clear the old object1.c tree from MGED, re-insert the primitives, and make a combination as before. This time make two objects using the same primitives, for later use. At the moment, only the object2.c combination will be displayed.
killtree object1.c
in sphere.s sph 0 0 0 4
in cube.s arb8 5 5 0 8 5 0 8 5 3 5 5 3 5 8 0 8 8 0 8 8 3 5 8 3
comb object1.c u sphere.s u cube.s
comb object2.c u sphere.s u cube.s
B object2.c
In the previous section, the translate command was used to shift the object up the z axis. That was a deliberate choice - translate moves to an absolute rather than a relative position. Attempt the same operation, this time with the cube.s primitive selected:
oed / object2.c/cube.s
translate 0 0 5
accept
Compare the results to the previous section. The model is indeed shifted up the z axis, but the distance is somewhat different and the cube now has one edge lined up with the z axis. This is how translate works - it interprets its arguments as a point at which the object should be positioned, rather than as a vector along which it should be shifted. (To translate the object using a vector interpretation use the tra command instead - its arguments (dx, dy, dz) are treated as how far to move something relative to the current position.) In the previous section, the sphere’s origin was the reference point. Now, the cube’s first coordinate is being moved to the same absolute position - with different overall results for the model.
As one further illustration, attempt a rot command with the cube selected and then again with the sphere selected:
oed / object2.c/cube.s
rot 0 90 0
accept
oed / object2.c/sphere.s
rot 0 90 0
accept
The first rot command rotated the model around a line parallel to the y axis using the first coordinate of cube.s. The second rotation started from the results of the previous rotation and rotated again around a line parallel to the y axis, but this time using the origin of sphere.s.
The l command provides a look at the final result:
mged> l object2.c object2.c: -- u sphere.s az=180, el=0, [-5,5,5] u cube.s az=180, el=0, [-5,5,5]
Selection of a particular primitive when using the two-path form of oed will depend on the particular situation. If no primitive has an appropriate default keypoint, use the one-path form to select the object’s bounding-box center or set the keypoint explicitly after entering object edit mode.
Flexible Manipulation with keypoint
The one-path form of oed uses the center of the specified object’s bounding box as the point from which translations, rotations, and scaling are defined. The two-path form instead derives this point from the specified primitive. BRL-CAD’s term for this point is the keypoint. For cases where neither default is appropriate, BRL-CAD also offers an option for explicitly setting the keypoint.
For example, to rotate object2.c around the z axis intersecting point (2,2,0) the keypoint must be set explicitly - this is not a point that can be selected by identifying a primitive.
First, check the current keypoint value: [8]
oed / object2.c/sphere.s
keypoint
V (-5, 5, 5)
In order to set a new keypoint, we simply supply an x y z style coordinate option to the keypoint command:
keypoint 2 2 0
keypoint
user-specified (2, 2, 0)
Notice that the keynote report identifies this keypoint as user-specified. Having set this new keypoint, we can now rotate about it:
rot 0 0 90
accept
mged> l object2.c object2.c: -- u sphere.s az=270, el=0, [5,9,5] u cube.s az=270, el=0, [5,9,5]
Notice that it is getting increasingly difficult to identify the transformations that will restore the original shape positions. There is no keypoint history mechanism so remember to carefully track what is done if retracing the steps performed on an object is important.
Sub-Combination Editing - using Left Hand Path Selection
The importance of the path_lhs portion of the oed command is better understood in the case of more complex objects. To illustrate such a case, a new shape and additional combination structures are added:
in cone.s trc -4 0 4 0 0 4 2 .01
comb object3.c u cone.s u object1.c
r bigobject.r u object1.c u object3.c
B bigobject.r
mged> tree bigobject.r bigobject.r/R u object1.c/ u sphere.s u cube.s u object3.c/ u cone.s u object1.c/ u sphere.s u cube.s
The first goal is to translate object1.c inside bigobject.r 4mm in the positive z direction without impacting any other part of the model. This means the object1.c inside object3.c inside bigobject.r should NOT be changed. A first attempt might be to work directly with object1.c:
mged> l object1.c object1.c: -- u sphere.s u cube.s
oed / object1.c/sphere.s
Error: Unable to find solid matching path
An error is returned, because this command attempts to edit object1.c as a top level object and it is not currently in display as a top level object due to the previous B cleanup. However, MGED does not force object1.c out of the display when bigobject.r is displayed. Assuming it was still present (simulate this by entering B bigobject.r object1.c into the MGED prompt) the previous command would have a different result:
B bigobject.r object1.c
oed / object1.c/sphere.s
BRL-CAD is now in object edit mode, and visually it looks like it is highlighting precisely the part of the model targeted for translation. However, when translated:
tra 0 0 4
accept
B bigobject.r object1.c
BOTH the object1.c present in bigobject.r and the definition of object1.c in object3.c have been translated - only one instance of the sphere and cube combination is visible. This is a subtle problem, but remember the original goal was to move ONLY the object1.c inside bigobject.r - this technique moved more than that. It would be especially problematic if a modeler didn’t realize that object1.c was still in the display (assuming it is intended only as a sub-object for other objects) and the above command visually "did the right thing" - before re-running the B command the result would look correct. There are two reliable hints something is wrong. If the l is run on bigobject.r, no transformation matrix is observed next to object1.c:
mged> l bigobject.r bigobject.r: REGION id=1005 (air=0, los=100, GIFTmater=1) -- u object1.c u object3.c
and examining the l readout for all object1.c instances the results are identical, when the bigobject.r’s object1.c was intended to change:
mged> l bigobject.r/object1.c bigobject.r/object1.c: -- u sphere.s [0,0,4] u cube.s [0,0,4] mged> l bigobject.r/object3.c/object1.c bigobject.r/object3.c/object1.c: -- u sphere.s [0,0,4] u cube.s [0,0,4] mged> l object1.c object1.c: -- u sphere.s [0,0,4] u cube.s [0,0,4]
The correct way to do the above task would be to use path_lhs to identify precisely where the transformation is to occur. Remember in the beginning of the article the lollipop example showed instances of lhs path usage - this is an example where it is needed. First, object2.c must be returned to its previous position:
oed / object1.c/sphere.s
tra 0 0 -4
accept
B bigobject.r
Next the correct commands are entered:
oed /bigobject.r object1.c/sphere.s
tra 0 0 4
accept
B bigobject.r
Visually it is already clearer - there are now two offset object1.c combinations in the display. Repeating the l command also shows the expected results, with the combination showing a transformation matrix and the object1.c reports showing differences:
mged> l bigobject.r bigobject.r: REGION id=1005 (air=0, los=100, GIFTmater=1) -- u object1.c [0,0,4] u object3.c mged> l bigobject.r/object1.c bigobject.r/object1.c: -- u sphere.s [0,0,4] u cube.s [0,0,4] mged> l bigobject.r/object3.c/object1.c bigobject.r/object3.c/object1.c: -- u sphere.s u cube.s mged> l object1.c object1.c: -- u sphere.s u cube.s
There is a lot of potential here for unintended consequences. On the other hand, in some cases the modeler may want all instances of object1.c to pick up the changes. Awareness of the dangers inherent to this type of operation is an important skill for a modeler to acquire. [9]
Summary
The major points of the oed command are:
-
oed is the standard tool when working with combinations and regions instead of primitives.
-
Object edit mode does not alter primitives directly but instead works on a transformation matrix that BRL-CAD uses when interpreting the primitive.
-
If path_rhs is omitted, path_lhs is the complete path to the object being edited and its bounding-box center is the keypoint.
-
If path_rhs is supplied, it must end in a primitive shape whose default keypoint will be used for object editing.
-
push is used to force changes corresponding to the transformation down to the primitives, but the operation is destructive in that it replaces the previous information defining the primitive.
-
keypoint is used in object edit mode as the point moved by translate operations and around which rotations occur. It is set from the object’s bounding-box center or the primitive supplied to oed, and can also be set manually in object edit mode with the keypoint command.
-
When editing objects inside other objects it is important to remember to use path_lhs to precisely identify the shape to be worked on - subtle and messy errors are possible if the wrong path is used.
-
When using oed in a script, the object must be in view so a B or draw command to select the object must be entered before using the oed command on it.
Acknowledgements
Thanks to Christopher Sean Morrison, Paul Tanenbaum and Ruth Foutz for proofreading, suggestions and corrections.