Saturday Snippet: Getting the object under the mouse in a context menu callback


For most context menus, the Target attribute contains both the selected objects (if any) and the object under the mouse.

def MyCustomMenu_Init( in_ctxt ):
    oMenu = in_ctxt.Source
    oMenu.Filter = "camera"
    oMenu.AddCallbackitem("Custom Camera Tool","MyCameraTool")
    return true

def MyCameraTool(in_ctxt):
    obj = in_ctxt.GetAttibute("Target")
    Application.LogMessage(obj)

I’m pretty sure I’m the one who dug up that info and put it in the docs (on the Menu Item Callback page). I did not, however, write this bit, which to my ear does not sound good at all:

If your menu is attached to a contextual menu, the currently selected objects are passed in to your callback. The target object under the cursor is also passed in as part of the selected objects. However if no objects are selected, then only the target is passed in. The objects can be retrieved through the Context.GetAttribute method with “Target” specified as the value for the AttributeName parameter. The selected/target objects are not passed in to the callback of a custom menu item attached to a regular menu.

I prefer something more like this:

For context menus, the callback gets the currently selected objects and the object under the mouse. You can get these target objects from the Target attribute with Context.GetAttribute.

Context matters: Add Point is always per-object


Unlike Clone Point, the output context of Add Point is always per-object, no matter what you plug into its input ports. So, for example, you cannot plug Add Point into an Execute on Emit port (which is a per-point port). Making Add Point per-object makes sense to me, because typically you want to add N points to a point cloud, not add N points for every point that is already in the target point cloud (that already sounds confusing).

One consequence of Add Point being per object is that you cannot use an If node to copy over some subset of points to the target point cloud. For example, if your If node is already plugged into something that makes it per-point then you’ll get a context error:

If your If node wasn’t plugged in yet, you’d get some red nodes like this:

The solution is to use a Filter on the other side [upstream] of the Add Point:

hat tips to Gray and Julian

Copying a polygon attribute to vertices


This question came up last weekend (on si-community and xsibase): how do I copy a polygon attribute to a point attribute?

As Chris_TC and gray pointed out, a point can be associated with multiple polygons. So you use VertexToPolygons to either build a per-point array of polygon attribute values, or you do something like average the polygon attribute values and store that as a point attribute value.

Context matters: from PolygonToVertices to VertexToPolygons


This came up last week on the XSI mailing list. The goal, as I understand it, was this: for each polygon, build an array that contains the indices of all polygon neighbors.

So, to start, you can get an array of vertex locations for each polygon:

But when try to use those locations to get VertexToPolygons, that’s when you hit a problem:

Usually if it’s a context mismatch, you won’t be able to plug in the connection. In this situation, you can plug the result of Point Index to Location into the Get VertexToPolygons, but as soon as you do, both nodes go red. The tooltip indicates that ICE cannot figure out the structure of the data, that is, is this a single value or an array of values? The error goes back up to the Index port on Point Index to Location (if you right-click Point Index to Location and click Show Messages, you’ll get “ERROR: The type of port ‘Index’ is not defined.”).

If you unplug Get PolygonToVertex, then things are ok:

So, it is possible to use a Repeat node to build the per-polygon array of neighbor polygons.

Context Matters: weight maps and polygons


I saw a few questions about context recently, so I thought I’d try to work my way through those questions, starting with some basic scenarios. First up, then, is a question about using weight maps to control subdivision.

In this case, we’re dealing with per-point and per-polygon contexts.

A problem with using weight maps to control subdivision is that weight maps are per-point:

So you’ll end up with a context mismatch if you try to do something like this:

The context mismatch makes sense, since every polygon has more than one point, so what can a per-point weight map value mean for a polygon? One way around this would be to do something like this, and use the average of the per-point weight map values, but in a per-polygon context:

Here’s the full ICE tree:

Another approach would be to somehow use a per-polygon location, because with a location you can get the interpolated value of the weight map values: