Beispiel #1
0
        /// <summary>
        /// Creates a new instance of Manipulator
        /// </summary>
        /// <param name="graphics">The IGraphicsDeviceService with which drawing will be performed</param>
        /// <param name="camera">A provider for camera view and projection data</param>
        /// <param name="input">A provider for input data</param>
        public Manipulator(IGraphicsDeviceService graphics, XICamera camera, XIInputProvider input)
        {
            mGraphics = graphics;
            mCamera   = camera;

            mInput = input;

            mSelectedAxes = AxisFlags.None;
            mActiveMode   = TransformationMode.None;
            mEnabledModes = TransformationMode.None;
            mVectorSpace  = VectorSpace.World;

            mPickBuffer = new XPickBuffer(graphics);

            mSettings = new ManipulatorSettings();
            mSettings.RestoreDefaults();

            mGraphics.DeviceCreated   += new EventHandler(OnDeviceCreated);
            mGraphics.DeviceReset     += new EventHandler(CreateDepthBuffer);
            mGraphics.DeviceResetting += new EventHandler(DisposeDepthBuffer);
            mGraphics.DeviceDisposing += new EventHandler(OnDeviceDisposing);

            if ((mGraphics.GraphicsDevice != null) && !mGraphics.GraphicsDevice.IsDisposed)
            {
                OnDeviceCreated(this, null);
                CreateDepthBuffer(this, null);
            }

            mUndoStack = new Stack <TransformState>();
            mRedoStack = new Stack <TransformState>();

            mDrawFunctions  = new DrawFunctions();
            mManipFunctions = new ManipFunctions();

            mDrawFunctions[TransformationMode.None][AxisFlags.X]
                    = mDrawFunctions[TransformationMode.None][AxisFlags.Y]
                    = mDrawFunctions[TransformationMode.None][AxisFlags.Z]
                    = delegate(AxisFlags axis)
                {
                Vector3 unit = GetUnitAxis(axis);
                XPrimitives.DrawLine(mGraphics.GraphicsDevice, Vector3.Zero, unit);
                };

            InitTranslation();
            InitRotation();
            InitScale();
        }
