Ayam: The Tcl Scripting Interface

Next Previous Contents

## 6. The Tcl Scripting Interface

The scripting interface is mainly a bunch of Tcl procedures and Tcl commands that a big part of Ayam uses internally.

Using Tcl, you could directly modify the code Ayam consists of. This is, however, not recommended for good reasons. So watch out for already existing procedures and commands when implementing your own!

Using procedures and commands not listed in this documentation is dangerous too. Implementation and interfaces of that commands may change in future versions.

The scripting interface may be used directly from the console of Ayam. You can, of course, also write procedures in your own Tcl script files, that may be loaded at any time into Ayam using the console and the Tcl command `"source"`.

You can also arrange for a script to be executed automatically on startup using the preference setting `"Main/Scripts"`.

Note that most of the listed commands work in background, without changing anything to the Ayam GUI and Ayam view windows, for execution speed. If you want your changes to become visible you have to update the various parts of the GUI explicitly (see also section Updating the GUI).

From scripts it may be necessary to check wether an error occured during the execution of a command. All commands return TCL_OK in any case so checking their return value avails to nothing, but they set the global Tcl variable `ay_error` to a value higher than 1 if an error occured. You need to set it to zero before and check it after the operation in question to see whether the operation performed successfully.

## 6.1 Global Variables

Several global variables exist in the Ayam Tcl context, that may be useful for scripts.

### The global array ay

The global array `"ay"` holds application state variables. Furthermore, you can find the paths to important widgets (e.g. the tree widget for the object hierarchy or the currently active view) in this array. Use `"parray ay"` in the console to see what is there. More documentation to come.

### The global array ayprefs

The global array `"ayprefs"` holds preferences data. The complete array is saved in the `"ayamrc"` file upon exit, so be careful when adding new entries. You can reset your `"ayamrc"` file anytime using the command line option `"-failsafe"`. Use `"parray ayprefs"` in the console to see what is there. More documentation to come.

### Miscellaneous global arrays and variables

Every property has a corresponding global array, where the data of the property is saved. This global array only holds useful data when the respective property GUI is active, or when it has been filled explicitly by the so called get-property callback. The data may be transferred back to the selected object using the so called set-property callback. The names of the array and the callbacks may be inferred from another global array that is named like the property itself, e.g. for the tags property the following global array is defined:

    array set Tags {
    arr   tagsPropData
    sproc setTagsp
    gproc getTagsp
    w     fTagsAttr
    }

If sproc or gproc are empty (`""`), a standard callback named `"setProp"` or `"getProp"` should be used.

The following global arrays and callbacks to get or set the data exist:

- property, array, get-property callback, set-property callback
- Transformations, transfPropData, getTrafo, setTrafo
- Attributes, attrPropData, getAttr, setAttrp
- Material, matPropData, getMat, setMat
- Tags, tagsPropData, getTagsp, setTagsp
- list incomplete, more to come...

More documentation to come.

## 6.2 Index of Procedures and Commands

### Creating Objects

To create new objects the `"crtOb"` command can be used.

- Synopsis: `"crtOb type [args]"`
- Description: New objects may be created with the command `"crtOb"`, type may be derived from the type name, as displayed in the tree view.

  Depending on the type, further arguments may (or have to) be given, other types expect objects to be selected:

  - `"NCurve"`: NURBS curves accept a single integer as length of the new curve, the length defaults to 4. Example: `"crtOb NCurve -length 10; uS; rV"`
  - `"NPatch"`: NURBS patches accept two integers as width and height of the new patch, width and height both default to 4. Example: `"crtOb NPatch -width 2 -height 2; uS; rV"`
  - `"Level"`: Levels must be given an additional argument determining the type of the new level, this argument may be one of: `"0"` (level), `"1"` (union), `"2"` (intersection), `"3"` (difference), `"4"` (primitive).
  - `"Material"`: Materials must be given an additional argument giving the name of the new material. Example: `"crtOb Material default; uS; rV"`
  - `"Instance"`: creates an instance of the selected object.
  - ...

