9: Rotate and Zoom

This entry is part 9 of a 12-part series on WPF 3D.

Interactive Views

Sawdust allows the user to interactively rotate and zoom a 3D model.  Like any other CAD-like program, understanding how something is put together can be much easier if you can look at it in just the right way.

I've tried two different ways of presenting this interactive feature to the user.  Both of them involve setting the Transform property on the Camera and then using the mouse or UI controls to modify that transform object.

Trackball

The easiest way is to use the Trackball object in the 3D Tools library.  All you have to do is create a Trackball, set your camera's Transform property to its Transform property, and set its EventSource property to an overlay like the one I described in part 8.

Trackball tb = new Trackball();
myViewport3D.Camera.Transform = tb.Transform;
tb.EventSource = myOverlay;

I shall not try to explain all the details of this class works.  Suffice it to say that it works very well and it is very easy it is to use.

Four Sliders

I mostly like the Trackball, but sometimes its rotation feature doesn't feel quite right.  I occasionally find myself fighting with the mouse to get the rotation just how I want it to be.

Lately I've been experimenting with another way of handling rotation and zoom.  Instead of a trackball, I've got four sliders.  Three of them control the rotation around the three primary axes.  The fourth one is the zoom.  In my UI they look like this:

The Camera in my Viewport3D is setup like this:

Point3D c = myObject.GetCenter();
center = new TranslateTransform3D(c.x, c.y, c.z);
rot_x = new AxisAngleRotation3D(
    new Vector3D(1, 0, 0),
    myObject.ViewRotX);
rot_y = new AxisAngleRotation3D(
    new Vector3D(0, 1, 0),
    myObject.ViewRotY);
rot_z = new AxisAngleRotation3D(
    new Vector3D(0, 0, 1),
    myObject.ViewRotZ);
zoom = new ScaleTransform3D(
    myObject.ViewZoom,
    myObject.ViewZoom,
    myObject.ViewZoom);

Transform3DGroup t = new Transform3DGroup();

t.Children.Add(zoom);

// the order of the following three is significant
t.Children.Add(new RotateTransform3D(rot_y));
t.Children.Add(new RotateTransform3D(rot_x));
t.Children.Add(new RotateTransform3D(rot_z));

t.Children.Add(center);

myViewport3D.Camera.Transform = t;

Note that I've actually got five transforms in my Transform3DGroup.  The TranslateTransform3D ensures that I am rotating around the center of my 3D object.  Then I've got one AxisAngleRotation3D for each of the three primary axes and a ScaleTransform3D for the zoom feature.

Now the four sliders:

<Slider Name="slider_zoom"
        Orientation="Vertical"
       
Minimum="0.2"
       
Maximum="10"
       
ValueChanged="slider_zoom_changed" />
<
Slider Name="slider_x"
       
DockPanel.Dock="Right"
        Orientation="Vertical"
       
Minimum="-180"
       
Maximum="180"
       
ValueChanged="slider_x_changed" />
<
Slider Name="slider_z"
       
DockPanel.Dock="Top"
       
Orientation="Horizontal"
       
Minimum="-180"
       
Maximum="180"
       
ValueChanged="slider_z_changed" />
<
Slider Name="slider_y"
       
DockPanel.Dock="Bottom"
        Orientation="Horizontal"
       
Minimum="-180"
       
Maximum="180"
       
ValueChanged="slider_y_changed" />

Finally, in the event handlers for each slider, I update the corresponding piece of the transform.  For example, to update the rotation around the X axis, I do something like this:

void slider_x_changed(object sender, RoutedEventArgs args)
{
    rot_x.Angle = slider_x.Value;
}

At the moment, my four sliders are serving me well, but I'll probably put something like the Trackball back into the app eventually.