/// <summary> /// Returns an x-axis that is suitable for drawing this plot. /// </summary> /// <returns>A suitable x-axis.</returns> public Axis SuggestXAxis() { SequenceAdapter data = new SequenceAdapter(this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData); Axis a = data.SuggestXAxis(); if (data.Count == 0) { return(a); } PointD p1; PointD p2; PointD p3; PointD p4; if (data.Count < 2) { p1 = data[0]; p1.X -= 1.0; p2 = data[0]; p3 = p1; p4 = p2; } else { p1 = data[0]; p2 = data[1]; p3 = data[data.Count - 2]; p4 = data[data.Count - 1]; } double offset1; double offset2; if (!center_) { offset1 = 0.0f; offset2 = p4.X - p3.X; } else { offset1 = (p2.X - p1.X) / 2.0f; offset2 = (p4.X - p3.X) / 2.0f; } a.WorldMin -= offset1; a.WorldMax += offset2; return(a); }
/// <summary> /// Draws the plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public override void Draw(IGraphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { SequenceAdapter data = new SequenceAdapter(DataSource, DataMember, OrdinateData, AbscissaData); TextDataAdapter textData = new TextDataAdapter(DataSource, DataMember, TextData); // first plot the marker // we can do this cast, since the constructor accepts only this type! for (int i = 0; i < data.Count; ++i) { try { PointD pt = data[i]; if (!Double.IsNaN(pt.X) && !Double.IsNaN(pt.Y)) { PointF xPos = xAxis.WorldToPhysical(pt.X, false); PointF yPos = yAxis.WorldToPhysical(pt.Y, false); Marker.Draw(g, (int)xPos.X, (int)yPos.Y); if (textData[i] != "") { SizeF size = g.MeasureString(textData[i], Font); switch (LabelTextPosition) { case LabelPositions.Above: g.DrawString(textData[i], Font, Brushes.Black, new PointF(xPos.X - size.Width / 2, yPos.Y - size.Height - Marker.Size * 2 / 3)); break; case LabelPositions.Below: g.DrawString(textData[i], Font, Brushes.Black, new PointF(xPos.X - size.Width / 2, yPos.Y + Marker.Size * 2 / 3)); break; case LabelPositions.Left: g.DrawString(textData[i], Font, Brushes.Black, new PointF(xPos.X - size.Width - Marker.Size * 2 / 3, yPos.Y - size.Height / 2)); break; case LabelPositions.Right: g.DrawString(textData[i], Font, Brushes.Black, new PointF(xPos.X + Marker.Size * 2 / 3, yPos.Y - size.Height / 2)); break; } } } } catch { throw new NPlotException("Error in TextPlot.Draw"); } } }
/// <summary> /// Returns a y-axis that is suitable for drawing this plot. /// </summary> /// <returns>A suitable y-axis.</returns> public Axis SuggestYAxis() { SequenceAdapter dataBottom_ = new SequenceAdapter(DataSource, DataMember, OrdinateDataBottom, AbscissaData); SequenceAdapter dataTop_ = new SequenceAdapter(DataSource, DataMember, OrdinateDataTop, AbscissaData); Axis axis = dataTop_.SuggestYAxis(); axis.LUB(dataBottom_.SuggestYAxis()); return(axis); }
/// <summary> /// Returns a y-axis that is suitable for drawing this plot. /// </summary> /// <returns>A suitable y-axis.</returns> public Axis SuggestYAxis() { if (this.isStacked_) { double tmpMax = 0.0f; ArrayList adapterList = new ArrayList(); HistogramPlot currentPlot = this; do { adapterList.Add(new SequenceAdapter( currentPlot.DataSource, currentPlot.DataMember, currentPlot.OrdinateData, currentPlot.AbscissaData) ); } while ((currentPlot = currentPlot.stackedTo_) != null); SequenceAdapter[] adapters = (SequenceAdapter[])adapterList.ToArray(typeof(SequenceAdapter)); for (int i = 0; i < adapters[0].Count; ++i) { double tmpHeight = 0.0f; for (int j = 0; j < adapters.Length; ++j) { tmpHeight += adapters[j][i].Y; } tmpMax = Math.Max(tmpMax, tmpHeight); } Axis a = new LinearAxis(0.0f, tmpMax); // TODO make 0.08 a parameter. a.IncreaseRange(0.08); return(a); } else { SequenceAdapter data = new SequenceAdapter(this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData); return(data.SuggestYAxis()); } }
/// <summary> /// Returns an X-axis suitable for use by this plot. The axis will be one that is just long /// enough to show all data. /// </summary> /// <returns>X-axis suitable for use by this plot.</returns> public Axis SuggestXAxis() { SequenceAdapter data = new SequenceAdapter(DataSource, DataMember, OrdinateData, AbscissaData); if (data.Count < 2) { return(data.SuggestXAxis()); } // else Axis a = data.SuggestXAxis(); PointD p1 = data[0]; PointD p2 = data[1]; PointD p3 = data[data.Count - 2]; PointD p4 = data[data.Count - 1]; double offset1; double offset2; if (!center_) { offset1 = 0.0f; offset2 = p4.X - p3.X; } else { offset1 = (p2.X - p1.X) / 2.0f; offset2 = (p4.X - p3.X) / 2.0f; } a.WorldMin -= offset1; a.WorldMax += offset2; return(a); }
/// <summary> /// Renders the histogram. /// </summary> /// <param name="g">The Graphics surface on which to draw</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public void Draw(Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { SequenceAdapter data = new SequenceAdapter(this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData); float yoff; for (int i = 0; i < data.Count; ++i) { // (1) determine the top left hand point of the bar (assuming not centered) PointD p1 = data[i]; if (double.IsNaN(p1.X) || double.IsNaN(p1.Y)) { continue; } // (2) determine the top right hand point of the bar (assuming not centered) PointD p2; if (i + 1 != data.Count) { p2 = data[i + 1]; if (double.IsNaN(p2.X) || double.IsNaN(p2.Y)) { continue; } p2.Y = p1.Y; } else if (i != 0) { p2 = data[i - 1]; if (double.IsNaN(p2.X) || double.IsNaN(p2.Y)) { continue; } double offset = p1.X - p2.X; p2.X = p1.X + offset; p2.Y = p1.Y; } else { double offset = 1.0f; p2.X = p1.X + offset; p2.Y = p1.Y; } // (3) now account for plots this may be stacked on top of. HistogramPlot currentPlot = this; yoff = 0.0f; double yval = 0.0f; while (currentPlot.isStacked_) { SequenceAdapter stackedToData = new SequenceAdapter( currentPlot.stackedTo_.DataSource, currentPlot.stackedTo_.DataMember, currentPlot.stackedTo_.OrdinateData, currentPlot.stackedTo_.AbscissaData); yval += stackedToData[i].Y; yoff = yAxis.WorldToPhysical(yval, false).Y; p1.Y += stackedToData[i].Y; p2.Y += stackedToData[i].Y; currentPlot = currentPlot.stackedTo_; } // (4) now account for centering if (center_) { double offset = (p2.X - p1.X) / 2.0f; p1.X -= offset; p2.X -= offset; } // (5) now account for BaseOffset (shift of bar sideways). p1.X += baseOffset_; p2.X += baseOffset_; // (6) now get physical coordinates of top two points. PointF xPos1 = xAxis.WorldToPhysical(p1.X, false); PointF yPos1 = yAxis.WorldToPhysical(p1.Y, false); PointF xPos2 = xAxis.WorldToPhysical(p2.X, false); PointF yPos2 = yAxis.WorldToPhysical(p2.Y, false); if (isStacked_) { currentPlot = this; while (currentPlot.isStacked_) { currentPlot = currentPlot.stackedTo_; } this.baseWidth_ = currentPlot.baseWidth_; } float width = xPos2.X - xPos1.X; float height; if (isStacked_) { height = -yPos1.Y + yoff; } else { height = -yPos1.Y + yAxis.PhysicalMin.Y; } float xoff = (1.0f - baseWidth_) / 2.0f * width; Rectangle r = new Rectangle((int)(xPos1.X + xoff), (int)yPos1.Y, (int)(width - 2 * xoff), (int)height); if (this.Filled) { if (r.Height != 0 && r.Width != 0) { // room for optimization maybe. g.FillRectangle(rectangleBrush_.Get(r), r); } } g.DrawRectangle(Pen, r.X, r.Y, r.Width, r.Height); } }
/// <summary> /// Draws the histogram. /// </summary> /// <param name="ctx">The Drawing Context with which to draw</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public void Draw(Context ctx, PhysicalAxis xAxis, PhysicalAxis yAxis) { double yoff; SequenceAdapter data = new SequenceAdapter (DataSource, DataMember, OrdinateData, AbscissaData); ctx.Save (); ctx.SetLineWidth (1); for (int i=0; i<data.Count; ++i ) { // (1) determine the top left hand point of the bar (assuming not centered) Point p1 = data[i]; if (double.IsNaN(p1.X) || double.IsNaN(p1.Y)) { continue; } // (2) determine the top right hand point of the bar (assuming not centered) Point p2 = Point.Zero;; if (i+1 != data.Count) { p2 = data[i+1]; if (double.IsNaN(p2.X) || double.IsNaN(p2.Y)) { continue; } p2.Y = p1.Y; } else if (i != 0) { p2 = data[i-1]; if (double.IsNaN(p2.X) || double.IsNaN(p2.Y)) { continue; } double offset = p1.X - p2.X; p2.X = p1.X + offset; p2.Y = p1.Y; } else { double offset = 1.0; p2.X = p1.X + offset; p2.Y = p1.Y; } // (3) now account for plots this may be stacked on top of. HistogramPlot currentPlot = this; yoff = 0.0; double yval = 0.0; while (currentPlot.isStacked_) { SequenceAdapter stackedToData = new SequenceAdapter ( currentPlot.stackedTo_.DataSource, currentPlot.stackedTo_.DataMember, currentPlot.stackedTo_.OrdinateData, currentPlot.stackedTo_.AbscissaData ); yval += stackedToData[i].Y; yoff = yAxis.WorldToPhysical (yval, false).Y; p1.Y += stackedToData[i].Y; p2.Y += stackedToData[i].Y; currentPlot = currentPlot.stackedTo_; } // (4) now account for centering if (center_) { double offset = (p2.X - p1.X) / 2.0; p1.X -= offset; p2.X -= offset; } // (5) now account for BaseOffset (shift of bar sideways). p1.X += baseOffset_; p2.X += baseOffset_; // (6) now get physical coordinates of top two points. Point xPos1 = xAxis.WorldToPhysical (p1.X, false); Point yPos1 = yAxis.WorldToPhysical (p1.Y, false); Point xPos2 = xAxis.WorldToPhysical (p2.X, false); if (isStacked_) { currentPlot = this; while (currentPlot.isStacked_) { currentPlot = currentPlot.stackedTo_; } baseWidth_ = currentPlot.baseWidth_; } double width = xPos2.X - xPos1.X; double height; if (isStacked_) { height = -yPos1.Y+yoff; } else { height = -yPos1.Y+yAxis.PhysicalMin.Y; } double xoff = (1.0 - baseWidth_)/2.0*width; Rectangle r = new Rectangle (xPos1.X+xoff, yPos1.Y, width-2*xoff, height); ctx.Rectangle (r); if (Filled) { if (r.Height != 0 && r.Width != 0) { // room for optimization maybe. ctx.SetColor (fillColor); ctx.FillPreserve (); } } ctx.SetColor (borderColor); ctx.Stroke (); } ctx.Restore (); }
/// <summary> /// Draws the point plot using the Drawing Context and x and y axes supplied /// </summary> /// <param name="ctx">The Drawing Context with which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public virtual void Draw(Context ctx, PhysicalAxis xAxis, PhysicalAxis yAxis) { SequenceAdapter data_ = new SequenceAdapter (DataSource, DataMember, OrdinateData, AbscissaData ); double leftCutoff_ = xAxis.PhysicalMin.X - marker.Size; double rightCutoff_ = xAxis.PhysicalMax.X + marker.Size; ctx.Save (); ctx.SetColor (marker.LineColor); ctx.SetLineWidth (marker.LineWidth); for (int i=0; i<data_.Count; ++i) { if (!Double.IsNaN(data_[i].X) && !Double.IsNaN(data_[i].Y)) { Point xPos = xAxis.WorldToPhysical (data_[i].X, false); if (xPos.X < leftCutoff_ || rightCutoff_ < xPos.X) { continue; } Point yPos = yAxis.WorldToPhysical (data_[i].Y, false); marker.Draw (ctx, xPos.X, yPos.Y); if (marker.DropLine) { Point yMin = new Point (data_[i].X, Math.Max (0.0, yAxis.Axis.WorldMin)); Point yStart = yAxis.WorldToPhysical (yMin.Y, false); ctx.MoveTo (xPos.X, yStart.Y); ctx.LineTo (xPos.X, yPos.Y); ctx.Stroke (); } } } ctx.Restore (); }
/// <summary> /// Returns an x-axis that is suitable for drawing this plot. /// </summary> /// <returns>A suitable x-axis.</returns> public Axis SuggestXAxis() { SequenceAdapter data_ = new SequenceAdapter (DataSource, DataMember, OrdinateData, AbscissaData); return data_.SuggestXAxis(); }
/// <summary> /// Returns an X-axis suitable for use by this plot. The axis will be one that is just long /// enough to show all data. /// </summary> /// <returns>X-axis suitable for use by this plot.</returns> public Axis SuggestXAxis() { SequenceAdapter data = new SequenceAdapter (DataSource, DataMember, OrdinateData, AbscissaData ); if (data.Count < 2) { return data.SuggestXAxis(); } // else Axis a = data.SuggestXAxis(); Point p1 = data[0]; Point p2 = data[1]; Point p3 = data[data.Count-2]; Point p4 = data[data.Count-1]; double offset1; double offset2; if (!center_) { offset1 = 0; offset2 = p4.X - p3.X; } else { offset1 = (p2.X - p1.X)/2; offset2 = (p4.X - p3.X)/2; } a.WorldMin -= offset1; a.WorldMax += offset2; return a; }
/// <summary> /// Returns an x-axis that is suitable for drawing this plot. /// </summary> /// <returns>A suitable x-axis.</returns> public Axis SuggestXAxis() { SequenceAdapter data = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); Axis a = data.SuggestXAxis(); if (data.Count==0) { return a; } PointD p1; PointD p2; PointD p3; PointD p4; if (data.Count < 2) { p1 = data[0]; p1.X -= 1.0; p2 = data[0]; p3 = p1; p4 = p2; } else { p1 = data[0]; p2 = data[1]; p3 = data[data.Count-2]; p4 = data[data.Count-1]; } double offset1; double offset2; if (!center_) { offset1 = 0.0f; offset2 = p4.X - p3.X; } else { offset1 = (p2.X - p1.X)/2.0f; offset2 = (p4.X - p3.X)/2.0f; } a.WorldMin -= offset1; a.WorldMax += offset2; return a; }
/// <summary> /// Writes text data of the plot object to the supplied string builder. It is /// possible to specify that only data in the specified range be written. /// </summary> /// <param name="sb">the StringBuilder object to write to.</param> /// <param name="region">a region used if onlyInRegion is true.</param> /// <param name="onlyInRegion">If true, only data enclosed in the provided region will be written.</param> public void WriteData( System.Text.StringBuilder sb, RectangleD region, bool onlyInRegion ) { SequenceAdapter data_ = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); sb.Append( "Label: " ); sb.Append( this.Label ); sb.Append( "\r\n" ); data_.WriteData( sb, region, onlyInRegion ); }
/// <summary> /// Draws the line plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> /// <param name="drawShadow">If true draw the shadow for the line. If false, draw line.</param> public void DrawLineOrShadow(Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis, bool drawShadow) { Pen shadowPen = null; if (drawShadow) { shadowPen = (Pen)this.Pen.Clone(); shadowPen.Color = this.ShadowColor; } SequenceAdapter data = new SequenceAdapter(this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData); ITransform2D t = Transform2D.GetTransformer(xAxis, yAxis); int numberPoints = data.Count; if (data.Count == 0) { return; } // clipping is now handled assigning a clip region in the // graphic object before this call if (numberPoints == 1) { PointF physical = t.Transform(data[0]); if (drawShadow) { g.DrawLine(shadowPen, physical.X - 0.5f + this.ShadowOffset.X, physical.Y + this.ShadowOffset.Y, physical.X + 0.5f + this.ShadowOffset.X, physical.Y + this.ShadowOffset.Y); } else { g.DrawLine(Pen, physical.X - 0.5f, physical.Y, physical.X + 0.5f, physical.Y); } } else { // prepare for clipping double leftCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMin, false); double rightCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMax, false); if (leftCutoff > rightCutoff) { Utils.Swap(ref leftCutoff, ref rightCutoff); } if (drawShadow) { // correct cut-offs double shadowCorrection = xAxis.PhysicalToWorld(ShadowOffset, false) - xAxis.PhysicalToWorld(new Point(0, 0), false); leftCutoff -= shadowCorrection; rightCutoff -= shadowCorrection; } for (int i = 1; i < numberPoints; ++i) { // check to see if any values null. If so, then continue. double dx1 = data[i - 1].X; double dx2 = data[i].X; double dy1 = data[i - 1].Y; double dy2 = data[i].Y; if (Double.IsNaN(dx1) || Double.IsNaN(dy1) || Double.IsNaN(dx2) || Double.IsNaN(dy2)) { continue; } // do horizontal clipping here, to speed up if ((dx1 < leftCutoff && dx2 < leftCutoff) || (rightCutoff < dx1 && rightCutoff < dx2)) { continue; } // else draw line. PointF p1 = t.Transform(data[i - 1]); PointF p2 = t.Transform(data[i]); // when very far zoomed in, points can fall ontop of each other, // and g.DrawLine throws an overflow exception if (p1.Equals(p2)) { continue; } if (drawShadow) { g.DrawLine(shadowPen, p1.X + ShadowOffset.X, p1.Y + ShadowOffset.Y, p2.X + ShadowOffset.X, p2.Y + ShadowOffset.Y); } else { g.DrawLine(Pen, p1.X, p1.Y, p2.X, p2.Y); } } } }
/// <summary> /// Draws the step plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public virtual void Draw(Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { SequenceAdapter data = new SequenceAdapter(DataSource, DataMember, OrdinateData, AbscissaData); double leftCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMin, false); double rightCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMax, false); for (int i = 0; i < data.Count; ++i) { PointD p1 = data[i]; if (Double.IsNaN(p1.X) || Double.IsNaN(p1.Y)) { continue; } PointD p2; PointD p3; if (i + 1 != data.Count) { p2 = data[i + 1]; if (Double.IsNaN(p2.X) || Double.IsNaN(p2.Y)) { continue; } p2.Y = p1.Y; p3 = data[i + 1]; } else { // Check that we are not dealing with a DataSource of 1 point. // This check is done here so it is only checked on the end // condition and not for every point in the DataSource. if (data.Count > 1) { p2 = data[i - 1]; } else { // TODO: Once log4net is set up post a message to the user that a step-plot of 1 really does not make any sense. p2 = p1; } double offset = p1.X - p2.X; p2.X = p1.X + offset; p2.Y = p1.Y; p3 = p2; } if (center_) { double offset = (p2.X - p1.X) / 2.0f; p1.X -= offset; p2.X -= offset; p3.X -= offset; } PointF xPos1 = xAxis.WorldToPhysical(p1.X, false); PointF yPos1 = yAxis.WorldToPhysical(p1.Y, false); PointF xPos2 = xAxis.WorldToPhysical(p2.X, false); PointF yPos2 = yAxis.WorldToPhysical(p2.Y, false); PointF xPos3 = xAxis.WorldToPhysical(p3.X, false); PointF yPos3 = yAxis.WorldToPhysical(p3.Y, false); // do horizontal clipping here, to speed up if ((p1.X < leftCutoff || p1.X > rightCutoff) && (p2.X < leftCutoff || p2.X > rightCutoff) && (p3.X < leftCutoff || p3.X > rightCutoff)) { continue; } if (!hideHorizontalSegments_) { if (scale_ != 1.0f) { float middle = (xPos2.X + xPos1.X) / 2.0f; float width = xPos2.X - xPos1.X; width *= scale_; g.DrawLine(Pen, (int)(middle - width / 2.0f), yPos1.Y, (int)(middle + width / 2.0f), yPos2.Y); } else { g.DrawLine(Pen, xPos1.X, yPos1.Y, xPos2.X, yPos2.Y); } } if (!hideVerticalSegments_) { g.DrawLine(Pen, xPos2.X, yPos2.Y, xPos3.X, yPos3.Y); } } }
/// <summary> /// Writes text data of the plot object to the supplied string builder. /// </summary> /// <param name="sb">the StringBuilder object to write to.</param> public void WriteData(System.Text.StringBuilder sb ) { SequenceAdapter data_ = new SequenceAdapter(this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData); sb.Append("["); sb.Append(this.Label); sb.AppendLine("]"); data_.WriteData(sb); }
/// <summary> /// Draws the point plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public virtual void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { SequenceAdapter data_ = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); float leftCutoff_ = xAxis.PhysicalMin.X - marker_.Size; float rightCutoff_ = xAxis.PhysicalMax.X + marker_.Size; for (int i=0; i<data_.Count; ++i) { var pointMarker = marker_; if (MarkerCallback != null) { pointMarker = MarkerCallback(data_[i].X, data_[i].Y); } if ( !Double.IsNaN(data_[i].X) && !Double.IsNaN(data_[i].Y) ) { PointF xPos = xAxis.WorldToPhysical( data_[i].X, false); if (xPos.X < leftCutoff_ || rightCutoff_ < xPos.X) continue; PointF yPos = yAxis.WorldToPhysical( data_[i].Y, false); pointMarker.Draw(g, (int)xPos.X, (int)yPos.Y); if (pointMarker.DropLine) { PointD yMin = new PointD( data_[i].X, Math.Max( 0.0f, yAxis.Axis.WorldMin ) ); PointF yStart = yAxis.WorldToPhysical( yMin.Y, false ); g.DrawLine(pointMarker.Pen, new Point((int)xPos.X, (int)yStart.Y), new Point((int)xPos.X, (int)yPos.Y)); } if (LabelFont != null) { var markerHalfSize = (float)pointMarker.Size / 2.0f; // Top-left corner var yText = data_[i].Y.ToString(); var textSize = g.MeasureString(yText, LabelFont); var labelRect = new RectangleF(xPos.X, yPos.Y, textSize.Width + LabelPadding + LabelPadding, textSize.Height + LabelPadding + LabelPadding); if (!g.ClipBounds.Contains(labelRect)) { // Try bottom-left corner labelRect.Y = yPos.Y - labelRect.Height; } if (!g.ClipBounds.Contains(labelRect)) { // Try bottom-right corner labelRect.X = xPos.X - labelRect.Width; } if (!g.ClipBounds.Contains(labelRect)) { // Try top-right corner labelRect.Y = yPos.Y; } g.FillRectangle(Brushes.White, labelRect); g.DrawRectangle(Pens.Black, labelRect.X, labelRect.Y, labelRect.Width, labelRect.Height); g.DrawString(yText, LabelFont, pointMarker.FillBrush, labelRect.X + LabelPadding, labelRect.Y + LabelPadding); } } } }
/// <summary> /// Draws the step plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public virtual void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { SequenceAdapter data = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); double leftCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMin, false); double rightCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMax, false); for (int i=0; i<data.Count; ++i) { PointD p1 = data[i]; if (Double.IsNaN(p1.X) || Double.IsNaN(p1.Y)) { continue; } PointD p2; PointD p3; if (i+1 != data.Count) { p2 = data[i+1]; if (Double.IsNaN(p2.X) || Double.IsNaN(p2.Y)) { continue; } p2.Y = p1.Y; p3 = data[i+1]; } else { p2 = data[i-1]; double offset = p1.X - p2.X; p2.X = p1.X + offset; p2.Y = p1.Y; p3 = p2; } if ( this.center_ ) { double offset = ( p2.X - p1.X ) / 2.0f; p1.X -= offset; p2.X -= offset; p3.X -= offset; } PointF xPos1 = xAxis.WorldToPhysical( p1.X, false ); PointF yPos1 = yAxis.WorldToPhysical( p1.Y, false ); PointF xPos2 = xAxis.WorldToPhysical( p2.X, false ); PointF yPos2 = yAxis.WorldToPhysical( p2.Y, false ); PointF xPos3 = xAxis.WorldToPhysical( p3.X, false ); PointF yPos3 = yAxis.WorldToPhysical( p3.Y, false ); // do horizontal clipping here, to speed up if ((p1.X < leftCutoff || p1.X > rightCutoff ) && (p2.X < leftCutoff || p2.X > rightCutoff ) && (p3.X < leftCutoff || p3.X > rightCutoff ) ) { continue; } if (!this.hideHorizontalSegments_) { if (scale_ != 1.0f) { float middle = (xPos2.X + xPos1.X) / 2.0f; float width = xPos2.X - xPos1.X; width *= this.scale_; g.DrawLine( Pen, (int)(middle-width/2.0f), yPos1.Y, (int)(middle+width/2.0f), yPos2.Y ); } else { g.DrawLine( Pen, xPos1.X, yPos1.Y, xPos2.X, yPos2.Y ); } } if (!this.hideVerticalSegments_) { g.DrawLine( Pen, xPos2.X, yPos2.Y, xPos3.X, yPos3.Y ); } } }
/// <summary> /// Draws the point plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public virtual void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { SequenceAdapter data_ = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); float leftCutoff_ = xAxis.PhysicalMin.X - marker_.Size; float rightCutoff_ = xAxis.PhysicalMax.X + marker_.Size; for (int i=0; i<data_.Count; ++i) { if ( !Double.IsNaN(data_[i].X) && !Double.IsNaN(data_[i].Y) ) { PointF xPos = xAxis.WorldToPhysical( data_[i].X, false); if (xPos.X < leftCutoff_ || rightCutoff_ < xPos.X) { continue; } PointF yPos = yAxis.WorldToPhysical( data_[i].Y, false); marker_.Draw( g, (int)xPos.X, (int)yPos.Y ); if (marker_.DropLine) { PointD yMin = new PointD( data_[i].X, Math.Max( 0.0f, yAxis.Axis.WorldMin ) ); PointF yStart = yAxis.WorldToPhysical( yMin.Y, false ); g.DrawLine( marker_.Pen, new Point((int)xPos.X,(int)yStart.Y), new Point((int)xPos.X,(int)yPos.Y) ); } } } }
/// <summary> /// Draws the line plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> /// <param name="drawShadow">If true draw the shadow for the line. If false, draw line.</param> public void DrawLineOrShadow(Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis, bool drawShadow) { Pen shadowPen = null; if (drawShadow) { shadowPen = (Pen)this.Pen.Clone(); shadowPen.Color = this.ShadowColor; } SequenceAdapter data = new SequenceAdapter(this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData); int numberPoints = data.Count; if (data.Count == 0) { return; } // clipping is now handled assigning a clip region in the // graphic object before this call if (numberPoints == 1) { PointF physical = Transform2D.GetTransformer(xAxis, yAxis).Transform(data[0]); if (drawShadow) { g.DrawLine(shadowPen, physical.X - 0.5f + this.ShadowOffset.X, physical.Y + this.ShadowOffset.Y, physical.X + 0.5f + this.ShadowOffset.X, physical.Y + this.ShadowOffset.Y); } else { g.DrawLine(Pen, physical.X - 0.5f, physical.Y, physical.X + 0.5f, physical.Y); } } else { // prepare for clipping double leftCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMin, false); double rightCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMax, false); if (leftCutoff > rightCutoff) { double temp = leftCutoff; leftCutoff = rightCutoff; rightCutoff = temp; } if (drawShadow) { // correct cut-offs double shadowCorrection = xAxis.PhysicalToWorld(ShadowOffset, false) - xAxis.PhysicalToWorld(new Point(0, 0), false); leftCutoff -= shadowCorrection; rightCutoff -= shadowCorrection; } // determine which points to plot double tradingTimeOffset = 0; List <PointD> plotPoints = new List <PointD>(numberPoints); PointD d1 = data[0]; PointD d2 = d1; if ((d1.X >= leftCutoff) && (d1.X <= rightCutoff)) { plotPoints.Add(d1); } DateTime prevTime = (DateTime)((System.Data.DataTable) this.DataSource).Rows[0][(string)this.AbscissaData]; for (int i = 1; i < numberPoints; ++i) { // check to see if any values null. If so, then continue. d1 = d2; d2 = data[i]; if (Double.IsNaN(d1.X) || Double.IsNaN(d1.Y) || Double.IsNaN(d2.X) || Double.IsNaN(d2.Y)) { continue; } // Get the X axis offset to strip out the non-trading time d1.X -= tradingTimeOffset; DateTime nextTime = (DateTime)((System.Data.DataTable) this.DataSource).Rows[i][(string)this.AbscissaData]; if (nextTime.TimeOfDay < prevTime.TimeOfDay) { tradingTimeOffset += (double)(nextTime - prevTime).Ticks; } prevTime = nextTime; d2.X -= tradingTimeOffset; // do horizontal clipping here, to speed up if ((d1.X < leftCutoff && d2.X < leftCutoff) || (rightCutoff < d1.X && rightCutoff < d2.X)) { continue; } // Add a point to plot plotPoints.Add(d2); } // create a transform, which takes into account the skipped time PhysicalAxis shunkAxis = new PhysicalAxis(new DateTimeAxis(xAxis.Axis.WorldMin, xAxis.Axis.WorldMax - tradingTimeOffset), xAxis.PhysicalMin, xAxis.PhysicalMax); ITransform2D t = Transform2D.GetTransformer(shunkAxis, yAxis); // plot those points for (int i = 1; i < plotPoints.Count; i++) { // else draw line. PointF p1 = t.Transform(plotPoints[i - 1]); PointF p2 = t.Transform(plotPoints[i]); // when very far zoomed in, points can fall ontop of each other, // and g.DrawLine throws an overflow exception if (p1.Equals(p2)) { continue; } if (drawShadow) { g.DrawLine(shadowPen, p1.X + ShadowOffset.X, p1.Y + ShadowOffset.Y, p2.X + ShadowOffset.X, p2.Y + ShadowOffset.Y); } else { g.DrawLine(Pen, p1.X, p1.Y, p2.X, p2.Y); } } } }
/// <summary> /// Renders the histogram. /// </summary> /// <param name="g">The Graphics surface on which to draw</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { SequenceAdapter data = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); float yoff; for ( int i=0; i<data.Count; ++i ) { // (1) determine the top left hand point of the bar (assuming not centered) PointD p1 = data[i]; if ( double.IsNaN(p1.X) || double.IsNaN(p1.Y) ) continue; // (2) determine the top right hand point of the bar (assuming not centered) PointD p2; if (i+1 != data.Count) { p2 = data[i+1]; if ( double.IsNaN(p2.X) || double.IsNaN(p2.Y) ) continue; p2.Y = p1.Y; } else if (i != 0) { p2 = data[i-1]; if ( double.IsNaN(p2.X) || double.IsNaN(p2.Y) ) continue; double offset = p1.X - p2.X; p2.X = p1.X + offset; p2.Y = p1.Y; } else { double offset = 1.0f; p2.X = p1.X + offset; p2.Y = p1.Y; } // (3) now account for plots this may be stacked on top of. HistogramPlot currentPlot = this; yoff = 0.0f; double yval = 0.0f; while (currentPlot.isStacked_) { SequenceAdapter stackedToData = new SequenceAdapter( currentPlot.stackedTo_.DataSource, currentPlot.stackedTo_.DataMember, currentPlot.stackedTo_.OrdinateData, currentPlot.stackedTo_.AbscissaData ); yval += stackedToData[i].Y; yoff = yAxis.WorldToPhysical( yval, false ).Y; p1.Y += stackedToData[i].Y; p2.Y += stackedToData[i].Y; currentPlot = currentPlot.stackedTo_; } // (4) now account for centering if ( center_ ) { double offset = ( p2.X - p1.X ) / 2.0f; p1.X -= offset; p2.X -= offset; } // (5) now account for BaseOffset (shift of bar sideways). p1.X += baseOffset_; p2.X += baseOffset_; // (6) now get physical coordinates of top two points. PointF xPos1 = xAxis.WorldToPhysical( p1.X, false ); PointF yPos1 = yAxis.WorldToPhysical( p1.Y, false ); PointF xPos2 = xAxis.WorldToPhysical( p2.X, false ); PointF yPos2 = yAxis.WorldToPhysical( p2.Y, false ); if (isStacked_) { currentPlot = this; while (currentPlot.isStacked_) { currentPlot = currentPlot.stackedTo_; } this.baseWidth_ = currentPlot.baseWidth_; } float width = xPos2.X - xPos1.X; float height; if (isStacked_) { height = -yPos1.Y+yoff; } else { height = -yPos1.Y+yAxis.PhysicalMin.Y; } float xoff = (1.0f - baseWidth_)/2.0f*width; Rectangle r = new Rectangle( (int)(xPos1.X+xoff), (int)yPos1.Y, (int)(width-2*xoff), (int)height ); if (this.Filled) { if (r.Height != 0 && r.Width != 0) { // room for optimization maybe. g.FillRectangle( rectangleBrush_.Get(r), r ); } } g.DrawRectangle( Pen, r.X, r.Y, r.Width, r.Height ); } }
/// <summary> /// Draws the line plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> /// <param name="drawShadow">If true draw the shadow for the line. If false, draw line.</param> public void DrawLineOrShadow( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis, bool drawShadow ) { Pen shadowPen = null; if (drawShadow) { shadowPen = (Pen)this.Pen.Clone(); shadowPen.Color = this.ShadowColor; } SequenceAdapter data = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); ITransform2D t = Transform2D.GetTransformer( xAxis, yAxis ); int numberPoints = data.Count; if (data.Count == 0) { return; } // clipping is now handled assigning a clip region in the // graphic object before this call if (numberPoints == 1) { PointF physical = t.Transform( data[0] ); if (drawShadow) { g.DrawLine( shadowPen, physical.X - 0.5f + this.ShadowOffset.X, physical.Y + this.ShadowOffset.Y, physical.X + 0.5f + this.ShadowOffset.X, physical.Y + this.ShadowOffset.Y ); } else { g.DrawLine( Pen, physical.X-0.5f, physical.Y, physical.X+0.5f, physical.Y); } } else { // prepare for clipping double leftCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMin, false); double rightCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMax, false); if (leftCutoff > rightCutoff) { Utils.Swap(ref leftCutoff, ref rightCutoff); } if (drawShadow) { // correct cut-offs double shadowCorrection = xAxis.PhysicalToWorld(ShadowOffset, false) - xAxis.PhysicalToWorld(new Point(0,0), false); leftCutoff -= shadowCorrection; rightCutoff -= shadowCorrection; } for (int i = 1; i < numberPoints; ++i) { // check to see if any values null. If so, then continue. double dx1 = data[i-1].X; double dx2 = data[i].X; double dy1 = data[i-1].Y; double dy2 = data[i].Y; if ( Double.IsNaN(dx1) || Double.IsNaN(dy1) || Double.IsNaN(dx2) || Double.IsNaN(dy2) ) { continue; } // do horizontal clipping here, to speed up if ((dx1 < leftCutoff || rightCutoff < dx1) && (dx2 < leftCutoff || rightCutoff < dx2)) { continue; } // else draw line. PointF p1 = t.Transform( data[i-1] ); PointF p2 = t.Transform( data[i] ); // when very far zoomed in, points can fall ontop of each other, // and g.DrawLine throws an overflow exception if (p1.Equals(p2)) continue; if (drawShadow) { g.DrawLine( shadowPen, p1.X + ShadowOffset.X, p1.Y + ShadowOffset.Y, p2.X + ShadowOffset.X, p2.Y + ShadowOffset.Y ); } else { g.DrawLine( Pen, p1.X, p1.Y, p2.X, p2.Y ); } } } }
/// <summary> /// Stack the histogram to another HistogramPlot. /// </summary> public void StackedTo(HistogramPlot hp) { SequenceAdapter data = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); SequenceAdapter hpData = new SequenceAdapter( hp.DataSource, hp.DataMember, hp.OrdinateData, hp.AbscissaData ); if ( hp != null ) { isStacked_ = true; if ( hpData.Count != data.Count ) { throw new NPlotException("Can stack HistogramPlot data only with the same number of datapoints."); } for ( int i=0; i < data.Count; ++i ) { if ( data[i].X != hpData[i].X ) { throw new NPlotException("Can stack HistogramPlot data only with the same X coordinates."); } if ( hpData[i].Y < 0.0f) { throw new NPlotException("Can stack HistogramPlot data only with positive Y coordinates."); } } } stackedTo_ = hp; }
/// <summary> /// Draws the line plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { SequenceAdapter dataTop = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateDataTop, this.AbscissaData ); SequenceAdapter dataBottom = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateDataBottom, this.AbscissaData ); ITransform2D t = Transform2D.GetTransformer( xAxis, yAxis ); for (int i=0; i<dataTop.Count; ++i) { PointF physicalBottom = t.Transform( dataBottom[i] ); PointF physicalTop = t.Transform( dataTop[i] ); if (physicalBottom != physicalTop) { Rectangle r = new Rectangle( (int)(physicalBottom.X - BarWidth/2), (int)physicalTop.Y, (int)BarWidth, (int)(physicalBottom.Y - physicalTop.Y) ); g.FillRectangle( this.rectangleBrush_.Get(r), r ); g.DrawRectangle( borderPen_, r ); } } }
/// <summary> /// Returns a y-axis that is suitable for drawing this plot. /// </summary> /// <returns>A suitable y-axis.</returns> public Axis SuggestYAxis() { if ( this.isStacked_ ) { double tmpMax = 0.0f; ArrayList adapterList = new ArrayList(); HistogramPlot currentPlot = this; do { adapterList.Add( new SequenceAdapter( currentPlot.DataSource, currentPlot.DataMember, currentPlot.OrdinateData, currentPlot.AbscissaData ) ); } while ((currentPlot = currentPlot.stackedTo_) != null); SequenceAdapter[] adapters = (SequenceAdapter[])adapterList.ToArray(typeof(SequenceAdapter)); for (int i=0; i<adapters[0].Count; ++i) { double tmpHeight = 0.0f; for (int j=0; j<adapters.Length; ++j) { tmpHeight += adapters[j][i].Y; } tmpMax = Math.Max(tmpMax, tmpHeight); } Axis a = new LinearAxis(0.0f,tmpMax); // TODO make 0.08 a parameter. a.IncreaseRange( 0.08 ); return a; } else { SequenceAdapter data = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); return data.SuggestYAxis(); } }
/// <summary> /// Draws the point plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public virtual void Draw(Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { SequenceAdapter data_ = new SequenceAdapter(this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData); float leftCutoff_ = xAxis.PhysicalMin.X - marker_.Size; float rightCutoff_ = xAxis.PhysicalMax.X + marker_.Size; for (int i = 0; i < data_.Count; ++i) { var pointMarker = marker_; if (MarkerCallback != null) { pointMarker = MarkerCallback(data_[i].X, data_[i].Y); } if (!Double.IsNaN(data_[i].X) && !Double.IsNaN(data_[i].Y)) { PointF xPos = xAxis.WorldToPhysical(data_[i].X, false); if (xPos.X < leftCutoff_ || rightCutoff_ < xPos.X) { continue; } PointF yPos = yAxis.WorldToPhysical(data_[i].Y, false); pointMarker.Draw(g, (int)xPos.X, (int)yPos.Y); if (pointMarker.DropLine) { PointD yMin = new PointD(data_[i].X, Math.Max(0.0f, yAxis.Axis.WorldMin)); PointF yStart = yAxis.WorldToPhysical(yMin.Y, false); g.DrawLine(pointMarker.Pen, new Point((int)xPos.X, (int)yStart.Y), new Point((int)xPos.X, (int)yPos.Y)); } if (LabelFont != null) { var markerHalfSize = (float)pointMarker.Size / 2.0f; // Top-left corner var yText = data_[i].Y.ToString(); var textSize = g.MeasureString(yText, LabelFont); var labelRect = new RectangleF(xPos.X, yPos.Y, textSize.Width + LabelPadding + LabelPadding, textSize.Height + LabelPadding + LabelPadding); if (!g.ClipBounds.Contains(labelRect)) { // Try bottom-left corner labelRect.Y = yPos.Y - labelRect.Height; } if (!g.ClipBounds.Contains(labelRect)) { // Try bottom-right corner labelRect.X = xPos.X - labelRect.Width; } if (!g.ClipBounds.Contains(labelRect)) { // Try top-right corner labelRect.Y = yPos.Y; } g.FillRectangle(Brushes.White, labelRect); g.DrawRectangle(Pens.Black, labelRect.X, labelRect.Y, labelRect.Width, labelRect.Height); g.DrawString(yText, LabelFont, pointMarker.FillBrush, labelRect.X + LabelPadding, labelRect.Y + LabelPadding); } } } }
/// <summary> /// Draws the line plot using the Context and Physical Axes provided /// </summary> /// <param name="ctx">The Drawing Context with which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> /// <param name="drawShadow">If true draw the shadow for the line. If false, draw line.</param> public void DrawLineOrShadow(Context ctx, PhysicalAxis xAxis, PhysicalAxis yAxis, bool drawShadow) { SequenceAdapter data = new SequenceAdapter (DataSource, DataMember, OrdinateData, AbscissaData); ITransform2D t = Transform2D.GetTransformer (xAxis, yAxis); int numberPoints = data.Count; if (data.Count == 0) { return; } ctx.Save (); ctx.SetLineWidth (lineWidth_); // clipping is now handled assigning a clip region in the // graphic object before this call if (numberPoints == 1) { Point physical = t.Transform (data[0]); if (drawShadow) { ctx.SetColor (shadowColor_); ctx.MoveTo (physical.X - 0.5 + ShadowOffset.X, physical.Y + ShadowOffset.Y); ctx.LineTo (physical.X + 0.5 + ShadowOffset.X, physical.Y + ShadowOffset.Y); ctx.Stroke (); } else { ctx.SetColor (lineColor_); ctx.MoveTo (physical.X-0.5, physical.Y); ctx.LineTo (physical.X+0.5, physical.Y); ctx.Stroke (); } } else { // prepare for clipping double leftCutoff = xAxis.PhysicalToWorld (xAxis.PhysicalMin, false); double rightCutoff = xAxis.PhysicalToWorld (xAxis.PhysicalMax, false); if (leftCutoff > rightCutoff) { Utils.Swap (ref leftCutoff, ref rightCutoff); } if (drawShadow) { // correct cut-offs double shadowCorrection = xAxis.PhysicalToWorld (ShadowOffset, false) - xAxis.PhysicalToWorld (new Point(0,0), false); leftCutoff -= shadowCorrection; rightCutoff -= shadowCorrection; } for (int i = 1; i < numberPoints; ++i) { // check to see if any values null. If so, then continue. double dx1 = data[i-1].X; double dx2 = data[i].X; double dy1 = data[i-1].Y; double dy2 = data[i].Y; if (Double.IsNaN(dx1) || Double.IsNaN(dy1) || Double.IsNaN(dx2) || Double.IsNaN(dy2)) { continue; } // do horizontal clipping here, to speed up if ((dx1 < leftCutoff && dx2 < leftCutoff) || (rightCutoff < dx1 && rightCutoff < dx2)) { continue; } // else draw line. Point p1 = t.Transform (data[i-1]); Point p2 = t.Transform (data[i]); // when very far zoomed in, points can fall ontop of each other, // and g.DrawLine throws an overflow exception if (p1.Equals(p2)) { continue; } if (drawShadow) { ctx.SetColor (shadowColor_); ctx.MoveTo (p1.X + ShadowOffset.X, p1.Y + ShadowOffset.Y); ctx.LineTo (p2.X + ShadowOffset.X, p2.Y + ShadowOffset.Y); ctx.Stroke (); } else { ctx.SetColor (lineColor_); ctx.MoveTo (p1.X, p1.Y); ctx.LineTo (p2.X, p2.Y); ctx.Stroke (); } } } ctx.Restore (); }
/// <summary> /// Draws the step plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public virtual void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { SequenceAdapter data = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); double leftCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMin, false); double rightCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMax, false); for (int i=0; i<data.Count; ++i) { PointD p1 = data[i]; if (Double.IsNaN(p1.X) || Double.IsNaN(p1.Y)) { continue; } PointD p2; PointD p3; if (i+1 != data.Count) { p2 = data[i+1]; if (Double.IsNaN(p2.X) || Double.IsNaN(p2.Y)) { continue; } p2.Y = p1.Y; p3 = data[i+1]; } else { // Check that we are not dealing with a DataSource of 1 point. // This check is done here so it is only checked on the end // condition and not for every point in the DataSource. if (data.Count > 1) { p2 = data[i - 1]; } else { // TODO: Once log4net is set up post a message to the user that a step-plot of 1 really does not make any sense. p2 = p1; } double offset = p1.X - p2.X; p2.X = p1.X + offset; p2.Y = p1.Y; p3 = p2; } if ( this.center_ ) { double offset = ( p2.X - p1.X ) / 2.0f; p1.X -= offset; p2.X -= offset; p3.X -= offset; } PointF xPos1 = xAxis.WorldToPhysical( p1.X, false ); PointF yPos1 = yAxis.WorldToPhysical( p1.Y, false ); PointF xPos2 = xAxis.WorldToPhysical( p2.X, false ); PointF yPos2 = yAxis.WorldToPhysical( p2.Y, false ); PointF xPos3 = xAxis.WorldToPhysical( p3.X, false ); PointF yPos3 = yAxis.WorldToPhysical( p3.Y, false ); // do horizontal clipping here, to speed up if ((p1.X < leftCutoff && p2.X < leftCutoff && p3.X < leftCutoff) || (p1.X > rightCutoff && p2.X > rightCutoff && p3.X > rightCutoff)) { continue; } if (!this.hideHorizontalSegments_) { if (scale_ != 1.0f) { float middle = (xPos2.X + xPos1.X) / 2.0f; float width = xPos2.X - xPos1.X; width *= this.scale_; g.DrawLine( Pen, (int)(middle-width/2.0f), yPos1.Y, (int)(middle+width/2.0f), yPos2.Y ); } else { g.DrawLine( Pen, xPos1.X, yPos1.Y, xPos2.X, yPos2.Y ); } } if (!this.hideVerticalSegments_) { g.DrawLine( Pen, xPos2.X, yPos2.Y, xPos3.X, yPos3.Y ); } } }
/// <summary> /// Draw the filled region /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public void Draw( System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { ITransform2D t = Transform2D.GetTransformer(xAxis, yAxis); Brush b = brush_; if (b == null) { b = areaBrush_.Get(new Rectangle(xAxis.PhysicalMin.X, yAxis.PhysicalMax.Y, xAxis.PhysicalLength, yAxis.PhysicalLength)); } if (hl1_ != null && hl2_ != null) { PointF[] points = new PointF[4]; points[0] = t.Transform(xAxis.Axis.WorldMin, hl1_.OrdinateValue); points[1] = t.Transform(xAxis.Axis.WorldMax, hl1_.OrdinateValue); points[2] = t.Transform(xAxis.Axis.WorldMax, hl2_.OrdinateValue); points[3] = t.Transform(xAxis.Axis.WorldMin, hl2_.OrdinateValue); g.FillPolygon(b, points); } else if (vl1_ != null && vl2_ != null) { PointF[] points = new PointF[4]; points[0] = t.Transform(vl1_.AbscissaValue, yAxis.Axis.WorldMin); points[1] = t.Transform(vl1_.AbscissaValue, yAxis.Axis.WorldMax); points[2] = t.Transform(vl2_.AbscissaValue, yAxis.Axis.WorldMax); points[3] = t.Transform(vl2_.AbscissaValue, yAxis.Axis.WorldMin); g.FillPolygon(b, points); } else if (lp1_ != null && lp2_ != null) { SequenceAdapter a1 = new SequenceAdapter(lp1_.DataSource, lp1_.DataMember, lp1_.OrdinateData, lp1_.AbscissaData); SequenceAdapter a2 = new SequenceAdapter(lp2_.DataSource, lp2_.DataMember, lp2_.OrdinateData, lp2_.AbscissaData); int count = a1.Count + a2.Count; PointF[] points = new PointF[count]; for (int i = 0; i < a1.Count; ++i) { points[i] = t.Transform(a1[i]); } for (int i = 0; i < a2.Count; ++i) { points[i + a1.Count] = t.Transform(a2[a2.Count - i - 1]); } g.FillPolygon(b, points); } else { throw new NPlotException("One of bounds was set to null"); } }
/// <summary> /// Draws the step plot using a Drawing Context against the provided x and y axes. /// </summary> /// <param name="ctx">The Drawing Context with which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public virtual void Draw(Context ctx, PhysicalAxis xAxis, PhysicalAxis yAxis ) { SequenceAdapter data = new SequenceAdapter (DataSource, DataMember, OrdinateData, AbscissaData); double leftCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMin, false); double rightCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMax, false); for (int i=0; i<data.Count; ++i) { Point p1 = data[i]; if (Double.IsNaN(p1.X) || Double.IsNaN(p1.Y)) { continue; } Point p2; Point p3; if (i+1 != data.Count) { p2 = data[i+1]; if (Double.IsNaN(p2.X) || Double.IsNaN(p2.Y)) { continue; } p2.Y = p1.Y; p3 = data[i+1]; } else { // Check that we are not dealing with a DataSource of 1 point. // This check is done here so it is only checked on the end // condition and not for every point in the DataSource. if (data.Count > 1) { p2 = data[i - 1]; } else { // TODO: Once log4net is set up post a message to the user that a step-plot of 1 really does not make any sense. p2 = p1; } double offset = p1.X - p2.X; p2.X = p1.X + offset; p2.Y = p1.Y; p3 = p2; } if (center_) { double offset = ( p2.X - p1.X ) / 2.0; p1.X -= offset; p2.X -= offset; p3.X -= offset; } Point xPos1 = xAxis.WorldToPhysical (p1.X, false); Point yPos1 = yAxis.WorldToPhysical (p1.Y, false); Point xPos2 = xAxis.WorldToPhysical (p2.X, false); Point yPos2 = yAxis.WorldToPhysical (p2.Y, false); Point xPos3 = xAxis.WorldToPhysical (p3.X, false); Point yPos3 = yAxis.WorldToPhysical (p3.Y, false); // do horizontal clipping here, to speed up if ((p1.X<leftCutoff && p2.X<leftCutoff && p3.X<leftCutoff) || (p1.X>rightCutoff && p2.X>rightCutoff && p3.X>rightCutoff)) { continue; } ctx.Save (); ctx.SetColor (Color); ctx.SetLineWidth (1); if (!this.hideHorizontalSegments_) { if (scale_ != 1) { double middle = (xPos2.X + xPos1.X) / 2; double width = xPos2.X - xPos1.X; width *= this.scale_; ctx.MoveTo (middle-width/2, yPos1.Y); ctx.LineTo (middle+width/2, yPos2.Y); } else { ctx.MoveTo (xPos1.X, yPos1.Y); ctx.LineTo (xPos2.X, yPos2.Y); } ctx.Stroke (); } if (!this.hideVerticalSegments_) { ctx.MoveTo (xPos2.X, yPos2.Y); ctx.LineTo (xPos3.X, yPos3.Y); ctx.Stroke (); } ctx.Restore (); } }
/// <summary> /// Returns a y-axis that is suitable for drawing this plot. /// </summary> /// <returns>A suitable y-axis.</returns> public Axis SuggestYAxis() { SequenceAdapter data_ = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); return data_.SuggestYAxis(); }
/// <summary> /// Draws the plot on a GDI+ surface against the provided x and y axes. /// </summary> /// <param name="g">The GDI+ surface on which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public override void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { SequenceAdapter data = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData ); TextDataAdapter textData = new TextDataAdapter( this.DataSource, this.DataMember, this.TextData ); // first plot the marker // we can do this cast, since the constructor accepts only this type! for (int i=0; i<data.Count; ++i) { try { PointD pt = data[i]; if ( !Double.IsNaN(pt.X) && !Double.IsNaN(pt.Y) ) { PointF xPos = xAxis.WorldToPhysical( pt.X, false); PointF yPos = yAxis.WorldToPhysical( pt.Y, false); Marker.Draw( g, (int)xPos.X, (int)yPos.Y ); if ( textData[i] != "" ) { SizeF size = g.MeasureString( textData[i], this.Font ); switch (labelTextPosition_) { case LabelPositions.Above: g.DrawString( textData[i], font_, Brushes.Black, new PointF(xPos.X-size.Width/2,yPos.Y-size.Height-Marker.Size*2/3)); break; case LabelPositions.Below: g.DrawString( textData[i], font_, Brushes.Black, new PointF(xPos.X-size.Width/2,yPos.Y+Marker.Size*2/3)); break; case LabelPositions.Left: g.DrawString( textData[i], font_, Brushes.Black, new PointF(xPos.X-size.Width-Marker.Size*2/3,yPos.Y-size.Height/2)); break; case LabelPositions.Right: g.DrawString( textData[i], font_, Brushes.Black, new PointF(xPos.X+Marker.Size*2/3,yPos.Y-size.Height/2)); break; } } } } catch { throw new NPlotException("Error in TextPlot.Draw"); } } }
/// <summary> /// Returns a y-axis that is suitable for drawing this plot. /// </summary> /// <returns>A suitable y-axis.</returns> public Axis SuggestYAxis() { SequenceAdapter dataBottom_ = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateDataBottom, this.AbscissaData ); SequenceAdapter dataTop_ = new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateDataTop, this.AbscissaData ); Axis axis = dataTop_.SuggestYAxis(); axis.LUB(dataBottom_.SuggestYAxis()); return axis; }
/// <summary> /// Draws the line plot using the Drawing Context and x and y axes provided /// </summary> /// <param name="ctx">The Drawing Context with which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public void Draw(Context ctx, PhysicalAxis xAxis, PhysicalAxis yAxis) { SequenceAdapter dataTop = new SequenceAdapter (DataSource, DataMember, OrdinateDataTop, AbscissaData); SequenceAdapter dataBottom = new SequenceAdapter (DataSource, DataMember, OrdinateDataBottom, AbscissaData); ITransform2D t = Transform2D.GetTransformer (xAxis, yAxis); ctx.Save (); for (int i=0; i<dataTop.Count; ++i) { Point physicalBottom = t.Transform (dataBottom[i]); Point physicalTop = t.Transform (dataTop[i]); if (physicalBottom != physicalTop) { Rectangle r = new Rectangle (physicalBottom.X - BarWidth/2, physicalTop.Y,BarWidth, (physicalBottom.Y - physicalTop.Y) ); ctx.SetColor (fillColor); ctx.Rectangle (r); ctx.FillPreserve (); ctx.SetColor (borderColor); ctx.Stroke (); } } ctx.Restore (); }