public WorldToPhysical ( double coord, bool clip ) : PointF | ||
coord | double | the world coordinate |
clip | bool | if true, the physical position returned will be clipped to the physical max / min position as appropriate if the world value is outside the limits of the axis. |
return | PointF |
/// <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(IGraphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { SequenceAdapter data_ = new SequenceAdapter(DataSource, DataMember, OrdinateData, 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 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 p1 = data_[i]; if (!Double.IsNaN(p1.X) && !Double.IsNaN(p1.Y)) { PointF xPos = xAxis.WorldToPhysical(p1.X, false); if (float.IsNaN(xPos.X) || xPos.X < leftCutoff_ || rightCutoff_ < xPos.X) { continue; } PointF yPos = yAxis.WorldToPhysical(p1.Y, false); if (float.IsNaN(yPos.Y)) { continue; } marker_.Draw(g, (int)xPos.X, (int)yPos.Y); if (marker_.DropLine) { PointD yMin = new PointD(p1.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> /// Calculates the physical (not world) separation between abscissa values. /// </summary> /// <param name="cd">Candle adapter containing data</param> /// <param name="xAxis">Physical x axis the data is plotted against.</param> /// <returns>physical separation between abscissa values.</returns> private static float CalculatePhysicalSeparation(CandleDataAdapter cd, PhysicalAxis xAxis) { if (cd.Count > 1) { int xPos1 = (int)(xAxis.WorldToPhysical(((PointOLHC)cd[0]).X, false)).X; int xPos2 = (int)(xAxis.WorldToPhysical(((PointOLHC)cd[1]).X, false)).X; int minDist = xPos2 - xPos1; if (cd.Count > 2) { // to be pretty sure we get the smallest gap. int xPos3 = (int)(xAxis.WorldToPhysical(((PointOLHC)cd[2]).X, false)).X; if (xPos3 - xPos2 < minDist) { minDist = xPos3 - xPos2; } if (cd.Count > 3) { int xPos4 = (int)(xAxis.WorldToPhysical(((PointOLHC)cd[3]).X, false)).X; if (xPos4 - xPos3 < minDist) { minDist = xPos4 - xPos3; } } } return(minDist); } return(0.0f); }
/// <summary> /// Draws the marker on a plot surface. /// </summary> /// <param name="g">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(IGraphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { PointF point = new PointF( xAxis.WorldToPhysical(x_, true).X, yAxis.WorldToPhysical(y_, true).Y); marker_.Draw(g, (int)point.X, (int)point.Y); }
/// <summary> /// Draws the text on a plot surface. /// </summary> /// <param name="g">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) { Point startPoint = new Point( (int)xAxis.WorldToPhysical(start_.X, true).X, (int)yAxis.WorldToPhysical(start_.Y, true).Y); g.DrawString(text_, font_, textBrush_, (int)startPoint.X, (int)startPoint.Y); }
/// <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> /// Draws the vertical 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(System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { int yMin = yAxis.PhysicalMin.Y; int yMax = yAxis.PhysicalMax.Y; yMin -= pixelIndent_; yMax += pixelIndent_; float length = Math.Abs(yMax - yMin); float lengthDiff = length - length * scale_; float indentAmount = lengthDiff / 2; yMin -= (int)indentAmount; yMax += (int)indentAmount; int xPos = (int)xAxis.WorldToPhysical(value_, false).X; g.DrawLine(pen_, new System.Drawing.Point(xPos, yMin), new System.Drawing.Point(xPos, yMax)); // todo: clip and proper logic for flipped axis min max. }
/// <summary> /// Draws the horizontal 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) { int xMin = xAxis.PhysicalMin.X; int xMax = xAxis.PhysicalMax.X; xMin += pixelIndent_; xMax -= pixelIndent_; float length = Math.Abs(xMax - xMin); float lengthDiff = length - length * scale_; float indentAmount = lengthDiff / 2; xMin += (int)indentAmount; xMax -= (int)indentAmount; int yPos = (int)yAxis.WorldToPhysical(value_, false).Y; g.DrawLine(pen_, new Point(xMin, yPos), new Point(xMax, yPos)); // todo: clip and proper logic for flipped axis min max. }
/// <summary> /// Draws the vertical 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(IGraphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { int yMin = yAxis.PhysicalMin.Y; int yMax = yAxis.PhysicalMax.Y; yMin -= PixelIndent; yMax += PixelIndent; float length = Math.Abs(yMax - yMin); float lengthDiff = length - length * LengthScale; float indentAmount = lengthDiff / 2; yMin -= (int)indentAmount; yMax += (int)indentAmount; int xPos = (int)xAxis.WorldToPhysical(AbscissaValue, false).X; g.DrawLine(Pen, new Point(xPos, yMin), new Point(xPos, yMax)); // todo: clip and proper logic for flipped axis min max. }
/// <summary> /// Does all the work in drawing grid lines. /// </summary> /// <param name="g">The graphics surface on which to render.</param> /// <param name="axis">TODO</param> /// <param name="orthogonalAxis">TODO</param> /// <param name="a">the list of world values to draw grid lines at.</param> /// <param name="horizontal">true if want horizontal lines, false otherwise.</param> /// <param name="p">the pen to use to draw the grid lines.</param> private void DrawGridLines( Graphics g, PhysicalAxis axis, PhysicalAxis orthogonalAxis, System.Collections.ArrayList a, bool horizontal, Pen p) { for (int i = 0; i < a.Count; ++i) { PointF p1 = axis.WorldToPhysical((double)a[i], true); PointF p2 = p1; PointF p3 = orthogonalAxis.PhysicalMax; PointF p4 = orthogonalAxis.PhysicalMin; if (horizontal) { p1.Y = p4.Y; p2.Y = p3.Y; } else { p1.X = p4.X; p2.X = p3.X; } // note: casting all drawing was necessary for sane display. why? g.DrawLine(p, (int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y); } }
/// <summary> /// Draws the arrow on a plot surface. /// </summary> /// <param name="g">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( System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { if (this.To.X > xAxis.Axis.WorldMax || this.To.X < xAxis.Axis.WorldMin) return; if (this.To.Y > yAxis.Axis.WorldMax || this.To.Y < yAxis.Axis.WorldMin) return; double angle = this.angle_; if (this.angle_ < 0.0) { int mul = -(int)(this.angle_ / 360.0) + 2; angle = angle_ + 360.0 * (double)mul; } double normAngle = (double)angle % 360.0; // angle in range 0 -> 360. Point toPoint = new Point( (int)xAxis.WorldToPhysical( to_.X, true ).X, (int)yAxis.WorldToPhysical( to_.Y, true ).Y ); float xDir = (float)Math.Cos( normAngle * 2.0 * Math.PI / 360.0 ); float yDir = (float)Math.Sin( normAngle * 2.0 * Math.PI / 360.0 ); toPoint.X += (int)(xDir*headOffset_); toPoint.Y += (int)(yDir*headOffset_); float xOff = physicalLength_ * xDir; float yOff = physicalLength_ * yDir; Point fromPoint = new Point( (int)(toPoint.X + xOff), (int)(toPoint.Y + yOff) ); g.DrawLine( pen_, toPoint, fromPoint ); Point[] head = new Point[3]; head[0] = toPoint; xOff = headSize_ * (float)Math.Cos( (normAngle-headAngle_/2.0f) * 2.0 * Math.PI / 360.0 ); yOff = headSize_ * (float)Math.Sin( (normAngle-headAngle_/2.0f) * 2.0 * Math.PI / 360.0 ); head[1] = new Point( (int)(toPoint.X + xOff), (int)(toPoint.Y + yOff) ); float xOff2 = headSize_ * (float)Math.Cos( (normAngle+headAngle_/2.0f) * 2.0 * Math.PI / 360.0 ); float yOff2 = headSize_ * (float)Math.Sin( (normAngle+headAngle_/2.0f) * 2.0 * Math.PI / 360.0 ); head[2] = new Point( (int)(toPoint.X + xOff2), (int)(toPoint.Y + yOff2) ); g.FillPolygon( arrowBrush_, head ); SizeF textSize = g.MeasureString( text_, font_ ); SizeF halfSize = new SizeF( textSize.Width / 2.0f, textSize.Height / 2.0f ); float quadrantSlideLength = halfSize.Width + halfSize.Height; float quadrantF = (float)normAngle / 90.0f; // integer part gives quadrant. int quadrant = (int)quadrantF; // quadrant in. float prop = quadrantF - (float)quadrant; // proportion of way through this qadrant. float dist = prop * quadrantSlideLength; // distance along quarter of bounds rectangle. // now find the offset from the middle of the text box that the // rear end of the arrow should end at (reverse this to get position // of text box with respect to rear end of arrow). // // There is almost certainly an elgant way of doing this involving // trig functions to get all the signs right, but I'm about ready to // drop off to sleep at the moment, so this blatent method will have // to do. PointF offsetFromMiddle = new PointF( 0.0f, 0.0f ); switch (quadrant) { case 0: if (dist > halfSize.Height) { dist -= halfSize.Height; offsetFromMiddle = new PointF( -halfSize.Width + dist, halfSize.Height ); } else { offsetFromMiddle = new PointF( -halfSize.Width, - dist ); } break; case 1: if (dist > halfSize.Width) { dist -= halfSize.Width; offsetFromMiddle = new PointF( halfSize.Width, halfSize.Height - dist ); } else { offsetFromMiddle = new PointF( dist, halfSize.Height ); } break; case 2: if (dist > halfSize.Height) { dist -= halfSize.Height; offsetFromMiddle = new PointF( halfSize.Width - dist, -halfSize.Height ); } else { offsetFromMiddle = new PointF( halfSize.Width, -dist ); } break; case 3: if (dist > halfSize.Width) { dist -= halfSize.Width; offsetFromMiddle = new PointF( -halfSize.Width, -halfSize.Height + dist ); } else { offsetFromMiddle = new PointF( -dist, -halfSize.Height ); } break; default: throw new NPlotException( "Programmer error." ); } g.DrawString( text_, font_, textBrush_, (int)(fromPoint.X - halfSize.Width - offsetFromMiddle.X), (int)(fromPoint.Y - halfSize.Height + offsetFromMiddle.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) { 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 candle plot with the specified Drawing Context and X,Y axes /// </summary> /// <param name="ctx">The Drawing Context with which to draw</param> /// <param name="xAxis">The physical X-Axis to draw against</param> /// <param name="yAxis">The physical Y-Axis to draw against</param> public void Draw(Context ctx, PhysicalAxis xAxis, PhysicalAxis yAxis) { CandleDataAdapter cd = new CandleDataAdapter (DataSource, DataMember, AbscissaData, OpenData, LowData, HighData, CloseData); double offset = 0; if (Centered) { offset = CalculatePhysicalSeparation (cd,xAxis)/2; } double addAmount = StickWidth/2; double stickWidth = StickWidth; if (StickWidth == AutoScaleStickWidth) { // default addAmount = 2; stickWidth = 4; double minDist = CalculatePhysicalSeparation (cd, xAxis); addAmount = minDist / 3; stickWidth = addAmount * 2; } ctx.Save (); ctx.SetLineWidth (1); /* // brant hyatt proposed. if (Style == Styles.Stick) { p.Width = stickWidth; addAmount = stickWidth + 2; } */ for (int i=0; i<cd.Count; ++i) { PointOLHC point = (PointOLHC)cd [i]; if ((!double.IsNaN (point.Open)) && (!double.IsNaN(point.High)) && (!double.IsNaN (point.Low)) && (!double.IsNaN(point.Close))) { double xPos = (xAxis.WorldToPhysical (point.X, false)).X; if (xPos + offset + addAmount < xAxis.PhysicalMin.X || xAxis.PhysicalMax.X < xPos + offset - addAmount) { continue; } double yLo = (yAxis.WorldToPhysical (point.Low, false)).Y; double yHi = (yAxis.WorldToPhysical (point.High, false)).Y; double yOpn = (yAxis.WorldToPhysical (point.Open, false)).Y; double yCls = (yAxis.WorldToPhysical (point.Close,false)).Y; if (Style == Styles.Stick) { /* // brant hyatt proposed. if (i > 0) { if ( ((PointOLHC)cd[i]).Close > ((PointOLHC)cd[i-1]).Close) { p.Color = BullishColor; } else { p.Color = BearishColor; } } */ ctx.SetColor (Color); ctx.MoveTo (xPos+offset, yLo); ctx.LineTo (xPos+offset, yHi); // Low to High line ctx.MoveTo (xPos-addAmount+offset, yOpn); ctx.LineTo (xPos+offset, yOpn); // Open line ctx.MoveTo (xPos+addAmount+offset, yCls); ctx.LineTo (xPos+offset, yCls); // Close line ctx.Stroke (); } else if (Style == Styles.Filled) { ctx.MoveTo (xPos+offset, yLo); ctx.LineTo (xPos+offset, yHi); ctx.Stroke (); if (yOpn > yCls) { ctx.SetColor (BullishColor); ctx.Rectangle (xPos-addAmount+offset, yCls, stickWidth, yOpn - yCls); ctx.FillPreserve (); ctx.SetColor (Color); ctx.Stroke (); } else if (yOpn < yCls) { ctx.SetColor (BearishColor); ctx.Rectangle (xPos-addAmount+offset, yOpn, stickWidth, yCls - yOpn); ctx.FillPreserve (); ctx.SetColor (Color); ctx.Stroke (); } else { // Cls == Opn ctx.MoveTo (xPos-addAmount+offset, yOpn); ctx.LineTo (xPos-addAmount+stickWidth+offset, yCls); ctx.Stroke (); } } } } ctx.Restore (); }
/// <summary> /// Transforms the given world point to physical coordinates /// </summary> /// <param name="x">x coordinate of world point to transform.</param> /// <param name="y">y coordinate of world point to transform.</param> /// <returns>the corresponding physical point.</returns> public PointF Transform(double x, double y) { return(new PointF( xAxis_.WorldToPhysical(x, false).X, yAxis_.WorldToPhysical(y, false).Y)); }
/// <summary> /// Draws the candle plot on a GDI+ surface agains 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) { CandleDataAdapter cd = new CandleDataAdapter(this.DataSource, this.DataMember, this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData); Brush bearishBrush = new SolidBrush(BearishColor); Brush bullishBrush = new SolidBrush(BullishColor); uint offset = 0; if (this.centered_) { offset = (uint)(CalculatePhysicalSeparation(cd, xAxis) / 2.0f); } uint addAmount = (uint)StickWidth / 2; uint stickWidth = (uint)StickWidth; if (StickWidth == AutoScaleStickWidth) { // default addAmount = 2; stickWidth = 4; float minDist = CalculatePhysicalSeparation(cd, xAxis); addAmount = (uint)(minDist / 3); stickWidth = addAmount * 2; } Pen p = new Pen(this.color_); /* * // brant hyatt proposed. * if (this.Style == Styles.Stick) * { * p.Width = stickWidth; * addAmount = stickWidth + 2; * } */ for (int i = 0; i < cd.Count; ++i) { PointOLHC point = (PointOLHC)cd[i]; if ((!double.IsNaN(point.Open)) && (!double.IsNaN(point.High)) && (!double.IsNaN(point.Low)) && (!double.IsNaN(point.Close))) { int xPos = (int)(xAxis.WorldToPhysical(point.X, false)).X; if (xPos + offset + addAmount < xAxis.PhysicalMin.X || xAxis.PhysicalMax.X < xPos + offset - addAmount) { continue; } int yPos1 = (int)(yAxis.WorldToPhysical(point.Low, false)).Y; int yPos2 = (int)(yAxis.WorldToPhysical(point.High, false)).Y; int yPos3 = (int)(yAxis.WorldToPhysical(point.Open, false)).Y; int yPos4 = (int)(yAxis.WorldToPhysical(point.Close, false)).Y; if (this.Style == Styles.Stick) { /* * // brant hyatt proposed. * if (i > 0) * { * if ( ((PointOLHC)cd[i]).Close > ((PointOLHC)cd[i-1]).Close) * { * p.Color = BullishColor; * } * else * { * p.Color = BearishColor; * } * } */ g.DrawLine(p, xPos + offset, yPos1, xPos + offset, yPos2); g.DrawLine(p, xPos - addAmount + offset, yPos3, xPos + offset, yPos3); g.DrawLine(p, xPos + offset, yPos4, xPos + addAmount + offset, yPos4); } else if (this.Style == Styles.Filled) { g.DrawLine(p, xPos + offset, yPos1, xPos + offset, yPos2); if (yPos3 > yPos4) { g.FillRectangle(bullishBrush, xPos - addAmount + offset, yPos4, stickWidth, yPos3 - yPos4); g.DrawRectangle(p, xPos - addAmount + offset, yPos4, stickWidth, yPos3 - yPos4); } else if (yPos3 < yPos4) { g.FillRectangle(bearishBrush, xPos - addAmount + offset, yPos3, stickWidth, yPos4 - yPos3); g.DrawRectangle(p, xPos - addAmount + offset, yPos3, stickWidth, yPos4 - yPos3); } else { g.DrawLine(p, xPos - addAmount + offset, yPos3, xPos - addAmount + stickWidth + offset, yPos3); } } } } }
/// <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 arrow on a plot surface. /// </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 ) { if (To.X > xAxis.Axis.WorldMax || To.X < xAxis.Axis.WorldMin) { return; } if (To.Y > yAxis.Axis.WorldMax || To.Y < yAxis.Axis.WorldMin) { return; } ctx.Save (); TextLayout layout = new TextLayout (); layout.Font = textFont_; layout.Text = text_; double angle = angle_; if (angle_ < 0.0) { int mul = -(int)(angle_ / 360.0) + 2; angle = angle_ + 360.0 * (double)mul; } double normAngle = (double)angle % 360.0; // angle in range 0 -> 360. Point toPoint = new Point ( xAxis.WorldToPhysical (to_.X, true).X, yAxis.WorldToPhysical (to_.Y, true).Y); double xDir = Math.Cos (normAngle * 2.0 * Math.PI / 360.0); double yDir = Math.Sin (normAngle * 2.0 * Math.PI / 360.0); toPoint.X += xDir*headOffset_; toPoint.Y += yDir*headOffset_; double xOff = physicalLength_ * xDir; double yOff = physicalLength_ * yDir; Point fromPoint = new Point( (int)(toPoint.X + xOff), (int)(toPoint.Y + yOff) ); ctx.SetLineWidth (1); ctx.SetColor (arrowColor_); ctx.MoveTo (fromPoint); ctx.LineTo (toPoint); ctx.Stroke (); xOff = headSize_ * Math.Cos ((normAngle-headAngle_/2) * 2.0 * Math.PI / 360.0); yOff = headSize_ * Math.Sin ((normAngle-headAngle_/2) * 2.0 * Math.PI / 360.0); ctx.LineTo (toPoint.X + xOff, toPoint.Y + yOff); double xOff2 = headSize_ * Math.Cos ((normAngle+headAngle_/2) * 2.0 * Math.PI / 360.0); double yOff2 = headSize_ * Math.Sin ((normAngle+headAngle_/2) * 2.0 * Math.PI / 360.0); ctx.LineTo (toPoint.X + xOff2, toPoint.Y + yOff2); ctx.LineTo (toPoint); ctx.ClosePath (); ctx.SetColor (arrowColor_); ctx.Fill (); Size textSize = layout.GetSize (); Size halfSize = new Size (textSize.Width/2, textSize.Height/2); double quadrantSlideLength = halfSize.Width + halfSize.Height; double quadrantD = normAngle / 90.0; // integer part gives quadrant. int quadrant = (int)quadrantD; // quadrant in. double prop = quadrantD - (double)quadrant; // proportion of way through this qadrant. double dist = prop * quadrantSlideLength; // distance along quarter of bounds rectangle. // now find the offset from the middle of the text box that the // rear end of the arrow should end at (reverse this to get position // of text box with respect to rear end of arrow). // // There is almost certainly an elgant way of doing this involving // trig functions to get all the signs right, but I'm about ready to // drop off to sleep at the moment, so this blatent method will have // to do. Point offsetFromMiddle = new Point (0, 0); switch (quadrant) { case 0: if (dist > halfSize.Height) { dist -= halfSize.Height; offsetFromMiddle = new Point ( -halfSize.Width + dist, halfSize.Height ); } else { offsetFromMiddle = new Point ( -halfSize.Width, - dist ); } break; case 1: if (dist > halfSize.Width) { dist -= halfSize.Width; offsetFromMiddle = new Point ( halfSize.Width, halfSize.Height - dist ); } else { offsetFromMiddle = new Point ( dist, halfSize.Height ); } break; case 2: if (dist > halfSize.Height) { dist -= halfSize.Height; offsetFromMiddle = new Point ( halfSize.Width - dist, -halfSize.Height ); } else { offsetFromMiddle = new Point ( halfSize.Width, -dist ); } break; case 3: if (dist > halfSize.Width) { dist -= halfSize.Width; offsetFromMiddle = new Point ( -halfSize.Width, -halfSize.Height + dist ); } else { offsetFromMiddle = new Point ( -dist, -halfSize.Height ); } break; default: throw new NPlotException( "Programmer error." ); } ctx.SetColor (textColor_); double x = fromPoint.X - halfSize.Width - offsetFromMiddle.X; double y = fromPoint.Y - halfSize.Height + offsetFromMiddle.Y; ctx.DrawTextLayout (layout, x, y); ctx.Restore (); }
/// <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> /// Draws the vertical line using the Context and the x and y axes specified /// </summary> /// <param name="ctx">The 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 yMin = yAxis.PhysicalMin.Y; double yMax = yAxis.PhysicalMax.Y; yMin -= PixelIndent; yMax += PixelIndent; double length = Math.Abs (yMax - yMin); double lengthDiff = length - length*LengthScale; double indentAmount = lengthDiff/2; yMin -= indentAmount; yMax += indentAmount; double xPos = xAxis.WorldToPhysical (AbscissaValue, false).X; ctx.Save (); ctx.SetLineWidth (1); ctx.SetColor (Color); ctx.MoveTo (xPos, yMin); ctx.LineTo (xPos, yMax); ctx.Stroke (); ctx.Restore (); // todo: clip and proper logic for flipped axis min max. }
/// <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 marker on a plot surface. /// </summary> /// <param name="g">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( System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { PointF point = new PointF( xAxis.WorldToPhysical( x_, true ).X, yAxis.WorldToPhysical( y_, true ).Y ); marker_.Draw( g, (int)point.X, (int)point.Y ); }
/// <summary> /// Does all the work in drawing grid lines. /// </summary> /// <param name="ctx">The graphics context with which to draw</param> /// <param name="axis">TODO</param> /// <param name="orthogonalAxis">TODO</param> /// <param name="a">the list of world values to draw grid lines at.</param> /// <param name="horizontal">true if want horizontal lines, false otherwise.</param> /// <param name="color">the color to draw the grid lines.</param> private void DrawGridLines(Context ctx, PhysicalAxis axis, PhysicalAxis orthogonalAxis, System.Collections.ArrayList a, bool horizontal) { for (int i=0; i<a.Count; ++i) { Point p1 = axis.WorldToPhysical ((double)a[i], true); Point p2 = p1; Point p3 = orthogonalAxis.PhysicalMax; Point p4 = orthogonalAxis.PhysicalMin; if (horizontal) { p1.Y = p4.Y; p2.Y = p3.Y; } else { p1.X = p4.X; p2.X = p3.X; } ctx.MoveTo (p1); ctx.LineTo (p2); // note: casting all drawing was necessary for sane display. why? //g.DrawLine( p, (int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y ); } ctx.SetLineWidth (1); ctx.SetColor (gridColor); ctx.SetLineDash (0, gridDash); ctx.Stroke (); }
/// <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> /// 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> /// 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> /// 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 horizontal line plot using the Context and the x and y axes specified /// </summary> /// <param name="ctx">The 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 xMin = xAxis.PhysicalMin.X; double xMax = xAxis.PhysicalMax.X; xMin += pixelIndent_; xMax -= pixelIndent_; double length = Math.Abs (xMax - xMin); double lengthDiff = length - length*scale_; double indentAmount = lengthDiff/2; xMin += indentAmount; xMax -= indentAmount; double yPos = yAxis.WorldToPhysical (value_, false).Y; ctx.Save (); ctx.SetLineWidth (1); ctx.SetColor (color_); ctx.MoveTo (xMin, yPos); ctx.LineTo (xMax, yPos); ctx.Stroke (); ctx.Restore (); // todo: clip and proper logic for flipped axis min max. }
/// <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> /// Draws the horizontal 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(System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { int xMin = xAxis.PhysicalMin.X; int xMax = xAxis.PhysicalMax.X; xMin += pixelIndent_; xMax -= pixelIndent_; float length = Math.Abs(xMax - xMin); float lengthDiff = length - length*scale_; float indentAmount = lengthDiff/2; xMin += (int)indentAmount; xMax -= (int)indentAmount; int yPos = (int)yAxis.WorldToPhysical( value_, false ).Y; g.DrawLine( pen_, new System.Drawing.Point( xMin, yPos ), new System.Drawing.Point( xMax, yPos ) ); // todo: clip and proper logic for flipped axis min max. }
/// <summary> /// Draws the candle plot on a GDI+ surface agains 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 ) { CandleDataAdapter cd = new CandleDataAdapter( this.DataSource, this.DataMember, this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData ); Brush bearishBrush = new SolidBrush( BearishColor ); Brush bullishBrush = new SolidBrush( BullishColor ); uint offset = 0; if (this.centered_) { offset = (uint)(CalculatePhysicalSeparation(cd,xAxis) / 2.0f); } uint addAmount = (uint)StickWidth/2; uint stickWidth = (uint)StickWidth; if (StickWidth == AutoScaleStickWidth) { // default addAmount = 2; stickWidth = 4; float minDist = CalculatePhysicalSeparation( cd, xAxis ); addAmount = (uint)(minDist / 3); stickWidth = addAmount * 2; } Pen p = new Pen(this.color_); /* // brant hyatt proposed. if (this.Style == Styles.Stick) { p.Width = stickWidth; addAmount = stickWidth + 2; } */ for (int i=0; i<cd.Count; ++i) { PointOLHC point = (PointOLHC)cd[i]; if ( (!double.IsNaN (point.Open)) && (!double.IsNaN(point.High)) && (!double.IsNaN (point.Low)) && (!double.IsNaN(point.Close)) ) { int xPos = (int)(xAxis.WorldToPhysical( point.X, false )).X; if (xPos + offset + addAmount < xAxis.PhysicalMin.X || xAxis.PhysicalMax.X < xPos + offset - addAmount) { continue; } int yPos1 = (int)(yAxis.WorldToPhysical( point.Low, false )).Y; int yPos2 = (int)(yAxis.WorldToPhysical( point.High, false )).Y; int yPos3 = (int)(yAxis.WorldToPhysical( point.Open, false )).Y; int yPos4 = (int)(yAxis.WorldToPhysical( point.Close, false )).Y; if (this.Style == Styles.Stick) { /* // brant hyatt proposed. if (i > 0) { if ( ((PointOLHC)cd[i]).Close > ((PointOLHC)cd[i-1]).Close) { p.Color = BullishColor; } else { p.Color = BearishColor; } } */ g.DrawLine( p, xPos+offset, yPos1, xPos+offset, yPos2 ); g.DrawLine( p, xPos-addAmount+offset, yPos3, xPos+offset, yPos3 ); g.DrawLine( p, xPos+offset, yPos4, xPos+addAmount+offset, yPos4 ); } else if (this.Style == Styles.Filled) { g.DrawLine( p, xPos+offset, yPos1, xPos+offset, yPos2 ); if (yPos3 > yPos4) { g.FillRectangle( bullishBrush, xPos-addAmount+offset, yPos4, stickWidth, yPos3 - yPos4 ); g.DrawRectangle( p, xPos-addAmount+offset, yPos4, stickWidth, yPos3 - yPos4 ); } else if (yPos3 < yPos4) { g.FillRectangle( bearishBrush, xPos-addAmount+offset, yPos3, stickWidth, yPos4 - yPos3 ); g.DrawRectangle( p, xPos-addAmount+offset, yPos3, stickWidth, yPos4 - yPos3 ); } else { g.DrawLine( p, xPos-addAmount+offset, yPos3, xPos-addAmount+stickWidth+offset, yPos3 ); } } } } }
/// <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 (); }
public void Draw(System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { int physicalX = (int)xAxis.WorldToPhysical(x, false).X; g.DrawLine(pen, new Point(physicalX, yAxis.PhysicalMin.Y), new Point(physicalX, yAxis.PhysicalMax.Y)); }
/// <summary> /// Draws the vertical 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(System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { int yMin = yAxis.PhysicalMin.Y; int yMax = yAxis.PhysicalMax.Y; yMin -= pixelIndent_; yMax += pixelIndent_; float length = Math.Abs(yMax - yMin); float lengthDiff = length - length*scale_; float indentAmount = lengthDiff/2; yMin -= (int)indentAmount; yMax += (int)indentAmount; int xPos = (int)xAxis.WorldToPhysical( value_, false ).X; g.DrawLine( pen_, new System.Drawing.Point( xPos, yMin ), new System.Drawing.Point( xPos, yMax ) ); // todo: clip and proper logic for flipped axis min max. }
/// <summary> /// Calculates the physical (not world) separation between abscissa values. /// </summary> /// <param name="cd">Candle adapter containing data</param> /// <param name="xAxis">Physical x axis the data is plotted against.</param> /// <returns>physical separation between abscissa values.</returns> private static float CalculatePhysicalSeparation( CandleDataAdapter cd, PhysicalAxis xAxis ) { if (cd.Count > 1) { int xPos1 = (int)(xAxis.WorldToPhysical( ((PointOLHC)cd[0]).X, false )).X; int xPos2 = (int)(xAxis.WorldToPhysical( ((PointOLHC)cd[1]).X, false )).X; int minDist = xPos2 - xPos1; if (cd.Count > 2) { // to be pretty sure we get the smallest gap. int xPos3 = (int)(xAxis.WorldToPhysical(((PointOLHC)cd[2]).X, false)).X; if (xPos3 - xPos2 < minDist) { minDist = xPos3 - xPos2; } if (cd.Count > 3) { int xPos4 = (int)(xAxis.WorldToPhysical(((PointOLHC)cd[3]).X, false)).X; if (xPos4 - xPos3 < minDist) { minDist = xPos4 - xPos3; } } } return minDist; } return 0.0f; }
/// <summary> /// Draws the marker on a plot surface. /// </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) { Point point = new Point ( xAxis.WorldToPhysical (x_, true).X, yAxis.WorldToPhysical (y_, true ).Y); marker_.Draw (ctx, point.X, point.Y ); }
/// <summary> /// Does all the work in drawing grid lines. /// </summary> /// <param name="g">The graphics surface on which to render.</param> /// <param name="axis">TODO</param> /// <param name="orthogonalAxis">TODO</param> /// <param name="a">the list of world values to draw grid lines at.</param> /// <param name="horizontal">true if want horizontal lines, false otherwise.</param> /// <param name="p">the pen to use to draw the grid lines.</param> private void DrawGridLines( Graphics g, PhysicalAxis axis, PhysicalAxis orthogonalAxis, List<double> a, bool horizontal, Pen p) { for (int i = 0; i < a.Count; ++i) { PointF p1 = axis.WorldToPhysical((double)a[i], true); PointF p2 = p1; PointF p3 = orthogonalAxis.PhysicalMax; PointF p4 = orthogonalAxis.PhysicalMin; if (horizontal) { p1.Y = p4.Y; p2.Y = p3.Y; } else { p1.X = p4.X; p2.X = p3.X; } // note: casting all drawing was necessary for sane display. why? g.DrawLine(p, (int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y); } }
/// <summary> /// Draws the text on a plot surface. /// </summary> /// <param name="g">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( System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { Point startPoint = new Point( (int)xAxis.WorldToPhysical( start_.X, true ).X, (int)yAxis.WorldToPhysical( start_.Y, true ).Y ); g.DrawString(text_, font_, textBrush_,(int)startPoint.X,(int)startPoint.Y); }
/// <summary> /// Draw on to the supplied graphics surface against the supplied axes. /// </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> /// <remarks>TODO: block positions may be off by a pixel or so. maybe. Re-think calculations</remarks> public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { if ( data_==null || data_.GetLength(0) == 0 || data_.GetLength(1) == 0 ) { return; } double worldWidth = xAxis.Axis.WorldMax - xAxis.Axis.WorldMin; double numBlocksHorizontal = worldWidth / this.xStep_; double worldHeight = yAxis.Axis.WorldMax - yAxis.Axis.WorldMin; double numBlocksVertical = worldHeight / this.yStep_; double physicalWidth = xAxis.PhysicalMax.X - xAxis.PhysicalMin.X; double blockWidth = physicalWidth / numBlocksHorizontal; bool wPositive = true; if (blockWidth < 0.0) { wPositive = false; } blockWidth = Math.Abs(blockWidth)+1; double physicalHeight = yAxis.PhysicalMax.Y - yAxis.PhysicalMin.Y; double blockHeight = physicalHeight / numBlocksVertical; bool hPositive = true; if (blockHeight < 0.0) { hPositive = false; } blockHeight = Math.Abs(blockHeight)+1; for (int i=0; i<data_.GetLength(0); ++i) { for (int j=0; j<data_.GetLength(1); ++j) { double wX = (double)j*this.xStep_ + xStart_; double wY = (double)i*this.yStep_ + yStart_; if ( !hPositive ) { wY += yStep_; } if (!wPositive ) { wX += xStep_; } if (this.center_) { wX -= this.xStep_/2.0; wY -= this.yStep_/2.0; } Pen p = new Pen( this.Gradient.GetColor( (data_[i,j]-this.dataMin_)/(this.dataMax_-this.dataMin_) ) ); int x = (int)xAxis.WorldToPhysical(wX,false).X; int y = (int)yAxis.WorldToPhysical(wY,false).Y; g.FillRectangle( p.Brush, x, y, (int)blockWidth, (int)blockHeight); //g.DrawRectangle(Pens.White,x,y,(int)blockWidth,(int)blockHeight); } } }
/// <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(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> /// Draw on to the supplied graphics surface against the supplied axes. /// </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> /// <remarks>TODO: block positions may be off by a pixel or so. maybe. Re-think calculations</remarks> public void Draw(Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { if (data_ == null || data_.GetLength(0) == 0 || data_.GetLength(1) == 0) { return; } double worldWidth = xAxis.Axis.WorldMax - xAxis.Axis.WorldMin; double numBlocksHorizontal = worldWidth / this.xStep_; double worldHeight = yAxis.Axis.WorldMax - yAxis.Axis.WorldMin; double numBlocksVertical = worldHeight / this.yStep_; double physicalWidth = xAxis.PhysicalMax.X - xAxis.PhysicalMin.X; double blockWidth = physicalWidth / numBlocksHorizontal; bool wPositive = true; if (blockWidth < 0.0) { wPositive = false; } blockWidth = Math.Abs(blockWidth) + 1; double physicalHeight = yAxis.PhysicalMax.Y - yAxis.PhysicalMin.Y; double blockHeight = physicalHeight / numBlocksVertical; bool hPositive = true; if (blockHeight < 0.0) { hPositive = false; } blockHeight = Math.Abs(blockHeight) + 1; for (int i = 0; i < data_.GetLength(0); ++i) { for (int j = 0; j < data_.GetLength(1); ++j) { double wX = (double)j * this.xStep_ + xStart_; double wY = (double)i * this.yStep_ + yStart_; if (!hPositive) { wY += yStep_; } if (!wPositive) { wX += xStep_; } if (this.center_) { wX -= this.xStep_ / 2.0; wY -= this.yStep_ / 2.0; } Pen p = new Pen(this.Gradient.GetColor((data_[i, j] - this.dataMin_) / (this.dataMax_ - this.dataMin_))); int x = (int)xAxis.WorldToPhysical(wX, false).X; int y = (int)yAxis.WorldToPhysical(wY, false).Y; g.FillRectangle(p.Brush, x, y, (int)blockWidth, (int)blockHeight); //g.DrawRectangle(Pens.White,x,y,(int)blockWidth,(int)blockHeight); } } }
/// <summary> /// Draws the arrow on a plot surface. /// </summary> /// <param name="g">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(System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { if (To.X > xAxis.Axis.WorldMax || To.X < xAxis.Axis.WorldMin) { return; } if (To.Y > yAxis.Axis.WorldMax || To.Y < yAxis.Axis.WorldMin) { return; } double angle = angle_; if (angle_ < 0.0) { int mul = -(int)(angle_ / 360.0) + 2; angle = angle_ + 360.0 * (double)mul; } double normAngle = (double)angle % 360.0; // angle in range 0 -> 360. Point toPoint = new Point( (int)xAxis.WorldToPhysical(to_.X, true).X, (int)yAxis.WorldToPhysical(to_.Y, true).Y); float xDir = (float)Math.Cos(normAngle * 2.0 * Math.PI / 360.0); float yDir = (float)Math.Sin(normAngle * 2.0 * Math.PI / 360.0); toPoint.X += (int)(xDir * headOffset_); toPoint.Y += (int)(yDir * headOffset_); float xOff = physicalLength_ * xDir; float yOff = physicalLength_ * yDir; Point fromPoint = new Point( (int)(toPoint.X + xOff), (int)(toPoint.Y + yOff)); g.DrawLine(pen_, toPoint, fromPoint); Point[] head = new Point[3]; head[0] = toPoint; xOff = headSize_ * (float)Math.Cos((normAngle - headAngle_ / 2.0f) * 2.0 * Math.PI / 360.0); yOff = headSize_ * (float)Math.Sin((normAngle - headAngle_ / 2.0f) * 2.0 * Math.PI / 360.0); head[1] = new Point( (int)(toPoint.X + xOff), (int)(toPoint.Y + yOff)); float xOff2 = headSize_ * (float)Math.Cos((normAngle + headAngle_ / 2.0f) * 2.0 * Math.PI / 360.0); float yOff2 = headSize_ * (float)Math.Sin((normAngle + headAngle_ / 2.0f) * 2.0 * Math.PI / 360.0); head[2] = new Point( (int)(toPoint.X + xOff2), (int)(toPoint.Y + yOff2)); g.FillPolygon(arrowBrush_, head); SizeF textSize = g.MeasureString(text_, font_); SizeF halfSize = new SizeF(textSize.Width / 2.0f, textSize.Height / 2.0f); float quadrantSlideLength = halfSize.Width + halfSize.Height; float quadrantF = (float)normAngle / 90.0f; // integer part gives quadrant. int quadrant = (int)quadrantF; // quadrant in. float prop = quadrantF - (float)quadrant; // proportion of way through this qadrant. float dist = prop * quadrantSlideLength; // distance along quarter of bounds rectangle. // now find the offset from the middle of the text box that the // rear end of the arrow should end at (reverse this to get position // of text box with respect to rear end of arrow). // // There is almost certainly an elgant way of doing this involving // trig functions to get all the signs right, but I'm about ready to // drop off to sleep at the moment, so this blatent method will have // to do. PointF offsetFromMiddle = new PointF(0.0f, 0.0f); switch (quadrant) { case 0: if (dist > halfSize.Height) { dist -= halfSize.Height; offsetFromMiddle = new PointF(-halfSize.Width + dist, halfSize.Height); } else { offsetFromMiddle = new PointF(-halfSize.Width, -dist); } break; case 1: if (dist > halfSize.Width) { dist -= halfSize.Width; offsetFromMiddle = new PointF(halfSize.Width, halfSize.Height - dist); } else { offsetFromMiddle = new PointF(dist, halfSize.Height); } break; case 2: if (dist > halfSize.Height) { dist -= halfSize.Height; offsetFromMiddle = new PointF(halfSize.Width - dist, -halfSize.Height); } else { offsetFromMiddle = new PointF(halfSize.Width, -dist); } break; case 3: if (dist > halfSize.Width) { dist -= halfSize.Width; offsetFromMiddle = new PointF(-halfSize.Width, -halfSize.Height + dist); } else { offsetFromMiddle = new PointF(-dist, -halfSize.Height); } break; default: throw new NPlotException("Programmer error."); } g.DrawString( text_, font_, textBrush_, (int)(fromPoint.X - halfSize.Width - offsetFromMiddle.X), (int)(fromPoint.Y - halfSize.Height + offsetFromMiddle.Y)); }
/// <summary> /// Draws the scale division label 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(System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis) { ArrayList yLargePositions = null; ArrayList ySmallPositions = null; yAxis.Axis.WorldTickPositions_FirstPass( yAxis.PhysicalMin, yAxis.PhysicalMax, out yLargePositions, out ySmallPositions ); // test if we have enough y ticks if (yLargePositions.Count <= this.YPosition) { if (yLargePositions.Count > 1) this.YPosition = yLargePositions.Count - 1; else return; } double minX = xAxis.PhysicalToWorld(xAxis.PhysicalMin,false); double maxX = xAxis.PhysicalToWorld(xAxis.PhysicalMax,false); double xOffset = this.XPosition; double xRef = xAxis.PhysicalMin.X; if (xOffset < 0) xRef = xAxis.PhysicalMax.X; if (xOffset < 1 && xOffset > 0) xOffset = (xAxis.PhysicalMax.X - xAxis.PhysicalMin.X) * this.XPosition; //*P8 P3*==*P2==*P4 // || // || // || *P7 // || // || // P5*==*P1==*P6 //Points for the main-line PointF p1 = new PointF(Convert.ToSingle(xRef + xOffset), 0.0f); PointF p2 = p1; PointF yTick1 = yAxis.WorldToPhysical((double)yLargePositions[yLargePositions.Count-this.YPosition],false); PointF yTick0 = yAxis.WorldToPhysical((double)yLargePositions[yLargePositions.Count-(this.YPosition+1)],false); p1.Y = yTick0.Y; p2.Y = yTick1.Y; //Points for the upper line PointF p3 = yTick1; PointF p4 = p3; p3.X = Convert.ToSingle(xRef + xOffset - 3); p4.X = Convert.ToSingle(xRef + xOffset + 3); //Points for the lower line PointF p5 = p3; PointF p6 = p4; p5.Y = yTick0.Y; p6.Y = yTick0.Y; //Label-point PointF p7 = p6; p7.Y = p7.Y + (p2.Y - p1.Y)/2; //Background-point PointF p8 = p3; p8.X = Convert.ToSingle((xRef + xOffset) - 14); double labelValue = (double)yLargePositions[yLargePositions.Count-this.YPosition] - (double)yLargePositions[yLargePositions.Count-(this.YPosition+1)]; StringBuilder label = new StringBuilder(); label.AppendFormat(this.NumberFormat, labelValue); float labelLength = g.MeasureString (label.ToString(), this.LabelFont).Width; float width = Convert.ToSingle((xRef + xOffset) + 14) - p8.X; float widthMin = (p7.X - p8.X) + labelLength + 2; if (widthMin > width) width = widthMin; float height = p1.Y-p2.Y; RectangleF bgRect = new RectangleF (p8.X, p8.Y, width, height); g.FillPath(this.BackgroundBrush, bgRect.RoundCorners(this.BackgroundCornerRadius)); g.DrawString(label.ToString(), this.LabelFont, this.ValueBrush, p7); g.DrawLine( this.LinePen, (int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y ); g.DrawLine( this.LinePen, (int)p3.X, (int)p3.Y, (int)p4.X, (int)p4.Y ); g.DrawLine( this.LinePen, (int)p5.X, (int)p5.Y, (int)p6.X, (int)p6.Y ); }