/// <summary>
        /// Draw Axis Label
        /// </summary>
        /// <param name="g">The GDI+ drawing surface on which to draw</param>
        /// <param name="offset">offset calculated by derived class that makes sure axis label
        /// misses tick labels.</param>
        /// <param name="axisPhysMin">The minimum physical extent of the axis</param>
        /// <param name="axisPhysMax">The maximum physical extent of the axis</param>
        /// <returns>boxed RectangleF indicating bounding box of label. null if no label printed.</returns>
        public object DrawLabel(Graphics g, PointF offset, PointF axisPhysMin, PointF axisPhysMax)
        {
            if (label_ != "")
            {
                // determine angle of axis.
                double theta = (double)System.Math.Atan2(
                    axisPhysMax.Y - axisPhysMin.Y,
                    axisPhysMax.X - axisPhysMin.X);

                double perpTheta = theta - Math.PI / 2.0f;                  // want to move text this way to center on axis.
                double y         = Math.Sin(perpTheta);
                double x         = Math.Cos(perpTheta);

                PointF average = new PointF(
                    (axisPhysMax.X + axisPhysMin.X) / 2.0f,
                    (axisPhysMax.Y + axisPhysMin.Y) / 2.0f);

                g.TranslateTransform(offset.X, offset.Y);        // this is done last.
                g.TranslateTransform(average.X, average.Y);
                theta = theta * 180.0f / Math.PI;                // convert to degrees.
                g.RotateTransform((float)theta);                 // this is done first.

                double lHt = g.MeasureString(label_, FontScaler.scaleFont(labelFont_, FontScale)).Height;
                double lWd = g.MeasureString(label_, FontScaler.scaleFont(labelFont_, FontScale)).Width;

                // bb centered around zero.
                RectangleF drawRect = new RectangleF((float)(-lWd / 2.0f), (float)(-lHt / 2.0f), (float)lWd, (float)lHt);

                StringFormat drawFormat = new StringFormat();
                drawFormat.Alignment = StringAlignment.Center;
                g.DrawString(label_, FontScaler.scaleFont(this.LabelFont, this.FontScale),
                             this.labelBrush_, drawRect, drawFormat);

                // now work out physical bounds of label. and return.
                Matrix  m         = g.Transform;
                Point[] recPoints = new Point[2];
                recPoints[0] = new Point((int)(-lWd / 2.0f), (int)(-lHt / 2.0f));
                recPoints[1] = new Point((int)(lWd / 2.0f), (int)(lHt / 2.0f));
                m.TransformPoints(recPoints);

                double     x1     = Math.Min(recPoints[0].X, recPoints[1].X);
                double     x2     = Math.Max(recPoints[0].X, recPoints[1].X);
                double     y1     = Math.Min(recPoints[0].Y, recPoints[1].Y);
                double     y2     = Math.Max(recPoints[0].Y, recPoints[1].Y);
                RectangleF bounds = new RectangleF((float)x1, (float)y1, (float)(x2 - x1), (float)(y2 - y1));

                g.ResetTransform();                 // reset transform before we return.

                return(bounds);
            }

            return(null);
        }
        public RectangleF Draw(Graphics g, int xPos, int yPos, ArrayList plots, float scale)
        {
            // determine max width and max height of label strings.
            float maxHt = 0.0f;
            float maxWd = 0.0f;

            for (int i = 0; i < plots.Count; ++i)
            {
                IPlot p   = (IPlot)plots[i];
                float lHt = g.MeasureString(p.Label, FontScaler.scaleFont(font_, scale)).Height;
                float lWd = g.MeasureString(p.Label, FontScaler.scaleFont(font_, scale)).Width;
                if (lHt > maxHt)
                {
                    maxHt = lHt;
                }
                if (lWd > maxWd)
                {
                    maxWd = lWd;
                }
            }

            float lineLength = 20.0f;
            float lineHeight = maxHt;
            float hSpacing   = 5.0f * scale;
            float vSpacing   = 3.0f * scale;
            float boxWidth   = hSpacing * 3.0f + lineLength + maxWd;
            float boxHeight  = vSpacing * (float)(plots.Count + 1) + maxHt * (float)plots.Count;

            float totalWidth  = boxWidth;
            float totalHeight = boxHeight;

            // draw box..
            if (BorderStyle == BorderType.Line)
            {
                g.FillRectangle(new SolidBrush(Color.White), xPos, yPos, boxWidth, boxHeight);
                g.DrawRectangle(new Pen(Color.Black), xPos, yPos, boxWidth, boxHeight);
            }
            else if (BorderStyle == BorderType.Shadow)
            {
                float offset = 4.0f * (float)scale;
                g.FillRectangle(new SolidBrush(Color.LightGray), xPos + offset, yPos + offset, boxWidth, boxHeight);
                g.FillRectangle(new SolidBrush(Color.White), xPos, yPos, boxWidth, boxHeight);
                g.DrawRectangle(new Pen(Color.Black), xPos, yPos, boxWidth, boxHeight);

                totalWidth  += offset;
                totalHeight += offset;
            }

            /*
             * else if ( this.BorderStyle == BorderType.Curved )
             * {
             *      // TODO. make this nice.
             * }
             */
            else
            {
                // do nothing.
            }

            // now draw entries in box..
            int unnamedCount = 0;

            for (int i = 0; i < plots.Count; ++i)
            {
                IPlot p        = (IPlot)plots[i];
                float lineXPos = xPos + hSpacing;
                float lineYPos = yPos + vSpacing + (float)i * (vSpacing + maxHt);
                p.DrawLegendLine(g, new RectangleF(lineXPos, lineYPos, lineLength, lineHeight));
                float  textXPos = lineXPos + hSpacing + lineLength;
                float  textYPos = lineYPos;
                string label    = p.Label;
                if (label == "")
                {
                    unnamedCount += 1;
                    label         = "Series " + unnamedCount.ToString();
                }
                g.DrawString(label, FontScaler.scaleFont(Font, scale),
                             new SolidBrush(Color.Black), textXPos, textYPos);
            }

            return(new RectangleF(xPos, yPos, totalWidth, totalHeight));
        }
        /// <summary>
        /// Draw the plot on the drawing surface
        /// </summary>
        /// <param name="g">The GDI+ drawing surface on which to render.</param>
        /// <param name="bounds">The bounding rectangle on the drawing surface to be considered the plot area.</param>
        public void Draw(Graphics g, Rectangle bounds)
        {
            if (plots_.Count == 0)
            {
                return;
                // TODO: better output in this case.
            }

            float scale = (float)DetermineScaleFactor(bounds.Width, bounds.Height);

            if (xAxis1_ == null)
            {
                xAxis1_ = (Axis)xAxis2_.Clone();
                xAxis1_.HideTickText = true;
                xAxis1_.TicksAngle   = -Math.PI / 2.0f;
            }

            if (xAxis2_ == null)
            {
                xAxis2_ = (Axis)xAxis1_.Clone();
                xAxis2_.HideTickText = true;
                xAxis2_.TicksAngle   = Math.PI / 2.0f;
            }

            if (yAxis1_ == null)
            {
                yAxis1_ = (Axis)yAxis2_.Clone();
                yAxis1_.HideTickText = true;
                yAxis1_.TicksAngle   = Math.PI / 2.0f;
            }

            if (yAxis2_ == null)
            {
                yAxis2_ = (Axis)yAxis1_.Clone();
                yAxis2_.HideTickText = true;
                yAxis2_.TicksAngle   = -Math.PI / 2.0f;
            }

            // TODO: fix this so these not automatically overwritten.
            xAxis1_.TickScale = scale;
            xAxis1_.FontScale = scale;
            yAxis1_.TickScale = scale;
            yAxis1_.FontScale = scale;
            xAxis2_.TickScale = scale;
            xAxis2_.FontScale = scale;
            yAxis2_.TickScale = scale;
            yAxis2_.FontScale = scale;

            // now have axes world info. set physical limits.
            // first guess axes positions, then find bounding box, then change
            // to align nicely with side of control.
            System.Drawing.Rectangle cb = bounds;
            RectangleF bb;

            // guess physical x axis (bottom). Put it at the bottom of the plot
            PhysicalAxis pXAxis1 = new PhysicalAxis(xAxis1_,
                                                    new Point(cb.Left, cb.Bottom), new Point(cb.Right, cb.Bottom));
            int bottomIndent = (int)(padding_);

            if (!pXAxis1.Axis.Hidden)
            {
                // evaluate its bounding box
                bb = pXAxis1.GetBoundingBox();
                // finally determine its indentation from the bottom
                bottomIndent = (int)(bottomIndent + bb.Bottom - cb.Bottom);
            }

            // guess physical y axis (left). Put it at the left side.
            PhysicalAxis pYAxis1 = new PhysicalAxis(yAxis1_,
                                                    new Point(cb.Left, cb.Bottom), new Point(cb.Left, cb.Top));
            int leftIndent = (int)(padding_);

            if (!pYAxis1.Axis.Hidden)
            {
                // evaluate its bounding box
                bb = pYAxis1.GetBoundingBox();
                // finally determine its indentation from the left
                leftIndent = (int)(leftIndent - bb.Left + cb.Left);
            }

            // guess secondary x axis (top).
            PhysicalAxis pXAxis2 = new PhysicalAxis(xAxis2_,
                                                    new Point(cb.Left, cb.Top), new Point(cb.Right, cb.Top));
            int    topIndent   = (int)(padding_);
            double titleHeight = FontScaler.scaleFont(titleFont_, scale).Height;

            if (!pXAxis2.Axis.Hidden)
            {
                // evaluate its bounding box
                bb        = pXAxis2.GetBoundingBox();
                topIndent = (int)(topIndent - bb.Top + cb.Top);

                // finally determine its indentation from the top
                // correct top indendation to take into account plot title
                if (title_ != "")
                {
                    topIndent += (int)((double)titleHeight * 1.3f);
                }
            }

            // guess secondary y axis (right). Put it at the right side.
            PhysicalAxis pYAxis2 = new PhysicalAxis(yAxis2_,
                                                    new Point(cb.Right, cb.Bottom), new Point(cb.Right, cb.Top));
            int rightIndent = (int)(padding_);

            if (!pYAxis2.Axis.Hidden)
            {
                // evaluate its bounding box
                bb = pYAxis2.GetBoundingBox();

                // finally determine its indentation from the right
                rightIndent = (int)(rightIndent + bb.Right - cb.Right);
            }

            // now determine if legend should change any of these (legend should be fully
            // visible at all times), and draw legend.
            Legend legend = null;
            float  lXPos  = 0.0f;
            float  lYPos  = 0.0f;

            if (showLegend_)
            {
                legend             = new Legend();
                legend.BorderStyle = LegendBorderStyle;
                RectangleF legendWidthHeight = legend.GetBoundingBox(0, 0, plots_, scale);

                // calculate legend position.

                lYPos = legendOffsetY_;

                if (legendOffsetXAxis_ == XAxisPosition.Bottom)
                {
                    lYPos += cb.Bottom - bottomIndent;
                    if (horizontalEdgeLegendPlacement_ == Legend.Placement.Inside)
                    {
                        lYPos -= legendWidthHeight.Height;
                    }
                }
                else
                {
                    lYPos += cb.Top + topIndent;
                    if (horizontalEdgeLegendPlacement_ == Legend.Placement.Outside)
                    {
                        lYPos -= legendWidthHeight.Height;
                    }
                }

                lXPos = legendOffsetX_;

                if (legendOffsetYAxis_ == YAxisPosition.Left)
                {
                    if (verticalEdgeLegendPlacement_ == Legend.Placement.Outside)
                    {
                        lXPos -= legendWidthHeight.Width;
                    }
                    lXPos += cb.Left + leftIndent;
                }
                else
                {
                    if (verticalEdgeLegendPlacement_ == Legend.Placement.Inside)
                    {
                        lXPos -= legendWidthHeight.Width;
                    }
                    lXPos += cb.Right - rightIndent;
                }

                // update axes positions if need to for legend position.

                if (lXPos < padding_)
                {
                    int changeAmount = -(int)lXPos + padding_;
                    // only allow axes to move away from bounds.
                    if (changeAmount > 0)
                    {
                        leftIndent += changeAmount;
                    }
                    lXPos += changeAmount;
                }

                if (lXPos + legendWidthHeight.Width > bounds.Right - padding_)
                {
                    int changeAmount = ((int)lXPos - bounds.Right + (int)legendWidthHeight.Width + padding_);
                    // only allow axes to move away from bounds.
                    if (changeAmount > 0)
                    {
                        rightIndent += changeAmount;
                    }
                    lXPos -= changeAmount;
                }

                if (lYPos < padding_)
                {
                    int changeAmount = -(int)lYPos + padding_;
                    // only allow axes to move away from bounds.
                    if (changeAmount > 0)
                    {
                        topIndent += changeAmount;
                    }
                    lYPos += changeAmount;
                }

                if (lYPos + legendWidthHeight.Height > bounds.Bottom - padding_)
                {
                    int changeAmount = ((int)lYPos - bounds.Bottom + (int)legendWidthHeight.Height + padding_);
                    // only allow axes to move away from bounds.
                    if (changeAmount > 0)
                    {
                        bottomIndent += changeAmount;
                    }
                    lYPos -= changeAmount;
                }
            }


            // now we have all the positions and we can proceed to "move" the axes to their
            // right places
            // primary axes (bottom, left)
            pXAxis1.PhysicalMin = new Point(cb.Left + leftIndent, cb.Bottom - bottomIndent);
            pXAxis1.PhysicalMax = new Point(cb.Right - rightIndent, cb.Bottom - bottomIndent);
            pYAxis1.PhysicalMin = new Point(cb.Left + leftIndent, cb.Bottom - bottomIndent);
            pYAxis1.PhysicalMax = new Point(cb.Left + leftIndent, cb.Top + topIndent);
            // secondary axes (top, right)
            pXAxis2.PhysicalMin = new Point(cb.Left + leftIndent, cb.Top + topIndent);
            pXAxis2.PhysicalMax = new Point(cb.Right - rightIndent, cb.Top + topIndent);
            pYAxis2.PhysicalMin = new Point(cb.Right - rightIndent, cb.Bottom - bottomIndent);
            pYAxis2.PhysicalMax = new Point(cb.Right - rightIndent, cb.Top + topIndent);


            // now we are ready to define the bounding box for the plot area (to use in clipping
            // operations.

            plotAreaBoundingBoxCache_ = new Rectangle(cb.Left + leftIndent, cb.Top + topIndent,
                                                      cb.Width - leftIndent - rightIndent,
                                                      cb.Height - topIndent - bottomIndent);

#if _IWANNASEE_
            bb = pXAxis1.GetBoundingBox();
            g.DrawRectangle(new Pen(Color.Orange), bb.X, bb.Y, bb.Width, bb.Height);
            bb = pXAxis2.GetBoundingBox();
            g.DrawRectangle(new Pen(Color.Orange), bb.X, bb.Y, bb.Width, bb.Height);
            bb = pYAxis1.GetBoundingBox();
            g.DrawRectangle(new Pen(Color.Orange), bb.X, bb.Y, bb.Width, bb.Height);
            bb = pYAxis2.GetBoundingBox();
            g.DrawRectangle(new Pen(Color.Orange), bb.X, bb.Y, bb.Width, bb.Height);
            g.DrawRectangle(new Pen(Color.Red, 5.0F), plotAreaBoundingBox_);
#endif


            // Fill in the background.
            if (plotBackColor_ != null)
            {
                g.FillRectangle(new System.Drawing.SolidBrush((Color)plotBackColor_),
                                pYAxis1.PhysicalMin.X,
                                pXAxis2.PhysicalMax.Y,
                                pXAxis1.PhysicalMax.X - pXAxis1.PhysicalMin.X,
                                pYAxis1.PhysicalMin.Y - pYAxis1.PhysicalMax.Y);
            }

            // draw title
            StringFormat drawFormat = new StringFormat();
            drawFormat.Alignment = StringAlignment.Center;
            g.DrawString(title_, FontScaler.scaleFont(titleFont_, scale),
                         titleBrush_,
                         new PointF((pXAxis2.PhysicalMax.X + pXAxis2.PhysicalMin.X) / 2.0f, cb.Top + padding_),
                         drawFormat);

            // now draw grid.
            DrawGrid(g, pXAxis1, pYAxis1, pXAxis2, pYAxis2);

            // now draw axes.
            pXAxis1.Draw(g);
            pXAxis2.Draw(g);
            pYAxis1.Draw(g);
            pYAxis2.Draw(g);

            // draw plots.
            for (int i = 0; i < plots_.Count; ++i)
            {
                IPlot         plot = (IPlot)plots_[i];
                XAxisPosition xap  = (XAxisPosition)xAxisPositions_[i];
                YAxisPosition yap  = (YAxisPosition)yAxisPositions_[i];

                PhysicalAxis xAxis;
                PhysicalAxis yAxis;

                if (xap == XAxisPosition.Bottom)
                {
                    xAxis = pXAxis1;
                }
                else
                {
                    xAxis = pXAxis2;
                }

                if (yap == YAxisPosition.Left)
                {
                    yAxis = pYAxis1;
                }
                else
                {
                    yAxis = pYAxis2;
                }

                // set the clipping region.. (necessary for zoom)
                g.Clip = new Region((Rectangle)plotAreaBoundingBoxCache_);
                // plot..
                plot.Draw(g, xAxis, yAxis);
                // reset it..
                g.ResetClip();

                // cache the physical axes we used on this draw;
                pXAxis1Cache_ = pXAxis1;
                pYAxis1Cache_ = pYAxis1;
                pXAxis2Cache_ = pXAxis2;
                pYAxis2Cache_ = pYAxis2;
            }

            if (legend != null)
            {
                legend.Draw(g, (int)lXPos, (int)lYPos, plots_, scale);
            }
        }
        /// <summary>
        /// Draws a tick on the axis
        /// </summary>
        /// <param name="g">The drawing surface</param>
        /// <param name="w">The tick position in world coordinates</param>
        /// <param name="size">The size of the tick (in pixels)</param>
        /// <param name="text">The text associated with the tick</param>
        /// <param name="textOffset">The Offset to draw from the auto calculated position</param>
        /// <param name="axisPhysMin">The minimum physical extent of the axis</param>
        /// <param name="axisPhysMax">The maximum physical extent of the axis</param>
        /// <returns> An ArrayList containing the offset from the axis required for an axis label
        /// to miss this tick, followed by a bounding rectangle for the tick and tickLabel drawn </returns>
        public virtual ArrayList DrawTick(Graphics g, double w, double size, string text,
                                          Point textOffset, PointF axisPhysMin, PointF axisPhysMax)
        {
            // determine start point.
            PointF s = WorldToPhysical(w, axisPhysMin, axisPhysMax, true);

            // determine offset from start point.
            PointF dir = this.AxisNormVector(axisPhysMin, axisPhysMax);

            // rotate clockwise by angle radians.
            double x1 = Math.Cos(-this.TicksAngle) * dir.X + Math.Sin(-this.TicksAngle) * dir.Y;
            double y1 = -Math.Sin(-this.TicksAngle) * dir.X + Math.Cos(-this.TicksAngle) * dir.Y;

            // scaling tick.
            dir = new PointF((float)(this.TickScale * size * x1), (float)(this.TickScale * size * y1));

            // draw it!
            g.DrawLine(this.linePen_, s.X, s.Y, s.X + dir.X, s.Y + dir.Y);

            // calculate bounds.
            double     minx        = Math.Min(s.X, s.X + dir.X);
            double     miny        = Math.Min(s.Y, s.Y + dir.Y);
            double     maxx        = Math.Max(s.X, s.X + dir.X);
            double     maxy        = Math.Max(s.Y, s.Y + dir.Y);
            RectangleF bounds      = new RectangleF((float)minx, (float)miny, (float)(maxx - minx), (float)(maxy - miny));
            PointF     labelOffset = new PointF(0.0f, 0.0f);

            // now draw associated text.
            if (text != "" && !HideTickText)
            {
                double lHt = g.MeasureString(text, FontScaler.scaleFont(font_, this.FontScale)).Height;
                double lWd = g.MeasureString(text, FontScaler.scaleFont(font_, this.FontScale)).Width;

                double textCenterX;
                double textCenterY;

                // if text is at pointy end of tick.
                if (!this.TickTextNextToAxis)
                {
                    // offset due to tick.
                    textCenterX = s.X + dir.X * 1.2f;
                    textCenterY = s.Y + dir.Y * 1.2f;

                    // offset due to text box size.
                    textCenterX += 0.5f * x1 * lWd;
                    textCenterY += 0.5f * y1 * lHt;
                }
                // else it's next to the axis.
                else
                {
                    // start location.
                    textCenterX = s.X;
                    textCenterY = s.Y;

                    // offset due to text box size.
                    textCenterX -= 0.5f * x1 * lWd;
                    textCenterY -= 0.5f * y1 * lHt;

                    // bring text away from the axis a little bit.
                    textCenterX -= x1 * (2.0f + FontScale);
                    textCenterY -= y1 * (2.0f + FontScale);
                }

                double bx1 = textCenterX - lWd / 2.0f;
                double by1 = textCenterY - lHt / 2.0f;
                double bx2 = lWd;
                double by2 = lHt;

                RectangleF drawRect = new RectangleF((float)bx1, (float)by1, (float)bx2, (float)by2);

                // g.DrawRectangle( new Pen(Color.Green),bx1, by1, bx2, by2 );

                bounds = RectangleF.Union(bounds, drawRect);

                // g.DrawRectangle( new Pen(Color.Purple), bounds.X, bounds.Y, bounds.Width, bounds.Height );

                StringFormat drawFormat = new StringFormat();
                drawFormat.Alignment = StringAlignment.Center;
                g.DrawString(text, FontScaler.scaleFont(this.TickTextFont, this.FontScale),
                             this.tickTextBrush_, drawRect, drawFormat);

                textCenterX -= s.X;
                textCenterY -= s.Y;
                textCenterX *= 2.3f;
                textCenterY *= 2.3f;

                labelOffset = new PointF((float)textCenterX, (float)textCenterY);
            }             // if (text != "" && !HideTickText )

            ArrayList toReturn = new ArrayList();

            toReturn.Add(labelOffset);
            toReturn.Add(bounds);

            return(toReturn);
        }