Exemple #1
0
        public override void Draw(Graphics g, AdvancedRect dataArea)
        {
            PointF pt = new PointF(
                Plot.XAxis.DataToCoordinate(X, dataArea),
                Plot.YAxes[YAxisName].DataToCoordinate(Y, dataArea));

            using (Pen p = crosshairPen.CreatePen())
            {
                g.DrawLine(p, pt.X - crosshairCircleRadius, pt.Y, pt.X + crosshairCircleRadius, pt.Y);
                g.DrawLine(p, pt.X, pt.Y - crosshairCircleRadius, pt.X, pt.Y + crosshairCircleRadius);
                g.DrawEllipse(p, pt.X - crosshairCircleRadius, pt.Y - crosshairCircleRadius, crosshairCircleRadius * 2, crosshairCircleRadius * 2);

                SizeF size = Size(g);
                switch (crosshair)
                {
                case AnnotationCrosshair.Left:
                    g.DrawLine(p, pt.X + crosshairCircleRadius, pt.Y, pt.X + crosshairCircleRadius + crosshairHandleSize, pt.Y);
                    pt.X += crosshairCircleRadius;
                    pt.X += crosshairHandleSize;
                    pt.Y -= size.Height / 2;
                    break;

                case AnnotationCrosshair.Right:
                    g.DrawLine(p, pt.X - crosshairCircleRadius, pt.Y, pt.X - crosshairCircleRadius - crosshairHandleSize, pt.Y);
                    pt.X -= crosshairCircleRadius;
                    pt.X -= crosshairHandleSize;
                    pt.X -= size.Width;
                    pt.Y -= size.Height / 2;
                    break;

                case AnnotationCrosshair.Bottom:
                    g.DrawLine(p, pt.X, pt.Y - crosshairCircleRadius, pt.X, pt.Y - crosshairCircleRadius - crosshairHandleSize);
                    pt.X -= size.Width / 2;
                    pt.Y -= size.Height;
                    pt.Y -= crosshairCircleRadius;
                    pt.Y -= crosshairHandleSize;
                    break;

                case AnnotationCrosshair.Top:
                    g.DrawLine(p, pt.X, pt.Y + crosshairCircleRadius, pt.X, pt.Y + crosshairCircleRadius + crosshairHandleSize);
                    pt.X -= size.Width / 2;
                    pt.Y += crosshairCircleRadius;
                    pt.Y += crosshairHandleSize;
                    break;
                }
            }
            DrawTextBox(g, pt);
        }
Exemple #2
0
        public override void Draw(Graphics g, AxisCollection yAxisCollection, XAxis xAxis, AdvancedRect area)
        {
            if (data.Count == 0)
            {
                return;
            }

            GraphicsState _s = g.Save();

            g.SmoothingMode = SmoothingMode.AntiAlias;

            NumericAxis yAxis = yAxisCollection[YAxisName];

            using (Pen p = line.CreatePen())
                using (Symbol s = (SymbolStyle != SymbolStyle.NoSymbols) ? symbol.CreateSymbol() : null)
                {
                    // find continuous groups of non-null Y points
                    // if our yAxis is LogRate, then we need to find continuous
                    // groups of non-null, >0 Y points.  Yay.
                    int start = 0, end = 0, i = 0;
                    while (i < data.Count)
                    {
                        // chomp all nulls
                        // then draw all non-nulls
                        while (i < data.Count && (data.GetY(i) == null || (yAxis.LogAxis && data.GetY(i) <= 0)))
                        {
                            i++;
                        }
                        // found a non-null at i.
                        start = i;
                        while (i < data.Count && data.GetY(i) != null && (!yAxis.LogAxis || data.GetY(i) > 0))
                        {
                            i++;
                        }
                        // found a null at i;
                        end = i;
                        if (start != end)
                        {
                            drawLines(g, p, s, xAxis, yAxis, area, start, end);
                        }
                    }
                }

            g.Restore(_s);
        }
