Mouse Tool Clauses

The <tool_body> of a mouse tool definition is made up of a sequence of tool clauses as follows:

<tool_body>     ::= { <tool_clause> }+

Tool clauses define the components of a tool and can be one of two basic things:

Formally, the syntax of a <tool_clause> is defined as follows:

<tool_clause>   ::= <local_variable_decl> |

                    <local_function_decl> |

                    <local_struct_decl>   |

                    <event_handler>

Locals

A <local_variable_decl>, <local_function_decl>, and <local_struct_decl> are exactly the same as local variable, function, and structure definitions in MAXScript:

<local_variable_decl> ::= local <decl> { , <decl> }

<decl>                ::= <name> [ = <expr> ] -- optional initial value

<local_function_decl> ::= [ mapped ](function | fn) <name> { <argument> } = <expr>

<local_struct_decl>   ::= struct <name> ( <member> { , <member> } )

<member>              ::= ( <name> [ = <expr> ] | <local_function_decl> )

Global variables cannot be declared as a tool clause, however they can be declared within event handler scripts. If you need to ensure a variable name references a global variable, declare the variable name as global immediately before defining the tool in your script.

When writing scripts, it is good programming practice to explicitly declare your local and global variables. Implicit declaration is provided as a short-hand, typically used when working in the Listener interactively or developing short scripts. When developing extended scripts, explicitly declaring variables can reduce errors and improve readability of the code. It is also recommend that you declare as local all variables unless you really want them to be global variables. The reasons for this are described in Scope of Variables.

Within the functions and event handlers of a tool, 13 pre-defined locals variables are always accessible. Each one holds a useful value relating to the mouse action that just occurred:

viewPoint         Point2

The current mouse position in viewport pixel coordinates.

worldPoint        Point3

The current mouse position projected on the active grid in world coordinates.

worldDist         Point3

The distance in X, Y, and Z from the previous click point to the current click point in world coordinates.

worldAngle        Point3

The angles around the world X, Y, and Z axes from the previous click point to the current click point in world coordinates.

gridPoint         Point3

The current mouse position projected on the active grid in the coordinate system of the active grid. If the active grid is the home grid, the coordinate system is the home grid plane active in the current viewport (that is, the construction plane). See the discussion of grid versus world values below.

gridDist          Point3

The distance in X, Y, and Z from the previous click point to the current click point in the coordinate system of the active grid. If the active grid is the home grid, the coordinate system is the home grid plane active in the current viewport (that is, the construction plane). See the discussion of grid versus world values below.

gridAngle         Point3

The angles around the world X, Y, and Z axes from the previous click point to the current click point in the coordinate system of the active grid. If the active grid is the home grid, the coordinate system is the home grid plane active in the current viewport (that is, the construction plane). See the discussion of grid versus world values below.

shiftKey          Boolean

true if SHIFT key was down.

ctrlKey           Boolean

true if CTRL key was down.

altKey            Boolean

true if ALT key was down.

lButton           Boolean

true if left mouse button was down.

mButton           Boolean

true if middle mouse button was down.

rButton           Boolean

true if right mouse button was down.

When a create mouse tool is used in a level 5 scripted plug-in, an additional local variable is accessible:

nodeTM            Matrix3

Provides read/write access to the transform of the node currently being created. This value is in the current grid coordinate system.

See Scripted Plug-ins for more information.

When writing SimpleObject scripted plug-ins, you should always use the gridPoint, gridDist, and gridAngle values rather than corresponding world values, as object-creation in SimpleObject scripted plug-ins is managed in the active grid coordinate system.

For gridDist, the .X and .Y components are the delta X and Y between the previous clicked point and the current mouse point in the plane of the current grid. The .Z component is a projection from the current Y screen coordinates onto a Z-vector (in the grid coordinate system) based at the last point clicked on the grid, such that the .Z value is the projected height up the Z-vector. For non-orthogonal viewports this is as expected, but for orthogonal viewports (in which you are always dragging in the XY plane of the grid), this is always the same as gridDist.y. If you are using the gridDist value to build the portion of an object on the grid, or determine the distance on the current grid from the last point, you want to use only the .X and .Y components. For example,

side1Len = gridDist.x

side2Len = gridDist.y

dist=length [gridDist.x,gridDist.y]

If you are specifying the height off of the current grid, you would typically use the .Z component. For example,

height = gridDist.z

For worldDist, the behavior is similar to that for gridDist, however the projected component is dependent on the construction plane of the viewport. For the Top, Bottom, and non-orthogonal viewports, the projected height is contained in the .Z component. For Left and Right viewports, the projected height is contained in the .X component. For Front and Back viewports, the projected height is contained in the .Y component.