Beispiel #2
0
        /// <summary>
        /// Initializes scale-specific manipulator data and functions
        /// </summary>
        private void InitScale()
        {
            // transformation matrices for use in drawing each axis
            Dictionary <AxisFlags, Matrix> axis_transforms = new Dictionary <AxisFlags, Matrix>();

            axis_transforms[AxisFlags.X] = Matrix.CreateRotationZ(-MathHelper.PiOver2);
            axis_transforms[AxisFlags.Y] = Matrix.Identity;
            axis_transforms[AxisFlags.Z] = Matrix.CreateRotationX(MathHelper.PiOver2);
            axis_transforms[AxisFlags.X | AxisFlags.Y] = Matrix.Identity;
            axis_transforms[AxisFlags.X | AxisFlags.Z] = Matrix.CreateRotationX(MathHelper.PiOver2);
            axis_transforms[AxisFlags.Y | AxisFlags.Z] = Matrix.CreateRotationY(-MathHelper.PiOver2);

            // the base draw handler for single-axis draw calls
            Action <Matrix> base_draw_axis = new Action <Matrix>(delegate(Matrix transform)
            {
                GraphicsDevice device = mGraphics.GraphicsDevice;
                ManipulatorSettings.ScaleSettings settings = mSettings.Scale;

                if ((settings.AxisDrawMode & AxisDirections.Positive) == AxisDirections.Positive)
                {
                    XPrimitives.DrawLine(device, Vector3.Zero, Vector3.UnitY * settings.AxisExtent, transform);
                    XPrimitives.DrawBox(device, Vector3.UnitY * settings.AxisExtent,
                                        Vector3.One * settings.AxisHandleSize, transform);
                }

                if ((settings.AxisDrawMode & AxisDirections.Negative) == AxisDirections.Negative)
                {
                    XPrimitives.DrawLine(device, Vector3.Zero, -Vector3.UnitY * settings.AxisExtent, transform);
                    XPrimitives.DrawBox(device, -Vector3.UnitY * settings.AxisExtent,
                                        Vector3.One * settings.AxisHandleSize, transform);
                }
            });

            // use the base single-axis draw handler for all single axes, and
            // transform using the appropriate axis transform from the dictionary above
            mDrawFunctions[TransformationMode.ScaleAxis][AxisFlags.X]
                    = mDrawFunctions[TransformationMode.ScaleAxis][AxisFlags.Y]
                    = mDrawFunctions[TransformationMode.ScaleAxis][AxisFlags.Z]
                    = delegate(AxisFlags axis)
                {
                base_draw_axis(axis_transforms[axis]);
                };

            Action <Matrix> base_draw_plane = new Action <Matrix>(delegate(Matrix transform)
            {
                GraphicsDevice device = mGraphics.GraphicsDevice;
                ManipulatorSettings.ScaleSettings settings = mSettings.Scale;

                bool draw_pos = (settings.AxisDrawMode & AxisDirections.Positive) == AxisDirections.Positive;
                bool draw_neg = (settings.AxisDrawMode & AxisDirections.Negative) == AxisDirections.Negative;

                if (draw_pos)
                {
                    XPrimitives.DrawLine(device, Vector3.UnitY * settings.AxisExtent,
                                         Vector3.UnitX * settings.AxisExtent, transform);
                    XPrimitives.DrawSphere(device, Vector3.UnitY * settings.AxisExtent * 0.5f
                                           + Vector3.UnitX * settings.AxisExtent * 0.5f, settings.PlaneHandleRadius, 10, 20, transform);
                }

                if (draw_neg)
                {
                    XPrimitives.DrawLine(device, -Vector3.UnitY * settings.AxisExtent,
                                         -Vector3.UnitX * settings.AxisExtent, transform);
                    XPrimitives.DrawSphere(device, -Vector3.UnitY * settings.AxisExtent * 0.5f
                                           + -Vector3.UnitX * settings.AxisExtent * 0.5f, settings.PlaneHandleRadius, 10, 20, transform);
                }

                if (draw_pos && draw_neg)
                {
                    XPrimitives.DrawLine(device, -Vector3.UnitY * settings.AxisExtent,
                                         Vector3.UnitX * settings.AxisExtent, transform);
                    XPrimitives.DrawSphere(device, -Vector3.UnitY * settings.AxisExtent * 0.5f
                                           + Vector3.UnitX * settings.AxisExtent * 0.5f, settings.PlaneHandleRadius, 10, 20, transform);

                    XPrimitives.DrawLine(device, Vector3.UnitY * settings.AxisExtent,
                                         -Vector3.UnitX * settings.AxisExtent, transform);
                    XPrimitives.DrawSphere(device, Vector3.UnitY * settings.AxisExtent * 0.5f
                                           + -Vector3.UnitX * settings.AxisExtent * 0.5f, settings.PlaneHandleRadius, 10, 20, transform);
                }
            });

            mDrawFunctions[TransformationMode.ScalePlane][AxisFlags.XY]
                    = mDrawFunctions[TransformationMode.ScalePlane][AxisFlags.XZ]
                    = mDrawFunctions[TransformationMode.ScalePlane][AxisFlags.YZ]
                    = delegate(AxisFlags axis)
                {
                base_draw_plane(axis_transforms[axis]);
                };

            mDrawFunctions[TransformationMode.ScaleUniform][AxisFlags.X | AxisFlags.Y | AxisFlags.Z]
                = delegate(AxisFlags axis)
                {
                ManipulatorSettings.ScaleSettings settings = mSettings.Scale;
                XPrimitives.DrawSphere(mGraphics.GraphicsDevice, Vector3.Zero,
                                       settings.UniformHandleRadius, 10, 20);
                };

            // all single-axis scaling will use the same manip function
            mManipFunctions[TransformationMode.ScaleAxis][AxisFlags.X]
                    = mManipFunctions[TransformationMode.ScaleAxis][AxisFlags.Y]
                    = mManipFunctions[TransformationMode.ScaleAxis][AxisFlags.Z]
                    = delegate()
                {
                // get the axis for the component being scaled
                Vector3 axis = GetUnitAxis(mSelectedAxes);

                // get a translation matrix on which the projection of the above axis
                // will be based
                Matrix tmtx = Matrix.CreateTranslation(mTransform.Translation);

                // project the axis into screen space
                Vector3 p0 = Viewport.Project(Vector3.Zero, ProjectionMatrix, ViewMatrix, tmtx);
                Vector3 p1 = Viewport.Project(axis, ProjectionMatrix, ViewMatrix, tmtx);

                // disregard the z component for 2D calculations
                p0.Z = p1.Z = 0;

                // Vector3 versions of the mouse input positions
                Vector3 ps = new Vector3(mInput.Start, 0);
                Vector3 pe = new Vector3(mInput.End, 0);

                // calculate the axis vector and vectors from the translation point
                // to each of the mouse positions
                Vector3 v0 = p1 - p0;
                Vector3 vs = ps - p0;
                Vector3 ve = pe - p0;

                // project both mouse positions onto the axis vector and calculate
                // their scalars
                float proj_s = Math.Abs(Vector3.Dot(vs, v0) / v0.Length());
                float proj_e = Math.Abs(Vector3.Dot(ve, v0) / v0.Length());

                // find the ratio between the projected scalar values
                Vector3 scale = mTransform.Scale;
                float   ratio = (proj_e / proj_s);

                // scale the appropriate axis by the ratio
                switch (mSelectedAxes)
                {
                case AxisFlags.X:
                    scale.X *= ratio;
                    break;

                case AxisFlags.Y:
                    scale.Y *= ratio;
                    break;

                case AxisFlags.Z:
                    scale.Z *= ratio;
                    break;
                }

                // clamp each component of the new scale to a sane value
                scale.X = MathHelper.Clamp(scale.X, float.Epsilon, float.MaxValue);
                scale.Y = MathHelper.Clamp(scale.Y, float.Epsilon, float.MaxValue);
                scale.Z = MathHelper.Clamp(scale.Z, float.Epsilon, float.MaxValue);

                // scale the transform
                mTransform.Scale = scale;
                };

            // all dual-axis scaling will use the same manip function
            mManipFunctions[TransformationMode.ScalePlane][AxisFlags.X | AxisFlags.Y]
                    = mManipFunctions[TransformationMode.ScalePlane][AxisFlags.X | AxisFlags.Z]
                    = mManipFunctions[TransformationMode.ScalePlane][AxisFlags.Y | AxisFlags.Z]
                    = delegate()
                {
                // get the plane that corresponds to the axes on which we are performing the scale
                Plane p = GetPlane(mSelectedAxes);

                // cast rays from the mouse start and end positions
                Ray start_ray = GetPickRay(mInput.Start);
                Ray end_ray   = GetPickRay(mInput.End);

                // intersect each ray with the scale plane
                float?start_hit = start_ray.Intersects(p);
                float?end_hit   = end_ray.Intersects(p);

                // bail out if either of the rays failed to intersect the plane
                if (!start_hit.HasValue || !end_hit.HasValue)
                {
                    return;
                }

                // calculate the intersection points of each ray along the plane
                Vector3 start_pos = start_ray.Position + (start_ray.Direction * start_hit.Value);
                Vector3 end_pos   = end_ray.Position + (end_ray.Direction * end_hit.Value);

                // find the vectors from the transform's position to each intersection point
                Vector3 start_to_pos = start_pos - mTransform.Translation;
                Vector3 end_to_pos   = end_pos - mTransform.Translation;

                // get the lengths of both of these vectors and find the ratio between them
                float start_len = start_to_pos.Length();
                float end_len   = end_to_pos.Length();

                Vector3 scale = mTransform.Scale;
                float   ratio = (start_len == 0)
                            ? (1)
                            : (end_len / start_len);

                // scale the selected components by the ratio
                if ((mSelectedAxes & AxisFlags.X) == AxisFlags.X)
                {
                    scale.X *= ratio;
                }
                if ((mSelectedAxes & AxisFlags.Y) == AxisFlags.Y)
                {
                    scale.Y *= ratio;
                }
                if ((mSelectedAxes & AxisFlags.Z) == AxisFlags.Z)
                {
                    scale.Z *= ratio;
                }

                // clamp each component of the new scale to a sane value
                scale.X = MathHelper.Clamp(scale.X, float.Epsilon, float.MaxValue);
                scale.Y = MathHelper.Clamp(scale.Y, float.Epsilon, float.MaxValue);
                scale.Z = MathHelper.Clamp(scale.Z, float.Epsilon, float.MaxValue);

                // scale the transform
                mTransform.Scale = scale;
                };

            mManipFunctions[TransformationMode.ScaleUniform][AxisFlags.X | AxisFlags.Y | AxisFlags.Z]
                = delegate()
                {
                // get the direction of the transformation's position to the camera position
                Vector3 pos_to_cam
                    = Matrix.Invert(ViewMatrix).Translation - mTransform.Translation;

                // normalize the direction for use in plane construction
                if (pos_to_cam != Vector3.Zero)
                {
                    pos_to_cam.Normalize();
                }

                // create a plane with the normal calculated above that passes through
                // the transform's position
                Plane p = Plane.Transform(new Plane(pos_to_cam, 0),
                                          Matrix.CreateTranslation(mTransform.Translation));

                // cast pick rays from the mouse start and end points
                Ray start_ray = GetPickRay(mInput.Start);
                Ray end_ray   = GetPickRay(mInput.End);

                // intersect each ray with the plane
                float?start_hit = start_ray.Intersects(p);
                float?end_hit   = end_ray.Intersects(p);

                // bail out if either of the rays fails to intersect the plane
                if (!start_hit.HasValue || !end_hit.HasValue)
                {
                    return;
                }

                // calculate the intersection points of each ray along the plane
                Vector3 start_pos = start_ray.Position + (start_ray.Direction * start_hit.Value);
                Vector3 end_pos   = end_ray.Position + (end_ray.Direction * end_hit.Value);

                // find the vectors from the transform's position to each intersection point
                Vector3 start_to_pos = start_pos - mTransform.Translation;
                Vector3 end_to_pos   = end_pos - mTransform.Translation;

                // get the lengths of both of these vectors and find the ratio between them
                float start_len = start_to_pos.Length();
                float end_len   = end_to_pos.Length();

                Vector3 scale = mTransform.Scale;
                float   ratio = (start_len == 0)
                            ? (1)
                            : (end_len / start_len);

                // multiply the scale uniformly by the ratio of the start and end vector lengths
                scale *= ratio;

                // clamp each component of the new scale to a sane value
                scale.X = MathHelper.Clamp(scale.X, float.Epsilon, float.MaxValue);
                scale.Y = MathHelper.Clamp(scale.Y, float.Epsilon, float.MaxValue);
                scale.Z = MathHelper.Clamp(scale.Z, float.Epsilon, float.MaxValue);

                // scale the transform
                mTransform.Scale = scale;
                };
        }