### Manipulating the Selection

This command is probably the most important one, because a lot of the other commands operate on selected objects only.

selOb - select object(s):

- Synopsis: `"selOb [index]"`
- Description: Use this command to set or clear the current selection, index may be an ordered list of indizes, a single index or empty. If no index is given, the current selection will be cleared.

### Clipboard Operations

These commands operate the object clipboard:

copOb - copy object:

- Synopsis: `"copOb"`
- Description: Copy the selected object(s) to the object clipboard.

cutOb - cut object:

- Synopsis: `"cutOb"`
- Description: Move the selected object(s) into the object clipboard.

pasOb - paste object:

- Synopsis: `"pasOb"`
- Description: Copy the selected object(s) from the object clipboard to the current level.

delOb - delete object:

- Synopsis: `"delOb"`
- Description: Delete the selected object(s) from the scene.

cmovOb - paste (move) object:

- Synopsis: `"cmovOb"`
- Description: Move the objects from the object clipboard to the current level.

The following commands operate the object property clipboard, which is totally independent from the object clipboard.

pclip_copy - copy a property to the clipboard

- Synopsis: `"pclip_copy mode"`
- Description: Copy the currently selected property from the currently selected object to the property clipboard. If mode is 0, omit all marked entries, if mode is 1 copy just marked entries.

pclip_paste - paste a property

- Synopsis: `"pclip_paste"`
- Description: Copy the property from the property clipboard to the currently selected object. Note that you may call this procedure also using the shortcut `"pasteProp"`.

### Hierarchy Operations

These commands manipulate the current level of Ayam.

goDown:

- Synopsis: `"goDown index"`
- Description: Enter the object determined by index. If index is 0 and the current level is inside some other object (not the root) the parent level will be entered instead.

goUp:

- Synopsis: `"goUp"`
- Description: Go one level up in the object hierarchy.

goTop:

- Synopsis: `"goTop"`
- Description: Go to the top level of the object hierarchy.

### Transformations

movOb - move objects:

- Synopsis: `"movOb dx dy dz"`
- Description: Move the selected object(s) by dx in direction of the objects X axis, by dy in direction of the objects Y axis and by dz in direction of the objects Z axis.

rotOb - rotate objects:

- Synopsis: `"rotOb dx dy dz"`
- Description: Rotate the selected object(s) by dx degrees around the objects X axis, then by dy degrees around objects Y axis and then by dz degrees around the objects Z axis. Note the order of the rotations!

scalOb - scale objects:

- Synopsis: `"scalOb dx dy dz"`
- Description: Scale the selected object(s) by a factor of dx in direction of the objects X axis, by a factor of dy in direction of the objects Y axis and by a factor of dz in direction of the objects Z axis.
- Note: A scale factor of zero is generally a bad idea and thus will be changed to 1.0 silently!

movSel - move selected points:

- Synopsis: `"movSel dx dy dz"`
- Description: Move the selected points by dx in direction of the objects X axis, by dy in direction of the objects Y axis and by dz in direction of the objects Z axis.

rotSel - rotate selected points:

- Synopsis: `"rotSel dx dy dz"`
- Description: Rotate the selected points by dx degrees around the objects X axis then by dy degrees around objects Y axis and then by dz degrees around the objects Z axis. Note the order of the rotations!

scalSel - scale selected points:

- Synopsis: `"scalSel dx dy dz"`
- Description: Scale the selected points by a factor of dx in direction of the objects X axis, by a factor of dy in direction of the objects Y axis and by a factor of dz in direction of the objects Z axis.
- Note: A scale factor of zero is generally a bad idea and thus will be changed to 1.0 silently!

delegTrafo - delegate transformations:

