Many haven’t realized this, but we added the ability to transform between 2D and 3D Visuals back in 3.5. This is handy if you need to draw 2D content around your 3D object or if you want to know the 2D position of a 3D point without doing a hit test. The methods are:

(Note that Visual and Visual3D already had TransformToFoo methods to move up and down their own trees.)

General transforms have a Transform method that operates on points and a TransformBounds method operates on rects for convenience. General transforms aren’t guaranteed to succeed so you need to be careful with them.

I wrote a little 3D XAML scene that animates a torus in a Canvas and then added this code to place a blue rectangle around the torus:

void CompositionTarget_Rendering(object sender, EventArgs e)
{
    GeneralTransform3DTo2D gt = visual3d.TransformToAncestor(viewport3d);
    // If null, the transform isn't possible at all
    if (gt != null)
    {
        Rect bounds = gt.TransformBounds(VisualTreeHelper.GetDescendantBounds(visual3d));
        // If empty, visual3d's specific bounds couldn't be transformed
        if (!bounds.IsEmpty)
        {
            rect.Width = bounds.Width;
            rect.Height = bounds.Height;
            Canvas.SetLeft(rect, bounds.Left);
            Canvas.SetTop(rect, bounds.Top);
        }
    }
}

And the result looks like:

torusBounds

The bounds you’ll get won’t necessarily be very tight, but they will always be inclusive.

You’ll notice that there isn’t a Visual.TransformToDescendant(Visual3D descendant) and that’s because when going from 2D to 3D you get an infinite number of points along a ray. This is when you need to resort to hit testing and pick the specific point you want.

-- Jordan