Beispiel #3
0
        /// <summary>
        /// Initializes rotation-specific manipulator data and functions
        /// </summary>
        private void InitRotation()
        {
            // transformation matrices by which to transform each axis when drawing
            Dictionary <AxisFlags, Matrix> axis_transforms = new Dictionary <AxisFlags, Matrix>();

            axis_transforms[AxisFlags.X] = Matrix.CreateRotationZ(MathHelper.PiOver2);
            axis_transforms[AxisFlags.Y] = Matrix.Identity;
            axis_transforms[AxisFlags.Z] = Matrix.CreateRotationX(MathHelper.PiOver2);

            // all rotation axes are drawn using a single circle strip
            mDrawFunctions[TransformationMode.Rotation][AxisFlags.X]
                    = mDrawFunctions[TransformationMode.Rotation][AxisFlags.Y]
                    = mDrawFunctions[TransformationMode.Rotation][AxisFlags.Z]
                    = delegate(AxisFlags axis)
                {
                ManipulatorSettings.RotationSettings settings = mSettings.Rotation;

                // draw a circle strip around the specified axis
                XPrimitives.DrawCircleStrip(mGraphics.GraphicsDevice, Vector3.Zero, settings.InnerRadius,
                                            settings.OuterRadius, 128, axis_transforms[axis]);
                };

            // we will use the same manip function for all single axes. dual and triple
            // axes are not supported for rotations
            mManipFunctions[TransformationMode.Rotation][AxisFlags.X]
                    = mManipFunctions[TransformationMode.Rotation][AxisFlags.Y]
                    = mManipFunctions[TransformationMode.Rotation][AxisFlags.Z]
                    = delegate()
                {
                // get the plane perpendicular to the rotation axis, transformed by
                // the current ITransform's world matrix
                Plane p = GetPlane(mSelectedAxes);

                // project rays from the start and end points of the mouse
                Ray start_ray = GetPickRay(mInput.Start);
                Ray end_ray   = GetPickRay(mInput.End);

                // intersect the rays with the perpendicular rotation plane
                float?hits = start_ray.Intersects(p);
                float?hite = end_ray.Intersects(p);

                // exit if either of the intersections is invalid
                if (!hits.HasValue || !hite.HasValue)
                {
                    return;
                }

                // calculate the intersection position of each ray on the plane
                Vector3 start_position = start_ray.Position + (start_ray.Direction * hits.Value);
                Vector3 end_position   = end_ray.Position + (end_ray.Direction * hite.Value);

                // get the direction vectors of the rotation origin to the start and end points
                Vector3 origin_to_start = Vector3.Normalize(start_position - mTransform.Translation);
                Vector3 origin_to_end   = Vector3.Normalize(end_position - mTransform.Translation);

                Vector3 rotation_axis = GetUnitAxis(mSelectedAxes);

                // calculate cross products of the direction vectors with the rotation axis
                Vector3 rot_cross_start = Vector3.Normalize(Vector3.Cross(rotation_axis, origin_to_start));
                Vector3 rot_cross_end   = Vector3.Normalize(Vector3.Cross(rotation_axis, origin_to_end));

                // calculate the cross product of the above start and end cross products
                Vector3 start_cross_end = Vector3.Normalize(Vector3.Cross(rot_cross_start, rot_cross_end));

                // dot the two direction vectors and get the arccos of the dot product to get
                // the angle between them, then multiply it by the sign of the dot product
                // of the derived cross product calculated above to obtain the direction
                // by which we should rotate with the angle
                float dot            = Vector3.Dot(origin_to_start, origin_to_end);
                float rotation_angle = (float)Math.Acos(dot)
                                       * Math.Sign(Vector3.Dot(rotation_axis, start_cross_end));

                // create a normalized quaternion representing the rotation from the start to end points
                Quaternion rot = Quaternion.Normalize(Quaternion.CreateFromAxisAngle(rotation_axis, rotation_angle));

                // add the calculated rotation to the current rotation
                mTransform.Rotation = rot * mTransform.Rotation;
                };
        }