Exemple #3
0
        public void DrawY(Graphics g, AdvancedRect area, AdvancedRect plotArea)
        {
            drawArea = area;

            GraphicsState _s = g.Save();

            //using (Brush br = new SolidBrush(Color.Purple))
            //    g.FillRectangle(br, area.Rect);

            if (title != null)
            {
                using (Brush br = titleFont.CreateBrush())
                    using (Font f = titleFont.CreateFont())
                    {
                        SizeF titleSize = g.MeasureString(title, f);

                        GraphicsState s = g.Save();
                        if (rightSide)
                        {
                            g.TranslateTransform(area.BottomRight.X, area.Center.Y - (titleSize.Width / 2));
                            g.RotateTransform(90);
                        }
                        else
                        {
                            g.TranslateTransform(area.TopLeft.X, area.Center.Y + (titleSize.Width / 2));
                            g.RotateTransform(-90);
                        }
                        StringFormat format = new StringFormat();
                        format.Alignment     = StringAlignment.Center;
                        format.LineAlignment = StringAlignment.Center;
                        g.DrawString(title, f, br, new PointF(0, 0));
                        g.Restore(s);
                    }
            }

            if (!logAxis)
            {
                int maxIntervals = (int)Math.Ceiling(area.Height * 1.25);

                // calculate label and tick mark intervals
                using (Brush br = labelFont.CreateBrush())
                    using (Font f = labelFont.CreateFont())
                        using (Pen p = tickPen.CreatePen())
                        {
                            foreach (double v in GenerateTickLocations(maxIntervals))
                            {
                                string txt    = FormatLabel(v);
                                float  yCoord = DataToCoordinate(v, area);

                                if (yCoord < area.TopLeft.Y || yCoord > area.BottomRight.Y)
                                {
                                    continue;
                                }

                                SizeF sz = g.MeasureString(txt, f);
                                if (rightSide)
                                {
                                    g.DrawLine(p, area.TopLeft.X, yCoord, area.TopLeft.X + tickLength, yCoord);
                                    g.DrawString(txt, f, br, area.TopLeft.X + tickLength, yCoord - (sz.Height / 2));
                                }
                                else
                                {
                                    g.DrawLine(p, area.BottomRight.X, yCoord, area.BottomRight.X - tickLength, yCoord);
                                    g.DrawString(txt, f, br, area.BottomRight.X - sz.Width - tickLength, yCoord - (sz.Height / 2));
                                }
                            }
                        }

                if (gridlinesEnabled)
                {
                    using (Pen p = gridlinePen.CreatePen())
                    {
                        foreach (double v in GenerateTickLocations(maxIntervals))
                        {
                            float yCoord = DataToCoordinate(v, area);
                            if (yCoord < area.TopLeft.Y || yCoord > area.BottomRight.Y)
                            {
                                continue;
                            }
                            g.DrawLine(p, plotArea.TopLeft.X, yCoord, plotArea.BottomRight.X, yCoord);
                        }
                    }
                }
            }
            else
            {
                // log axis drawing
                int start = (int)Math.Floor(Math.Log10(ScaleMinimum));
                int end   = (int)Math.Ceiling(Math.Log10(ScaleMaximum));

                using (Brush br = labelFont.CreateBrush())
                    using (Font f = labelFont.CreateFont())
                        using (Pen p = tickPen.CreatePen())
                            using (Pen p2 = gridlinePen.CreatePen())
                            {
                                for (int i = start; i <= end; i++)
                                {
                                    double v      = Math.Pow(10, i);
                                    float  yCoord = DataToCoordinate(v, area);

                                    // ticks and labels
                                    string txt = FormatLabel(v);
                                    SizeF  sz  = g.MeasureString(txt, f);
                                    if (yCoord >= area.TopLeft.Y && yCoord <= area.BottomRight.Y)
                                    {
                                        if (rightSide)
                                        {
                                            g.DrawLine(p, area.TopLeft.X, yCoord, area.TopLeft.X + tickLength, yCoord);
                                            g.DrawString(txt, f, br, area.TopLeft.X + tickLength, yCoord - (sz.Height / 2));
                                        }
                                        else
                                        {
                                            g.DrawLine(p, area.BottomRight.X, yCoord, area.BottomRight.X - tickLength, yCoord);
                                            g.DrawString(txt, f, br, area.BottomRight.X - sz.Width - tickLength, yCoord - (sz.Height / 2));
                                        }
                                    }

                                    if (gridlinesEnabled && i != end)
                                    {
                                        double v2    = Math.Pow(10, i + 1);
                                        double delta = (v2 - v) / 9;
                                        for (int j = 1; j < 10; j++)
                                        {
                                            double v3 = v + (delta * j);
                                            yCoord = DataToCoordinate(v3, area);
                                            if (yCoord > area.TopLeft.Y && yCoord < area.BottomRight.Y)
                                            {
                                                g.DrawLine(p2, plotArea.TopLeft.X, yCoord, plotArea.BottomRight.X, yCoord);
                                            }
                                        }
                                    }
                                }
                            }
            }

            // Because there can be multiple Y axises that are not always next to the data area,
            // we draw our own border.  The plot might stroke over it again later, but that's
            // not such a big deal.
            using (Pen p = borderPen.CreatePen())
            {
                if (rightSide)
                {
                    g.DrawLine(p, area.TopLeft, new PointF(area.TopLeft.X, area.BottomRight.Y));
                }
                else
                {
                    g.DrawLine(p, area.BottomRight, new PointF(area.BottomRight.X, area.TopLeft.Y));
                }
            }

            g.Restore(_s);
        }
