Exemplo n.º 1
0
        /// <summary>
        /// This will return an image of the specified size with the thrust graph for this nozzle
        /// Totally jacked the image code from the wiki http://wiki.kerbalspaceprogram.com/wiki/Module_code_examples, with some modifications
        /// 
        /// 
        /// </summary>
        /// <param name="imgH">Hight of the desired output, in pixels</param>
        /// <param name="imgW">Width of the desired output, in pixels</param>
        /// <param name="burnTime">Chart for burn time in seconds</param>
        /// <returns></returns>
        public static Texture2D stackThrustPredictPic(int imgH, int imgW, int burnTime, FloatCurve atmoCurve, AdvSRBNozzle nozzle)
        {
            //First we set up the analyzer
            //Step 1: Figure out fuel sources
            nozzle.FuelStackSearcher(nozzle.FuelSourcesList);
            //Step 2: Set up mass variables
            float stackTotalMass = 0f;
            stackTotalMass = nozzle.fullStackFuelMass;

            float remStackFuel = nozzle.fullStackFuelMass;

            //stackTotalMass = CalcStackWetMass(FuelSourcesList, stackTotalMass);

            //float[] segmentFuelArray = new float[nozzle.FuelSourcesList.Count];

            int i = 0;
            //foreach (Part p in nozzle.FuelSourcesList)
            //{
            //    segmentFuelArray[i] = p.GetResourceMass();
            //    i++;
            //}

            //float stackCurrentMass = stackTotalMass;
            //Now we set up the image maker
            Texture2D image = new Texture2D(imgW, imgH);
            int graphXmin = 19;
            int graphXmax = imgW - 20;
            int graphYmin = 19;
            //Step 3: Set Up color variables
            Color brightG = Color.black;
            brightG.r = .3372549f;
            brightG.g = 1;
            Color mediumG = Color.black;
            mediumG.r = 0.16862745f;
            mediumG.g = .5f;
            Color lowG = Color.black;
            lowG.r = 0.042156863f;
            lowG.g = .125f;

            Color MassColor = Color.blue;
            Color ThrustColor = Color.cyan;
            Color ExtraThrustColor = Color.yellow;

            //Step 4: Define text arrays
            KSF_CharArrayUtils.populateCharArrays();
            //Step 5a: Define time markings (every 10 seconds gets a verticle line)
            System.Collections.Generic.List<int> timeLines = new System.Collections.Generic.List<int>();
            double xScale = (imgW - 40) / (double)burnTime;
            //print("xScale: " + xScale);
            calcTimeLines((float)burnTime, 10, timeLines, (float)xScale, 20);
            //Step 5b: Define vertical line markings (9 total to give 10 sections)
            System.Collections.Generic.List<int> horzLines = new System.Collections.Generic.List<int>();
            calcHorizLines(imgH - graphYmin, horzLines, 9, 20);
            //Step 6: Clear the background

            //Set all the pixels to black. If you don't do this the image contains random junk.
            for (int y = 0; y < image.height; y++)
            {
                for (int x = 0; x < image.width; x++)
                {
                    image.SetPixel(x, y, Color.black);
                }
            }

            //Step 7a: Draw Time Lines
            for (int y = 0; y < image.height; y++)
            {
                for (int x = 0; x < image.width; x++)
                {
                    if (timeLines.Contains(x) && y > graphYmin)
                        image.SetPixel(x, y, lowG);
                }
            }

            //Step 7b: Draw Vert Lines
            for (int y = 0; y < image.height; y++)
            {
                for (int x = 0; x < image.width; x++)
                {
                    if (horzLines.Contains(y) && x < graphXmax && x > graphXmin)
                        image.SetPixel(x, y, lowG);
                }
            }

            //Step 7c: Draw Bounding Lines
            for (int y = 0; y < image.height; y++)
            {
                for (int x = 0; x < image.width; x++)
                {
                    if ((x == graphXmin | x == graphXmax) && (y > graphYmin | y == graphYmin))
                        image.SetPixel(x, y, mediumG);

                    if (y == graphYmin && graphXmax > x && graphXmin < x)
                        image.SetPixel(x, y, mediumG);
                }
            }

            //Step 8a: Populate graphArray
            double simStep = .2;
            i = 0;
            //double peakThrustTime = 0;
            double peakThrustAmt = 0;

            //set up the array for the graphs
            int graphArraySize = Convert.ToInt16(Convert.ToDouble(burnTime) / simStep);
            double[,] graphArray = new double[graphArraySize, 4];

            //one time setups
            //graphArray[i, 0] = stackMassFlow(FuelSourcesList, (float)(i * simStep), segmentFuelArray, simStep);

            graphArray[i, 0] = nozzle.MassFlow.Evaluate((float)(i * simStep)) * nozzle.fullStackFuelMass;
            graphArray[i, 1] = stackTotalMass;
            graphArray[i, 2] = 9.80665 * atmoCurve.Evaluate(1) * graphArray[i, 0];
            graphArray[i, 3] = graphArray[i, 2] - (graphArray[i, 1] * 9.80665);

            remStackFuel -= (float)graphArray[i, 0];

            //fForce = 9.81f * fCurrentIsp * fFuelFlowMass / TimeWarp.fixedDeltaTime;
            do
            {
                i++;
                //
                //graphArray[i, 0] = stackMassFlow(FuelSourcesList, (float)(i * simStep), segmentFuelArray, simStep);

                graphArray[i, 0] = nozzle.MassFlow.Evaluate((float)(i * simStep)) * nozzle.fullStackFuelMass;

                graphArray[i, 1] = graphArray[i - 1, 1] - (graphArray[i - 1, 0] * simStep);

                if (remStackFuel > 0)
                    graphArray[i, 2] = 9.80665 * atmoCurve.Evaluate(1) * graphArray[i, 0];
                else
                    graphArray[i, 2] = 0;

                if (graphArray[i, 2] > 0)
                    graphArray[i, 3] = graphArray[i, 2] - (graphArray[i, 1] * 9.80665);
                else
                    graphArray[i, 3] = 0;

                if (graphArray[i, 2] > peakThrustAmt)
                {
                    peakThrustAmt = graphArray[i, 2];
                    //peakThrustTime = i;
                }

                remStackFuel -= (float)graphArray[i, 0] * (float)simStep;

                //print("generating params i=" + i + " peak at " + Convert.ToInt16(Convert.ToDouble(simDuration) / simStep));
            } while (i + 1 < graphArraySize);

            //Step 8b: Make scales for the y axis
            double yScaleMass = 1;
            double yScaleThrust = 1;
            int usableY;
            usableY = imgH - 20;

            yScaleMass = usableY / (Mathf.CeilToInt((float)(graphArray[0, 1] / 10)) * 10);
            float inter;
            inter = (float)peakThrustAmt / 100;
            //print("1: " + inter);
            inter = Mathf.CeilToInt(inter);
            //print("22: " + inter);
            inter = inter * 100;
            //print("3: " + inter);
            inter = usableY / inter;
            //print("4: " + inter);

            yScaleThrust = inter;

            //print(yScaleThrust + ":" + peakThrustAmt + ":" + usableY);

            Debug.Log("graphed scales");

            //Step 8c: Graph the mass
            int lineWidth = 3;
            for (int x = graphXmin; x < graphXmax; x++)
            {
                int fx = fGraph(xScale, x - 20, yScaleMass, graphArray, simStep, graphArraySize, 1, 20);

                for (int y = fx; y < fx + lineWidth; y++)
                {
                    image.SetPixel(x, y, MassColor);
                }
            }

            //Step 8d: Graph the thrust
            lineWidth = 3;
            for (int x = graphXmin; x < graphXmax; x++)
            {
                int fx = fGraph(xScale, x - 20, yScaleThrust, graphArray, simStep, graphArraySize, 2, 20);
                for (int y = fx; y < fx + lineWidth; y++)
                {
                    image.SetPixel(x, y, ThrustColor);
                }
            }

            //Step 8e: Graph the thrust extra
            lineWidth = 2;
            for (int x = graphXmin; x < graphXmax; x++)
            {
                int fx = fGraph(xScale, x - 20, yScaleThrust, graphArray, simStep, graphArraySize, 3, 20);
                for (int y = fx; y < fx + lineWidth; y++)
                {
                    image.SetPixel(x, y, ExtraThrustColor);
                }
            }

            //Step 9: Set up boxes for time

            //int i = 0;
            string s;
            i = 0;
            int length = 0;
            int pos = 0;
            int startpos = 0;
            Texture2D tex;

            do
            {
                s = "";

                pos = timeLines[i];
                i++;
                s = i * 10 + " s";

                //print("composite string: " + s);

                length = calcStringPixLength(KSF_CharArrayUtils.convertStringToCharArray(s));
                //print("length: " + length);

                startpos = Mathf.FloorToInt((float)pos - 0.5f * (float)length);

                Color[] region = image.GetPixels(startpos, 23, length, 11);

                for (int c = 0; c < region.Length; c++)
                {
                    region[c] = brightG;
                }

                image.SetPixels(startpos, 23, length, 11, region);

                tex = convertCharArrayToTex(KSF_CharArrayUtils.convertStringToCharArray(s), length, brightG);

                Color[] region2 = tex.GetPixels();

                image.SetPixels(startpos + 2, 25, length - 4, 7, region2);

                length = 0;

            } while (i < timeLines.Count);

            //set up boxes for horizontal lines, mass first
            startpos = 0;
            length = 0;
            pos = 0;
            i = 0;
            do
            {
                s = "";
                pos = horzLines[i];
                i++;
                s = ((Mathf.CeilToInt((float)(graphArray[0, 1] / 10)) * i)).ToString() + " t";
                length = calcStringPixLength(KSF_CharArrayUtils.convertStringToCharArray(s));
                startpos = Mathf.FloorToInt((float)pos - 5f);
                Color[] region = image.GetPixels(23, startpos, length, 11);
                for (int c = 0; c < region.Length; c++)
                {
                    region[c] = brightG;
                }
                image.SetPixels(23, startpos, length, 11, region);
                tex = convertCharArrayToTex(KSF_CharArrayUtils.convertStringToCharArray(s), length, brightG);
                Color[] region2 = tex.GetPixels();
                image.SetPixels(25, startpos + 2, length - 4, 7, region2);
                length = 0;
            } while (i < horzLines.Count);

            //set up boxes for horizontal lines,  thrust
            startpos = 0;
            length = 0;
            pos = 0;
            i = 0;
            do
            {
                s = "";
                pos = horzLines[i];
                i++;
                s = ((Mathf.CeilToInt((float)(peakThrustAmt / 100)) * (i * 10))).ToString() + " k";
                length = calcStringPixLength(KSF_CharArrayUtils.convertStringToCharArray(s));
                startpos = Mathf.FloorToInt((float)pos - 5f);
                Color[] region = image.GetPixels(imgW - 60, startpos, length, 11);
                for (int c = 0; c < region.Length; c++)
                {
                    region[c] = brightG;
                }
                image.SetPixels(imgW - 60, startpos, length, 11, region);
                tex = convertCharArrayToTex(KSF_CharArrayUtils.convertStringToCharArray(s), length, brightG);
                Color[] region2 = tex.GetPixels();
                image.SetPixels(imgW - 58, startpos + 2, length - 4, 7, region2);
                length = 0;
            } while (i < horzLines.Count);

            image.Apply();
            return image;
        }
