Q:

Need help writing conversion methods between 2D and 3D (Point3DToPoint and PointAndZToPoint3D)

I'm new to WPF 3D, so I may just be missing something obvious, but how do I convert from 3D to 2D and (for a given z location) from 2D to 3D?
Specifically, I need two conversion methods:

Point3DToPoint - If I have an (x, y, z) coordinate in the 3D world, how do I determine the (x, y) coordinate on the projected 2D surface. Method signature: public Point Point3DToPoint(Point3D point3D)
PointAndZToPoint3D - If I have an (x, y) coordinate on the projected 2D surface and a z location in the 3D world, how do I determine the (x, y, z) coordinate in the 3D world? Method signature: public Point3D PointAndZToPoint3D(Point point, double z)

I'd like the 2D coordinate to be the location measured from the upper-left corner of Viewport3D and the 3D coordinate to be the location relative to the origin (0, 0, 0) of the 3D world.
Note 1: I found this related question, but it only addresses conversion from 3D to 2D (not the reverse), and I'm not sure if the answers are up-to-date.
Note 2: I'm currently using .NET 3.5, but if there are improvements in .NET 4.0 that would help me, please let me know.

A:

Charles Petzold's 3D Library, which can be downloaded here under "The Petzold.Media3D library", contains a class ViewportInfo with these two static methods:

public static Point Point3DToPoint2D(Viewport3D viewport, Point3d point)
public static bool Point2DToPoint3D(Viewport3D viewport, Point, ptIn, out LineRange range)

Point2DToPoint3D isn't an exact match for PointAndZToPoint3D() because it returns (via an out parameter) a LineRange rather than a specific point, but it just so happens that LineRange has a method PointFromZ(double zValue), which provides the point where the ray intersects the plane defined by z = zValue.
Code Sample:
using System.Windows;
using System.Windows.Input;
using Petzold.Media3D;

namespace _3DTester
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        /* This MouseDown event handler prints:
          (1) the current position of the mouse
          (2) the 3D mouse location on the ground plane (z = 0)
          (3) the 2D mouse location converted from the 3D location */

        private void Window_MouseDown(object sender, MouseEventArgs e)
        {
            var range = new LineRange();
            var isValid = ViewportInfo.Point2DtoPoint3D(Viewport, e.GetPosition(Viewport), out range);
            if (!isValid)
                MouseLabel.Content = "(no data)";
            else
            {
                var point3D = range.PointFromZ(0);
                var point = ViewportInfo.Point3DtoPoint2D(Viewport, point3D);
                MouseLabel.Content = e.GetPosition(Viewport) + "\n" + point3D + "\n" + point;
            }
        }
    }
}

XAML Code
<Window
    x:Class="_3DTester.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1"
    Height="300"
    Width="300"
    MouseDown="Window_MouseDown">
    <Grid>
        <Viewport3D Name="Viewport">
            <Viewport3D.Camera>
                <PerspectiveCamera
                    Position="0,0,30"
                    LookDirection="0,0,-1" 
                    UpDirection="0,1,0" />
            </Viewport3D.Camera>
            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <Model3DGroup>
                        <DirectionalLight Color="White" Direction="1,-1,-1" />
                        <GeometryModel3D>
                            <GeometryModel3D.Geometry>
                                <MeshGeometry3D
                                    Positions="0,0,10 -5,-5,0 -5,5,0 5,5,0 5,-5,0"
                                    TriangleIndices="2 1 0  2 0 3  4 3 0  1 4 0" />
                            </GeometryModel3D.Geometry>
                            <GeometryModel3D.Material>
                                <DiffuseMaterial Brush="Red" />
                            </GeometryModel3D.Material>
                        </GeometryModel3D>
                    </Model3DGroup>
                </ModelVisual3D.Content>
            </ModelVisual3D>
        </Viewport3D>
        <Label Name="MouseLabel" Content="(no data)" />
    </Grid>
</Window>

