private void TurnOrbit(double theta)
        {
            var maneuverPos = CurrentNode.patch.getRelativePositionAtUT(CurrentNode.UT).xzy;
            var maneuverVel = CurrentNode.patch.getOrbitalVelocityAtUT(CurrentNode.UT).xzy;

            if (maneuverPos.NotNAN() && !maneuverPos.IsZero() && maneuverVel.NotNAN())
            {
                var nprog = maneuverVel.normalized;
                var nnorm = Vector3d.Cross(maneuverVel, maneuverPos).normalized;
                var nrad  = Vector3d.Cross(nnorm, nprog);

                if (!nprog.IsZero() && !nnorm.IsZero() && !nrad.IsZero())
                {
                    var dv      = CurrentNode.DeltaV;
                    var calcVel = maneuverVel + nrad * dv.x + nnorm * dv.y + nprog * dv.z;
                    NodeTools.TurnVector(ref calcVel, maneuverPos, theta);
                    var newDV = calcVel - maneuverVel;

                    CurrentSavedNode.UpdateDvAbs(Vector3d.Dot(newDV, nrad),
                                                 Vector3d.Dot(newDV, nnorm),
                                                 Vector3d.Dot(newDV, nprog));
                    return;
                }
            }
            // position and velocity are perfectly parallel (less probable)
            // or
            // KSP API returned NaN or some other weird shit (much more probable)
            ScreenMessages.PostScreenMessage(KSP.Localization.Localizer.Format("precisemaneuver_erroneous_orbit"), 2.0f, ScreenMessageStyle.UPPER_CENTER);
        }
        internal void CircularizeOrbit()
        {
            var maneuverPos = CurrentNode.patch.getRelativePositionAtUT(CurrentNode.UT).xzy;
            var maneuverVel = CurrentNode.patch.getOrbitalVelocityAtUT(CurrentNode.UT).xzy;

            if (maneuverPos.NotNAN() && !maneuverPos.IsZero() && maneuverVel.NotNAN())
            {
                var nprog  = maneuverVel.normalized;
                var nnorm  = Vector3d.Cross(maneuverVel, maneuverPos).normalized;
                var nrad   = Vector3d.Cross(nnorm, nprog);
                var curVel = CurrentNode.nextPatch.getOrbitalVelocityAtUT(CurrentNode.UT).xzy;
                if (!nprog.IsZero() && !nnorm.IsZero() && !nrad.IsZero() && curVel.NotNAN())
                {
                    double rezSpeed = Math.Sqrt(CurrentNode.patch.referenceBody.gravParameter / maneuverPos.magnitude);

                    var normVel = Vector3d.Cross(maneuverPos, curVel);
                    var newVel  = Vector3d.Cross(normVel, maneuverPos).normalized *rezSpeed;
                    var newDV   = newVel - maneuverVel;

                    CurrentSavedNode.UpdateDvAbs(Vector3d.Dot(newDV, nrad),
                                                 Vector3d.Dot(newDV, nnorm),
                                                 Vector3d.Dot(newDV, nprog));
                    return;
                }
            }
            // position and velocity are perfectly parallel (less probable)
            // or
            // KSP API returned NaN or some other weird shit (much more probable)
            ScreenMessages.PostScreenMessage(KSP.Localization.Localizer.Format("precisemaneuver_erroneous_orbit"), 2.0f, ScreenMessageStyle.UPPER_CENTER);
        }
        internal void UpdateNodes()
        {
            var newtarget = NodeTools.GetTargetOrbit(CurrentNode.patch.referenceBody);

            if (newtarget != target)
            {
                target = newtarget;
                NotifyTargetChanged();
            }

            UpdateCurrentNode();
            bool origSame = CurrentSavedNode.OrigSame(CurrentNode);
            bool changed  = CurrentSavedNode.Changed;

            if (changed)
            {
                if (origSame)
                {
                    Vector3d newdv = CurrentSavedNode.dV;
                    if (CurrentNode.attachedGizmo != null)
                    {
                        CurrentNode.attachedGizmo.DeltaV = newdv;
                        CurrentNode.attachedGizmo.UT     = CurrentSavedNode.UT;
                    }
                    CurrentNode.OnGizmoUpdated(newdv, CurrentSavedNode.UT);
                    CurrentSavedNode.UpdateOrig();
                }
            }
            if (!origSame)
            {
                CurrentSavedNode.ResetSavedNode(CurrentNode);
            }
            if (changed || !origSame)
            {
                NotifyDvUTChanged();
            }
        }
        internal void LoadPreset(string name)
        {
            var dv = PreciseManeuverConfig.Instance.GetPreset(name);

            CurrentSavedNode.UpdateDvAbs(dv.x, dv.y, dv.z);
        }
 internal void EndAtomicChange()
 {
     CurrentSavedNode.EndAtomicChange();
 }
 internal void BeginAtomicChange()
 {
     CurrentSavedNode.BeginAtomicChange();
 }
 internal void Redo()
 {
     CurrentSavedNode.Redo();
 }
 internal void Undo()
 {
     CurrentSavedNode.Undo();
 }