public TrackedItem(IMapObject mapObject, MapObject_ChasePoint_Forces translate, MapObject_ChaseOrientation_Torques rotate, double? graduleTo100PercentDuration, double? delayBeforeGradule, bool didOriginalLimitRotation)
            {
                this.MapObject = mapObject;
                this.Translate = translate;
                this.Rotate = rotate;

                this.GraduleTo100PercentDuration = graduleTo100PercentDuration;
                this.DelayBeforeGradule = delayBeforeGradule;
                this.ElapsedTime = 0d;

                this.DidOriginalLimitRotation = didOriginalLimitRotation;
            }
        private void UpdateChasePanel()
        {
            try
            {
                lblError.Text = "";

                ClearChasePanels();

                ChaseType chaseType;
                if (Enum.TryParse(cboChaseType.SelectedValue.ToString().Replace(" ", "_"), out chaseType))
                {
                    switch (chaseType)
                    {
                        case ChaseType.Linear_Velocity:
                            if (_panel_LinearVelocity != null)
                            {
                                _object_LinearVelocity = _panel_LinearVelocity.GetChaseObject(_bodyBall);
                            }
                            break;

                        case ChaseType.Linear_Force:
                            if (_panel_LinearForce != null)
                            {
                                _object_LinearForce = _panel_LinearForce.GetChaseObject_Linear(_bodyBall);
                            }
                            break;

                        case ChaseType.Orientation_Velocity:
                            if (_panel_OrientationVelocity != null)
                            {
                                _object_OrientationVelocity = _panel_OrientationVelocity.GetChaseObject(_bodyBall);
                            }
                            break;

                        case ChaseType.Orientation_Torque:
                            if (_panel_OrientationForce != null)
                            {
                                _object_OrientationForce = _panel_OrientationForce.GetChaseObject_Orientation(_bodyBall);
                            }
                            break;

                        default:
                            throw new ApplicationException("Unknown ChaseType: " + chaseType.ToString());
                    }
                }
            }
            catch (Exception ex)
            {
                lblError.Text = ex.Message;
            }
        }
        private void ClearChasePanels()
        {
            if (_object_LinearVelocity != null)
            {
                _object_LinearVelocity.Dispose();
                _object_LinearVelocity = null;
            }

            if (_object_LinearForce != null)
            {
                _object_LinearForce.Dispose();
                _object_LinearForce = null;
            }

            if (_object_OrientationVelocity != null)
            {
                _object_OrientationVelocity.Dispose();
                _object_OrientationVelocity = null;
            }

            if (_object_OrientationForce != null)
            {
                _object_OrientationForce.Dispose();
                _object_OrientationForce = null;
            }
        }
        public MapObject_ChaseOrientation_Torques GetChaseObject_Orientation(IMapObject item)
        {
            if (_isLinear)
            {
                throw new InvalidOperationException("This method can only be called when the control represents orientation");
            }

            MapObject_ChaseOrientation_Torques retVal = new MapObject_ChaseOrientation_Torques(item);

            //TODO: May want to expose these.  I think they're unnecessary, and the result of overdesign
            //retVal.MaxAcceleration = 
            //retVal.MaxForce = 

            List<ChaseOrientation_Torque> torques = new List<ChaseOrientation_Torque>();

            foreach (UIElement entry in pnlForces.Children)
            {
                ChaseOrientation_Torque chaseObject = null;
                if (entry is ForceEntry)
                {
                    chaseObject = ((ForceEntry)entry).GetChaseObject_Orientation();
                }
                else
                {
                    throw new ApplicationException("Unknown type of entry: " + entry.ToString());
                }

                //NOTE: Doing a null check, because if they uncheck enabled, it will come back null
                if (chaseObject != null)
                {
                    torques.Add(chaseObject);
                }
            }

            if (torques.Count > 0)
            {
                retVal.Torques = torques.ToArray();
                return retVal;
            }
            else
            {
                // Don't bother returning something that will fail on update
                return null;
            }
        }
        public void Add(IMapObject item, bool shouldLimitRotation, double? graduleTo100PercentDuration = null, double? delayBeforeGradule = null)
        {
            if (_items.Any(o => o.MapObject.Equals(item)))
            {
                // It's already added
                return;
            }

            Tuple<double, double>[] gradient;

            #region Forces

            MapObject_ChasePoint_Forces chaseForces = null;

            if (_shouldApplyForces)
            {
                List<ChasePoint_Force> forces = new List<ChasePoint_Force>();

                // Attraction Force
                gradient = new[]
                {
                    Tuple.Create(0d, 0d),     // distance, %
                    Tuple.Create(.7d, .28d),
                    Tuple.Create(1d, 1d),
                };
                forces.Add(new ChasePoint_Force(ChaseDirectionType.Attract_Direction, 500, gradient: gradient));

                // This acts like a shock absorber
                gradient = new[]
                {
                    Tuple.Create(0d, .25d),
                    Tuple.Create(.75d, 1d),
                    //Tuple.Create(3d, 0d),
                };
                forces.Add(new ChasePoint_Force(ChaseDirectionType.Drag_Velocity_Along, 10));

                chaseForces = new MapObject_ChasePoint_Forces(item, false);
                chaseForces.Forces = forces.ToArray();
            }

            #endregion
            #region Torques - ORIG

            //MapObject_ChaseOrientation_Torques chaseTorques = null;

            //if (_shouldApplyTorques && shouldLimitRotation)
            //{
            //    List<ChaseOrientation_Torque> torques = new List<ChaseOrientation_Torque>();

            //    double mult = 60;

            //    // Attraction
            //    gradient = new[]
            //    {
            //        Tuple.Create(0d, 0d),     // distance, %
            //        Tuple.Create(10d, 1d),
            //    };
            //    torques.Add(new ChaseOrientation_Torque(ChaseDirectionType.Attract_Direction, .6 * mult, gradient: gradient));

            //    // Drag
            //    torques.Add(new ChaseOrientation_Torque(ChaseDirectionType.Drag_Velocity_Orth, .015 * mult));

            //    gradient = new[]
            //    {
            //        Tuple.Create(0d, 1d),
            //        Tuple.Create(1.6d, .3d),
            //        Tuple.Create(5d, 0d),
            //    };
            //    //torques.Add(new ChaseOrientation_Torque(ChaseDirectionType.Drag_Velocity_AlongIfVelocityToward, .04 * mult, gradient: gradient));
            //    torques.Add(new ChaseOrientation_Torque(ChaseDirectionType.Drag_Velocity_AlongIfVelocityAway, .04 * mult, gradient: gradient));


            //    chaseTorques = new MapObject_ChaseOrientation_Torques(item);
            //    chaseTorques.Torques = torques.ToArray();
            //}

            #endregion
            #region Torques

            MapObject_ChaseOrientation_Torques chaseTorques = null;

            if (_shouldApplyTorques && shouldLimitRotation)
            {
                List<ChaseOrientation_Torque> torques = new List<ChaseOrientation_Torque>();

                double mult = 300; //600;

                // Attraction
                gradient = new[]
                {
                    Tuple.Create(0d, 0d),     // distance, %
                    Tuple.Create(10d, 1d),
                };
                torques.Add(new ChaseOrientation_Torque(ChaseDirectionType.Attract_Direction, .4 * mult, gradient: gradient));

                // Drag
                gradient = new[]        // this gradient is needed, because there needs to be no drag along the desired axis (otherwise, this drag will fight with the user's desire to rotate the ship)
                {
                    Tuple.Create(0d, 0d),     // distance, %
                    Tuple.Create(5d, 1d),
                };
                torques.Add(new ChaseOrientation_Torque(ChaseDirectionType.Drag_Velocity_Orth, .0739 * mult, gradient: gradient));

                torques.Add(new ChaseOrientation_Torque(ChaseDirectionType.Drag_Velocity_AlongIfVelocityAway, .0408 * mult));

                chaseTorques = new MapObject_ChaseOrientation_Torques(item);
                chaseTorques.Torques = torques.ToArray();
            }

            #endregion

            _items.Add(new TrackedItem(item, chaseForces, chaseTorques, graduleTo100PercentDuration, delayBeforeGradule, shouldLimitRotation));
        }