AGI Components with Insight3D Alpha 2008 r8
Picking

Picking allows users to select and interact with objects in the 3D scene. Picks are usually executed in response to a mouse click, mouse move, key press, or any combination of these. For picking on overlays, see the overlays picking overview.

Topic Description
ExecutePick Regardless of the triggering event, a pick is always executed with AgGxScene.ExecutePick.
Normal Picking Commonly used to zoom to a primitive or bring up a context menu.
Roll-over Picking Used to select objects or display the cursor's cartographic position over a globe as the mouse moves across the window.
Drill Picking Used when the same location is clicked or double clicked a second time so the object under the top object can be acted upon.
ExecutePick

The ExecutePick method returns information about objects in the scene that are at a specified pixel. The input is an x and y coordinate. The origin is the top, left corner of the 3D control. In most cases, the input to ExecutePick will be the location of the mouse cursor to pick objects under the cursor. This section explains the collection returned by ExecutePick, and the following sections describe how to use ExecutePick to implement different types of picking.

ExecutePick returns a collection of pick results, IAgGxPickResultCollection. Each pick result, IAgGxPickResult, represents one occurrence of a pick. This includes the object(s) involved in the pick and their position in fixed and inertial frames. This is shown in the image below.

If multiple objects are at a pixel, the collection will contain multiple pick results. The first item in the collection will be the top most object. Subsequent items will be "under" the previous item. For example, if a viewer is looking straight down at a primitive on the ground and ExecutePick is called with coordinates that match the primitive, both the primitive and central body will be returned as shown below.

CopyC#
IAgGxPickResultCollection collection = scene.ExecutePick(x, y);

if (collection.Count == 2)
{
    IAgGxPrimitive primitive = collection[0].Objects[0] as IAgGxPrimitive;
    IAgGxCentralBody centralBody = collection[1].Objects[0] as IAgGxCentralBody;

    if (primitive != null && centralBody != null)
    {
        // primitive and central body underneath were picked
    }
}

In other cases, the collection will only contain a single pick result. For example, if a model primitive representing a satellite is picked, the central body may not be returned as shown below.

CopyC#
IAgGxPickResultCollection collection = scene.ExecutePick(x, y);

if (collection.Count == 1)
{
    IAgGxPrimitiveModel model = collection[0].Objects[0] as IAgGxPrimitiveModel;

    if (model != null)
    {
        // Just a model primitive was picked
    }
}

As shown in the code examples, a pick result contains a collection of objects. In most cases, this collection will only contain one object. One exception is when a picked primitive is in a composite. In this case, both the primitive and its composite will be in the objects collection as shown in the following image and code example.

CopyC#
IAgGxPickResultCollection collection = scene.ExecutePick(x, y);

if (collection.Count == 2)
{
    IAgGxVariantCollection objects = collection[0].Objects;

    if (objects.Count == 2)
    {
        IAgGxPrimitiveComposite composite = objects[0] as IAgGxPrimitiveComposite;
        IAgGxPrimitive primitive = objects[1] as IAgGxPrimitive;
        IAgGxCentralBody centralBody = collection[1].Objects[0] as IAgGxCentralBody;

        if (composite != null && 
            primitive != null &&
            centralBody != null)
        {
            // A primitive in a composite and the central body 
            // underneath were picked
        }
    }
}
Normal Picking

In normal picking, ExecutePick is called as the result of an event, such as a mouse click in the 3D control, to perform an action, such as zooming to a primitive. This is demonstrated by the following example from the HowTo.

CopyC#
// 
// Get a collection of picked objects under the mouse location.
// The collection is sorted with the closest object at index zero.
// 
IAgGxPickResultCollection collection = scene.ExecutePick(mouseX, mouseY);

if (!collection.Empty)
{
    IAgGxPrimitiveComposite models = collection[0].Objects[0] as IAgGxPrimitiveComposite;

    if (models == m_Models)
    {
        IAgGxPrimitiveModel model = collection[0].Objects[1] as IAgGxPrimitiveModel;

        CameraUtil.ZoomToPrimitiveOnGround(scene, model);
    }
}

This code snippet is called in response to a double click event in the 3D control. A pick is executed using the current position of the mouse cursor. If the collection returned is empty, nothing was picked. Otherwise, the topmost picked object is cast to a model primitive. Since we only want to zoom to a model if it is in our composite of models, the model is checked to see if it is in the composite before it is zoomed to. If the cast to a model did not succeed, perhaps because only the central body was picked, model will be null and Contains will return false, so the zoom would not occur.

Roll-over Picking

In roll-over picking, ExecutePick is called in response to the mouse moving across the 3D control. The results of the pick may be used to display the mouse cursor's cartographic position over a globe or, as shown in the following example, to highlight the primitive under the cursor.

CopyC#
// 
// Get a collection of picked objects under the mouse location.
// The collection is sorted with the closest object at index zero.
// 
IAgGxPickResultCollection collection = scene.ExecutePick(mouseX, mouseY);

if (!collection.Empty)
{
    IAgGxPrimitiveComposite models = collection[0].Objects[0] as IAgGxPrimitiveComposite;

    if (models == m_Models)       // Was a model in our composite picked?
    {
        IAgGxPrimitiveModel model = collection[0].Objects[1] as IAgGxPrimitiveModel;

        model.SetRGB(0, 255, 255);      // Selected model: cyan

        if (model != m_SelectedModel)   // Only redraw if needed
        {
            if (m_SelectedModel != null)
            {
                m_SelectedModel.SetRGB(255, 0, 0);  // Unselect previous model: red
            }
            m_SelectedModel = model;
            scene.Draw();
        }
        return;
    }
}

if (m_SelectedModel != null)
{
    m_SelectedModel.SetRGB(255, 0, 0);  // Unselect model: red
    m_SelectedModel = null;
    scene.Draw();
}

m_SelectedModel is a reference to the currently highlighted model, that is, the model currently under the cursor. Initially, it is null. ExecutePick is called with the mouse cursor's position in response to a mouse move event.

The model under the cursor is highlighted by setting its color to cyan. The previously selected model is set to red. Note that a model may be unhighlighted because either another model is now under the cursor or no model in the composite in under the mouse cursor. Since redrawing the scene every time the mouse moves may affect performance, the scene is only redrawn (via scene.Draw) if a model is highlighted or unhighlighted.

Drill Picking

Drill picking is used to "drill down" to objects underneath the top object. For example, the first time a user double clicks, the top object is picked. If the mouse doesn't move and the scene doesn't change (e.g. primitives aren't added or removed), the second time the user double clicks, the object underneath the top object is picked. And so on.

This is implemented using the ExecutePick method. The collection from the first call is saved and the closest object, the object at index 0 in the collection, is the picked object. The next time a pick occurs (e.g. next double click), if the mouse position and scene did not change, the picked object is now at index 1 in the collection. Typically, the index will roll back to 0 once it goes all the way through the collection.