/// <summary> /// Draw the the PlotSurface and contents (axes, drawables, and legend) using the /// Drawing Context supplied and the bounding rectangle for the PlotSurface to cover /// </summary> /// <param name="ctx">The Drawing Context with which to draw.</param> /// <param name="bounds">The rectangle within which to draw</param> public void Draw(Context ctx, Rectangle bounds) { Point titleOrigin = Point.Zero; ctx.Save(); // determine font sizes and tick scale factor. double scale = DetermineScaleFactor(bounds.Width, bounds.Height); // if there is nothing to plot, draw title and return. if (drawables.Count == 0) { // draw title //TODO: Title should be centred here - not its origin Point origin = Point.Zero; titleOrigin.X = bounds.Width / 2; titleOrigin.Y = bounds.Height / 2; DrawTitle(ctx, titleOrigin, scale); ctx.Restore(); return; } // determine the [non physical] axes to draw based on the axis properties set. Axis XAxis1 = null; Axis XAxis2 = null; Axis YAxis1 = null; Axis YAxis2 = null; DetermineAxesToDraw(out XAxis1, out XAxis2, out YAxis1, out YAxis2); // apply scale factor to axes as desired. if (XAxis1.AutoScaleTicks) { XAxis1.TickScale = scale; } if (XAxis1.AutoScaleText) { XAxis1.FontScale = scale; } if (YAxis1.AutoScaleTicks) { YAxis1.TickScale = scale; } if (YAxis1.AutoScaleText) { YAxis1.FontScale = scale; } if (XAxis2.AutoScaleTicks) { XAxis2.TickScale = scale; } if (XAxis2.AutoScaleText) { XAxis2.FontScale = scale; } if (YAxis2.AutoScaleTicks) { YAxis2.TickScale = scale; } if (YAxis2.AutoScaleText) { YAxis2.FontScale = scale; } // determine the default physical positioning of those axes. PhysicalAxis pXAxis1 = null; PhysicalAxis pYAxis1 = null; PhysicalAxis pXAxis2 = null; PhysicalAxis pYAxis2 = null; DeterminePhysicalAxesToDraw( bounds, XAxis1, XAxis2, YAxis1, YAxis2, out pXAxis1, out pXAxis2, out pYAxis1, out pYAxis2); double oldXAxis2Height = pXAxis2.PhysicalMin.Y; // Apply axes constraints for (int i = 0; i < axesConstraints.Count; ++i) { ((AxesConstraint)axesConstraints[i]).ApplyConstraint( pXAxis1, pYAxis1, pXAxis2, pYAxis2); } // draw legend if have one. // Note: this will update axes if necessary. Point legendPosition = new Point(0, 0); if (legend != null) { legend.UpdateAxesPositions( pXAxis1, pYAxis1, pXAxis2, pYAxis2, drawables, scale, Padding, bounds, out legendPosition); } double newXAxis2Height = pXAxis2.PhysicalMin.Y; double titleExtraOffset = oldXAxis2Height - newXAxis2Height; // now we are ready to define the clipping region plotAreaBoundingBoxCache = new Rectangle( Math.Min(pXAxis1.PhysicalMin.X, pXAxis1.PhysicalMax.X), Math.Min(pYAxis1.PhysicalMax.Y, pYAxis1.PhysicalMin.Y), Math.Abs(pXAxis1.PhysicalMax.X - pXAxis1.PhysicalMin.X + 1), Math.Abs(pYAxis1.PhysicalMin.Y - pYAxis1.PhysicalMax.Y + 1) ); bbXAxis1Cache = pXAxis1.GetBoundingBox(); bbXAxis2Cache = pXAxis2.GetBoundingBox(); bbYAxis1Cache = pYAxis1.GetBoundingBox(); bbYAxis2Cache = pYAxis2.GetBoundingBox(); Rectangle plotBounds = (Rectangle)plotAreaBoundingBoxCache; // set the clipping region.. (necessary for zoom) // Note: although clipping is enforced by the clip region, it is probably more efficient // for each Drawable to check against the plotBounds and not draw if points are outside. // This hasn't yet been implemented ctx.Save(); ctx.Rectangle(plotBounds); ctx.Clip(); // Fill in the plot background. if (plotBackImage != null) { // Ensure plotBounds has integer size for correct tiling/drawing plotBounds.Width = Math.Truncate(plotBounds.Width); plotBounds.Height = Math.Truncate(plotBounds.Height); ctx.DrawImage(Utils.TiledImage(plotBackImage, plotBounds.Size), plotBounds); } else if (plotBackGradient != null) { // Scale plotBackGradient to plotBounds double startX = plotBounds.X + (plotBackGradient.StartPoint.X * plotBounds.Width); double startY = plotBounds.Y + (plotBackGradient.StartPoint.Y * plotBounds.Height); double endX = plotBounds.X + (plotBackGradient.EndPoint.X * plotBounds.Width); double endY = plotBounds.Y + (plotBackGradient.EndPoint.Y * plotBounds.Height); LinearGradient g = new LinearGradient(startX, startY, endX, endY); g.AddColorStop(0, plotBackGradient.StartColor); g.AddColorStop(1, plotBackGradient.EndColor); ctx.Rectangle(plotBounds); ctx.Pattern = g; ctx.Fill(); } else { ctx.Rectangle(plotBounds); ctx.SetColor(plotBackColor); ctx.Fill(); } // draw title at centre of Physical X-axis and at top of plot titleOrigin.X = (pXAxis2.PhysicalMax.X + pXAxis2.PhysicalMin.X) / 2.0; titleOrigin.Y = bounds.Top + Padding - titleExtraOffset; Size s = DrawTitle(ctx, titleOrigin, scale); bbTitleCache = new Rectangle(titleOrigin.X - s.Width / 2, titleOrigin.Y, s.Width, s.Height); // draw drawables.. bool legendDrawn = false; for (int i_o = 0; i_o < ordering.Count; ++i_o) { int i = (int)ordering.GetByIndex(i_o); double zOrder = (double)ordering.GetKey(i_o); if (zOrder > legendZOrder) { // draw legend. if (!legendDrawn && legend != null) { legend.Draw(ctx, legendPosition, drawables, scale); legendDrawn = true; } } IDrawable drawable = (IDrawable)drawables[i]; XAxisPosition xap = (XAxisPosition)xAxisPositions[i]; YAxisPosition yap = (YAxisPosition)yAxisPositions[i]; PhysicalAxis drawXAxis; PhysicalAxis drawYAxis; if (xap == XAxisPosition.Bottom) { drawXAxis = pXAxis1; } else { drawXAxis = pXAxis2; } if (yap == YAxisPosition.Left) { drawYAxis = pYAxis1; } else { drawYAxis = pYAxis2; } drawable.Draw(ctx, drawXAxis, drawYAxis); } if (!legendDrawn && legend != null) { legend.Draw(ctx, legendPosition, drawables, scale); } ctx.Restore(); // end of clipping region // cache the physical axes we used on this draw; pXAxis1Cache = pXAxis1; pYAxis1Cache = pYAxis1; pXAxis2Cache = pXAxis2; pYAxis2Cache = pYAxis2; // now draw axes. Rectangle axisBounds; pXAxis1.Draw(ctx, out axisBounds); pXAxis2.Draw(ctx, out axisBounds); pYAxis1.Draw(ctx, out axisBounds); pYAxis2.Draw(ctx, out axisBounds); #if DEBUG_BOUNDING_BOXES ctx.SetColor(Colors.Orange); ctx.Rectangle((Rectangle)bbXAxis1Cache); ctx.Rectangle((Rectangle)bbXAxis2Cache); ctx.Rectangle((Rectangle)bbYAxis1Cache); ctx.Rectangle((Rectangle)bbYAxis2Cache); ctx.Stroke(); ctx.SetColor(Colors.Red); ctx.Rectangle((Rectangle)plotAreaBoundingBoxCache); ctx.Rectangle((Rectangle)bbTitleCache); ctx.Stroke(); #endif ctx.Restore(); }
void DeterminePhysicalAxesToDraw(Rectangle bounds, Axis XAxis1, Axis XAxis2, Axis YAxis1, Axis YAxis2, out PhysicalAxis pXAxis1, out PhysicalAxis pXAxis2, out PhysicalAxis pYAxis1, out PhysicalAxis pYAxis2 ) { Rectangle cb = bounds; pXAxis1 = new PhysicalAxis (XAxis1, new Point (cb.Left, cb.Bottom), new Point (cb.Right, cb.Bottom) ); pYAxis1 = new PhysicalAxis (YAxis1, new Point (cb.Left, cb.Bottom), new Point (cb.Left, cb.Top) ); pXAxis2 = new PhysicalAxis (XAxis2, new Point (cb.Left, cb.Top), new Point (cb.Right, cb.Top) ); pYAxis2 = new PhysicalAxis (YAxis2, new Point (cb.Right, cb.Bottom), new Point (cb.Right, cb.Top) ); double bottomIndent = Padding; if (!pXAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis1.GetBoundingBox (); // finally determine its indentation from the bottom bottomIndent = bottomIndent + bb.Bottom - cb.Bottom; } double leftIndent = Padding; if (!pYAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis1.GetBoundingBox(); // finally determine its indentation from the left leftIndent = leftIndent - bb.Left + cb.Left; } // determine title size double scale = DetermineScaleFactor (bounds.Width, bounds.Height); Font scaled_font; if (AutoScaleTitle) { scaled_font = TitleFont.WithScaledSize (scale); } else { scaled_font = TitleFont; } Size titleSize; using (TextLayout layout = new TextLayout ()) { layout.Font = scaled_font; layout.Text = Title; titleSize = layout.GetSize (); }; double topIndent = Padding; if (!pXAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis2.GetBoundingBox(); topIndent = topIndent - bb.Top + cb.Top; // finally determine its indentation from the top // correct top indendation to take into account plot title if (Title != "" ) { topIndent += titleSize.Height * 1.3; } } double rightIndent = Padding; if (!pYAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis2.GetBoundingBox(); // finally determine its indentation from the right rightIndent += (bb.Right-cb.Right); } // now we have all the default calculated 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); }
void DeterminePhysicalAxesToDraw(Rectangle bounds, Axis XAxis1, Axis XAxis2, Axis YAxis1, Axis YAxis2, out PhysicalAxis pXAxis1, out PhysicalAxis pXAxis2, out PhysicalAxis pYAxis1, out PhysicalAxis pYAxis2) { Rectangle cb = bounds; pXAxis1 = new PhysicalAxis(XAxis1, new Point(cb.Left, cb.Bottom), new Point(cb.Right, cb.Bottom)); pYAxis1 = new PhysicalAxis(YAxis1, new Point(cb.Left, cb.Bottom), new Point(cb.Left, cb.Top)); pXAxis2 = new PhysicalAxis(XAxis2, new Point(cb.Left, cb.Top), new Point(cb.Right, cb.Top)); pYAxis2 = new PhysicalAxis(YAxis2, new Point(cb.Right, cb.Bottom), new Point(cb.Right, cb.Top)); double bottomIndent = Padding; if (!pXAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis1.GetBoundingBox(); // finally determine its indentation from the bottom bottomIndent = bottomIndent + bb.Bottom - cb.Bottom; } double leftIndent = Padding; if (!pYAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis1.GetBoundingBox(); // finally determine its indentation from the left leftIndent = leftIndent - bb.Left + cb.Left; } // determine title size double scale = DetermineScaleFactor(bounds.Width, bounds.Height); Font scaled_font; if (AutoScaleTitle) { scaled_font = TitleFont.WithScaledSize(scale); } else { scaled_font = TitleFont; } Size titleSize; using (TextLayout layout = new TextLayout()) { layout.Font = scaled_font; layout.Text = Title; titleSize = layout.GetSize(); }; double topIndent = Padding; if (!pXAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis2.GetBoundingBox(); topIndent = topIndent - bb.Top + cb.Top; // finally determine its indentation from the top // correct top indendation to take into account plot title if (Title != "") { topIndent += titleSize.Height * 1.3; } } double rightIndent = Padding; if (!pYAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis2.GetBoundingBox(); // finally determine its indentation from the right rightIndent += (bb.Right - cb.Right); } // now we have all the default calculated 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); }