// ----------------------------------------------------------------------------
        // alternate version: keep FORWARD parallel to velocity, adjust UP according
        // to a no-basis-in-reality "banking" behavior, something like what birds and
        // airplanes do

        // XXX experimental cwr 6-5-03


        public void regenerateLocalSpaceForBanking(Vector3 newVelocity, float elapsedTime)
        {
            // the length of this global-upward-pointing vector controls the vehicle's
            // tendency to right itself as it is rolled over from turning acceleration
            Vector3 globalUp = new Vector3(0, 0.2f, 0);

            // acceleration points toward the center of local path curvature, the
            // length determines how much the vehicle will roll while turning
            Vector3 accelUp = _smoothedAcceleration * 0.05f;

            // combined banking, sum of UP due to turning and global UP
            Vector3 bankUp = accelUp + globalUp;

            // blend bankUp into vehicle's UP basis vector
            float   smoothRate = elapsedTime * 3;
            Vector3 tempUp     = up();

            tempUp = OpenSteerUtility.blendIntoAccumulator(smoothRate, bankUp, tempUp);
            tempUp.Normalise();
            setUp(tempUp);

            //  annotationLine (position(), position() + (globalUp * 4), gWhite);  // XXX
            //  annotationLine (position(), position() + (bankUp   * 4), gOrange); // XXX
            //  annotationLine (position(), position() + (accelUp  * 4), gRed);    // XXX
            //  annotationLine (position(), position() + (up ()    * 1), gYellow); // XXX

            // adjust orthonormal basis vectors to be aligned with new velocity
            if (speed() > 0)
            {
                regenerateOrthonormalBasisUF(newVelocity / speed());
            }
        }
        // ----------------------------------------------------------------------------
        // apply a given steering force to our momentum,
        // adjusting our orientation to maintain velocity-alignment.


        public void applySteeringForce(Vector3 force, float elapsedTime)
        {
            Vector3 adjustedForce = adjustRawSteeringForce(force); //, elapsedTime);

            // enforce limit on magnitude of steering force
            //Vector3 clippedForce = adjustedForce.truncateLength (maxForce ());
            //Vector3 clippedForce = adjustedForce.truncateLength(maxForce());

            Vector3 clippedForce = truncateLength(adjustedForce, maxForce());

            // compute acceleration and velocity
            Vector3 newAcceleration = (clippedForce / mass());
            Vector3 newVelocity     = velocity();

            // damp out abrupt changes and oscillations in steering acceleration
            // (rate is proportional to time step, then clipped into useful range)
            if (elapsedTime > 0)
            {
                float smoothRate = OpenSteerUtility.clip(9 * elapsedTime, 0.15f, 0.4f);
                _smoothedAcceleration = OpenSteerUtility.blendIntoAccumulator(smoothRate,
                                                                              newAcceleration,
                                                                              _smoothedAcceleration);
            }

            // Euler integrate (per frame) acceleration into velocity
            newVelocity += _smoothedAcceleration * elapsedTime;

            // enforce speed limit

            //newVelocity = newVelocity.truncateLength (maxSpeed ());
            newVelocity = truncateLength(newVelocity, maxSpeed());


            // update Speed
            setSpeed(newVelocity.Length);


            // Euler integrate (per frame) velocity into position
            setPosition(Position + (newVelocity * elapsedTime));

            // regenerate local space (by default: align vehicle's forward axis with
            // new velocity, but this behavior may be overridden by derived classes.)
            regenerateLocalSpace(newVelocity); //, elapsedTime);

            // maintain path curvature information
            measurePathCurvature(elapsedTime);

            // running average of recent positions
            _smoothedPosition = OpenSteerUtility.blendIntoAccumulator(elapsedTime * 0.06f, // QQQ
                                                                      Position,
                                                                      _smoothedPosition);
        }
        // ----------------------------------------------------------------------------
        // measure path curvature (1/turning-radius), maintain smoothed version


        void measurePathCurvature(float elapsedTime)
        {
            if (elapsedTime > 0)
            {
                Vector3 dP = _lastPosition - Position;
                Vector3 dF = (_lastForward - forward()) / dP.Length;
                //SI - BIT OF A WEIRD FIX HERE . NOT SURE IF ITS CORRECT
                //Vector3 lateral = dF.perpendicularComponent (forward ());
                Vector3 lateral = OpenSteerUtility.perpendicularComponent(dF, forward());

                float sign = (lateral.DotProduct(side()) < 0) ? 1.0f : -1.0f;
                _curvature = lateral.Length * sign;
                //OpenSteerUtility.blendIntoAccumulator(elapsedTime * 4.0f, _curvature,_smoothedCurvature);
                _smoothedCurvature = OpenSteerUtility.blendIntoAccumulator(elapsedTime * 4.0f, _curvature, _smoothedCurvature);

                _lastForward  = forward();
                _lastPosition = Position;
            }
        }