/// <summary> /// Constructs the optimal ITransform2D object for the supplied x and y axes. /// </summary> /// <param name="xAxis">The xAxis to use for the world to physical transform.</param> /// <param name="yAxis">The yAxis to use for the world to physical transform.</param> /// <returns>An ITransform2D derived object for converting from world to physical coordinates.</returns> public static ITransform2D GetTransformer( PhysicalAxis xAxis, PhysicalAxis yAxis ) { ITransform2D ret = null; // if (xAxis.Axis.IsLinear && yAxis.Axis.IsLinear && !xAxis.Axis.Reversed && !yAxis.Axis.Reversed) // ret = new FastTransform2D( xAxis, yAxis ); // else // ret = new DefaultTransform2D( xAxis, yAxis ); ret = new DefaultTransform2D( xAxis, yAxis ); return ret; }
/// <summary> /// Construct from a fully-blown physical axis. /// </summary> /// <param name="physicalAxis">the physical axis to get initial values from.</param> public PageAlignedPhysicalAxis( PhysicalAxis physicalAxis ) { worldMin_ = physicalAxis.Axis.WorldMin; worldMax_ = physicalAxis.Axis.WorldMax; worldLength_ = worldMax_ - worldMin_; if ( physicalAxis.PhysicalMin.X == physicalAxis.PhysicalMax.X ) { pMin_ = physicalAxis.PhysicalMin.Y; pMax_ = physicalAxis.PhysicalMax.Y; } else if ( physicalAxis.PhysicalMin.Y == physicalAxis.PhysicalMax.Y ) { pMin_ = physicalAxis.PhysicalMin.X; pMax_ = physicalAxis.PhysicalMax.X; } else { throw new FlorenceException( "Physical axis is not page aligned" ); } pLength_ = pMax_ - pMin_; }
/// <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="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> /// Constructor /// </summary> /// <param name="xAxis">The x-axis to use for transforms</param> /// <param name="yAxis">The y-axis to use for transforms</param> public DefaultTransform2D(PhysicalAxis xAxis, PhysicalAxis yAxis) { xAxis_ = xAxis; yAxis_ = yAxis; }
/// <summary> /// Applies the constraint to the axes. /// </summary> /// <param name="pXAxis1">The bottom x-axis.</param> /// <param name="pYAxis1">The left y-axis.</param> /// <param name="pXAxis2">The top x-axis.</param> /// <param name="pYAxis2">The right y-axis.</param> public override void ApplyConstraint( PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, PhysicalAxis pXAxis2, PhysicalAxis pYAxis2 ) { int desiredLength = (int)(pXAxis1.Axis.WorldLength / (double)this.pWorldLength_); int currentLength = pXAxis1.PhysicalLength; int delta = currentLength - desiredLength; int changeLeft = delta / 2; int changeRight = delta / 2; if (this.holdFixedY_ != null) { if ( (PlotSurface2D.YAxisPosition)this.holdFixedY_ == PlotSurface2D.YAxisPosition.Left ) { changeLeft = 0; changeRight = delta; } else { changeLeft = delta; changeRight = 0; } } pXAxis1.PhysicalMin = new Point( pXAxis1.PhysicalMin.X+changeLeft, pXAxis1.PhysicalMin.Y ); pXAxis1.PhysicalMax = new Point( pXAxis1.PhysicalMax.X-changeRight, pXAxis1.PhysicalMax.Y ); pXAxis2.PhysicalMin = new Point( pXAxis2.PhysicalMin.X+changeLeft, pXAxis2.PhysicalMin.Y ); pXAxis2.PhysicalMax = new Point( pXAxis2.PhysicalMax.X-changeRight, pXAxis2.PhysicalMax.Y ); pYAxis1.PhysicalMin = new Point( pYAxis1.PhysicalMin.X+changeLeft, pYAxis1.PhysicalMin.Y ); pYAxis1.PhysicalMax = new Point( pYAxis1.PhysicalMax.X+changeLeft, pYAxis1.PhysicalMax.Y ); pYAxis2.PhysicalMin = new Point( pYAxis2.PhysicalMin.X-changeRight, pYAxis2.PhysicalMin.Y ); pYAxis2.PhysicalMax = new Point( pYAxis2.PhysicalMax.X-changeRight, pYAxis2.PhysicalMax.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 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 { // Ensure that we do not go outside of the graphics capabilities if ((Math.Abs(p1.X) + Math.Abs(p2.X)) < 0x4000007F && (Math.Abs(p1.Y) + Math.Abs(p2.Y)) < 0x4000007F) { g.DrawLine(Pen, p1.X, p1.Y, p2.X, p2.Y); } } } } }
private void Init() { drawables_ = new ArrayList(); xAxisPositions_ = new ArrayList(); yAxisPositions_ = new ArrayList(); zPositions_ = new ArrayList(); ordering_ = new SortedList(); try { FontFamily fontFamily = new FontFamily("Arial"); TitleFont = new Font(fontFamily, 14, FontStyle.Regular, GraphicsUnit.Pixel); } catch (System.ArgumentException) { throw new FlorenceException("Error: Arial font is not installed on this system"); } padding_ = 10; title_ = ""; autoScaleTitle_ = false; autoScaleAutoGeneratedAxes_ = false; xAxis1_ = null; xAxis2_ = null; yAxis1_ = null; yAxis2_ = null; pXAxis1Cache_ = null; pYAxis1Cache_ = null; pXAxis2Cache_ = null; pYAxis2Cache_ = null; titleBrush_ = new SolidBrush( Color.Black ); plotBackColor_ = Color.White; outerBackColor = null; this.legend_ = null; smoothingMode_ = System.Drawing.Drawing2D.SmoothingMode.None; axesConstraints_ = new ArrayList(); }
/// <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 { // Ensure that we do not go outside of the graphics capabilities if ((Math.Abs(p1.X) + Math.Abs(p2.X)) < 0x4000007F && (Math.Abs(p1.Y) + Math.Abs(p2.Y)) < 0x4000007F) { 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; RectangleF r = new RectangleF(xPos1.X + xoff, yPos1.Y, width - 2 * xoff, 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); } }
private static Point WorldToPhysical(PointD world_point, PhysicalAxis xAxis, PhysicalAxis yAxis) { return new Point((int)xAxis.WorldToPhysical(world_point.X, false).X, (int)yAxis.WorldToPhysical(world_point.Y, false).Y); }
/// <summary> /// Draws the line 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; int yMin = yAxis.PhysicalMin.Y; int yMax = yAxis.PhysicalMax.Y; PointD[] world_points = new PointD[2]; int point_index = 0; // intercept point on left border var value_at_left_border = this.Intercept + this.Slope * xAxis.Axis.WorldMin; if (value_at_left_border >= yAxis.Axis.WorldMin && value_at_left_border < yAxis.Axis.WorldMax) { world_points[point_index] = new PointD(xAxis.Axis.WorldMin, value_at_left_border); point_index += 1; } // intercept point on right border var value_at_right_border = this.Intercept + this.Slope * xAxis.Axis.WorldMax; if (value_at_right_border > yAxis.Axis.WorldMin && value_at_right_border <= yAxis.Axis.WorldMax) { world_points[point_index] = new PointD(xAxis.Axis.WorldMax, value_at_right_border); point_index += 1; } // intercept point on bottom border var value_at_bottom_border = (yAxis.Axis.WorldMin - this.Intercept) / this.Slope; if (value_at_bottom_border > xAxis.Axis.WorldMin && value_at_bottom_border <= xAxis.Axis.WorldMax) { world_points[point_index] = new PointD(value_at_bottom_border, yAxis.Axis.WorldMin); point_index += 1; } // intercept point on top border var value_at_top_border = (yAxis.Axis.WorldMax - this.Intercept) / this.Slope; if (value_at_top_border >= xAxis.Axis.WorldMin && value_at_top_border < xAxis.Axis.WorldMax) { world_points[point_index] = new PointD(value_at_top_border, yAxis.Axis.WorldMax); } var physical_point = new Point[2]; physical_point[0] = WorldToPhysical(world_points[0], xAxis, yAxis); physical_point[1] = WorldToPhysical(world_points[1], xAxis, yAxis); g.DrawLine(this.Pen, physical_point[0], physical_point[1]); // todo: clip and proper logic for flipped axis min max. }
/// <summary> /// Applies the constraint to the axes. Must be overriden. /// </summary> /// <param name="pXAxis1">The bottom x-axis.</param> /// <param name="pYAxis1">The left y-axis.</param> /// <param name="pXAxis2">The top x-axis.</param> /// <param name="pYAxis2">The right y-axis.</param> public abstract void ApplyConstraint( PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, PhysicalAxis pXAxis2, PhysicalAxis pYAxis2 );
/// <summary> /// Applies the constraint to the axes. /// </summary> /// <param name="pXAxis1">The bottom x-axis.</param> /// <param name="pYAxis1">The left y-axis.</param> /// <param name="pXAxis2">The top x-axis.</param> /// <param name="pYAxis2">The right y-axis.</param> public override void ApplyConstraint( PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, PhysicalAxis pXAxis2, PhysicalAxis pYAxis2 ) { int desiredLength = (int)(pYAxis1.Axis.WorldLength / this.pWorldLength_); int currentLength = pYAxis1.PhysicalLength; int delta = currentLength - desiredLength; int changeBottom = -delta / 2; int changeTop = -delta / 2; if (this.holdFixedX_ != null) { if ( (PlotSurface2D.XAxisPosition)this.holdFixedX_ == PlotSurface2D.XAxisPosition.Bottom ) { changeBottom = 0; changeTop = -delta; } else { changeBottom = -delta; changeTop = 0; } } pYAxis1.PhysicalMin = new Point( pYAxis1.PhysicalMin.X, pYAxis1.PhysicalMin.Y+changeBottom ); pYAxis1.PhysicalMax = new Point( pYAxis1.PhysicalMax.X, pYAxis1.PhysicalMax.Y-changeTop ); pYAxis2.PhysicalMin = new Point( pYAxis2.PhysicalMin.X, pYAxis2.PhysicalMin.Y+changeBottom ); pYAxis2.PhysicalMax = new Point( pYAxis2.PhysicalMax.X, pYAxis2.PhysicalMax.Y-changeTop ); pXAxis1.PhysicalMin = new Point( pXAxis1.PhysicalMin.X, pXAxis1.PhysicalMin.Y+changeBottom ); pXAxis1.PhysicalMax = new Point( pXAxis1.PhysicalMax.X, pXAxis1.PhysicalMax.Y+changeBottom ); pXAxis2.PhysicalMin = new Point( pXAxis2.PhysicalMin.X, pXAxis2.PhysicalMin.Y-changeTop ); pXAxis2.PhysicalMax = new Point( pXAxis2.PhysicalMax.X, pXAxis2.PhysicalMax.Y-changeTop ); }
/// <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> /// Updates the PlotSurface2D axes to compensate for the legend. /// </summary> /// <param name="pXAxis1">the bottom x axis</param> /// <param name="pYAxis1">the left y axis</param> /// <param name="pXAxis2">the top x axis</param> /// <param name="pYAxis2">the right y axis</param> /// <param name="plots">list of plots.</param> /// <param name="scale">scale parameter (for text and other)</param> /// <param name="padding">padding around plot within bounds.</param> /// <param name="bounds">graphics surface bounds</param> /// <param name="position">legend position</param> public void UpdateAxesPositions( PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, PhysicalAxis pXAxis2, PhysicalAxis pYAxis2, ArrayList plots, float scale, int padding, Rectangle bounds, out Point position) { int leftIndent = 0; int rightIndent = 0; int bottomIndent = 0; int topIndent = 0; position = new Point(0, 0); // now determine if legend should change any of these (legend should be fully // visible at all times), and draw legend. Rectangle legendWidthHeight = this.GetBoundingBox(new Point(0, 0), plots, scale); if (legendWidthHeight.Width > bounds.Width) { legendWidthHeight.Width = bounds.Width; } // (1) calculate legend position. // y position.Y = this.yOffset_; if (this.xAttach_ == PlotSurface2D.XAxisPosition.Bottom) { position.Y += pYAxis1.PhysicalMin.Y; if (this.horizontalEdgePlacement_ == Legend.Placement.Inside) { position.Y -= legendWidthHeight.Height; } } else { position.Y += pYAxis1.PhysicalMax.Y; if (this.horizontalEdgePlacement_ == Legend.Placement.Outside) { position.Y -= legendWidthHeight.Height; } } // x position.X = this.xOffset_; if (this.yAttach_ == PlotSurface2D.YAxisPosition.Left) { if (this.verticalEdgePlacement_ == Legend.Placement.Outside) { position.X -= legendWidthHeight.Width; } position.X += pXAxis1.PhysicalMin.X; } else { if (this.verticalEdgePlacement_ == Legend.Placement.Inside) { position.X -= legendWidthHeight.Width; } position.X += pXAxis1.PhysicalMax.X; } // determine update amounts for axes if (!this.neverShiftAxes_) { if (position.X < padding) { int changeAmount = -position.X + padding; // only allow axes to move away from bounds. if (changeAmount > 0) { leftIndent = changeAmount; } position.X += changeAmount; } if (position.X + legendWidthHeight.Width > bounds.Right - padding) { int changeAmount = (position.X - bounds.Right + legendWidthHeight.Width + padding); // only allow axes to move away from bounds. if (changeAmount > 0.0f) { rightIndent = changeAmount; } position.X -= changeAmount; } if (position.Y < padding) { int changeAmount = -position.Y + padding; // only allow axes to move away from bounds. if (changeAmount > 0.0f) { topIndent = changeAmount; } position.Y += changeAmount; } if (position.Y + legendWidthHeight.Height > bounds.Bottom - padding) { int changeAmount = (position.Y - bounds.Bottom + legendWidthHeight.Height + padding); // only allow axes to move away from bounds. if (changeAmount > 0.0f) { bottomIndent = changeAmount; } position.Y -= changeAmount; } // update axes. pXAxis1.PhysicalMin = new Point(pXAxis1.PhysicalMin.X + leftIndent, pXAxis1.PhysicalMin.Y - bottomIndent); pXAxis1.PhysicalMax = new Point(pXAxis1.PhysicalMax.X - rightIndent, pXAxis1.PhysicalMax.Y - bottomIndent); pYAxis1.PhysicalMin = new Point(pYAxis1.PhysicalMin.X + leftIndent, pYAxis1.PhysicalMin.Y - bottomIndent); pYAxis1.PhysicalMax = new Point(pYAxis1.PhysicalMax.X + leftIndent, pYAxis1.PhysicalMax.Y + topIndent); pXAxis2.PhysicalMin = new Point(pXAxis2.PhysicalMin.X + leftIndent, pXAxis2.PhysicalMin.Y + topIndent); pXAxis2.PhysicalMax = new Point(pXAxis2.PhysicalMax.X - rightIndent, pXAxis2.PhysicalMax.Y + topIndent); pYAxis2.PhysicalMin = new Point(pYAxis2.PhysicalMin.X - rightIndent, pYAxis2.PhysicalMin.Y - bottomIndent); pYAxis2.PhysicalMax = new Point(pYAxis2.PhysicalMax.X - rightIndent, pYAxis2.PhysicalMax.Y + topIndent); } }
/// <summary> /// Draw the the PlotSurface2D and all contents [axes, drawables, and legend] on the /// supplied graphics surface. /// </summary> /// <param name="g">The graphics surface on which to draw.</param> /// <param name="bounds">A bounding box on this surface that denotes the area on the /// surface to confine drawing to.</param> public void Draw( Graphics g, Rectangle bounds ) { // Draw background if (outerBackColor != null && outerBackColor != Color.Transparent) g.FillRectangle(new SolidBrush(outerBackColor.Value), bounds); else if (outerBackColor == null) { if (plotBackColor_ != null) g.FillRectangle(new SolidBrush(plotBackColor_.Value), bounds); else if (plotBackBrush_ != null) g.FillRectangle(plotBackBrush_.Get(bounds), bounds); else g.FillRectangle(new SolidBrush(Color.White), bounds); } // determine font sizes and tick scale factor. float scale = DetermineScaleFactor( bounds.Width, bounds.Height ); // if there is nothing to plot, return. if ( drawables_.Count == 0 ) { // draw title float x_center = (bounds.Left + bounds.Right)/2.0f; float y_center = (bounds.Top + bounds.Bottom)/2.0f; Font scaled_font; if (this.AutoScaleTitle) { scaled_font = Utils.ScaleFont( titleFont_, scale ); } else { scaled_font = titleFont_; } g.DrawString( title_, scaled_font, this.titleBrush_, new PointF(x_center,y_center), titleDrawFormat_ ); return; } // determine the [non physical] axes to draw based on the axis properties set. Axis xAxis1 = null; Axis xAxis2 = null; Axis yAxis1 = null; Axis yAxis2 = null; this.DetermineAxesToDraw( out xAxis1, out xAxis2, out yAxis1, out yAxis2 ); // apply scale factor to axes as desired. if (xAxis1.AutoScaleTicks) { xAxis1.TickScale = scale; } if (xAxis1.AutoScaleText) { xAxis1.FontScale = scale; } if (yAxis1.AutoScaleTicks) { yAxis1.TickScale = scale; } if (yAxis1.AutoScaleText) { yAxis1.FontScale = scale; } if (xAxis2.AutoScaleTicks) { xAxis2.TickScale = scale; } if (xAxis2.AutoScaleText) { xAxis2.FontScale = scale; } if (yAxis2.AutoScaleTicks) { yAxis2.TickScale = scale; } if (yAxis2.AutoScaleText) { yAxis2.FontScale = scale; } // determine the default physical positioning of those axes. PhysicalAxis pXAxis1 = null; PhysicalAxis pYAxis1 = null; PhysicalAxis pXAxis2 = null; PhysicalAxis pYAxis2 = null; this.DeterminePhysicalAxesToDraw( bounds, xAxis1, xAxis2, yAxis1, yAxis2, out pXAxis1, out pXAxis2, out pYAxis1, out pYAxis2 ); float oldXAxis2Height = pXAxis2.PhysicalMin.Y; // Apply axes constraints for (int i=0; i<axesConstraints_.Count; ++i) { ((AxesConstraint)axesConstraints_[i]).ApplyConstraint( pXAxis1, pYAxis1, pXAxis2, pYAxis2 ); } ///////////////////////////////////////////////////////////////////////// // draw legend if have one. // Note: this will update axes if necessary. Point legendPosition = new Point(0,0); if (this.legend_ != null) { legend_.UpdateAxesPositions( pXAxis1, pYAxis1, pXAxis2, pYAxis2, this.drawables_, scale, this.padding_, bounds, out legendPosition ); } float newXAxis2Height = pXAxis2.PhysicalMin.Y; float titleExtraOffset = oldXAxis2Height - newXAxis2Height; // now we are ready to define the bounding box for the plot area (to use in clipping // operations. plotAreaBoundingBoxCache_ = new Rectangle( Math.Min( pXAxis1.PhysicalMin.X, pXAxis1.PhysicalMax.X ), Math.Min( pYAxis1.PhysicalMax.Y, pYAxis1.PhysicalMin.Y ), Math.Abs( pXAxis1.PhysicalMax.X - pXAxis1.PhysicalMin.X + 1 ), Math.Abs( pYAxis1.PhysicalMin.Y - pYAxis1.PhysicalMax.Y + 1 ) ); bbXAxis1Cache_ = pXAxis1.GetBoundingBox(); bbXAxis2Cache_ = pXAxis2.GetBoundingBox(); bbYAxis1Cache_ = pYAxis1.GetBoundingBox(); bbYAxis2Cache_ = pYAxis2.GetBoundingBox(); // Fill in the background. if ( this.plotBackColor_ != null ) { g.FillRectangle( new System.Drawing.SolidBrush( (Color)this.plotBackColor_ ), (Rectangle)plotAreaBoundingBoxCache_ ); } else if (this.plotBackBrush_ != null) { g.FillRectangle( this.plotBackBrush_.Get( (Rectangle)plotAreaBoundingBoxCache_ ), (Rectangle)plotAreaBoundingBoxCache_ ); } else if (this.plotBackImage_ != null) { g.DrawImage( Utils.TiledImage( this.plotBackImage_ , new Size( ((Rectangle)plotAreaBoundingBoxCache_).Width, ((Rectangle)plotAreaBoundingBoxCache_).Height ) ), (Rectangle)plotAreaBoundingBoxCache_ ); } // draw title float xt = (pXAxis2.PhysicalMax.X + pXAxis2.PhysicalMin.X)/2.0f; float yt = bounds.Top + this.padding_ - titleExtraOffset; Font scaledFont; if (this.AutoScaleTitle) { scaledFont = Utils.ScaleFont( titleFont_, scale ); } else { scaledFont = titleFont_; } g.DrawString( title_, scaledFont, this.titleBrush_, new PointF(xt,yt), titleDrawFormat_ ); //count number of new lines in title. int nlCount = 0; for (int i=0; i<title_.Length; ++i) { if (title_[i] == '\n') nlCount += 1; } SizeF s = g.MeasureString(title_,scaledFont); bbTitleCache_ = new Rectangle( (int)(xt-s.Width/2), (int)(yt), (int)(s.Width), (int)(s.Height)*(nlCount+1) ); // draw drawables.. System.Drawing.Drawing2D.SmoothingMode smoothSave = g.SmoothingMode; g.SmoothingMode = this.smoothingMode_; bool legendDrawn = false; for ( int i_o = 0; i_o < ordering_.Count; ++i_o ) { int i = (int)ordering_.GetByIndex(i_o); double zOrder = (double)ordering_.GetKey( i_o ); if (zOrder > this.legendZOrder_) { // draw legend. if ( !legendDrawn && this.legend_ != null ) { legend_.Draw( g, legendPosition, this.drawables_, scale ); legendDrawn = true; } } IDrawable drawable = (IDrawable)drawables_[i]; XAxisPosition xap = (XAxisPosition)xAxisPositions_[i]; YAxisPosition yap = (YAxisPosition)yAxisPositions_[i]; PhysicalAxis drawXAxis; PhysicalAxis drawYAxis; if ( xap == XAxisPosition.Bottom ) { drawXAxis = pXAxis1; } else { drawXAxis = pXAxis2; } if ( yap == YAxisPosition.Left ) { drawYAxis = pYAxis1; } else { drawYAxis = pYAxis2; } // set the clipping region.. (necessary for zoom) g.Clip = new Region((Rectangle)plotAreaBoundingBoxCache_); // plot. drawable.Draw( g, drawXAxis, drawYAxis ); // reset it.. g.ResetClip(); } if ( !legendDrawn && this.legend_ != null ) { legend_.Draw( g, legendPosition, this.drawables_, scale ); } // cache the physical axes we used on this draw; this.pXAxis1Cache_ = pXAxis1; this.pYAxis1Cache_ = pYAxis1; this.pXAxis2Cache_ = pXAxis2; this.pYAxis2Cache_ = pYAxis2; g.SmoothingMode = smoothSave; // now draw axes. Rectangle axisBounds; pXAxis1.Draw( g, out axisBounds ); pXAxis2.Draw( g, out axisBounds ); pYAxis1.Draw( g, out axisBounds ); pYAxis2.Draw( g, out axisBounds ); #if DEBUG_BOUNDING_BOXES g.DrawRectangle( new Pen(Color.Orange), (Rectangle) bbXAxis1Cache_ ); g.DrawRectangle( new Pen(Color.Orange), (Rectangle) bbXAxis2Cache_ ); g.DrawRectangle( new Pen(Color.Orange), (Rectangle) bbYAxis1Cache_ ); g.DrawRectangle( new Pen(Color.Orange), (Rectangle) bbYAxis2Cache_ ); g.DrawRectangle( new Pen(Color.Red,5.0F),(Rectangle) plotAreaBoundingBoxCache_); //if(this.ShowLegend)g.DrawRectangle( new Pen(Color.Chocolate, 3.0F), (Rectangle) bbLegendCache_); g.DrawRectangle( new Pen(Color.DeepPink,2.0F), (Rectangle) bbTitleCache_); #endif }
/// <summary> /// MouseDown method for AxisDrag interaction /// </summary> /// <param name="X">mouse X position</param> /// <param name="Y">mouse Y position</param> /// <param name="keys">mouse and keyboard modifiers</param> /// <param name="ps">the InteractivePlotSurface2D</param> public override bool DoMouseDown(int X, int Y, Modifier keys, InteractivePlotSurface2D ps) { // if the mouse is inside the plot area [the tick marks may be here, // and are counted as part of the axis], then don't invoke drag. if (ps.PlotAreaBoundingBoxCache.Contains(X, Y)) { return false; } if ((keys & Modifier.Button1) != 0) { // see if hit with axis. NB Only one axis object will be returned ArrayList objects = ps.HitTest(new Point(X, Y)); foreach (object o in objects) { if (o is Florence.Axis) { dragging_ = true; axis_ = (Axis)o; if (ps.PhysicalXAxis1Cache.Axis == axis_) { physicalAxis_ = ps.PhysicalXAxis1Cache; ps.plotCursor = CursorType.LeftRight; } else if (ps.PhysicalXAxis2Cache.Axis == axis_) { physicalAxis_ = ps.PhysicalXAxis2Cache; ps.plotCursor = CursorType.LeftRight; } else if (ps.PhysicalYAxis1Cache.Axis == axis_) { physicalAxis_ = ps.PhysicalYAxis1Cache; ps.plotCursor = CursorType.UpDown; } else if (ps.PhysicalYAxis2Cache.Axis == axis_) { physicalAxis_ = ps.PhysicalYAxis2Cache; ps.plotCursor = CursorType.UpDown; } startPoint_ = new Point(X, Y); // don't combine these - Mono lastPoint_ = startPoint_; // bug #475205 prior to 2.4 // evaluate focusRatio about which axis is expanded float x = startPoint_.X - physicalAxis_.PhysicalMin.X; float y = startPoint_.Y - physicalAxis_.PhysicalMin.Y; double r = Math.Sqrt(x * x + y * y); focusRatio_ = r / physicalAxis_.PhysicalLength; return false; } } } return false; }
private void DeterminePhysicalAxesToDraw( Rectangle bounds, Axis xAxis1, Axis xAxis2, Axis yAxis1, Axis yAxis2, out PhysicalAxis pXAxis1, out PhysicalAxis pXAxis2, out PhysicalAxis pYAxis1, out PhysicalAxis pYAxis2 ) { System.Drawing.Rectangle cb = bounds; pXAxis1 = new PhysicalAxis( xAxis1, new Point( cb.Left, cb.Bottom ), new Point( cb.Right, cb.Bottom ) ); pYAxis1 = new PhysicalAxis( yAxis1, new Point( cb.Left, cb.Bottom ), new Point( cb.Left, cb.Top ) ); pXAxis2 = new PhysicalAxis( xAxis2, new Point( cb.Left, cb.Top), new Point( cb.Right, cb.Top) ); pYAxis2 = new PhysicalAxis( yAxis2, new Point( cb.Right, cb.Bottom ), new Point( cb.Right, cb.Top ) ); int bottomIndent = padding_; if (!pXAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis1.GetBoundingBox(); // finally determine its indentation from the bottom bottomIndent = bottomIndent + bb.Bottom - cb.Bottom; } int leftIndent = padding_; if (!pYAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis1.GetBoundingBox(); // finally determine its indentation from the left leftIndent = leftIndent - bb.Left + cb.Left; } int topIndent = padding_; float scale = this.DetermineScaleFactor( bounds.Width, bounds.Height ); int titleHeight; if (this.AutoScaleTitle) { titleHeight = Utils.ScaleFont(titleFont_, scale).Height; } else { titleHeight = titleFont_.Height; } //count number of new lines in title. int nlCount = 0; for (int i=0; i<title_.Length; ++i) { if (title_[i] == '\n') nlCount += 1; } titleHeight = (int)( ((float)nlCount*0.75 + 1.0f) * (float)titleHeight); if (!pXAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis2.GetBoundingBox(); topIndent = topIndent - bb.Top + cb.Top; // finally determine its indentation from the top // correct top indendation to take into account plot title if (title_ != "" ) { topIndent += (int)(titleHeight * 1.3f); } } int rightIndent = padding_; if (!pYAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis2.GetBoundingBox(); // finally determine its indentation from the right rightIndent = (int)(rightIndent + bb.Right-cb.Right); } // now we have all the default calculated positions and we can proceed to // "move" the axes to their right places // primary axes (bottom, left) pXAxis1.PhysicalMin = new Point( cb.Left+leftIndent, cb.Bottom-bottomIndent ); pXAxis1.PhysicalMax = new Point( cb.Right-rightIndent, cb.Bottom-bottomIndent ); pYAxis1.PhysicalMin = new Point( cb.Left+leftIndent, cb.Bottom-bottomIndent ); pYAxis1.PhysicalMax = new Point( cb.Left+leftIndent, cb.Top+topIndent ); // secondary axes (top, right) pXAxis2.PhysicalMin = new Point( cb.Left+leftIndent, cb.Top+topIndent ); pXAxis2.PhysicalMax = new Point( cb.Right-rightIndent, cb.Top+topIndent ); pYAxis2.PhysicalMin = new Point( cb.Right-rightIndent, cb.Bottom-bottomIndent ); pYAxis2.PhysicalMax = new Point( cb.Right-rightIndent, cb.Top+topIndent ); }
/// <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 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> /// Constructor /// </summary> /// <param name="xAxis">The x-axis to use for transforms</param> /// <param name="yAxis">The y-axis to use for transforms</param> public FastTransform2D(PhysicalAxis xAxis, PhysicalAxis yAxis) { xAxis_ = new PageAlignedPhysicalAxis(xAxis); yAxis_ = new PageAlignedPhysicalAxis(yAxis); }
/// <summary> /// Updates the PlotSurface2D axes to compensate for the legend. /// </summary> /// <param name="pXAxis1">the bottom x axis</param> /// <param name="pYAxis1">the left y axis</param> /// <param name="pXAxis2">the top x axis</param> /// <param name="pYAxis2">the right y axis</param> /// <param name="plots">list of plots.</param> /// <param name="scale">scale parameter (for text and other)</param> /// <param name="padding">padding around plot within bounds.</param> /// <param name="bounds">graphics surface bounds</param> /// <param name="position">legend position</param> public void UpdateAxesPositions( PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, PhysicalAxis pXAxis2, PhysicalAxis pYAxis2, ArrayList plots, float scale, int padding, Rectangle bounds, out Point position ) { int leftIndent = 0; int rightIndent = 0; int bottomIndent = 0; int topIndent = 0; position = new Point(0,0); // now determine if legend should change any of these (legend should be fully // visible at all times), and draw legend. Rectangle legendWidthHeight = this.GetBoundingBox( new Point(0,0), plots, scale ); if (legendWidthHeight.Width > bounds.Width) { legendWidthHeight.Width = bounds.Width; } // (1) calculate legend position. // y position.Y = this.yOffset_; if ( this.xAttach_ == PlotSurface2D.XAxisPosition.Bottom ) { position.Y += pYAxis1.PhysicalMin.Y; if ( this.horizontalEdgePlacement_ == Legend.Placement.Inside ) { position.Y -= legendWidthHeight.Height; } } else { position.Y += pYAxis1.PhysicalMax.Y; if ( this.horizontalEdgePlacement_ == Legend.Placement.Outside ) { position.Y -= legendWidthHeight.Height; } } // x position.X = this.xOffset_; if ( this.yAttach_ == PlotSurface2D.YAxisPosition.Left ) { if ( this.verticalEdgePlacement_ == Legend.Placement.Outside ) { position.X -= legendWidthHeight.Width; } position.X += pXAxis1.PhysicalMin.X; } else { if ( this.verticalEdgePlacement_ == Legend.Placement.Inside ) { position.X -= legendWidthHeight.Width; } position.X += pXAxis1.PhysicalMax.X; } // determine update amounts for axes if ( !this.neverShiftAxes_ ) { if ( position.X < padding ) { int changeAmount = -position.X + padding; // only allow axes to move away from bounds. if ( changeAmount > 0 ) { leftIndent = changeAmount; } position.X += changeAmount; } if ( position.X + legendWidthHeight.Width > bounds.Right - padding ) { int changeAmount = (position.X - bounds.Right + legendWidthHeight.Width + padding ); // only allow axes to move away from bounds. if ( changeAmount > 0.0f ) { rightIndent = changeAmount; } position.X -= changeAmount; } if ( position.Y < padding ) { int changeAmount = -position.Y + padding; // only allow axes to move away from bounds. if ( changeAmount > 0.0f ) { topIndent = changeAmount; } position.Y += changeAmount; } if ( position.Y + legendWidthHeight.Height > bounds.Bottom - padding ) { int changeAmount = (position.Y - bounds.Bottom + legendWidthHeight.Height + padding ); // only allow axes to move away from bounds. if ( changeAmount > 0.0f ) { bottomIndent = changeAmount; } position.Y -= changeAmount; } // update axes. pXAxis1.PhysicalMin = new Point( pXAxis1.PhysicalMin.X + leftIndent, pXAxis1.PhysicalMin.Y - bottomIndent ); pXAxis1.PhysicalMax = new Point( pXAxis1.PhysicalMax.X - rightIndent, pXAxis1.PhysicalMax.Y - bottomIndent ); pYAxis1.PhysicalMin = new Point( pYAxis1.PhysicalMin.X + leftIndent, pYAxis1.PhysicalMin.Y - bottomIndent ); pYAxis1.PhysicalMax = new Point( pYAxis1.PhysicalMax.X + leftIndent, pYAxis1.PhysicalMax.Y + topIndent ); pXAxis2.PhysicalMin = new Point( pXAxis2.PhysicalMin.X + leftIndent, pXAxis2.PhysicalMin.Y + topIndent ); pXAxis2.PhysicalMax = new Point( pXAxis2.PhysicalMax.X - rightIndent, pXAxis2.PhysicalMax.Y + topIndent ); pYAxis2.PhysicalMin = new Point( pYAxis2.PhysicalMin.X - rightIndent, pYAxis2.PhysicalMin.Y - bottomIndent ); pYAxis2.PhysicalMax = new Point( pYAxis2.PhysicalMax.X - rightIndent, pYAxis2.PhysicalMax.Y + topIndent ); } }
/// <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; RectangleF r = new RectangleF( xPos1.X+xoff, yPos1.Y, width-2*xoff, 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 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 FlorenceException("Error in TextPlot.Draw"); } } }
/// <summary> /// Draws the grid /// </summary> /// <param name="g">The graphics surface on which to draw</param> /// <param name="xAxis">The physical x axis to draw horizontal lines parallel to.</param> /// <param name="yAxis">The physical y axis to draw vertical lines parallel to.</param> public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis ) { ArrayList xLargePositions = null; ArrayList yLargePositions = null; ArrayList xSmallPositions = null; ArrayList ySmallPositions = null; if (this.horizontalGridType_ != GridType.None) { xAxis.Axis.WorldTickPositions_FirstPass( xAxis.PhysicalMin, xAxis.PhysicalMax, out xLargePositions, out xSmallPositions ); DrawGridLines( g, xAxis, yAxis, xLargePositions, true, this.MajorGridPen ); } if (this.verticalGridType_ != GridType.None) { yAxis.Axis.WorldTickPositions_FirstPass( yAxis.PhysicalMin, yAxis.PhysicalMax, out yLargePositions, out ySmallPositions ); DrawGridLines( g, yAxis, xAxis, yLargePositions, false, this.MajorGridPen ); } if (this.horizontalGridType_ == GridType.Fine) { xAxis.Axis.WorldTickPositions_SecondPass( xAxis.PhysicalMin, xAxis.PhysicalMax, xLargePositions, ref xSmallPositions ); DrawGridLines( g, xAxis, yAxis, xSmallPositions, true, this.MinorGridPen ); } if (this.verticalGridType_ == GridType.Fine) { yAxis.Axis.WorldTickPositions_SecondPass( yAxis.PhysicalMin, yAxis.PhysicalMax, yLargePositions, ref ySmallPositions ); DrawGridLines( g, yAxis, xAxis, ySmallPositions, false, this.MinorGridPen ); } }
private void DeterminePhysicalAxesToDraw(Rectangle bounds, Axis xAxis1, Axis xAxis2, Axis yAxis1, Axis yAxis2, out PhysicalAxis pXAxis1, out PhysicalAxis pXAxis2, out PhysicalAxis pYAxis1, out PhysicalAxis pYAxis2) { System.Drawing.Rectangle cb = bounds; pXAxis1 = new PhysicalAxis(xAxis1, new Point(cb.Left, cb.Bottom), new Point(cb.Right, cb.Bottom)); pYAxis1 = new PhysicalAxis(yAxis1, new Point(cb.Left, cb.Bottom), new Point(cb.Left, cb.Top)); pXAxis2 = new PhysicalAxis(xAxis2, new Point(cb.Left, cb.Top), new Point(cb.Right, cb.Top)); pYAxis2 = new PhysicalAxis(yAxis2, new Point(cb.Right, cb.Bottom), new Point(cb.Right, cb.Top)); int bottomIndent = padding_; if (!pXAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis1.GetBoundingBox(); // finally determine its indentation from the bottom bottomIndent = bottomIndent + bb.Bottom - cb.Bottom; } int leftIndent = padding_; if (!pYAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis1.GetBoundingBox(); // finally determine its indentation from the left leftIndent = leftIndent - bb.Left + cb.Left; } int topIndent = padding_; float scale = this.DetermineScaleFactor(bounds.Width, bounds.Height); int titleHeight; if (this.AutoScaleTitle) { titleHeight = Utils.ScaleFont(titleFont_, scale).Height; } else { titleHeight = titleFont_.Height; } //count number of new lines in title. int nlCount = 0; for (int i = 0; i < title_.Length; ++i) { if (title_[i] == '\n') { nlCount += 1; } } titleHeight = (int)(((float)nlCount * 0.75 + 1.0f) * (float)titleHeight); if (!pXAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis2.GetBoundingBox(); topIndent = topIndent - bb.Top + cb.Top; // finally determine its indentation from the top // correct top indendation to take into account plot title if (title_ != "") { topIndent += (int)(titleHeight * 1.3f); } } int rightIndent = padding_; if (!pYAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis2.GetBoundingBox(); // finally determine its indentation from the right rightIndent = (int)(rightIndent + bb.Right - cb.Right); } // now we have all the default calculated positions and we can proceed to // "move" the axes to their right places // primary axes (bottom, left) pXAxis1.PhysicalMin = new Point(cb.Left + leftIndent, cb.Bottom - bottomIndent); pXAxis1.PhysicalMax = new Point(cb.Right - rightIndent, cb.Bottom - bottomIndent); pYAxis1.PhysicalMin = new Point(cb.Left + leftIndent, cb.Bottom - bottomIndent); pYAxis1.PhysicalMax = new Point(cb.Left + leftIndent, cb.Top + topIndent); // secondary axes (top, right) pXAxis2.PhysicalMin = new Point(cb.Left + leftIndent, cb.Top + topIndent); pXAxis2.PhysicalMax = new Point(cb.Right - rightIndent, cb.Top + topIndent); pYAxis2.PhysicalMin = new Point(cb.Right - rightIndent, cb.Bottom - bottomIndent); pYAxis2.PhysicalMax = new Point(cb.Right - rightIndent, cb.Top + topIndent); }
/// <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 FlorenceException("Programmer error."); } g.DrawString( text_, font_, textBrush_, (int)(fromPoint.X - halfSize.Width - offsetFromMiddle.X), (int)(fromPoint.Y - halfSize.Height + offsetFromMiddle.Y)); }
/// <summary> /// Draw the the PlotSurface2D and all contents [axes, drawables, and legend] on the /// supplied graphics surface. /// </summary> /// <param name="g">The graphics surface on which to draw.</param> /// <param name="bounds">A bounding box on this surface that denotes the area on the /// surface to confine drawing to.</param> public void Draw(Graphics g, Rectangle bounds) { // Draw background if (outerBackColor != null && outerBackColor != Color.Transparent) { g.FillRectangle(new SolidBrush(outerBackColor.Value), bounds); } else if (outerBackColor == null) { if (plotBackColor_ != null) { g.FillRectangle(new SolidBrush(plotBackColor_.Value), bounds); } else if (plotBackBrush_ != null) { g.FillRectangle(plotBackBrush_.Get(bounds), bounds); } else { g.FillRectangle(new SolidBrush(Color.White), bounds); } } // determine font sizes and tick scale factor. float scale = DetermineScaleFactor(bounds.Width, bounds.Height); // if there is nothing to plot, return. if (drawables_.Count == 0) { // draw title float x_center = (bounds.Left + bounds.Right) / 2.0f; float y_center = (bounds.Top + bounds.Bottom) / 2.0f; Font scaled_font; if (this.AutoScaleTitle) { scaled_font = Utils.ScaleFont(titleFont_, scale); } else { scaled_font = titleFont_; } g.DrawString(title_, scaled_font, this.titleBrush_, new PointF(x_center, y_center), titleDrawFormat_); return; } // determine the [non physical] axes to draw based on the axis properties set. Axis xAxis1 = null; Axis xAxis2 = null; Axis yAxis1 = null; Axis yAxis2 = null; this.DetermineAxesToDraw(out xAxis1, out xAxis2, out yAxis1, out yAxis2); // apply scale factor to axes as desired. if (xAxis1.AutoScaleTicks) { xAxis1.TickScale = scale; } if (xAxis1.AutoScaleText) { xAxis1.FontScale = scale; } if (yAxis1.AutoScaleTicks) { yAxis1.TickScale = scale; } if (yAxis1.AutoScaleText) { yAxis1.FontScale = scale; } if (xAxis2.AutoScaleTicks) { xAxis2.TickScale = scale; } if (xAxis2.AutoScaleText) { xAxis2.FontScale = scale; } if (yAxis2.AutoScaleTicks) { yAxis2.TickScale = scale; } if (yAxis2.AutoScaleText) { yAxis2.FontScale = scale; } // determine the default physical positioning of those axes. PhysicalAxis pXAxis1 = null; PhysicalAxis pYAxis1 = null; PhysicalAxis pXAxis2 = null; PhysicalAxis pYAxis2 = null; this.DeterminePhysicalAxesToDraw( bounds, xAxis1, xAxis2, yAxis1, yAxis2, out pXAxis1, out pXAxis2, out pYAxis1, out pYAxis2); float oldXAxis2Height = pXAxis2.PhysicalMin.Y; // Apply axes constraints for (int i = 0; i < axesConstraints_.Count; ++i) { ((AxesConstraint)axesConstraints_[i]).ApplyConstraint( pXAxis1, pYAxis1, pXAxis2, pYAxis2); } ///////////////////////////////////////////////////////////////////////// // draw legend if have one. // Note: this will update axes if necessary. Point legendPosition = new Point(0, 0); if (this.legend_ != null) { legend_.UpdateAxesPositions( pXAxis1, pYAxis1, pXAxis2, pYAxis2, this.drawables_, scale, this.padding_, bounds, out legendPosition); } float newXAxis2Height = pXAxis2.PhysicalMin.Y; float titleExtraOffset = oldXAxis2Height - newXAxis2Height; // now we are ready to define the bounding box for the plot area (to use in clipping // operations. plotAreaBoundingBoxCache_ = new Rectangle( Math.Min(pXAxis1.PhysicalMin.X, pXAxis1.PhysicalMax.X), Math.Min(pYAxis1.PhysicalMax.Y, pYAxis1.PhysicalMin.Y), Math.Abs(pXAxis1.PhysicalMax.X - pXAxis1.PhysicalMin.X + 1), Math.Abs(pYAxis1.PhysicalMin.Y - pYAxis1.PhysicalMax.Y + 1) ); bbXAxis1Cache_ = pXAxis1.GetBoundingBox(); bbXAxis2Cache_ = pXAxis2.GetBoundingBox(); bbYAxis1Cache_ = pYAxis1.GetBoundingBox(); bbYAxis2Cache_ = pYAxis2.GetBoundingBox(); // Fill in the background. if (this.plotBackColor_ != null) { g.FillRectangle( new System.Drawing.SolidBrush((Color)this.plotBackColor_), (Rectangle)plotAreaBoundingBoxCache_); } else if (this.plotBackBrush_ != null) { g.FillRectangle( this.plotBackBrush_.Get((Rectangle)plotAreaBoundingBoxCache_), (Rectangle)plotAreaBoundingBoxCache_); } else if (this.plotBackImage_ != null) { g.DrawImage( Utils.TiledImage(this.plotBackImage_, new Size( ((Rectangle)plotAreaBoundingBoxCache_).Width, ((Rectangle)plotAreaBoundingBoxCache_).Height)), (Rectangle)plotAreaBoundingBoxCache_); } // draw title float xt = (pXAxis2.PhysicalMax.X + pXAxis2.PhysicalMin.X) / 2.0f; float yt = bounds.Top + this.padding_ - titleExtraOffset; Font scaledFont; if (this.AutoScaleTitle) { scaledFont = Utils.ScaleFont(titleFont_, scale); } else { scaledFont = titleFont_; } g.DrawString(title_, scaledFont, this.titleBrush_, new PointF(xt, yt), titleDrawFormat_); //count number of new lines in title. int nlCount = 0; for (int i = 0; i < title_.Length; ++i) { if (title_[i] == '\n') { nlCount += 1; } } SizeF s = g.MeasureString(title_, scaledFont); bbTitleCache_ = new Rectangle((int)(xt - s.Width / 2), (int)(yt), (int)(s.Width), (int)(s.Height) * (nlCount + 1)); // draw drawables.. System.Drawing.Drawing2D.SmoothingMode smoothSave = g.SmoothingMode; g.SmoothingMode = this.smoothingMode_; bool legendDrawn = false; for (int i_o = 0; i_o < ordering_.Count; ++i_o) { int i = (int)ordering_.GetByIndex(i_o); double zOrder = (double)ordering_.GetKey(i_o); if (zOrder > this.legendZOrder_) { // draw legend. if (!legendDrawn && this.legend_ != null) { legend_.Draw(g, legendPosition, this.drawables_, scale); legendDrawn = true; } } IDrawable drawable = (IDrawable)drawables_[i]; XAxisPosition xap = (XAxisPosition)xAxisPositions_[i]; YAxisPosition yap = (YAxisPosition)yAxisPositions_[i]; PhysicalAxis drawXAxis; PhysicalAxis drawYAxis; if (xap == XAxisPosition.Bottom) { drawXAxis = pXAxis1; } else { drawXAxis = pXAxis2; } if (yap == YAxisPosition.Left) { drawYAxis = pYAxis1; } else { drawYAxis = pYAxis2; } // set the clipping region.. (necessary for zoom) g.Clip = new Region((Rectangle)plotAreaBoundingBoxCache_); // plot. drawable.Draw(g, drawXAxis, drawYAxis); // reset it.. g.ResetClip(); } if (!legendDrawn && this.legend_ != null) { legend_.Draw(g, legendPosition, this.drawables_, scale); } // cache the physical axes we used on this draw; this.pXAxis1Cache_ = pXAxis1; this.pYAxis1Cache_ = pYAxis1; this.pXAxis2Cache_ = pXAxis2; this.pYAxis2Cache_ = pYAxis2; g.SmoothingMode = smoothSave; // now draw axes. Rectangle axisBounds; pXAxis1.Draw(g, out axisBounds); pXAxis2.Draw(g, out axisBounds); pYAxis1.Draw(g, out axisBounds); pYAxis2.Draw(g, out axisBounds); #if DEBUG_BOUNDING_BOXES g.DrawRectangle(new Pen(Color.Orange), (Rectangle)bbXAxis1Cache_); g.DrawRectangle(new Pen(Color.Orange), (Rectangle)bbXAxis2Cache_); g.DrawRectangle(new Pen(Color.Orange), (Rectangle)bbYAxis1Cache_); g.DrawRectangle(new Pen(Color.Orange), (Rectangle)bbYAxis2Cache_); g.DrawRectangle(new Pen(Color.Red, 5.0F), (Rectangle)plotAreaBoundingBoxCache_); //if(this.ShowLegend)g.DrawRectangle( new Pen(Color.Chocolate, 3.0F), (Rectangle) bbLegendCache_); g.DrawRectangle(new Pen(Color.DeepPink, 2.0F), (Rectangle)bbTitleCache_); #endif }
/// <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> /// 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> /// 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> /// 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 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 ) { if (this.shadow_) { this.DrawLineOrShadow( g, xAxis, yAxis, true ); } this.DrawLineOrShadow( g, xAxis, yAxis, false ); }
/// <summary> /// Applies the constraint to the axes. /// </summary> /// <param name="pXAxis1">The bottom x-axis.</param> /// <param name="pYAxis1">The left y-axis.</param> /// <param name="pXAxis2">The top x-axis.</param> /// <param name="pYAxis2">The right y-axis.</param> public override void ApplyConstraint( PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, PhysicalAxis pXAxis2, PhysicalAxis pYAxis2) { double xWorldRange = Math.Abs(pXAxis1.Axis.WorldMax - pXAxis1.Axis.WorldMin); double xPhysicalRange = Math.Abs(pXAxis1.PhysicalMax.X - pXAxis1.PhysicalMin.X); double xDirPixelSize = xWorldRange / xPhysicalRange; double yWorldRange = Math.Abs(pYAxis1.Axis.WorldMax - pYAxis1.Axis.WorldMin); double yPhysicalRange = Math.Abs(pYAxis1.PhysicalMax.Y - pYAxis1.PhysicalMin.Y); double yDirPixelSize = yWorldRange / yPhysicalRange; double currentAspectRatio = yDirPixelSize / xDirPixelSize; // we want to change the current aspect ratio to be the desired. // to do this, we may only add the world pixel lengths. if (this.a_ > currentAspectRatio) { // want to increase aspect ratio. Therefore, want to add some amount // to yDirPixelSize (numerator). double toAdd = (this.a_ - currentAspectRatio) * xDirPixelSize; int newHeight = (int)(Math.Abs(pYAxis1.Axis.WorldMax - pYAxis1.Axis.WorldMin) / (yDirPixelSize + toAdd)); int changeInHeight = (int)yPhysicalRange - newHeight; int changeBottom = changeInHeight / 2; int changeTop = changeInHeight / 2; if (this.holdFixedX_ != null) { if ((PlotSurface2D.XAxisPosition) this.holdFixedX_ == PlotSurface2D.XAxisPosition.Bottom) { changeBottom = 0; changeTop = changeInHeight; } else { changeBottom = changeInHeight; changeTop = 0; } } pYAxis1.PhysicalMin = new Point(pYAxis1.PhysicalMin.X, pYAxis1.PhysicalMin.Y - changeBottom); pYAxis1.PhysicalMax = new Point(pYAxis1.PhysicalMax.X, pYAxis1.PhysicalMax.Y + changeTop); pYAxis2.PhysicalMin = new Point(pYAxis2.PhysicalMin.X, pYAxis2.PhysicalMin.Y - changeBottom); pYAxis2.PhysicalMax = new Point(pYAxis2.PhysicalMax.X, pYAxis2.PhysicalMax.Y + changeTop); pXAxis1.PhysicalMin = new Point(pXAxis1.PhysicalMin.X, pXAxis1.PhysicalMin.Y - changeBottom); pXAxis1.PhysicalMax = new Point(pXAxis1.PhysicalMax.X, pXAxis1.PhysicalMax.Y - changeBottom); pXAxis2.PhysicalMin = new Point(pXAxis2.PhysicalMin.X, pXAxis2.PhysicalMin.Y + changeTop); pXAxis2.PhysicalMax = new Point(pXAxis2.PhysicalMax.X, pXAxis2.PhysicalMax.Y + changeTop); } else { // want to decrease aspect ratio. Therefore, want to add some amount // to xDirPixelSize (denominator). double toAdd = yDirPixelSize / this.a_ - xDirPixelSize; int newWidth = (int)(Math.Abs(pXAxis1.Axis.WorldMax - pXAxis1.Axis.WorldMin) / (xDirPixelSize + toAdd)); int changeInWidth = (int)xPhysicalRange - newWidth; int changeLeft = changeInWidth / 2; int changeRight = changeInWidth / 2; if (this.holdFixedY_ != null) { if ((PlotSurface2D.YAxisPosition) this.holdFixedY_ == PlotSurface2D.YAxisPosition.Left) { changeLeft = 0; changeRight = changeInWidth; } else { changeLeft = changeInWidth; changeRight = 0; } } pXAxis1.PhysicalMin = new Point(pXAxis1.PhysicalMin.X + changeLeft, pXAxis1.PhysicalMin.Y); pXAxis1.PhysicalMax = new Point(pXAxis1.PhysicalMax.X - changeRight, pXAxis1.PhysicalMax.Y); pXAxis2.PhysicalMin = new Point(pXAxis2.PhysicalMin.X + changeLeft, pXAxis2.PhysicalMin.Y); pXAxis2.PhysicalMax = new Point(pXAxis2.PhysicalMax.X - changeRight, pXAxis2.PhysicalMax.Y); pYAxis1.PhysicalMin = new Point(pYAxis1.PhysicalMin.X + changeLeft, pYAxis1.PhysicalMin.Y); pYAxis1.PhysicalMax = new Point(pYAxis1.PhysicalMax.X + changeLeft, pYAxis1.PhysicalMax.Y); pYAxis2.PhysicalMin = new Point(pYAxis2.PhysicalMin.X - changeRight, pYAxis2.PhysicalMin.Y); pYAxis2.PhysicalMax = new Point(pYAxis2.PhysicalMax.X - changeRight, pYAxis2.PhysicalMax.Y); } }
/// <summary> /// MouseUp method for AxisDrag interaction /// </summary> /// <param name="X">mouse X position</param> /// <param name="Y"> mouse Y position</param> /// <param name="keys"> mouse and keyboard modifiers</param> /// <param name="ps">the InteractivePlotSurface2D</param> public override bool DoMouseUp(int X, int Y, Modifier keys, InteractivePlotSurface2D ps) { if (dragging_) { dragging_ = false; axis_ = null; physicalAxis_ = null; lastPoint_ = new Point(); ps.plotCursor = CursorType.LeftPointer; } return false; }
/// <summary> /// Applies the constraint to the axes. Must be overriden. /// </summary> /// <param name="pXAxis1">The bottom x-axis.</param> /// <param name="pYAxis1">The left y-axis.</param> /// <param name="pXAxis2">The top x-axis.</param> /// <param name="pYAxis2">The right y-axis.</param> public abstract void ApplyConstraint( PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, PhysicalAxis pXAxis2, PhysicalAxis pYAxis2);
/// <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 FlorenceException( "Programmer error." ); } g.DrawString( text_, font_, textBrush_, (int)(fromPoint.X - halfSize.Width - offsetFromMiddle.X), (int)(fromPoint.Y - halfSize.Height + offsetFromMiddle.Y) ); }
private static Point WorldToPhysical(PointD world_point, PhysicalAxis xAxis, PhysicalAxis yAxis) { return(new Point((int)xAxis.WorldToPhysical(world_point.X, false).X, (int)yAxis.WorldToPhysical(world_point.Y, false).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> 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> /// 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 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 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) ); } } } }