Exemple #4
0
        public void DrawX(Graphics g, AdvancedRect area, AdvancedRect plotArea)
        {
            drawArea = area;

            //using (Brush br = new SolidBrush(Color.Green))
            //    g.FillRectangle(br, area.Rect);
            GraphicsState _s = g.Save();

            using (Font f = titleFont.CreateFont())
                using (Brush br = titleFont.CreateBrush())
                {
                    string txt = "Time (";
                    if (weeklyLabels)
                    {
                        txt += "weeks)";
                    }
                    else if (monthlyLabels || quarterlyLabels)
                    {
                        txt += "months)";
                    }
                    else if (yearlyLabels)
                    {
                        txt += "years)";
                    }
                    SizeF sz = g.MeasureString(txt, f);
                    g.DrawString(txt, f, br, area.Center.X - (sz.Width / 2), area.BottomRight.Y - sz.Height);
                }

            using (Brush br = labelFont.CreateBrush())
                using (Font f = labelFont.CreateFont())
                    using (Pen p = tickPen.CreatePen())
                        using (Pen pgrid = gridlinePen.CreatePen())
                            using (Pen pminor = minorTickPen.CreatePen())
                            {
                                float tick    = tickLength;
                                float dayTick = minorTickLength;

                                TimeSpan dur;
                                if (weeklyLabels)
                                {
                                    dur = week;
                                }
                                else if (monthlyLabels)
                                {
                                    dur = month;
                                }
                                else if (quarterlyLabels)
                                {
                                    dur = new TimeSpan(month.Ticks * 3);
                                }
                                else
                                {
                                    dur = year;
                                }

                                int cnt = (int)(ScaleMinimum.Ticks / dur.Ticks);
                                while (true)
                                {
                                    TimeSpan loc = new TimeSpan(dur.Ticks * cnt);
                                    float    x1  = DataToCoordinate(loc, area);
                                    cnt++;

                                    if (x1 < area.TopLeft.X)
                                    {
                                        continue;
                                    }
                                    if (x1 > area.BottomRight.X)
                                    {
                                        break;
                                    }

                                    g.DrawLine(p, x1, area.TopLeft.Y, x1, area.TopLeft.Y + tick);
                                    if (gridlinesEnabled && x1 != area.TopLeft.X && x1 != area.BottomRight.X)
                                    {
                                        g.DrawLine(pgrid, x1, plotArea.TopLeft.Y, x1, plotArea.BottomRight.Y);
                                    }

                                    if (dailyTicks)
                                    {
                                        for (int d = 0; d < 7; d++)
                                        {
                                            float xd = DataToCoordinate(loc + new TimeSpan(d, 0, 0, 0), area);
                                            if (xd > area.TopLeft.X && xd < area.BottomRight.X)
                                            {
                                                g.DrawLine(pminor, xd, area.TopLeft.Y, xd, area.TopLeft.Y + dayTick);
                                            }
                                        }
                                    }

                                    int labelNumber = cnt - 1;
                                    if (quarterlyLabels)
                                    {
                                        labelNumber *= 3;
                                    }
                                    string txt = String.Format("{0}", labelNumber);
                                    SizeF  sz  = g.MeasureString(txt, f);
                                    g.DrawString(txt, f, br, x1 - (sz.Width / 2), area.TopLeft.Y + tick);
                                }
                            }

            g.Restore(_s);
        }