- Synopsis: `"delegTrafo"`
- Description: delegates the transformations associated with the selected objects to their child objects. Additionally, the transformations of the selected objects will be reset to the default values.

applyTrafo - apply transformations:

- Synopsis: `"applyTrafo sel|all"`
- Description: applies the transformations encoded in the transformation attributes of the selected objects to the points (either all points, or just the selected ones if there are any) of those objects. Additionally, the transformations of the selected objects will be reset to the default values.

### Manipulating Shaders

shaderSet:

- Synopsis: `"shaderSet shadertype [varname]"`
- Description: Set the shader of type shadertype for the selected object. Type may be one of `"surface"`, `"displacement"`, `"light"`, `"imager"`, `"atmosphere"`, `"exterior"` or `"interior"`. If varname is not given, the shader in question is deleted from the object instead.

shaderGet:

- Synopsis: `"shaderGet shadertype varname"`
- Description: Get the shader of type shadertype for the selected object, . Type may be one of `"surface"`, `"displacement"`, `"light"`, `"imager"`, `"atmosphere"`, `"exterior"` or `"interior"`. The shader will be written to an array pointed to by varname.

### Manipulating Tags

addTag:

- Synopsis: `"addTag type value"`
- Description: Add a tag with type-string type and value-string value to the currently selected objects(s). It is legal to deliver `""` as value parameter. This is e.g. needed for the `"NoExport"` tag.

delTags:

- Synopsis: `"delTags type"`
- Description: Delete all tags of type-string type from the currently selected objects(s). If type is `"all"`, all tags are deleted from the currently selected objects(s).

getTags:

- Synopsis: `"getTags tvname vvname"`
- Description: Get all tags from the currently selected objects and put them as lists into two variables named tvname for the tag type-strings and vvname for the tag value-strings.

setTags:

- Synopsis: `"setTags tags"`
- Description: Clear all tags from the currently selected object and set new tags. The tag type-strings are taken from the list elements with even index numbers and the tag value-strings from the list elements with odd index numbers.

### Manipulating NURBS Curves and Surfaces

clampNC:

- Synopsis: `"clampNC"`
- Description: Clamp the knot vector of the selected NURBS curves without changing the shape of the curves. The knot type of the clamped curve will be changed to custom and the knots will have o equal values at start and end (where o is the order of the curve).

elevateNC:

- Synopsis: `"elevateNC n"`
- Description: Elevate the order of the selected NURBS curves without changing the shape of the curves by n. The knot type of the elevated curves will be changed to custom.

insknNC - insert knot:

- Synopsis: `"insknNC u r"`
- Description: Insert a new knot at the position specified by u (u must be in the valid range of the knot vector of the selected curves) r times. The valid range is determined by the current knot vector U as follows: `U[p] <= u <= U[n]`, where p is the degree (order-1) of the curve and n is the length of the curve. The knot type of the curves will always be changed to custom but the shape of the curves will not change!

refineNC:

- Synopsis: `"refineNC [{u1 u2 un}]"`
- Description: Refine the knot vector of the selected NURBS curve without changing the shape of the curve with n new knots {u1 u2 un}. Or, if no list of new knots is given, add a new knot into each interval in the old knot vector. The knot type of the refined curve will be changed to custom.

revertNC:

- Synopsis: `"revertNC"`
- Description: Revert the direction of the selected NURBS curves.

rescaleKnNC:

- Synopsis: `"rescaleKnNC"`
- Description: Rescale the knot vector(s) of the selected NURBS curve(s) to the range 0.0 1.0. The knot type of the curve has to be `"Custom"`! This operation does not change the shape of the curve.

splitNP:

- Synopsis: `"splitNP (u|v)"`
- Description: splits the selected NURBPatch into NURBS curves, along u or v.

buildNP:

- Synopsis: `"buildNPatch"`
- Description: builds a NURBPatch from the selected NURBS curves.

### Manipulating Points

