/// <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); } }