Exemple #5
0
        public void PaintOn(Graphics g, RectangleF _area)
        {
            GraphicsState _s = g.Save();

            // _area is assumed to be provided in the same coordinate system as
            // g.PageUnit provides.  The plotting library works assuming that
            // PageUnit is Inch, so we must transform the Rectangle into the
            // appropriate rect.
            PointF[] trans = new PointF[] {
                new PointF(_area.Left, _area.Top),
                new PointF(_area.Left + _area.Width, _area.Top + _area.Height)
            };
            g.TransformPoints(CoordinateSpace.Device, CoordinateSpace.Page, trans);
            g.PageUnit = GraphicsUnit.Inch;
            g.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Device, trans);
            AdvancedRect area = new AdvancedRect(trans[0], trans[1]);

            using (Brush bg = background.CreateBrush())
                g.FillRectangle(bg, area.Rect);

            outerMargins.Apply(ref area);

            //using (Brush tmp = new SolidBrush(Color.Red))
            //    g.FillRectangle(tmp, area.Rect);

            SizeF headerSize = new SizeF(0, 0);

            if (centerHeader != null)
            {
                using (Brush br = centerHeaderFont.CreateBrush())
                    using (Font f = centerHeaderFont.CreateFont())
                    {
                        headerSize = g.MeasureString(centerHeader, f);
                        g.DrawString(centerHeader, f, br, area.Center.X - (headerSize.Width / 2), area.TopLeft.Y);
                    }
            }
            if (rightHeader != null || leftHeader != null)
            {
                using (Brush left_br = leftHeaderFont.CreateBrush())
                    using (Font left_f = leftHeaderFont.CreateFont())
                        using (Brush right_br = rightHeaderFont.CreateBrush())
                            using (Font right_f = rightHeaderFont.CreateFont())
                            {
                                SizeF leftHeaderSize  = (leftHeader != null) ? g.MeasureString(leftHeader, left_f) : new SizeF(0, 0);
                                SizeF rightHeaderSize = (rightHeader != null) ? g.MeasureString(rightHeader, right_f) : new SizeF(0, 0);

                                // determine y location = is this going to be aligned to the bottom of the center
                                // header, or underneath it?  underneath is necessary if the two will hit each other.
                                float left_xLoc  = area.TopLeft.X;
                                float right_xLoc = area.BottomRight.X - rightHeaderSize.Width;
                                float left_yLoc;
                                float right_yLoc;
                                if ((right_xLoc - 0.2f) < (area.Center.X + (headerSize.Width / 2)) ||
                                    (left_xLoc + leftHeaderSize.Width + 0.2f) > (area.Center.X - (headerSize.Width / 2)))
                                {
                                    // looks like we're too close - have to move down a bit.
                                    left_yLoc          = right_yLoc = area.TopLeft.Y + headerSize.Height;
                                    headerSize.Height += Math.Max(rightHeaderSize.Height, leftHeaderSize.Height);
                                }
                                else
                                {
                                    // We're good to place it next to the header.
                                    // But, check our height - are the side headers bigger than the center header?
                                    if (rightHeaderSize.Height > headerSize.Height || leftHeaderSize.Height > headerSize.Height)
                                    {
                                        // the side headers are bigger than the center header.  Make enough room.
                                        headerSize.Height = Math.Max(leftHeaderSize.Height, rightHeaderSize.Height);
                                    }
                                    right_yLoc = area.TopLeft.Y + (headerSize.Height - rightHeaderSize.Height);
                                    left_yLoc  = area.TopLeft.Y + (headerSize.Height - leftHeaderSize.Height);
                                }
                                if (rightHeader != null)
                                {
                                    g.DrawString(rightHeader, right_f, right_br, right_xLoc, right_yLoc);
                                }
                                if (leftHeader != null)
                                {
                                    g.DrawString(leftHeader, left_f, left_br, left_xLoc, left_yLoc);
                                }
                            }
            }
            if (headerSize.Height != 0)
            {
                area.TopLeft.Y += headerSize.Height;
                area.TopLeft.Y += 0.1f;
            }

            if (displayLegend)
            {
                area.BottomRight.Y -= DrawLegend(g, area);
            }

            float yAxisWidthLeft  = yAxes.CalculateWidthLeft(g);
            float yAxisWidthRight = yAxes.CalculateWidthRight(g);
            float xAxisHeight     = xAxis.CalculateHeight(g, area.Width - yAxisWidthLeft - yAxisWidthRight);

            AdvancedRect xAxisArea = area.Clone();

            xAxisArea.TopLeft.X     += yAxisWidthLeft;
            xAxisArea.BottomRight.X -= yAxisWidthRight;
            xAxisArea.TopLeft.Y      = xAxisArea.BottomRight.Y - xAxisHeight;

            // calculate internal data area
            area.TopLeft.X     += yAxisWidthLeft;
            area.BottomRight.X -= yAxisWidthRight;
            area.BottomRight.Y -= xAxisHeight;

            yAxes.Draw(g, area);
            xAxis.DrawX(g, xAxisArea, area);

            series.Draw(g, yAxes, xAxis, area);

            annotations.Draw(g, area);

            using (Pen p = borderPen.CreatePen())
            {
                g.DrawLine(p, area.TopLeft.X, area.TopLeft.Y, area.TopLeft.X, area.BottomRight.Y);
                g.DrawLine(p, area.TopLeft.X, area.TopLeft.Y, area.BottomRight.X, area.TopLeft.Y);
                g.DrawLine(p, area.BottomRight.X, area.BottomRight.Y, area.BottomRight.X, area.TopLeft.Y);
                g.DrawLine(p, area.BottomRight.X, area.BottomRight.Y, area.TopLeft.X, area.BottomRight.Y);
            }

            dataArea = area;

            g.Restore(_s);
        }