Use these two commands to read or manipulate single points of arbitrary objects. Note that the exact arguments needed, depend on the type of the object, e.g. a NURBS curve requires just one index parameter (indexu), whereas a NURBS patch requires two (indexu and indexv).

getPnt:

- Synopsis: `"getPnt [-trafo] indexu [indexv] varx vary varz [varw]"`
- Description: Get a control point of the currently selected object and write it's coordinates into the variables varx, vary, varz and varw. If the special argument `"-trafo"` is given, the coordinates will be transformed by the values given in the objects Transformation property.

setPnt:

- Synopsis: `"setNP indexu [indexv] x y z [w]"`
- Description: Set a control point of the currently selected object to the coordinates x, y, z and w.

### Updating the GUI

rV - redraw all views:

- Synopsis: `"rV"`
- Description: Redraws all currently open views, except for iconified views and views where automatic redraw has been turned off.

uS - update select:

- Synopsis: `"uS [update_prop maintain_selection]"`
- Description: Update the object listbox or tree view after a change to the object hierarchy.

  If update_prop is 0 no update of the property GUIs will take place. If maintain_selection is 1 the old selection will be established again. If both arguments are omitted update_prop defaults to 1 and maintain_selection to 0.

- Deficiencies: uS completely removes the object tree from the tree widget and rebuilds it, which can be a very time consuming operation (depending on the complexity of the scene). There are some options to speed this process up:
  - If there were just changes to the current level (and below) the global array entry `"ay(ul)"` (UpdateLevel) may be set to the current level before calling `"uS"`. This will not remove and update the complete scene but just the part below `"ay(ul)"`. Example: `set ay(ul) $ay(CurrentLevel); uS;`.
  - If objects have been created and thus just need to be added to the current level, the command `"uCR"` may be used instead of `"uS"`.
  - If just names or types of objects of the current level changed, the command `"uCL cl"` may be used instead of `"uS"`.

uCL - update current level:

- Synopsis: `"uCL mode [args]"`
- Description: see discussion of `"uS"` above.

uCR - update current level after create:

- Synopsis: `"uCR"`
- Description: see discussion of `"uS"` above.

plb_update - property listbox update:

- Synopsis: `"plb_update"`
- Description: Clear the current property GUI, ask the selected object for a list of properties and insert them in the property listbox, then rebuild the property GUI (with the same index as the property seleted before; this is not necessarily a property of the same type).

### Custom Objects

io_lc - load custom:

- Synopsis: `"io_lc filename"`
- Description: Load the custom object from file filename.

### Applying Commands to a Number of Objects

There are two commands that help to apply arbitrary commands to a number of objects, forAll and forAllT.

forAll:

- Synopsis: `"forAll recursive command"`
- Description: The forAll command executes command for all objects that have been selected currently, or for every object of the current level if nothing has been selected. If recursive is 1 then forAll will recurse into every object (if it has child objects) before the execution of command. Note that forAll will run slowly if a property GUI is displayed. You can make it run faster by de-selecting the property using e.g. the property context menu first.
- Deficiencies:
  - A recursive forAll will e.g. also descend into NURBS patches (if they have trim curves) and apply the command to the trim curves, which might not exactly be what you want. Use `"forAllT"` in this case.
  - The command will not have access to global arrays unless e.g. one of the following construct is in use:

    `"forAll 0 { uplevel #0 { commands } }"`

    `"forAll 0 { global arrayname; commands }"`

  - It is not possible to use commands that change the object hierarchy (e.g. deleting or inserting objects). The commands may just modify existing objects.

forAllT:

- Synopsis: `"forAllT type recursive command"`
- Description: forAllT works the same way as forAll, with an additional type check. The command will not be executed if the type of the current object does not match the argument type. Note that forAllT will run slowly if a property GUI is displayed. You can make it run faster by de-selecting the property using e.g. the property context menu first.

  Note that the type strings will be converted to lowercase before comparison, so that it is legal to use forAllT e.g. this way:

  `"forAllT ncurve 0 {puts $i}"`