When you create a node in the 3ds max user-interface, the local Z axis of the node is perpendicular to the construction plane. When you create a node using MAXScript, by default the local Z axis of the node points along the direction of the world Z axis. If you create a node in a tool (other than in a SimpleObject scripted plug-in), you must take into account the construction plane orientation if you want to duplicate 3ds max's behavior when creating a node. The easiest way to do this is to create the node in a coordsys grid context and specify the position of the node during creation in grid coordinate space. An example of this is shown in the following script.

Example:

tool PointCreator

(

local p, createpoint

-- define a function to perform actual node creation. Setting coordsys to

-- 'grid' in order for the alignment of the node's local Z axis to be

-- perpendicular to the construction grid

fn createpoint = in coordsys grid p=point pos:gridPoint

--

-- set up so that a node is created on a mouse button down, move node

-- drag, release node at mouse button up.

--

-- if clickno == 1, then we are at first mouse click, which is a mouse

-- button down. If clickno != 1, at following mouse button up.

on mousePoint clickno do

(

if clickno == 1

then createPoint()

-- if p == undefined, then clicked twice without mouse movement

-- (double clicked). No point object present, so just ignore this click.

else if p != undefined do (p.pos=worldPoint;p=undefined)

)

-- if p != undefined, we are moving a previously created node

-- if p == undefined, and left mouse button is down, create a node

on mouseMove clickno do

(

if p != undefined

then p.pos=worldPoint

else if lbutton do createPoint()

)

)

-- start the tool. No exit condition defined, so right-click to exit.

startTool PointCreator

Event Handlers

An <event_handler> is a special function definition local to a tool that you provide to handle the processing you want to occur when a user clicks in a viewport, right-clicks, or starts or stops the tool. Each user action generates a named event and any event handler you supply for that event is called when the action occurs. The available event handlers are:

on start do <expr>

Called when the tool starts.

on end do <expr>

Called when the tool ends.

on freeMove do <expr>

Called when the mouse moves prior to the first click.

on mousePoint <arg> do <expr>

Called for each mouse click, parameter arg contains the number of the click.

on mouseMove <arg> do <expr>

Called when the mouse moves anytime after the first click.

on mouseAbort <arg> do <expr>

Called when the user right-clicks to cancel or presses the ESC key.

The <arg> parameter on each of the mouse event handlers lets you know which mouse click you are in. Mouse clicks are actually only the mouse click release, except for the initial mouse click. If you do a mouse click, drag, release, move, click, drag, and release, the on mousePoint event handler is called three times: the first click, the first release, and the second release, with <arg> values of 1, 2, and 3, respectively. The mouse click counter is incremented after processing the on mousePoint event handler, so the <arg> parameter for the mouseMove and mouseAbort event handlers specify the click count for the next mouse click. In the previous example, when you drag between the first click and first release, the <arg> value for a mouseMove event handler will have a value of 2. During the following move, click, and drag it will have a value of 3.

The following script simply demonstrates when the various event handlers are called. Simply run this script and drag in the viewports. Right-click or press the ESC key to stop the tool's execution.

Example:

tool foo

(

on freeMove do print "Free Moving"

on mousePoint clickno do format "Point: %\n" clickno

on mouseAbort clickno do format "Abort: %\n" clickno

on mouseMove clickno do format "Moving: %\n" clickno

on start do print "Starting"

on end do print "Ending"

)

startTool foo prompt:"Hello!"

All of the mouse action event handlers may return the special value #stop which causes the tool to stop. In the following mousePoint event handler example, the first click creates the box and the release (second click) terminates the tool.

on mousePoint clickno do

if clickno == 1

then b = box pos:worldPoint

else #stop

The following script implements the following functions: on mouse click three spot lights are created at the mouse point. While holding the mouse button down, drag the mouse to position the classical key, back and fill light positions. During this drag, holding down the SHIFT key will flip the fill light side. Release the mouse button and move the mouse to change the elevation of the lights. Click the mouse button when the lights are at the correct height. The back light is raised 1.5 times the key light's height, and the fill light is raised 0.75 times the key light's height.

To start this tool, you'd say:

startTool three_lights

and then drag in one of the viewports.

Example:

tool three_lights

(

local key, fill, back, targ

on mousePoint click do coordsys grid

(

if click == 1 then -- create key, back & fill lights at mousedown

(

targ=targetobject pos:gridPoint

key = targetspot pos:gridPoint name:"key" target:targ

back = targetspot pos:gridPoint name:"back" target:targ

fill = targetspot pos:gridPoint name:"fill" target:targ

)

if click == 3 then #stop

)

on mouseMove click do

(

if click == 2 then -- drag out & round on x-y plane

(

coordsys grid key.pos = gridPoint

coordsys targ back.pos = - key.pos

local p = if shiftKey then 90 else -90

coordsys targ fill.pos = key.pos * ((eulerangles 0 0 p) as quat)

)

else if click == 3 then -- drag up to elevate lights

(

in coordsys targ

(

local Z = gridDist.z

key.pos.z = Z

back.pos.z = Z * 1.5

fill.pos.z = Z * 0.5

)

)

)

)

See also