Exemple #6
0
 public PenSymbol(PenDescription f, float size)
     : base(size)
 {
     p = f.CreatePen();
 }
Exemple #7
0
        public void DrawX(Graphics g, AdvancedRect area, AdvancedRect plotArea)
        {
            drawArea = area;

            //using (Brush br = new SolidBrush(Color.Green))
            //    g.FillRectangle(br, area.Rect);
            GraphicsState _s = g.Save();

            using (Brush br = labelFont.CreateBrush())
                using (Font f = labelFont.CreateFont())
                    using (Font f2 = smallLabelFont.CreateFont())
                        using (Pen p = tickPen.CreatePen())
                            using (Pen pgrid = gridlinePen.CreatePen())
                                using (Pen pminor = minorTickPen.CreatePen())
                                {
                                    CalculateNext nextMajor = GetNextMajorFunction();

                                    // Same dealy-o, but for minor ticks / labels.
                                    CalculateNext nextMinor = null;
                                    if (axisType == AxisType.MonthsHorizontalWithDailyTicks || axisType == AxisType.MonthsHorizontalWithDailyLabels)
                                    {
                                        nextMinor = delegate(DateTime dt) { return(cal.AddDays(dt, 1)); }
                                    }
                                    ;
                                    else if (axisType == AxisType.DailyWithHourlyTicks || axisType == AxisType.DailyWithHourlyLabels)
                                    {
                                        nextMinor = delegate(DateTime dt) { return(cal.AddHours(dt, 1)); }
                                    }
                                    ;
                                    else if (axisType == AxisType.Quarters)
                                    {
                                        nextMinor = delegate(DateTime dt) { return(cal.AddMonths(dt, 1)); }
                                    }
                                    ;

                                    // Determine major label format -- {0} is the major label v,
                                    // {1} is the quarter
                                    string majorLabel = null;
                                    if (axisType == AxisType.Quarters)
                                    {
                                        majorLabel = "Q{1} {0:\\'yy}";
                                    }
                                    else if (axisType == AxisType.DailyWithHourlyTicks || axisType == AxisType.DailyWithHourlyLabels)
                                    {
                                        majorLabel = "{0:MMM %d \\'yy}";
                                    }
                                    else
                                    {
                                        majorLabel = "{0:MMM \\'yy}";
                                    }

                                    // Determine minor label format:
                                    string minorLabel = null;
                                    if (axisType == AxisType.MonthsHorizontalWithDailyLabels)
                                    {
                                        minorLabel = "{0:%d}";
                                    }
                                    else if (axisType == AxisType.DailyWithHourlyLabels)
                                    {
                                        minorLabel = "{0:%h%t}";
                                    }

                                    // Add some extra spacing if putting minor labels in.
                                    float minorLabelSpacing = 0;
                                    if (nextMinor != null && minorLabel != null)
                                    {
                                        minorLabelSpacing = 1f / 16;
                                    }

                                    // StringFormat for drawing major labels:
                                    StringFormat majorForm = new StringFormat();
                                    if (axisType == AxisType.MonthsVertical || axisType == AxisType.Quarters)
                                    {
                                        majorForm.FormatFlags = StringFormatFlags.DirectionVertical;
                                    }

                                    // Determine first major label value.
                                    DateTime dtLeft;
                                    if (axisType == AxisType.Quarters)
                                    {
                                        // set v to beginning of quarter which ScaleMinimum is in
                                        dtLeft = new DateTime(ScaleMinimum.Year, (((ScaleMinimum.Month - 1) / 3) * 3) + 1, 1);
                                    }
                                    else if (axisType == AxisType.DailyWithHourlyTicks || axisType == AxisType.DailyWithHourlyLabels)
                                    {
                                        // set v to beginning of day
                                        dtLeft = new DateTime(ScaleMinimum.Year, ScaleMinimum.Month, ScaleMinimum.Day);
                                    }
                                    else
                                    {
                                        // set v to beginning of month
                                        dtLeft = new DateTime(ScaleMinimum.Year, ScaleMinimum.Month, 1);
                                    }

                                    for (int i = 0; i < calculateNumLabels(); i++)
                                    {
                                        float xLeft = DataToCoordinate(dtLeft, area);
                                        xLeft = Math.Max(xLeft, area.TopLeft.X);

                                        if (xLeft > area.BottomRight.X)
                                        {
                                            break;
                                        }

                                        DateTime dtRight = nextMajor(dtLeft);
                                        float    xRight  = DataToCoordinate(dtRight, area);
                                        xRight = Math.Min(xRight, area.BottomRight.X);

                                        // Major ticks & gridlines:
                                        g.DrawLine(p, xLeft, area.TopLeft.Y, xLeft, area.TopLeft.Y + tickLength);
                                        if (gridlinesEnabled && xLeft > area.TopLeft.X && xLeft < area.BottomRight.X)
                                        {
                                            g.DrawLine(pgrid, xLeft, plotArea.TopLeft.Y, xLeft, plotArea.BottomRight.Y);
                                        }

                                        // Minor ticks:
                                        if (nextMinor != null)
                                        {
                                            DateTime dtMinorLeft = nextMinor(dtLeft);
                                            while (dtMinorLeft < dtRight)
                                            {
                                                float xMinorLeft = DataToCoordinate(dtMinorLeft, area);
                                                if (xMinorLeft > area.TopLeft.X && xMinorLeft < area.BottomRight.X)
                                                {
                                                    g.DrawLine(pminor, xMinorLeft, area.TopLeft.Y, xMinorLeft, area.TopLeft.Y + minorTickLength);
                                                }
                                                dtMinorLeft = nextMinor(dtMinorLeft);
                                            }
                                        }

                                        if (minorLabel != null && nextMinor != null)
                                        {
                                            DateTime dtMinorLeft = dtLeft;
                                            while (dtMinorLeft < dtRight)
                                            {
                                                DateTime dtMinorRight = nextMinor(dtMinorLeft);

                                                float xMinorLeft  = DataToCoordinate(dtMinorLeft, area);
                                                float xMinorRight = DataToCoordinate(dtMinorRight, area);
                                                if (xMinorLeft >= area.TopLeft.X && xMinorRight <= area.BottomRight.X)
                                                {
                                                    string dtxt   = String.Format(minorLabel, dtMinorLeft);
                                                    SizeF  dsz    = g.MeasureString(dtxt, f2);
                                                    float  xlabel = ((xMinorLeft + xMinorRight) / 2) - (dsz.Width / 2);
                                                    if (xlabel > area.TopLeft.X && (xlabel + dsz.Width) < area.BottomRight.X)
                                                    {
                                                        g.DrawString(dtxt, f2, br, ((xMinorLeft + xMinorRight) / 2) - (dsz.Width / 2), area.TopLeft.Y);
                                                    }
                                                }

                                                dtMinorLeft = dtMinorRight;
                                            }
                                        }

                                        // Draw major label:
                                        string txt    = String.Format(majorLabel, dtLeft, (dtLeft.Month / 3) + 1);
                                        SizeF  sz     = g.MeasureString(txt, f, 100, majorForm);
                                        float  draw_x = ((xLeft + xRight) / 2) - (sz.Width / 2);
                                        if (draw_x > area.TopLeft.X && (draw_x + sz.Width) < area.BottomRight.X)
                                        {
                                            g.DrawString(txt, f, br, ((xLeft + xRight) / 2) - (sz.Width / 2), area.TopLeft.Y + tickLength + minorLabelSpacing, majorForm);
                                        }

                                        dtLeft = dtRight;
                                    }

                                    // one final tick to signify end of last visible month
                                    float xL = DataToCoordinate(dtLeft, area);
                                    if (xL < area.BottomRight.X)
                                    {
                                        g.DrawLine(p, xL, area.TopLeft.Y, xL, area.TopLeft.Y + tickLength);
                                        if (gridlinesEnabled)
                                        {
                                            g.DrawLine(pgrid, xL, plotArea.TopLeft.Y, xL, plotArea.BottomRight.Y);
                                        }
                                    }
                                }

            g.Restore(_s);
        }