How To - Practical Examples >

# How To ... Move Objects to a Surface

How To > Move Objects to a Surface

The following will let the user move objects along their Z axis to fit a specified surface. Such a script can be used to automatically plant trees, poles etc. on the ground.

Related Topics:

Defining MacroScripts

IntersectRay

NATURAL LANGUAGE
Package the code as macroScript to be able to use as a button, menu item or shortcut.
Define a custom function for moving objects to a surface down the Z axis
Find the intersection along -Z starting from the position of the object to be moved to the target object.
If no intersection can be found, project the position of the object up the Z and intersect again down the –Z axis.
Return the intersection as result of the function.
Enable the macroScript only when objects are selected.
When the macroScript is executed, let the user pick a target object.
Filter the pick to allow only Geometry objects to be picked.
In case a valid object has been picked, enable an undo context
Go through all objects and call the intersection function for each one.
As long as the intersection is not undefined, move the object to the intersection.

MAXSCRIPT
    macroscript MoveToSurface category: "HowTo"
    (
    fn g_filter o = superclassof o == Geometryclass
    fn find_intersection z_node node_to_z =
    (
     local testRay = ray node_to_z.pos [0,0,-1]
     local nodeMaxZ = z_node.max.z
     testRay.pos.z = nodeMaxZ + 0.0001 * abs nodeMaxZ
     intersectRay z_node testRay
    )
    on isEnabled return selection.count > 0
    on Execute do
    (
     target_mesh = pickObject message:"Pick Target Surface:" filter:g_filter
     if isValidNode target_mesh then
     (
      undo "MoveToSurface" on
      (
       for i inselection do
       (
        int_point = find_intersection target_mesh i
        if int_point != undefined then i.pos = int_point.pos
       )--end i loop
      )--end undo
     )--end if
    )--end execute
    )--end script

### Step-By-Step

    macroscript MoveToSurface category:"HowTo" (

The macroScript will be called MoveToSurface . To use the script, you can go to Customize... and drag the script from the category "HowTo" to a toolbar, a menu, a quad menu or assign to a keyboard shortcut.

Defining Macro Scripts

    fn g_filter o = superclassof o == Geometryclass

This custom function will be used as filter for geometry objects. It will accept an object as parameter and will return true when the object is of Geometry Superclass and false otherwise.

    fn find_intersection z_node node_to_z = (

This custom function will do the actual ray intersection. It will accept two parameters – the target object and the object to be moved to the target object’s surface.

Creating Functions

    local testRay = ray node_to_z.pos [0,0,-1]

Here we define a custom Ray value with a starting position at the position of the object to be moved and direction along the -Z axis.

Ray Values

    local nodeMaxZ = z_node.max.z

We also get the max. Z coordinate of the surface to intersect with.

    testRay.pos.z = nodeMaxZ + 0.0001 * abs nodeMaxZ

Then we move the Z position of the ray slightly above the highest Z point of the surface to intersect with. This way we can be sure that the ray will have a chance to hit the surface along the -Z axis.

    intersectRay z_node test_ray

This built-in intersectRay function is given a node and a ray (spatial vector with a start point and direction) and returns the point in space where the ray hits the surface of the node, or undefined if there is no intersection. The result of this intersection will be also the return value of the function as it is the last value calculated.

IntersectRay

    ) on isEnabled return selection.count > 0

The script can only do meaningful job when at least one object is selected. If there are no selected objects in the scene, the script will be disabled. The 'on isEnabled' handler controls the enabled state by evaluating the expression after the 'return' statement. When the result is false , the script’s button resp. Menu item is greyed out and cannot be activated.

Macroscript_Body_Event_Handlers

    on Execute do (

The body of the script is contained in the 'on Execute' handler. It is executed when the script is started by pressing its button, selecting its menu item or pressing assigned shortcut keys.

Macroscript_Body_Event_Handlers

    target_mesh = pickObject message:"Pick Target Surface:" filter:g_filter

The pickObject function lets the user pick objects in the scene. The filter function we provided allows the picking of Geometry objects only – the mouse will not register any other objects like Lights, Helpers etc. when moving over them! The result will be written to the user variable target_mesh. It can be either the picked node or undefined in case the picking has been canceled.

Picking Scene Nodes By Hit

    if isValidNode target_mesh then (

If the picked object is a valid object (which means the user actually picked an object and did not press Escape or right-clicked the mouse to cancel)...

If Expression

isValidNode

    undo "MoveToSurface" on (

...then we define an undo context to be able to reset all objects back to their original state if necessary...

undo

    for i in selection do (

...and create a loop that will go through all objects in the current selection. Each iteration of the loop, the variable i will contain another object from the selection.

For Loop

    int_point = find_intersection target_mesh i

Here we call the user function we defined and pass the target_mesh object picked by the user and the current objects from the loop through the selection. The result will be either undefined or the point of intersection – the projection of the object’s position onto the surface of the target object along –Z.

    if int_point != undefined then i.pos = int_point.pos

If the intersection really exists, we simply set the position of the current object to that point. If it doesn’t (the intersection is undefined), we skip this step.

If Expression

Undefined Value

    )--end i loop )--end undo )--end if )--end execute )--end script

### Using the Script

Evaluate the script. To use it, you can use Customize... to drag the script from the category "HowTo" to a toolbar, a menu, a quad menu or to assign to a keyboard shortcut.

Select any number of objects you wish to move to a surface. Start the script and pick the object to move to.

### Where to go from here

Using this script as the starting point, you could add axes controls to allow alignment in any world or local direction.

Back to

"How To" Tutorials Index Page