/// <summary>
        /// Draws grid lines corresponding to a vertical scale using GDI+</summary>
        /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="majorSpacing">Spacing, in pixels, between major tick marks</param>
        /// <param name="linePen">Grid line pen</param>
        /// <param name="g">Graphics GDI+ drawing surface</param>
        public static void DrawVerticalScaleGrid(
            Matrix transform,
            RectangleF graphRect,
            int majorSpacing,
            Pen linePen,
            System.Drawing.Graphics g)
        {
            double     xScale     = transform.Elements[0];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double min        = Math.Min(graphRect.Left, graphRect.Right);
            double max        = Math.Max(graphRect.Left, graphRect.Right);
            double tickAnchor = D2dUtil.CalculateTickAnchor(min, max);
            double step       = D2dUtil.CalculateStep(min, max, Math.Abs(clientRect.Right - clientRect.Left), majorSpacing, 0.0);

            if (step > 0)
            {
                double offset = tickAnchor - min;
                offset = offset - MathUtil.Remainder(offset, step) + step;
                for (double x = tickAnchor - offset; x <= max; x += step)
                {
                    double cx = (x - graphRect.Left) * xScale + clientRect.Left;
                    g.DrawLine(linePen, (float)cx, clientRect.Top, (float)cx, clientRect.Bottom);
                }
            }
        }
        /// <summary>
        /// Draws grid lines corresponding to a horizontal scale using GDI+</summary>
        /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="majorSpacing">Spacing, in pixels, between major tick marks</param>
        /// <param name="linePen">Grid line pen</param>
        /// <param name="g">Graphics GDI+ drawing surface</param>
        public static void DrawHorizontalScaleGrid(
            Matrix transform,
            RectangleF graphRect,
            int majorSpacing,
            Pen linePen,
            System.Drawing.Graphics g)
        {
            double     yScale     = transform.Elements[3];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double min        = Math.Min(graphRect.Top, graphRect.Bottom);
            double max        = Math.Max(graphRect.Top, graphRect.Bottom);
            double tickAnchor = D2dUtil.CalculateTickAnchor(min, max);
            double step       = D2dUtil.CalculateStep(min, max, Math.Abs(clientRect.Bottom - clientRect.Top), majorSpacing, 0.0);

            if (step > 0)
            {
                double offset = tickAnchor - min;
                offset = offset - MathUtil.Remainder(offset, step) + step;
                for (double y = tickAnchor - offset; y <= max; y += step)
                {
                    double cy;
                    if (yScale > 0)
                    {
                        cy = (y - min) * yScale + clientRect.Top;
                    }
                    else
                    {
                        cy = (y - max) * yScale + clientRect.Top;
                    }

                    g.DrawLine(linePen, clientRect.Left, (float)cy, clientRect.Right, (float)cy);
                }
            }
        }
        /// <summary>
        /// Labels junctions on a grid background using GDI+</summary>
        /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="step">Grid step in canvas coordinates</param>
        /// <param name="font">Font</param>
        /// <param name="textBrush">Text brush</param>
        /// <param name="g">Graphics GDI+ drawing surface</param>
        public static void LabelGrid(
            Matrix transform,
            RectangleF graphRect,
            double step,
            Font font,
            Brush textBrush,
            System.Drawing.Graphics g)
        {
            double     xScale     = transform.Elements[0];
            double     yScale     = transform.Elements[3];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double    screenStep        = Math.Min(Math.Abs(xScale * step), Math.Abs(yScale * step));
            const int MIN_LABEL_SPACING = 96;

            while (screenStep < MIN_LABEL_SPACING)
            {
                screenStep *= 2;
                step       *= 2;
            }

            double xStart = graphRect.Left - MathUtil.Remainder(graphRect.Left, step);
            double yStart = graphRect.Top - MathUtil.Remainder(graphRect.Top, step);

            for (double y = yStart; y <= graphRect.Bottom; y += step)
            {
                double cy      = (y - graphRect.Top) * yScale + clientRect.Top;
                string yString = String.Format("{0:G4}", y);

                for (double x = xStart; x <= graphRect.Right; x += step)
                {
                    double cx       = (x - graphRect.Left) * xScale + clientRect.Left;
                    string xyString = String.Format("({0:G4}, " + yString + ")", x);

                    g.DrawString(xyString, font, textBrush, (float)cx, (float)cy);
                }
            }
        }
        /// <summary>
        /// Draws vertical grid lines using GDI+</summary>
        /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="step">Grid step in canvas coordinates</param>
        /// <param name="color">Grid line color</param>
        /// <param name="g">Graphics GDI+ drawing surface</param>
        public static void DrawVerticalGrid(
            Matrix transform,
            RectangleF graphRect,
            double step,
            Color color,
            System.Drawing.Graphics g)
        {
            double     xScale     = transform.Elements[0];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double screenStep = Math.Abs(xScale * step);
            Pen    pen        = CreateFadedPen(screenStep, color);

            double start = graphRect.Left - MathUtil.Remainder(graphRect.Left, step) + step;

            for (double x = start; x < graphRect.Right; x += step)
            {
                double cx = (x - graphRect.Left) * xScale + clientRect.Left;
                g.DrawLine(pen, (float)cx, clientRect.Top, (float)cx, clientRect.Bottom);
            }

            pen.Dispose();
        }
        /// <summary>
        /// Draws horizontal grid using Direct2D</summary>
        /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="step">Grid step in canvas coordinates</param>
        /// <param name="color">Grid line color</param>
        /// <param name="g">Graphics Direct2D drawing surface</param>
        public static void DrawHorizontalGrid(
            Matrix transform,
            RectangleF graphRect,
            double step,
            Color color,
            D2dGraphics g)
        {
            double     yScale     = transform.Elements[3];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double screenStep = Math.Abs(yScale * step);

            int a = ComputeOpacity(screenStep);

            color = Color.FromArgb(a, color);
            double start = graphRect.Top - MathUtil.Remainder(graphRect.Top, step) + step;

            for (double y = start; y < graphRect.Bottom; y += step)
            {
                double cy = (y - graphRect.Top) * yScale + clientRect.Top;
                g.DrawLine(clientRect.Left, (float)cy, clientRect.Right, (float)cy, color);
            }
        }
        /// <summary>
        /// Draws horizontal grid lines using GDI+</summary>
        /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="step">Grid step in canvas coordinates</param>
        /// <param name="color">Grid line color</param>
        /// <param name="g">Graphics GDI+ drawing surface</param>
        public static void DrawHorizontalGrid(
            Matrix transform,
            RectangleF graphRect,
            double step,
            Color color,
            System.Drawing.Graphics g)
        {
            double     yScale     = transform.Elements[3];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double screenStep = Math.Abs(yScale * step);
            Pen    pen        = CreateFadedPen(screenStep, color);

            double start = graphRect.Top - MathUtil.Remainder(graphRect.Top, step) + step;

            for (double y = start; y < graphRect.Bottom; y += step)
            {
                double cy = (y - graphRect.Top) * yScale + clientRect.Top;
                g.DrawLine(pen, clientRect.Left, (float)cy, clientRect.Right, (float)cy);
            }

            pen.Dispose();
        }
        /// <summary>
        /// Draws vertical grid lines using Direct2D</summary>
        /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="step">Grid step in canvas coordinates</param>
        /// <param name="color">Grid line color</param>
        /// <param name="g">Graphics Direct2D drawing surface</param>
        public static void DrawVerticalGrid(
            Matrix transform,
            RectangleF graphRect,
            double step,
            Color color,
            D2dGraphics g)
        {
            double     xScale     = transform.Elements[0];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double screenStep = Math.Abs(xScale * step);
            int    a          = ComputeOpacity(screenStep);

            color = Color.FromArgb(a, color);

            double start = graphRect.Left - MathUtil.Remainder(graphRect.Left, step) + step;

            for (double x = start; x < graphRect.Right; x += step)
            {
                double cx = (x - graphRect.Left) * xScale + clientRect.Left;
                g.DrawLine((float)cx, clientRect.Top, (float)cx, clientRect.Bottom, color);
            }
        }
        /// <summary>
        /// Draws a vertical chart scale using GDI+</summary>
        /// <param name="transform">Transform from graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="left">Scale left</param>
        /// <param name="majorSpacing">Spacing, in pixels, between major tick marks</param>
        /// <param name="minimumGraphStep">Minimum spacing, in graph (world) space, between ticks.
        /// For example, 1.0 would limit ticks to being drawn on whole integers.</param>
        /// <param name="linePen">Scale line pen</param>
        /// <param name="font">Scale font</param>
        /// <param name="textBrush">Text brush</param>
        /// <param name="g">Graphics GDI+ drawing surface</param>
        public static void DrawVerticalScale(
            Matrix transform,
            RectangleF graphRect,
            bool left,
            int majorSpacing,
            float minimumGraphStep,
            Pen linePen,
            Font font,
            Brush textBrush,
            System.Drawing.Graphics g)
        {
            double     yScale     = transform.Elements[3];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double tickEnd, minorTickStart, textStart;

            Matrix temp     = g.Transform.Clone();
            Matrix vertical = g.Transform;

            vertical.Translate(clientRect.Right, clientRect.Bottom);
            vertical.Rotate(90);
            vertical.Translate(-clientRect.Left, -clientRect.Top);
            g.Transform = vertical;
            if (left)
            {
                tickEnd        = clientRect.Right - clientRect.X;
                minorTickStart = tickEnd - 6;
                textStart      = tickEnd - 19;
            }
            else
            {
                tickEnd        = clientRect.Left + 1;
                minorTickStart = tickEnd + 6;
                textStart      = tickEnd + 8;
            }

            double min = Math.Min(graphRect.Top, graphRect.Bottom);
            double max = Math.Max(graphRect.Top, graphRect.Bottom);

            double tickAnchor     = D2dUtil.CalculateTickAnchor(min, max);
            double majorGraphStep = D2dUtil.CalculateStep(
                min, max, Math.Abs(clientRect.Bottom - clientRect.Top), majorSpacing, minimumGraphStep);
            int    numMinorTicks = D2dUtil.CalculateNumMinorTicks(majorGraphStep, minimumGraphStep, 5);
            double cMinorStep    = (majorGraphStep / numMinorTicks) * yScale;

            if (majorGraphStep > 0)
            {
                double offset = tickAnchor - min;
                offset = offset - MathUtil.Remainder(offset, majorGraphStep) + majorGraphStep;
                for (double x = tickAnchor - offset; x <= max; x += majorGraphStep)
                {
                    double cx = (x - min) * yScale + clientRect.Left;
                    //g.DrawLine(linePen, (float)cx, (float)majorTickStart, (float)cx, (float)tickEnd);
                    // draw minor ticks
                    double cmx = cx;
                    for (int i = 0; i < numMinorTicks; i++)
                    {
                        cmx += cMinorStep;
                        g.DrawLine(linePen, (float)cmx, (float)minorTickStart, (float)cmx, (float)tickEnd);
                    }
                    string xString = String.Format("{0:G8}", Math.Round(x, 6));
                    g.DrawString(xString, font, textBrush, (float)cx + 2, (float)textStart);
                }
            }
            g.Transform = temp;
        }
        /// <summary>
        /// Draws a horizontal chart scale using GDI+</summary>
        /// <param name="transform">Graph (world) to Window's client (screen) transform</param>
        /// <param name="graphRect">Graph rectangle</param>
        /// <param name="top">Whether the scale should be aligned along the top of the rectangle</param>
        /// <param name="majorSpacing">Spacing, in pixels, between major tick marks</param>
        /// <param name="minimumGraphStep">Minimum spacing, in graph (world) space, between ticks.
        /// For example, 1.0 would limit ticks to being drawn on whole integers.</param>
        /// <param name="linePen">Scale line pen</param>
        /// <param name="font">Scale font</param>
        /// <param name="textBrush">Text brush</param>
        /// <param name="g">Graphics GDI+ drawing surface</param>
        public static void DrawHorizontalScale(
            Matrix transform,
            RectangleF graphRect,
            bool top,
            int majorSpacing,
            float minimumGraphStep,
            Pen linePen,
            Font font,
            Brush textBrush,
            System.Drawing.Graphics g)
        {
            double     xScale     = transform.Elements[0];
            RectangleF clientRect = GdiUtil.Transform(transform, graphRect);

            double tickEnd, majorTickStart, minorTickStart, textStart;

            if (top)
            {
                tickEnd        = clientRect.Top + 1;
                majorTickStart = tickEnd + 12;
                minorTickStart = tickEnd + 6;
                textStart      = tickEnd + 8;
            }
            else
            {
                tickEnd        = clientRect.Bottom - 1;
                majorTickStart = tickEnd - 12;
                minorTickStart = tickEnd - 6;
                textStart      = tickEnd - 19;
            }

            double min = Math.Min(graphRect.Left, graphRect.Right);
            double max = Math.Max(graphRect.Left, graphRect.Right);

            double tickAnchor     = D2dUtil.CalculateTickAnchor(min, max);
            double majorGraphStep = D2dUtil.CalculateStep(
                min, max, Math.Abs(clientRect.Right - clientRect.Left), majorSpacing, minimumGraphStep);
            int    numMinorTicks = D2dUtil.CalculateNumMinorTicks(majorGraphStep, minimumGraphStep, 5);
            double cMinorStep    = (majorGraphStep / numMinorTicks) * xScale;

            if (majorGraphStep > 0)
            {
                double offset = tickAnchor - min;
                offset = offset - MathUtil.Remainder(offset, majorGraphStep);

                // draw leading minor ticks
                double cmx;
                cmx = ((tickAnchor - (offset + majorGraphStep)) - min) * xScale + clientRect.Left + cMinorStep;
                for (int i = 0; i < numMinorTicks - 1 && cmx < clientRect.Right; i++)
                {
                    // cull minor ticks outside of the view
                    if (cmx > clientRect.Left)
                    {
                        g.DrawLine(linePen, (float)cmx, (float)minorTickStart, (float)cmx, (float)tickEnd);
                    }
                    cmx += cMinorStep;
                }

                for (double x = tickAnchor - offset; x < max; x += majorGraphStep)
                {
                    double cx = (x - min) * xScale + clientRect.Left;
                    g.DrawLine(linePen, (float)cx, (float)majorTickStart, (float)cx, (float)tickEnd);
                    string xString = String.Format("{0:G8}", Math.Round(x, 6));
                    g.DrawString(xString, font, textBrush, (float)cx + 1, (float)textStart);

                    // draw minor ticks
                    cmx = cx + cMinorStep;
                    for (int i = 0; i < numMinorTicks - 1 && cmx < clientRect.Right; i++)
                    {
                        g.DrawLine(linePen, (float)cmx, (float)minorTickStart, (float)cmx, (float)tickEnd);
                        cmx += cMinorStep;
                    }
                }
            }
        }