Exemplo n.º 2
0
        public void segmentGUI()
        {
            if (DebugDetail > 0)
                Debug.Log("AdvSRB: in segmentGUI");

            //this is the "Segment" mode of the GUI, which allows one to customize burn times for each segment
            string segGUIname = "";
            //AdvSRBNozzle SRB;

            //must populate the buttons with the most current fuel sources
            //nozzle.FuelStackSearcher(nozzle.FuelSourcesList);

            //set length of internal text box to be 50 pix per segment
            segListLength = lNozzles.Count * 50;

            //automatically select first segment as current GUI
            if (lNozzles.Count > 0 && segCurrentGUI == null)
            {
                segCurrentGUI = lNozzles[0];
                iSegments = 0;
                refreshNodeInfo = true;
            }

            segListVector = GUI.BeginScrollView(new Rect(GUImainRect.xMin + 10, GUImainRect.yMin + 10, 120, 420), segListVector, new Rect(0, 0, 100, segListLength + 30));

            GUI.Label(new Rect(15, 0, 100, 15), "Nozzle End");
            for (int i = 0; i < lNozzles.Count; i++)
            {
                SRB = lNozzles[iSegments].GetComponent<AdvSRBNozzle>();
                ////if (SRB.GUIshortName != "")
                ////    segGUIname = SRB.GUIshortName;
                ////else
                segGUIname = "Unknown";

                //segGUIname = SRB.name;

                segGUIname = SRB.name;

                if (i == iSegments)
                {
                    if (GUI.Button(new Rect(5, i * 50 + 20, 95, 40),"* " + segGUIname))
                    {
                        if (lNozzles[i] != segCurrentGUI)
                        {
                            segPriorGUI = segCurrentGUI;
                            segCurrentGUI = lNozzles[i];
                            refreshSegGraph = true;
                        }
                    }
                }
                else
                {
                    if (GUI.Button(new Rect(5, i * 50 + 20, 95, 40), segGUIname))
                    {
                        if (lNozzles[i] != segCurrentGUI)
                        {
                            segPriorGUI = segCurrentGUI;
                            segCurrentGUI = lNozzles[i];
                            iSegments = i;
                            refreshSegGraph = true;
                        }
                    }
                }

            }
            GUI.Label(new Rect(15, segListLength + 15, 100, 10), "Top o' Stack");
            GUI.EndScrollView();

            //if (GUI.Button(new Rect(GUImainRect.xMin + 10, GUImainRect.yMin + 445, 120, 20), "Apply to Symmetry"))
            //{
            //    AdvSRBSegment s;
            //    AdvSRBSegment sb;

            //    if (segCurrentGUI != null)
            //    {
            //        s = segCurrentGUI.GetComponent<AdvSRBSegment>();

            //        foreach (Part p in segCurrentGUI.symmetryCounterparts)
            //        {
            //            Debug.Log("Symmetry Found! " + p.partName);

            //            sb = p.GetComponent<AdvSRBSegment>();

            //            sb.MassFlow = s.MassFlow;

            //            sb.BurnProfile = s.BurnProfile;
            //        }
            //    }
            //    refreshNodeInfo = true;
            //}

            if (isAutoNode)
            {
                if (GUI.Button(new Rect(GUImainRect.xMin + 10, GUImainRect.yMin + 470, 120, 20), "Go to Manual Node"))
                {
                    isAutoNode = !isAutoNode;

                    refreshNodeInfo = true;
                }
            }
            else
            {
                if (GUI.Button(new Rect(GUImainRect.xMin + 10, GUImainRect.yMin + 470, 120, 20), "Go to Auto Nodes"))
                {
                    isAutoNode = !isAutoNode;

                    refreshNodeInfo = true;
                }
            }

            SRB = segCurrentGUI.GetComponent<AdvSRBNozzle>();

            SRB.StackSearchAndFuel();

            //copy/paste functionality
            //if (segCurrentGUI != null)
            //{
            //    SRB = segCurrentGUI.GetComponent<AdvSRBSegment>();

            //    if (GUI.Button(new Rect(GUImainRect.xMin + 10, GUImainRect.yMin + 500, 55, 20), "Copy"))
            //    {
            //        //klipboard = SRB.AnimationCurveToString(SRB.MassFlow);
            //    }

            //    if (GUI.Button(new Rect(GUImainRect.xMin + 75, GUImainRect.yMin + 500, 55, 20), "Paste"))
            //    {
            //        //SRB.MassFlow = SRB.AnimationCurveFromString(klipboard);
            //        //SRB.BurnProfile = SRB.AnimationCurveToString(SRB.MassFlow);

            //        refreshNodeInfo = true;
            //        refreshSegGraph = true;
            //    }
            //}

            int width = 0;
            if (segCurrentGUI != null)
            {
                //SRB = segCurrentGUI.GetComponent<AdvSRBSegment>();
                width = SRB.MassFlow.length * 60;
                if (refreshSegGraph)
                {
                    System.Collections.Generic.List<Part> fSL = new System.Collections.Generic.List<Part>(); //filled once during OnActivate, is the master list
                    fSL.Add(segCurrentGUI);
                    segGraph = AdvSRBGraphUtils.stackThrustPredictPic(310, 490, Convert.ToInt16(simDuration), SRB.atmosphereCurve, SRB);
                    refreshSegGraph = false; ;
                }

                GUI.Box(new Rect(GUImainRect.xMin + 140, GUImainRect.yMin + 190, 510, 330), segGraph);

                if (isAutoNode)
                {
                    //populate the list box
                    int listLength = 0;

                    listLength = 30 * (autoTypeList.Count + 1);

                    //draw the selection box to select which auto mode to use
                    autoListVector = GUI.BeginScrollView(new Rect(GUImainRect.xMin + 140, GUImainRect.yMin + 10, 200, 170), autoListVector, new Rect(0, 0, 180, listLength));

                    for (int i = 0; i < autoTypeList.Count; i++)
                    {
                        if (GUI.Button(new Rect(5, i * 30 + 5, 175, 20), autoTypeList[i].shortName()))
                        {
                            autoTypeCurrent = i;
                        }
                    }
                    GUI.EndScrollView();

                    GUI.Label(new Rect(GUImainRect.xMin + 350, GUImainRect.yMin + 10, 300, 40), autoTypeList[autoTypeCurrent].description());

                    autoTypeList[autoTypeCurrent].drawGUI(GUImainRect);

                    ////if (autoTypeList[autoTypeCurrent].useWithSegment())
                    ////{
                    ////    this is the button to compute the segment values
                    ////    if (GUI.Button(new Rect(GUImainRect.xMin + 500, GUImainRect.yMin + 140, 150, 20), "Evaluate for Segment"))
                    ////    {
                    ////        SRB.MassFlow = autoTypeList[autoTypeCurrent].computeCurve(nozzle.atmosphereCurve, segCurrentGUI);

                    ////        SRB.BurnProfile = SRB.AnimationCurveToString(SRB.MassFlow);

                    ////        refreshSegGraph = true;
                    ////    }
                    ////}

                    if (autoTypeList[autoTypeCurrent].useWithStack())
                    {
                        //this is the button to compute stack values
                        if (GUI.Button(new Rect(GUImainRect.xMin + 500, GUImainRect.yMin + 170, 150, 20), "Evaluate for Stack"))
                        {
                            //AdvSRBSegment s;

                            SRB.MassFlow = autoTypeList[autoTypeCurrent].computeCurve(SRB.atmosphereCurve, SRB.fullStackFuelMass);
                            SRB.BurnProfile = AdvSRBUtils.AnimationCurveToString(SRB.MassFlow);

                            //foreach (Part p in nozzle.FuelSourcesList)
                            //{
                            //    //s = p.Modules.OfType<AdvSRBSegment>().FirstOrDefault();

                            //    //s.MassFlow = autoTypeList[autoTypeCurrent].computeCurve(nozzle.atmosphereCurve, p);
                            //    //s.BurnProfile = s.AnimationCurveToString(s.MassFlow);
                            //}

                            refreshSegGraph = true;
                        }
                    }

                }
                else
                {
                    nodeListVector = GUI.BeginScrollView(new Rect(GUImainRect.xMin + 140, GUImainRect.yMin + 10, 510, 40), nodeListVector, new Rect(0, 0, width + 70, 22));
                    if (segCurrentGUI != null)
                    {
                        SRB = segCurrentGUI.GetComponent<AdvSRBNozzle>();

                        for (int i = 0; i < SRB.MassFlow.length + 1; i++)
                        {
                            if (i < SRB.MassFlow.length)
                            {
                                if (i == nodeNumber)
                                {
                                    if (GUI.Button(new Rect(3 + i * 60, 4, 54, 18), "*Node " + (i + 1)))
                                    {
                                        nodePriorNumber = nodeNumber;
                                        nodeNumber = i;
                                        refreshNodeInfo = true;
                                    }
                                }
                                else
                                {
                                    if (GUI.Button(new Rect(3 + i * 60, 4, 54, 18), "Node " + (i + 1)))
                                    {
                                        nodePriorNumber = nodeNumber;
                                        nodeNumber = i;
                                        refreshNodeInfo = true;
                                    }
                                }
                            }
                            else
                            {
                                if (GUI.Button(new Rect(3 + i * 60, 4, 64, 18), "Add Node"))
                                {
                                    nodePriorNumber = nodeNumber;
                                    nodeNumber = i;
                                    SRB.MassFlow.AddKey(SRB.MassFlow.keys[i - 1].time + 10, 0);
                                    refreshNodeInfo = true;
                                }
                            }
                        }
                    }
                    GUI.EndScrollView();

                    //set up node editing tools

                    if (segCurrentGUI != null && refreshNodeInfo)
                    {
                        //SRB = segCurrentGUI.GetComponent<AdvSRBSegment>();
                        //lbMsgBox = "The currently selected segment is " + SRB.GUIshortName + " and the current node is Node " + (nodeNumber + 1); //removed May 4, 2014: asterisks make redundant

                        tbTime = SRB.MassFlow.keys[nodeNumber].time.ToString();
                        tbValue = SRB.MassFlow.keys[nodeNumber].value.ToString();
                        tbInTan = SRB.MassFlow.keys[nodeNumber].inTangent.ToString();
                        tbOutTan = SRB.MassFlow.keys[nodeNumber].outTangent.ToString();

                        refreshNodeInfo = false;
                    }
                    else
                    {
                        if (segCurrentGUI == null)
                        {
                            lbMsgBox = "Please select a segment on the left to begin editing";

                            tbTime = "";
                            tbValue = "";
                            tbInTan = "";
                            tbOutTan = "";
                        }
                    }

                    GUI.Label(new Rect(GUImainRect.xMin + 162, GUImainRect.yMin + 80, 78, 15), "Node Time (s)");
                    GUI.Label(new Rect(GUImainRect.xMin + 292, GUImainRect.yMin + 80, 78, 15), "Node Value");
                    GUI.Label(new Rect(GUImainRect.xMin + 422, GUImainRect.yMin + 80, 78, 15), "In Tangent");
                    GUI.Label(new Rect(GUImainRect.xMin + 552, GUImainRect.yMin + 80, 78, 15), "Out Tangent");

                    tbTime = GUI.TextField(new Rect(140 + GUImainRect.xMin, 100 + GUImainRect.yMin, 120, 20), tbTime);
                    tbValue = GUI.TextField(new Rect(270 + GUImainRect.xMin, 100 + GUImainRect.yMin, 120, 20), tbValue);
                    tbInTan = GUI.TextField(new Rect(400 + GUImainRect.xMin, 100 + GUImainRect.yMin, 120, 20), tbInTan);
                    tbOutTan = GUI.TextField(new Rect(530 + GUImainRect.xMin, 100 + GUImainRect.yMin, 120, 20), tbOutTan);

                    GUI.Label(new Rect(GUImainRect.xMin + 140, GUImainRect.yMin + 60, 510, 15), convertMassFlowToThrust(Convert.ToSingle(tbValue), SRB.atmosphereCurve, lbMsgBox));

                    simDuration = GUI.TextField(new Rect(140 + GUImainRect.xMin, 160 + GUImainRect.yMin, 40, 20), simDuration);
                    GUI.Label(new Rect(GUImainRect.xMin + 190, GUImainRect.yMin + 160, 150, 20), "Simulation Run Time (s)");

                    //save node changes
                    if (segCurrentGUI != null)
                    {
                        //SRB = segCurrentGUI.GetComponent<AdvSRBSegment>();
                        if (GUI.Button(new Rect(GUImainRect.xMin + 490, GUImainRect.yMin + 160, 160, 20), "Save Changes to Node"))
                        {
                            Keyframe k = new Keyframe();
                            k.time = Convert.ToSingle(tbTime);
                            k.value = Convert.ToSingle(tbValue);
                            k.inTangent = Convert.ToSingle(tbInTan);
                            k.outTangent = Convert.ToSingle(tbOutTan);

                            SRB.MassFlow.RemoveKey(nodeNumber);
                            SRB.MassFlow.AddKey(k);

                            SRB.BurnProfile = AdvSRBUtils.AnimationCurveToString(SRB.MassFlow);
                            refreshNodeInfo = true;

                            refreshSegGraph = true;
                        }

                        if (GUI.Button(new Rect(GUImainRect.xMin + 140, GUImainRect.yMin + 130, 160, 20), "Smooth Tangents"))
                        {
                            SRB.MassFlow.SmoothTangents(nodeNumber, 1);

                            SRB.BurnProfile = AdvSRBUtils.AnimationCurveToString(SRB.MassFlow);
                        }

                        if (GUI.Button(new Rect(GUImainRect.xMin + 320, GUImainRect.yMin + 130, 160, 20), "Flat In Tan"))
                        {
                            float deltaY;
                            float deltaX;

                            deltaY = SRB.MassFlow.keys[nodeNumber].value - SRB.MassFlow.keys[nodeNumber - 1].value;
                            deltaX = SRB.MassFlow.keys[nodeNumber].time - SRB.MassFlow.keys[nodeNumber - 1].time;

                            tbInTan = (deltaY / deltaX).ToString();

                            SRB.BurnProfile = AdvSRBUtils.AnimationCurveToString(SRB.MassFlow);
                        }

                        if (GUI.Button(new Rect(GUImainRect.xMin + 490, GUImainRect.yMin + 130, 160, 20), "Flat Out Tan"))
                        {
                            float deltaY;
                            float deltaX;

                            deltaY = SRB.MassFlow.keys[nodeNumber + 1].value - SRB.MassFlow.keys[nodeNumber].value;
                            deltaX = SRB.MassFlow.keys[nodeNumber + 1].time - SRB.MassFlow.keys[nodeNumber].time;

                            tbOutTan = (deltaY / deltaX).ToString();

                            SRB.BurnProfile = AdvSRBUtils.AnimationCurveToString(SRB.MassFlow);
                        }

                        if (nodeNumber != 0)
                        {
                            if (GUI.Button(new Rect(GUImainRect.xMin + 380, GUImainRect.yMin + 160, 100, 20), "Remove Node"))
                            {
                                SRB.MassFlow.RemoveKey(nodeNumber);

                                SRB.BurnProfile = AdvSRBUtils.AnimationCurveToString(SRB.MassFlow);
                                refreshNodeInfo = true;

                                refreshSegGraph = true;
                            }
                        }

                        //highlight the selected booster segment in the editor

                        //segCurrentGUI.SetHighlight(true);
                    }
                }
            }
        }