/// <summary>
        /// Calculate the speed from the turn rate and radius.
        /// </summary>
        /// <param name="plot"></param>
        /// <returns></returns>
        private double CalculateSpeed(TurnPerformancePlot plot)
        {
            double timeToTurnCompleteCircleInSecs = 360 / plot.TurnRateInDegPerSecond;
            double cirumference = (plot.TurnRaduis * 2) * Math.PI;

            return((cirumference / 5280) / (timeToTurnCompleteCircleInSecs / 60 / 60));
        }
 /// <summary>
 /// Draw the minor title under the major title.
 /// </summary>
 /// <param name="grfx"></param>
 /// <param name="fastestTurnRatePlot"></param>
 private void DrawMinorTitle(Graphics grfx, TurnPerformancePlot fastestTurnRatePlot)
 {
     if (plots != null && plots.Count > 0)
     {
         string elapsedTimeMinorTitle = string.Format("After {0} elapsed seconds", Math.Round(this.displayTurnDegrees / fastestTurnRatePlot.TurnRateInDegPerSecond, 1));
         Point  minorTitlePoint       = GetTitleLocationPoint(grfx);
         minorTitlePoint.Y += (int)TitleFont.GetHeight();
         minorTitlePoint.X  = (int)(this.Width / 2 - grfx.MeasureString(elapsedTimeMinorTitle, MinorTitleFont).Width / 2);
         grfx.DrawString(elapsedTimeMinorTitle, MinorTitleFont, MinorTitlePen.Brush, minorTitlePoint);
     }
 }
 /// <summary>
 /// Format up the tool tip text.
 /// </summary>
 /// <param name="plot"></param>
 /// <returns></returns>
 private string GetToolTipText(TurnPerformancePlot plot)
 {
     return(string.Format("  {0}\n    Turn Radius = {1} {2}\n    Turn Rate = {3} degrees/second\n    Turn Speed = {4} {5}\n    Stall Speed = {6} {5}\n    Time To Turn 360 = {7} seconds",
                          plot.PlotTitle,
                          Math.Round(plot.TurnRaduis, 1),
                          TurnRadiusUnitName,
                          Math.Round(plot.TurnRateInDegPerSecond, 1),
                          Math.Round(CalculateSpeed(plot), 0),
                          TurnSpeedUnitName,
                          Math.Round(plot.StallSpeed, 1),
                          Math.Round(360 / plot.TurnRateInDegPerSecond, 1)));
 }
        /// <summary>
        /// Find the plot with the quickest turn rate.
        /// </summary>
        /// <returns></returns>
        private TurnPerformancePlot FindFastestTurnRate()
        {
            TurnPerformancePlot fastestTurnRatePlot = new TurnPerformancePlot();

            foreach (TurnPerformancePlot plot in plots)
            {
                if (plot.TurnRateInDegPerSecond > fastestTurnRatePlot.TurnRateInDegPerSecond)
                {
                    fastestTurnRatePlot = plot;
                }
            }

            return(fastestTurnRatePlot);
        }
        /// <summary>
        /// Find the plot with the largest turn radius.
        /// </summary>
        /// <returns></returns>
        private TurnPerformancePlot FindLargestTurnRadius()
        {
            TurnPerformancePlot largestTurnRadiusPlot = new TurnPerformancePlot();

            foreach (TurnPerformancePlot plot in plots)
            {
                if (plot.TurnRaduis > largestTurnRadiusPlot.TurnRaduis)
                {
                    largestTurnRadiusPlot = plot;
                }
            }

            return(largestTurnRadiusPlot);
        }
        private void BuildTurnPerformancePlot(string aircraftName, bool sustained, TurnData noFlapsData, TurnData fullFlapsData)
        {
            if (chkboxClean.Checked)
            {
                TurnPerformancePlot cleanPlot = new TurnPerformancePlot();
                AddTurnPlot(cleanPlot, noFlapsData, sustained, aircraftName, TurnPerformancePlot.FlapPosition.None);
            }

            if (chkboxFullFlaps.Checked)
            {
                TurnPerformancePlot fullFlapsPlot = new TurnPerformancePlot();
                AddTurnPlot(fullFlapsPlot, fullFlapsData, sustained, aircraftName, TurnPerformancePlot.FlapPosition.Full);
            }
        }
 /// <summary>
 /// Get the point array which represents a small equlateral triangle to be used as the arc end-cap.
 /// </summary>
 /// <param name="plot"></param>
 /// <param name="widthOfTriangleSide"></param>
 /// <param name="X"></param>
 /// <param name="Y"></param>
 /// <returns></returns>
 private Point[] GetFlapPositionTrianglePoints(TurnPerformancePlot plot, int widthOfTriangleSide, int X, int Y)
 {
     if (plot.FlapSetting == TurnPerformancePlot.FlapPosition.None)
     {
         Point pointA = new Point(X - widthOfTriangleSide, Y + widthOfTriangleSide);
         Point pointB = new Point(X + widthOfTriangleSide, Y + widthOfTriangleSide);
         Point pointC = new Point(X, Y - widthOfTriangleSide);
         Point pointD = new Point(X - widthOfTriangleSide, Y + widthOfTriangleSide);
         return(new Point[] { pointA, pointB, pointC, pointD });
     }
     else
     {
         Point pointA = new Point(X - widthOfTriangleSide, Y - widthOfTriangleSide);
         Point pointB = new Point(X + widthOfTriangleSide, Y - widthOfTriangleSide);
         Point pointC = new Point(X, Y + widthOfTriangleSide);
         Point pointD = new Point(X - widthOfTriangleSide, Y - widthOfTriangleSide);
         return(new Point[] { pointA, pointB, pointC, pointD });
     }
 }
        private void AddTurnPlot(TurnPerformancePlot plot, TurnData turnData, bool sustained, string aircraftName, TurnPerformancePlot.FlapPosition flapPosition)
        {
            plot.StallSpeed = turnData.StallSpeed;
            plot.FlapSetting = flapPosition;
            Pen plotPen = new Pen(Registry.Instance.AircraftColours.GetAircraftColour(aircraftName));
            plotPen.Width = 1F;
            plot.PlotPen = plotPen;
            if (sustained)
            {
                plot.PlotTitle = string.Format("{0} (Sust/{1})", aircraftName, flapPosition == TurnPerformancePlot.FlapPosition.Full ? "Full Flaps" : "Clean");
                plot.TurnRaduis = turnData.SustainedTurnRadius;
                plot.TurnRateInDegPerSecond = turnData.SustainedTurnRate;
            }
            else
            {
                plot.PlotTitle = string.Format("{0} (Inst/{1})", aircraftName, flapPosition == TurnPerformancePlot.FlapPosition.Full ? "Full Flaps" : "Clean");
                plotPen.DashPattern = new float[] { 2, 2 };
                plot.TurnRaduis = turnData.TurnRadiusAtCorner;
                plot.TurnRateInDegPerSecond = turnData.TurnRateAtCorner;
            }

            turnPerformanceUserControl.AddPlot(plot);
        }
        private void AddTurnPlot(TurnPerformancePlot plot, TurnData turnData, bool sustained, string aircraftName, TurnPerformancePlot.FlapPosition flapPosition)
        {
            plot.StallSpeed  = turnData.StallSpeed;
            plot.FlapSetting = flapPosition;
            Pen plotPen = new Pen(Registry.Instance.AircraftColours.GetAircraftColour(aircraftName));

            plotPen.Width = 1F;
            plot.PlotPen  = plotPen;
            if (sustained)
            {
                plot.PlotTitle              = string.Format("{0} (Sust/{1})", aircraftName, flapPosition == TurnPerformancePlot.FlapPosition.Full ? "Full Flaps" : "Clean");
                plot.TurnRaduis             = turnData.SustainedTurnRadius;
                plot.TurnRateInDegPerSecond = turnData.SustainedTurnRate;
            }
            else
            {
                plot.PlotTitle              = string.Format("{0} (Inst/{1})", aircraftName, flapPosition == TurnPerformancePlot.FlapPosition.Full ? "Full Flaps" : "Clean");
                plotPen.DashPattern         = new float[] { 2, 2 };
                plot.TurnRaduis             = turnData.TurnRadiusAtCorner;
                plot.TurnRateInDegPerSecond = turnData.TurnRateAtCorner;
            }

            turnPerformanceUserControl.AddPlot(plot);
        }
        private void BuildTurnPerformancePlot(string aircraftName, bool sustained, TurnData noFlapsData, TurnData fullFlapsData)
        {
            if (chkboxClean.Checked)
            {
                TurnPerformancePlot cleanPlot = new TurnPerformancePlot();
                AddTurnPlot(cleanPlot, noFlapsData, sustained, aircraftName, TurnPerformancePlot.FlapPosition.None);
            }

            if(chkboxFullFlaps.Checked)
            {
                TurnPerformancePlot fullFlapsPlot = new TurnPerformancePlot();
                AddTurnPlot(fullFlapsPlot, fullFlapsData, sustained, aircraftName, TurnPerformancePlot.FlapPosition.Full);
            }
        }
 public void AddPlot(TurnPerformancePlot plot)
 {
     plots.Add(plot);
 }
        /// <summary>
        /// Add the legend on the right hand side of the control to show which lines
        /// are which.
        /// </summary>
        /// <param name="grfx"></param>
        /// <param name="graphRectangle"></param>
        private void AddLegend(Graphics grfx, Rectangle graphRectangle)
        {
            if (plots.Count == 0)
            {
                return;
            }

            float maxLegendTxtWidth = 0;

            foreach (TurnPerformancePlot plot in plots)
            {
                SizeF size = grfx.MeasureString(plot.PlotTitle, LegendFont);
                if (size.Width > maxLegendTxtWidth)
                {
                    maxLegendTxtWidth = size.Width;
                }
            }

            const int lineLength = 25;

            int legendHeight = LegendFont.Height * plots.Count + LegendFont.Height;
            int legendWidth  = (int)(maxLegendTxtWidth + lineLength + 20);

            Point     legendLocation  = new Point(this.Width - legendWidth - 10, graphRectangle.Top - 5);
            Size      legendSize      = new Size(legendWidth, legendHeight);
            Rectangle legendRectangle = new Rectangle(legendLocation, legendSize);

            int       shadowWidth     = (int)(graphRectangle.Height / 50);
            Point     shadowLocation  = new Point(legendRectangle.Left + shadowWidth, legendRectangle.Top + shadowWidth);
            Rectangle shadowRectangle = new Rectangle(shadowLocation, legendSize);

            grfx.DrawRectangle(Pens.LightGray, shadowRectangle);
            grfx.FillRectangle(Brushes.LightGray, shadowRectangle.X + 1, shadowRectangle.Y + 1, shadowRectangle.Width - 1, shadowRectangle.Height - 1);


            grfx.DrawRectangle(Pens.Black, legendRectangle);
            grfx.FillRectangle(new Pen(this.BackColor).Brush, legendRectangle.X + 1, legendRectangle.Y + 1, legendRectangle.Width - 1, legendRectangle.Height - 1);

            for (int i = 1; i < plots.Count + 1; i++)
            {
                TurnPerformancePlot plot        = plots[i - 1];
                const int           linePadding = 10;

                // Draw the plot line as the key.
                Point startLinePoint = new Point(legendRectangle.Left + linePadding, legendRectangle.Top + i * LegendFont.Height);
                Point endLinePoint   = new Point(legendRectangle.Left + linePadding + lineLength, legendRectangle.Top + i * LegendFont.Height);
                grfx.DrawLine(plot.PlotPen, startLinePoint, endLinePoint);

                // draw the triangle end cap at the end of the line.
                int widthOfEndCap = (int)(graphRectangle.Height / 70);
                Pen endPointPen   = new Pen(plot.PlotPen.Brush, widthOfEndCap / (float)1.3);
                grfx.DrawPolygon(endPointPen, GetFlapPositionTrianglePoints(plot, widthOfEndCap, endLinePoint.X, endLinePoint.Y));

                // Draw the text description of the line.
                string legendText         = plot.PlotTitle;
                PointF textLocationPointF = new PointF(legendRectangle.Left + lineLength + linePadding * 2,
                                                       (legendRectangle.Top + i * LegendFont.Height) - (LegendFont.Height / 2));
                grfx.DrawString(legendText, LegendFont, Brushes.Black, textLocationPointF);


                // Add the tooltip.
                Rectangle toolTipRectangle = new Rectangle(legendRectangle.Left, (int)textLocationPointF.Y + 2, legendRectangle.Width, LegendFont.Height - 2);
                tipRegionCollection.Add(new ToolTipRegion(toolTipRectangle, GetToolTipText(plot)));
            }
        }
        /// <summary>
        /// Handle the Paint event.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics grfx        = e.Graphics;
            Size     controlSize = e.ClipRectangle.Size;

            DrawTitle(grfx, controlSize);

            int initialGraphWidth     = (this.Size.Width - LeftPadding - RightPadding);
            int initialGraphHeight    = (int)initialGraphWidth / 2;
            int minorGridLinesSpacing = (int)(initialGraphWidth / NumberOfMinorXAxisGridMarks);

            // Define the graph rectangle. The X axis runs along the bottom, the Y axis runs along the Left.
            Rectangle graphRectangle = new Rectangle(LeftPadding,
                                                     TopPadding,
                                                     NumberOfMinorXAxisGridMarks * minorGridLinesSpacing,
                                                     NumberOfMinorXAxisGridMarks * minorGridLinesSpacing / 2);

            // Draw the graph Axis
            DrawGraphAxis(grfx, graphRectangle);

            // Draw the minor grid lines.
            DrawMinorGridLines(grfx, graphRectangle, minorGridLinesSpacing);

            TurnPerformancePlot largestTurnRadiusPlot = FindLargestTurnRadius();
            float largestTurnRadius          = largestTurnRadiusPlot.TurnRaduis;
            float largestTurnDiameter        = largestTurnRadius * 2;
            int   largestRoundedUpTurnRadius = (int)(largestTurnDiameter / UnitMultiple + 1) * UnitMultiple;
            int   unitWidth = (int)(((largestRoundedUpTurnRadius / NumberOfMinorXAxisGridMarks) / UnitMultiple) + 1) * UnitMultiple;
            int   widthPxBetweenMinorMarkings = (int)(graphRectangle.Width / NumberOfMinorXAxisGridMarks);

            // Draw X Axis minor gridline values.
            DrawXAxisMinorGridLineValues(grfx, graphRectangle, widthPxBetweenMinorMarkings, unitWidth);

            // Draw Y Axis minor gridline values.
            DrawYAxisMinorGridLineValues(grfx, graphRectangle, widthPxBetweenMinorMarkings, unitWidth);

            // Draw the axis labels.
            DrawAxisLabels(grfx, graphRectangle);

            TurnPerformancePlot fastestTurnRatePlot = FindFastestTurnRate();

            // Draw the minor title.
            DrawMinorTitle(grfx, fastestTurnRatePlot);

            tipRegionCollection.Clear();

            // For each plot to draw...
            foreach (TurnPerformancePlot plot in plots)
            {
                if (plot.TurnRateInDegPerSecond <= 0F)
                {
                    continue;
                }

                float arcDegrees = plot.TurnRateInDegPerSecond * (this.displayTurnDegrees / fastestTurnRatePlot.TurnRateInDegPerSecond);
                int   arcRadius  = (int)((plot.TurnRaduis / unitWidth) * widthPxBetweenMinorMarkings);
                int   arcBoundingRectangleLeft = LeftPadding + 1;
                int   arcBoundingRectangleTop  = (graphRectangle.Height - arcRadius) + graphRectangle.Y - 1;

                //Draw the turn circle.
                grfx.DrawArc(plot.PlotPen,
                             new Rectangle((int)arcBoundingRectangleLeft, (int)arcBoundingRectangleTop, arcRadius * 2, arcRadius * 2),
                             180,
                             arcDegrees);

                // Draw the 'continuation' dashed line.
                int widthOfDashes = (int)(graphRectangle.Height / 60);
                if (widthOfDashes == 0)
                {
                    widthOfDashes = 1;
                }

                // draw the continuation dashed arc to complete the 180 degrees.
                Pen dashedPen = new Pen(Brushes.Gray);
                dashedPen.DashPattern = new float[] { widthOfDashes, widthOfDashes };
                grfx.DrawArc(dashedPen,
                             new Rectangle((int)arcBoundingRectangleLeft, (int)arcBoundingRectangleTop, arcRadius * 2, arcRadius * 2),
                             180 + arcDegrees,
                             180 - arcDegrees);

                // Find the end point of the arc so we can pop an end-cap and a label on it.
                int Y = (int)(arcRadius * Math.Sin(arcDegrees * Math.PI / 180));
                int X = (int)(arcRadius * Math.Cos(arcDegrees * Math.PI / 180));
                Y = arcBoundingRectangleTop + arcRadius - Y;
                X = arcBoundingRectangleLeft + arcRadius - X;

                // draw an end-cap at the end of the arc.
                int widthOfEndCap = (int)(graphRectangle.Height / 70);
                Pen endPointPen   = new Pen(plot.PlotPen.Brush, widthOfEndCap / (float)1.3);
                grfx.DrawPolygon(endPointPen, GetFlapPositionTrianglePoints(plot, widthOfEndCap, X, Y));

                Point toolTipPoint = new Point(X - (widthOfEndCap / 2) * 2 + 1,
                                               Y - (widthOfEndCap / 2) * 2 + 1);

                Rectangle toolTipRectangle = new Rectangle(toolTipPoint.X - widthOfEndCap, toolTipPoint.Y - widthOfEndCap, widthOfEndCap * 3, widthOfEndCap * 3);
                tipRegionCollection.Add(new ToolTipRegion(toolTipRectangle, GetToolTipText(plot)));
            }

            AddLegend(grfx, graphRectangle);

            if (ShowToolTipRegions)
            {
                foreach (ToolTipRegion region in tipRegionCollection)
                {
                    grfx.DrawRectangle(new Pen(Brushes.Black, 2f), region.Region);
                }
            }
        }