Beispiel #4
0
        /// <summary>
        /// Initializes translation-specific manipulator data and functions
        /// </summary>
        private void InitTranslation()
        {
            Dictionary <AxisFlags, Matrix> axis_transforms = new Dictionary <AxisFlags, Matrix>();

            axis_transforms[AxisFlags.X] = Matrix.CreateRotationZ(-MathHelper.PiOver2);
            axis_transforms[AxisFlags.Y] = Matrix.Identity;
            axis_transforms[AxisFlags.Z] = Matrix.CreateRotationX(MathHelper.PiOver2);
            axis_transforms[AxisFlags.X | AxisFlags.Y] = Matrix.Identity;
            axis_transforms[AxisFlags.X | AxisFlags.Z] = Matrix.CreateRotationX(MathHelper.PiOver2);
            axis_transforms[AxisFlags.Y | AxisFlags.Z] = Matrix.CreateRotationY(-MathHelper.PiOver2);

            // the base draw handler for single-axis draw calls
            Action <Matrix> base_draw_axis = new Action <Matrix>(delegate(Matrix transform)
            {
                GraphicsDevice device = mGraphics.GraphicsDevice;

                ManipulatorSettings.TranslationSettings settings = mSettings.Translation;

                // draw the axis cylinder to represent the axis itself
                if ((settings.AxisDrawMode & AxisDirections.Positive) == AxisDirections.Positive)
                {
                    XPrimitives.DrawCylinder(device, Vector3.UnitY * settings.AxisExtent * 0.5f,
                                             settings.AxisExtent * 0.5f, settings.AxisRadius, 8, transform);
                    XPrimitives.DrawCone(device, Vector3.UnitY * settings.AxisExtent, settings.ConeRadius,
                                         settings.ConeHeight, 8, transform);
                }

                if ((settings.AxisDrawMode & AxisDirections.Negative) == AxisDirections.Negative)
                {
                    Matrix y_invert = Matrix.CreateRotationX(MathHelper.Pi);

                    XPrimitives.DrawCylinder(device, -Vector3.UnitY * settings.AxisExtent * 0.5f,
                                             settings.AxisExtent * 0.5f, settings.AxisRadius, 8, transform);
                    XPrimitives.DrawCone(device, Vector3.UnitY * settings.AxisExtent, settings.ConeRadius,
                                         settings.ConeHeight, 8, y_invert * transform);
                }
            });

            // use the base single axis draw function for each of the main axes, and
            // transform using the appropriate axis transform from the array defined aboved
            mDrawFunctions[TransformationMode.TranslationAxis][AxisFlags.X]
                    = mDrawFunctions[TransformationMode.TranslationAxis][AxisFlags.Y]
                    = mDrawFunctions[TransformationMode.TranslationAxis][AxisFlags.Z]
                    = delegate(AxisFlags axis)
                {
                base_draw_axis(axis_transforms[axis]);
                };

            Action <Matrix> base_draw_plane = new Action <Matrix>(delegate(Matrix transform)
            {
                GraphicsDevice device = mGraphics.GraphicsDevice;
                ManipulatorSettings.TranslationSettings settings = mSettings.Translation;

                Vector3 up    = Vector3.UnitY * settings.PlaneQuadrantSize;
                Vector3 right = Vector3.UnitX * settings.PlaneQuadrantSize;

                bool draw_pos = (settings.AxisDrawMode & AxisDirections.Positive) == AxisDirections.Positive;
                bool draw_neg = (settings.AxisDrawMode & AxisDirections.Negative) == AxisDirections.Negative;

                if (draw_pos)
                {
                    XPrimitives.DrawTriangle(device, up, right, Vector3.Zero, transform);
                }

                if (draw_neg)
                {
                    XPrimitives.DrawTriangle(device, -up, -right, Vector3.Zero, transform);
                }

                if (draw_pos && draw_neg)
                {
                    XPrimitives.DrawTriangle(device, -up, right, Vector3.Zero, transform);
                    XPrimitives.DrawTriangle(device, up, -right, Vector3.Zero, transform);
                }
            });

            mDrawFunctions[TransformationMode.TranslationPlane][AxisFlags.X | AxisFlags.Y]
                    = mDrawFunctions[TransformationMode.TranslationPlane][AxisFlags.X | AxisFlags.Z]
                    = mDrawFunctions[TransformationMode.TranslationPlane][AxisFlags.Y | AxisFlags.Z]
                    = delegate(AxisFlags axis)
                {
                base_draw_plane(axis_transforms[axis]);
                };

            // all single-axis translations will use the same manip function
            mManipFunctions[TransformationMode.TranslationAxis][AxisFlags.X]
                    = mManipFunctions[TransformationMode.TranslationAxis][AxisFlags.Y]
                    = mManipFunctions[TransformationMode.TranslationAxis][AxisFlags.Z]
                    = delegate()
                {
                // get the unit version of the seclected axis
                Vector3 axis = GetUnitAxis(mSelectedAxes);

                // we need to project using the translation component of the current
                // ITransform in order to obtain a projected unit axis originating
                // from the transform's position
                Matrix translation = Matrix.CreateTranslation(mTransform.Translation);

                // project the origin onto the screen at the transform's position
                Vector3 start_position = Viewport.Project(Vector3.Zero, ProjectionMatrix, ViewMatrix, translation);
                // project the unit axis onto the screen at the transform's position
                Vector3 end_position = Viewport.Project(axis, ProjectionMatrix, ViewMatrix, translation);

                // calculate the normalized direction vector of the unit axis in screen space
                Vector3 screen_direction = Vector3.Normalize(end_position - start_position);

                // calculate the projected mouse delta along the screen direction vector
                end_position = start_position + (screen_direction * (Vector3.Dot(new Vector3(mInput.Delta, 0f), screen_direction)));

                // unproject the screen points back into world space using the translation transform
                // to get the world space start and end positions in regard to the mouse delta along
                // the mouse direction vector
                start_position = Viewport.Unproject(start_position, ProjectionMatrix, ViewMatrix, translation);
                end_position   = Viewport.Unproject(end_position, ProjectionMatrix, ViewMatrix, translation);

                // calculate the difference vector between the world space start and end points
                Vector3 difference = end_position - start_position;

                // create a view frustum based on the  current view and projection matrices
                BoundingFrustum frust = new BoundingFrustum(ViewMatrix * ProjectionMatrix);

                // if the new translation position is within the current frustum, then add the difference
                // to the current transform's translation component, otherwise the transform would be outside of
                // the screen
                if (frust.Contains(mTransform.Translation + difference) == ContainmentType.Contains)
                {
                    mTransform.Translation += difference;
                }
                };

            // all planetranslations will use the same manip function
            mManipFunctions[TransformationMode.TranslationPlane][AxisFlags.X | AxisFlags.Y]
                    = mManipFunctions[TransformationMode.TranslationPlane][AxisFlags.X | AxisFlags.Z]
                    = mManipFunctions[TransformationMode.TranslationPlane][AxisFlags.Y | AxisFlags.Z]
                    = delegate()
                {
                // get the plane representing the two selected axes
                Plane p = GetPlane(mSelectedAxes);

                // cast rays into the scene from the mouse start and end points
                Ray sray = GetPickRay(mInput.Start);
                Ray eray = GetPickRay(mInput.End);

                // intersect the pick rays with the dual axis plane we want to move along
                float?sisect = sray.Intersects(p);
                float?eisect = eray.Intersects(p);

                // if either of the intersections is invalid then bail out as it would
                // be impossible to calculate the difference
                if (!sisect.HasValue || !eisect.HasValue)
                {
                    return;
                }

                // obtain the intersection points of each ray with the dual axis plane
                Vector3 spos = sray.Position + (sray.Direction * sisect.Value);
                Vector3 epos = eray.Position + (eray.Direction * eisect.Value);

                // calculate the difference between the intersection points
                Vector3 diff = epos - spos;

                // obtain the current view frustum using the camera's view and projection matrices
                BoundingFrustum frust = new BoundingFrustum(ViewMatrix * ProjectionMatrix);

                // if the new translation is within the current camera frustum, then add the difference
                // to the current transformation's translation component
                if (frust.Contains(mTransform.Translation + diff) == ContainmentType.Contains)
                {
                    mTransform.Translation += diff;
                }
                };
        }