- Deficiencies:
  - The command will not have access to global arrays unless e.g. one of the following constructs is in use:

    `"forAllT ncurve 0 {uplevel #0 {commands} }"`

    `"forAllT ncurve 0 { global arrayname; commands }"`

  - It is not possible to use commands that change the object hierarchy (e.g. deleting or inserting objects). The commands may just modify existing objects.

### Scene IO

newScene:

- Synopsis: `"newScene"`
- Description: clears the current scene.

replaceScene:

- Synopsis: `"replaceScene filename"`
- Description: clears the current scene, then loads a new scene from filename.

insertScene:

- Synopsis: `"insertScene filename"`
- Description: inserts a scene from filename.

saveScene:

- Synopsis: `"saveScene filename"`
- Description: saves the current scene to filename.

### Reporting Errors

ayError:

- Synopsis: `"ayError code place detail"`
- Description: This command reports errors or warnings. You should always use ayError instead of puts because the error reporting mechanism of Ayam features compression of repeated messages and logging to files. Code should be one of: 1: warning, 2: error, 3: flush messages, 4: unspecified output. There are more codes defined (see ayam.h, look for Return/Error Codes) but they are not needed in the Tcl script context. Place should describe the procedure where the error occured. Detail is the string to be output.

### Miscellaneous

getType:

- Synopsis: `"getType varname"`
- Description: This command writes the type of the selected object into the variable varname. The types are the well known strings that are displayed in the hierarchy list box if the objects are not named (NPatch, NCurve, Sphere, etc.).

tmpGet:

- Synopsis: `"tmpGet tmpdir varname"`
- Description: This command calculates a name for a temporary file in tmpdir and puts the complete name into varname.

hasChild:

- Synopsis: `"hasChild"`
- Description: This command returns 0 if the selected object has no child objects, otherwise it returns 1.

undo:

- Synopsis: `"undo [redo|save|clear]"`
- Description:
  - If called without arguments, this command performs the undo operation.
  - If the argument is `"redo"`, this command performs the redo operation.
  - If the argument is `"save"`, the currently selected objects are saved to the undo buffer for future undo operations.
  - If the argument is `"clear"`, the undo buffer is cleared.

convOb:

- Synopsis: `"convOb [-inplace]"`
- Description: This command calls the registered converter for the selected object(s). If the option `"-inplace"` is used the new object(s) will replace the old object(s).

forceNot:

- Synopsis: `"forceNot"`
- Description: This command calls the registered notification callback for the selected object(s) and their parents, or if no object is selected for all objects of the scene.

## 6.3 Scripting Interface Examples

Here are some example scripts for the Ayam Tcl scripting interface.

You may copy and paste all examples directly from the documentation into the console of Ayam.

### Moving Objects

The following example script shows how to move a selected object to a specified position in space.

    proc placeOb { x y z } {
      global transfPropData

      # copy Transformations-property data to
      # global array "transfPropData"
      getTrafo

      # set array values according to procedure parameters
      set transfPropData(Translate_X) $x
      set transfPropData(Translate_Y) $y
      set transfPropData(Translate_Z) $z

      # copy Transformations-property data from
      # global array "transfPropData" to selected object
      setTrafo
    }
    # placeOb

In order to move all selected objects to 1 1 1 you may enter the following into the console:

    forAll 0 {placeOb 1 1 1}

But perhaps you would rather like a small GUI for that? No problem, the following snippet adds an entry to the custom menu that opens a small requester for the x-, y-, and z-values and calls the `"placeOb"` procedure (defined above) with them:

    global ay
    $ay(cm) add command -label "Place Object" -command {
     runTool {x y z} {"X:" "Y:" "Z:"} "forAll 0 {placeOb %0 %1 %2}"
     uS
    }

### Moving NURBS points

The following example script snippet shows how to move control points of a NURBS curve.

    # first, we create a new NURBS curve with 30 control points
    set len 30
    crtOb NCurve -length $len
    # update selection
    uS
    # select last object (the newly created curve)
    sL
    # prepare moving
    set i 0
    set w 1.0
    set angle 0
    while { $i < $len } {

        set x [expr 3*cos($angle)]
        set y [expr 3*sin($angle)]
        set z [expr $i/3.0]

        # move control point to new position
        setPnt $i $x $y $z $w

        incr angle 90
        incr i
    }
    # redraw all views
    rV

Now use this as path for a Sweep. For instance, using the next small script.

### Easy Sweep

The following example script shows how to easily create a sweep from a selected path curve (avoiding the manual and lengthy creation and parameterisation of a suitable cross section).

    proc easySweep { } {
     # first, we create a sweep object
     crtOb Sweep

     # now, we need to move the selected curve (path) to
     # the sweep and create a cross-section curve there too
     # for that, we move the currently selected curve to the clipboard
     cutOb
     uS

     # how does the current level look like?
     getLevel a b

     # enter the Sweep (the last object in the current level)
     goDown [expr [llength $a]-1]
     uS

     # now, we create a new curve (a closed BSpline suitable as cross section)
     crtClosedBS 8
     uS

     # how does the current level look like?
     getLevel a b

     # select last object (the newly created curve)
     selOb [expr [llength $a]-1]

     # now, we rotate and scale the curve
     rotOb 0 90 0
     scalOb 0.25 0.25 1.0

     # move trajectory back (we use "cmovOb" and _not_ "pasOb", because we
     # really move (and not copy) the curve
     cmovOb
     # go up to where we came from
     goUp

     # update GUI
     uS
     sL
     # redraw all views
     rV
    }
    # easySweep

You may add this command to the main menu as well:

    global ay
    $ay(cm) add command -label "Easy Sweep" -command {
     easySweep
    }

### Tool Box Buttons

Here is another example that shows how you may add buttons to the tool box. myImage should be an image created e.g. from a GIF file of the size 25 by 25 pixels.

    global ay

    # create an image from a GIF file:
    image create photo myImage -format gif -file /home/user/agif

    set b .tbw.f.mybutton

    # if the button not already exists:
    if { ![winfo exists $b] } {

      # create it:
      button $b -padx 0 -pady 0 -image myImage -command myCommand

      # tell Ayam about the new button:
      # you can use linsert, to insert the button in a specific
      # place or just append to the end of the list
      lappend ay(toolbuttons) mybutton

      # display the button:
      # from now on, it will be under the
      # automatic tool box layout management
      toolbox_layout
    }

This example shows that a) buttons have to be created in the frame `".tbw.f"` b) Ayam manages a list of all buttons in the global array ay in `"ay(toolbuttons)"`, the order in that list is the order in which buttons appear in the tool box c) automatic layout management is carried out by the procedure `"toolbox_layout"`.

Adding buttons with just text is a little bit more involved, as the sizes of the new buttons often do not fit well in the icon button scheme with its constant button size.

Here is an example that adds two buttons to the bottom of the tool box spanning the whole window (this works best with the standard tool box layout of 4 by 12 buttons):

    # create a frame:
    set f [frame .tbw.f.fcollex]

    # create two buttons inside the frame:
    button $f.b1 -width 5 -text "Coll." -command { collNC; rV; }
    button $f.b2 -width 5 -text "Expl." -command { explNC; rV; }
    pack $f.b1 $f.b2 -side left -fill x -expand yes

    # calculate the row number below the last row:
    set row [expr [lindex [grid size .tbw.f] 1] + 1]

    # now display the frame at calculated row, spanning the whole window:
    grid $f -row $row -column 0 -columnspan [lindex [grid size .tbw.f] 0]

Next Previous Contents