/// <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 XwPlotException( "Physical axis is not page aligned" ); } pLength_ = pMax_ - pMin_; }
/// <summary> /// Draws the line plot using the Context and Physical Axes provided /// </summary> /// <param name="ctx">The Drawing Context with which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> /// <param name="drawShadow">If true draw the shadow for the line. If false, draw line.</param> public void DrawLineOrShadow(Context ctx, PhysicalAxis xAxis, PhysicalAxis yAxis, bool drawShadow) { SequenceAdapter data = new SequenceAdapter (DataSource, DataMember, OrdinateData, AbscissaData); ITransform2D t = Transform2D.GetTransformer (xAxis, yAxis); int numberPoints = data.Count; if (data.Count == 0) { return; } ctx.Save (); ctx.SetLineWidth (lineWidth); if (lineDash != null) { ctx.SetLineDash (0, lineDash); } if (numberPoints == 1) { Point physical = t.Transform (data[0]); if (drawShadow) { ctx.SetColor (shadowColor); ctx.MoveTo (physical.X - 0.5 + ShadowOffset.X, physical.Y + ShadowOffset.Y); ctx.LineTo (physical.X + 0.5 + ShadowOffset.X, physical.Y + ShadowOffset.Y); ctx.Stroke (); } else { ctx.SetColor (lineColor); ctx.MoveTo (physical.X-0.5, physical.Y); ctx.LineTo (physical.X+0.5, physical.Y); ctx.Stroke (); } } else { // prepare for clipping double leftCutoff = xAxis.PhysicalToWorld (xAxis.PhysicalMin, false); double rightCutoff = xAxis.PhysicalToWorld (xAxis.PhysicalMax, false); if (leftCutoff > rightCutoff) { Utils.Swap (ref leftCutoff, ref rightCutoff); } if (drawShadow) { // correct cut-offs double shadowCorrection = xAxis.PhysicalToWorld (ShadowOffset, false) - xAxis.PhysicalToWorld (new Point(0,0), false); leftCutoff -= shadowCorrection; rightCutoff -= shadowCorrection; } for (int i = 1; i < numberPoints; ++i) { // check to see if any values null. If so, then continue. double dx1 = data[i-1].X; double dx2 = data[i].X; double dy1 = data[i-1].Y; double dy2 = data[i].Y; if (Double.IsNaN(dx1) || Double.IsNaN(dy1) || Double.IsNaN(dx2) || Double.IsNaN(dy2)) { continue; } // do horizontal clipping here, to speed up if ((dx1 < leftCutoff && dx2 < leftCutoff) || (rightCutoff < dx1 && rightCutoff < dx2)) { continue; } // else draw line. Point p1 = t.Transform (data[i-1]); Point p2 = t.Transform (data[i]); // when very far zoomed in, points can fall ontop of each other, // and g.DrawLine throws an overflow exception if (p1.Equals(p2)) { continue; } if (drawShadow) { ctx.SetColor (shadowColor); ctx.MoveTo (p1.X + ShadowOffset.X, p1.Y + ShadowOffset.Y); ctx.LineTo (p2.X + ShadowOffset.X, p2.Y + ShadowOffset.Y); ctx.Stroke (); } else { ctx.SetColor (lineColor); ctx.MoveTo (p1.X, p1.Y); ctx.LineTo (p2.X, p2.Y); ctx.Stroke (); } } } ctx.Restore (); }
/// <summary> /// 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 ) { double desiredLength = pXAxis1.Axis.WorldLength/pWorldLength_; double currentLength = pXAxis1.PhysicalLength; double delta = currentLength - desiredLength; double changeLeft = delta / 2; double changeRight = delta / 2; if (holdFixedY_ != null) { if ((YAxisPosition)holdFixedY_ == 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> /// 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 (a_ > currentAspectRatio) { // want to increase aspect ratio. Therefore, want to add some amount // to yDirPixelSize (numerator). double toAdd = (a_ - currentAspectRatio) * xDirPixelSize; double newHeight = Math.Abs (pYAxis1.Axis.WorldMax - pYAxis1.Axis.WorldMin) / (yDirPixelSize + toAdd); double changeInHeight = yPhysicalRange - newHeight; double changeBottom = changeInHeight/2; double changeTop = changeInHeight/2; if (holdFixedX_ != null) { if ((XAxisPosition)holdFixedX_ == 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; double newWidth = Math.Abs(pXAxis1.Axis.WorldMax - pXAxis1.Axis.WorldMin) / (xDirPixelSize + toAdd); double changeInWidth = xPhysicalRange - newWidth; double changeLeft = changeInWidth / 2; double changeRight = changeInWidth / 2; if (holdFixedY_ != null) { if ((YAxisPosition)holdFixedY_ == 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> /// 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> /// 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 (a_ > currentAspectRatio) { // want to increase aspect ratio. Therefore, want to add some amount // to yDirPixelSize (numerator). double toAdd = (a_ - currentAspectRatio) * xDirPixelSize; double newHeight = Math.Abs(pYAxis1.Axis.WorldMax - pYAxis1.Axis.WorldMin) / (yDirPixelSize + toAdd); double changeInHeight = yPhysicalRange - newHeight; double changeBottom = changeInHeight / 2; double changeTop = changeInHeight / 2; if (holdFixedX_ != null) { if ((XAxisPosition)holdFixedX_ == 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; double newWidth = Math.Abs(pXAxis1.Axis.WorldMax - pXAxis1.Axis.WorldMin) / (xDirPixelSize + toAdd); double changeInWidth = xPhysicalRange - newWidth; double changeLeft = changeInWidth / 2; double changeRight = changeInWidth / 2; if (holdFixedY_ != null) { if ((YAxisPosition)holdFixedY_ == 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); } }
void DeterminePhysicalAxesToDraw(Rectangle bounds, Axis XAxis1, Axis XAxis2, Axis YAxis1, Axis YAxis2, out PhysicalAxis pXAxis1, out PhysicalAxis pXAxis2, out PhysicalAxis pYAxis1, out PhysicalAxis pYAxis2 ) { Rectangle cb = bounds; pXAxis1 = new PhysicalAxis (XAxis1, new Point (cb.Left, cb.Bottom), new Point (cb.Right, cb.Bottom) ); pYAxis1 = new PhysicalAxis (YAxis1, new Point (cb.Left, cb.Bottom), new Point (cb.Left, cb.Top) ); pXAxis2 = new PhysicalAxis (XAxis2, new Point (cb.Left, cb.Top), new Point (cb.Right, cb.Top) ); pYAxis2 = new PhysicalAxis (YAxis2, new Point (cb.Right, cb.Bottom), new Point (cb.Right, cb.Top) ); double bottomIndent = Padding; if (!pXAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis1.GetBoundingBox (); // finally determine its indentation from the bottom bottomIndent = bottomIndent + bb.Bottom - cb.Bottom; } double leftIndent = Padding; if (!pYAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis1.GetBoundingBox(); // finally determine its indentation from the left leftIndent = leftIndent - bb.Left + cb.Left; } // determine title size double scale = DetermineScaleFactor (bounds.Width, bounds.Height); Font scaled_font; if (AutoScaleTitle) { scaled_font = TitleFont.WithScaledSize (scale); } else { scaled_font = TitleFont; } Size titleSize; using (TextLayout layout = new TextLayout ()) { layout.Font = scaled_font; layout.Text = Title; titleSize = layout.GetSize (); }; double topIndent = Padding; if (!pXAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis2.GetBoundingBox(); topIndent = topIndent - bb.Top + cb.Top; // finally determine its indentation from the top // correct top indendation to take into account plot title if (Title != "" ) { topIndent += titleSize.Height * 1.3; } } double rightIndent = Padding; if (!pYAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis2.GetBoundingBox(); // finally determine its indentation from the right rightIndent += (bb.Right-cb.Right); } // now we have all the default calculated positions and we can proceed to // "move" the axes to their right places // primary axes (bottom, left) pXAxis1.PhysicalMin = new Point (cb.Left+leftIndent, cb.Bottom-bottomIndent); pXAxis1.PhysicalMax = new Point (cb.Right-rightIndent, cb.Bottom-bottomIndent); pYAxis1.PhysicalMin = new Point (cb.Left+leftIndent, cb.Bottom-bottomIndent); pYAxis1.PhysicalMax = new Point (cb.Left+leftIndent, cb.Top+topIndent); // secondary axes (top, right) pXAxis2.PhysicalMin = new Point (cb.Left+leftIndent, cb.Top+topIndent); pXAxis2.PhysicalMax = new Point (cb.Right-rightIndent, cb.Top+topIndent); pYAxis2.PhysicalMin = new Point (cb.Right-rightIndent, cb.Bottom-bottomIndent); pYAxis2.PhysicalMax = new Point (cb.Right-rightIndent, cb.Top+topIndent); }
/// <summary> /// Draw the the PlotSurface and contents (axes, drawables, and legend) using the /// Drawing Context supplied and the bounding rectangle for the PlotSurface to cover /// </summary> /// <param name="ctx">The Drawing Context with which to draw.</param> /// <param name="bounds">The rectangle within which to draw</param> public void Draw(Context ctx, Rectangle bounds) { Point titleOrigin = Point.Zero; ctx.Save (); // determine font sizes and tick scale factor. double scale = DetermineScaleFactor (bounds.Width, bounds.Height); // if there is nothing to plot, draw title and return. if (drawables.Count == 0) { // draw title //TODO: Title should be centred here - not its origin Point origin = Point.Zero; titleOrigin.X = bounds.Width/2; titleOrigin.Y = bounds.Height/2; DrawTitle (ctx, titleOrigin, scale); ctx.Restore (); return; } // determine the [non physical] axes to draw based on the axis properties set. Axis XAxis1 = null; Axis XAxis2 = null; Axis YAxis1 = null; Axis YAxis2 = null; DetermineAxesToDraw (out XAxis1, out XAxis2, out YAxis1, out YAxis2); // apply scale factor to axes as desired. if (XAxis1.AutoScaleTicks) { XAxis1.TickScale = scale; } if (XAxis1.AutoScaleText) { XAxis1.FontScale = scale; } if (YAxis1.AutoScaleTicks) { YAxis1.TickScale = scale; } if (YAxis1.AutoScaleText) { YAxis1.FontScale = scale; } if (XAxis2.AutoScaleTicks) { XAxis2.TickScale = scale; } if (XAxis2.AutoScaleText) { XAxis2.FontScale = scale; } if (YAxis2.AutoScaleTicks) { YAxis2.TickScale = scale; } if (YAxis2.AutoScaleText) { YAxis2.FontScale = scale; } // determine the default physical positioning of those axes. PhysicalAxis pXAxis1 = null; PhysicalAxis pYAxis1 = null; PhysicalAxis pXAxis2 = null; PhysicalAxis pYAxis2 = null; DeterminePhysicalAxesToDraw ( bounds, XAxis1, XAxis2, YAxis1, YAxis2, out pXAxis1, out pXAxis2, out pYAxis1, out pYAxis2 ); double oldXAxis2Height = pXAxis2.PhysicalMin.Y; // Apply axes constraints for (int i=0; i<axesConstraints.Count; ++i) { ((AxesConstraint)axesConstraints[i]).ApplyConstraint( pXAxis1, pYAxis1, pXAxis2, pYAxis2 ); } // draw legend if have one. // Note: this will update axes if necessary. Point legendPosition = new Point(0,0); if (legend != null) { legend.UpdateAxesPositions ( pXAxis1, pYAxis1, pXAxis2, pYAxis2, drawables, scale, Padding, bounds, out legendPosition ); } double newXAxis2Height = pXAxis2.PhysicalMin.Y; double titleExtraOffset = oldXAxis2Height - newXAxis2Height; // now we are ready to define the clipping region plotAreaBoundingBoxCache = new Rectangle ( Math.Min (pXAxis1.PhysicalMin.X, pXAxis1.PhysicalMax.X), Math.Min (pYAxis1.PhysicalMax.Y, pYAxis1.PhysicalMin.Y), Math.Abs (pXAxis1.PhysicalMax.X - pXAxis1.PhysicalMin.X + 1), Math.Abs (pYAxis1.PhysicalMin.Y - pYAxis1.PhysicalMax.Y + 1) ); bbXAxis1Cache = pXAxis1.GetBoundingBox (); bbXAxis2Cache = pXAxis2.GetBoundingBox (); bbYAxis1Cache = pYAxis1.GetBoundingBox (); bbYAxis2Cache = pYAxis2.GetBoundingBox (); Rectangle plotBounds = (Rectangle)plotAreaBoundingBoxCache; // set the clipping region.. (necessary for zoom) // Note: although clipping is enforced by the clip region, it is probably more efficient // for each Drawable to check against the plotBounds and not draw if points are outside. // This hasn't yet been implemented ctx.Save (); ctx.Rectangle (plotBounds); ctx.Clip (); // Fill in the plot background. if (plotBackImage != null) { // Ensure plotBounds has integer size for correct tiling/drawing plotBounds.Width = Math.Truncate (plotBounds.Width); plotBounds.Height = Math.Truncate (plotBounds.Height); ctx.DrawImage (Utils.TiledImage (plotBackImage , plotBounds.Size), plotBounds); } else if (plotBackGradient != null) { // Scale plotBackGradient to plotBounds double startX = plotBounds.X + (plotBackGradient.StartPoint.X * plotBounds.Width); double startY = plotBounds.Y + (plotBackGradient.StartPoint.Y * plotBounds.Height); double endX = plotBounds.X + (plotBackGradient.EndPoint.X * plotBounds.Width); double endY = plotBounds.Y + (plotBackGradient.EndPoint.Y * plotBounds.Height); LinearGradient g = new LinearGradient (startX, startY, endX, endY); g.AddColorStop (0, plotBackGradient.StartColor); g.AddColorStop (1, plotBackGradient.EndColor); ctx.Rectangle (plotBounds); ctx.Pattern = g; ctx.Fill (); } else { ctx.Rectangle (plotBounds); ctx.SetColor (plotBackColor); ctx.Fill (); } // draw title at centre of Physical X-axis and at top of plot titleOrigin.X = (pXAxis2.PhysicalMax.X + pXAxis2.PhysicalMin.X)/2.0; titleOrigin.Y = bounds.Top + Padding - titleExtraOffset; Size s = DrawTitle (ctx, titleOrigin, scale); bbTitleCache = new Rectangle (titleOrigin.X-s.Width/2, titleOrigin.Y, s.Width, s.Height); // draw drawables.. bool legendDrawn = false; for (int i_o = 0; i_o < ordering.Count; ++i_o) { int i = (int)ordering.GetByIndex (i_o); double zOrder = (double)ordering.GetKey (i_o); if (zOrder > legendZOrder) { // draw legend. if (!legendDrawn && legend != null) { legend.Draw (ctx, legendPosition, drawables, scale); legendDrawn = true; } } IDrawable drawable = (IDrawable)drawables[i]; XAxisPosition xap = (XAxisPosition)xAxisPositions[i]; YAxisPosition yap = (YAxisPosition)yAxisPositions[i]; PhysicalAxis drawXAxis; PhysicalAxis drawYAxis; if (xap == XAxisPosition.Bottom) { drawXAxis = pXAxis1; } else { drawXAxis = pXAxis2; } if (yap == YAxisPosition.Left) { drawYAxis = pYAxis1; } else { drawYAxis = pYAxis2; } drawable.Draw (ctx, drawXAxis, drawYAxis); } if (!legendDrawn && legend != null) { legend.Draw (ctx, legendPosition, drawables, scale); } ctx.Restore (); // end of clipping region // cache the physical axes we used on this draw; pXAxis1Cache = pXAxis1; pYAxis1Cache = pYAxis1; pXAxis2Cache = pXAxis2; pYAxis2Cache = pYAxis2; // now draw axes. Rectangle axisBounds; pXAxis1.Draw (ctx, out axisBounds); pXAxis2.Draw (ctx, out axisBounds); pYAxis1.Draw (ctx, out axisBounds); pYAxis2.Draw (ctx, out axisBounds); #if DEBUG_BOUNDING_BOXES ctx.SetColor (Colors.Orange); ctx.Rectangle ((Rectangle)bbXAxis1Cache); ctx.Rectangle ((Rectangle)bbXAxis2Cache); ctx.Rectangle ((Rectangle)bbYAxis1Cache); ctx.Rectangle ((Rectangle)bbYAxis2Cache); ctx.Stroke (); ctx.SetColor (Colors.Red); ctx.Rectangle ((Rectangle)plotAreaBoundingBoxCache); ctx.Rectangle ((Rectangle)bbTitleCache); ctx.Stroke (); #endif ctx.Restore (); }
/// <summary> /// OnButtonPressed method for AxisDrag interaction /// </summary> public override bool OnButtonPressed(ButtonEventArgs args, PlotCanvas pc) { // 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 scaling if (pc.PlotAreaBoundingBoxCache.Contains(args.X, args.Y)) { return false; } if (args.Button == PointerButton.Left) { // see if hit with axis. NB Only one axis object will be returned ArrayList objects = pc.HitTest (new Point(args.X, args.Y)); foreach (object o in objects) { if (o is Axis) { dragging = true; Axis axis = (Axis)o; if (pc.PhysicalXAxis1Cache.Axis == axis) { physicalAxis = pc.PhysicalXAxis1Cache; translateX = true; } else if (pc.PhysicalXAxis2Cache.Axis == axis) { physicalAxis = pc.PhysicalXAxis2Cache; translateX = true; } else if (pc.PhysicalYAxis1Cache.Axis == axis) { physicalAxis = pc.PhysicalYAxis1Cache; translateY = true; } else if (pc.PhysicalYAxis2Cache.Axis == axis) { physicalAxis = pc.PhysicalYAxis2Cache; translateY = true; } lastPoint = new Point (args.X, args.Y); return false; } } } return false; }
/// <summary> /// OnButtonReleased method for AxisDrag interaction /// </summary> public override bool OnButtonReleased(ButtonEventArgs args, PlotCanvas pc) { if (dragging) { physicalAxis = null; lastPoint = unset; dragging = false; translateX = false; translateY = false; } return false; }
/// <summary> /// Draws the line plot using the Context and Physical Axes provided /// </summary> /// <param name="ctx">The Drawing Context with which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> /// <param name="drawShadow">If true draw the shadow for the line. If false, draw line.</param> public void DrawLineOrShadow(Context ctx, PhysicalAxis xAxis, PhysicalAxis yAxis, bool drawShadow) { SequenceAdapter data = new SequenceAdapter(DataSource, DataMember, OrdinateData, AbscissaData); ITransform2D t = Transform2D.GetTransformer(xAxis, yAxis); int numberPoints = data.Count; if (data.Count == 0) { return; } ctx.Save(); ctx.SetLineWidth(lineWidth); if (lineDash != null) { ctx.SetLineDash(0, lineDash); } if (numberPoints == 1) { Point physical = t.Transform(data[0]); if (drawShadow) { ctx.SetColor(shadowColor); ctx.MoveTo(physical.X - 0.5 + ShadowOffset.X, physical.Y + ShadowOffset.Y); ctx.LineTo(physical.X + 0.5 + ShadowOffset.X, physical.Y + ShadowOffset.Y); ctx.Stroke(); } else { ctx.SetColor(lineColor); ctx.MoveTo(physical.X - 0.5, physical.Y); ctx.LineTo(physical.X + 0.5, physical.Y); ctx.Stroke(); } } else { // prepare for clipping double leftCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMin, false); double rightCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMax, false); if (leftCutoff > rightCutoff) { Utils.Swap(ref leftCutoff, ref rightCutoff); } if (drawShadow) { // correct cut-offs double shadowCorrection = xAxis.PhysicalToWorld(ShadowOffset, false) - xAxis.PhysicalToWorld(new Point(0, 0), false); leftCutoff -= shadowCorrection; rightCutoff -= shadowCorrection; } for (int i = 1; i < numberPoints; ++i) { // check to see if any values null. If so, then continue. double dx1 = data[i - 1].X; double dx2 = data[i].X; double dy1 = data[i - 1].Y; double dy2 = data[i].Y; if (Double.IsNaN(dx1) || Double.IsNaN(dy1) || Double.IsNaN(dx2) || Double.IsNaN(dy2)) { continue; } // do horizontal clipping here, to speed up if ((dx1 < leftCutoff && dx2 < leftCutoff) || (rightCutoff < dx1 && rightCutoff < dx2)) { continue; } // else draw line. Point p1 = t.Transform(data[i - 1]); Point p2 = t.Transform(data[i]); // when very far zoomed in, points can fall ontop of each other, // and g.DrawLine throws an overflow exception if (p1.Equals(p2)) { continue; } if (drawShadow) { ctx.SetColor(shadowColor); ctx.MoveTo(p1.X + ShadowOffset.X, p1.Y + ShadowOffset.Y); ctx.LineTo(p2.X + ShadowOffset.X, p2.Y + ShadowOffset.Y); ctx.Stroke(); } else { ctx.SetColor(lineColor); ctx.MoveTo(p1.X, p1.Y); ctx.LineTo(p2.X, p2.Y); ctx.Stroke(); } } } ctx.Restore(); }
/// <summary> /// Draws the 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 bar = new Rectangle(xPos1.X + xoff, yPos1.Y, width - 2 * xoff, height); ctx.Rectangle(bar); if (Filled) { if (bar.Height != 0 && bar.Width != 0) { if (FillGradient != null) { // Scale FillGradient to bar rectangle double sX = bar.X + fillGradient.StartPoint.X * bar.Width; double sY = bar.Y + fillGradient.StartPoint.Y * bar.Height; double eX = bar.X + fillGradient.EndPoint.X * bar.Width; double eY = bar.Y + fillGradient.EndPoint.Y * bar.Height; LinearGradient g = new LinearGradient(sX, sY, eX, eY); g.AddColorStop(0, FillGradient.StartColor); g.AddColorStop(1, FillGradient.EndColor); ctx.Pattern = g; } else { ctx.SetColor(FillColor); } ctx.FillPreserve(); } } ctx.SetColor(BorderColor); ctx.Stroke(); } ctx.Restore(); }
/// <summary> /// Draws the plot using the Drawing Context and X, Y axes supplied. /// </summary> public override void Draw(Context ctx, 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); TextLayout layout = new TextLayout(); layout.Font = Font; ctx.Save(); ctx.SetColor(Colors.Black); for (int i = 0; i < data.Count; ++i) { try { Point p = data[i]; if (!Double.IsNaN(p.X) && !Double.IsNaN(p.Y)) { Point xPos = xAxis.WorldToPhysical(p.X, false); Point yPos = yAxis.WorldToPhysical(p.Y, false); // first plot the marker Marker.Draw(ctx, xPos.X, yPos.Y); // then the label if (textData[i] != "") { layout.Text = textData[i]; Size size = layout.GetSize(); switch (labelTextPosition) { case LabelPositions.Above: p.X = xPos.X - size.Width / 2; p.Y = yPos.Y - size.Height - Marker.Size * 2 / 3; break; case LabelPositions.Below: p.X = xPos.X - size.Width / 2; p.Y = yPos.Y + Marker.Size * 2 / 3; break; case LabelPositions.Left: p.X = xPos.X - size.Width - Marker.Size * 2 / 3; p.Y = yPos.Y - size.Height / 2; break; case LabelPositions.Right: p.X = xPos.X + Marker.Size * 2 / 3; p.Y = yPos.Y - size.Height / 2; break; } ctx.DrawTextLayout(layout, p); } } } catch { throw new XwPlotException("Error in TextPlot.Draw"); } } ctx.Restore(); }
/// <summary> /// OnButtonPressed method for AxisScale interaction /// </summary> public override bool OnButtonPressed(ButtonEventArgs args, PlotCanvas pc) { // 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 scaling if (pc.PlotAreaBoundingBoxCache.Contains(args.X, args.Y)) { return false; } if (args.Button == PointerButton.Left) { // see if hit with axis. NB Only one axis object will be returned ArrayList objects = pc.HitTest (new Point(args.X, args.Y)); foreach (object o in objects) { if (o is Axis) { dragging = true; axis = (Axis)o; if (pc.PhysicalXAxis1Cache.Axis == axis) { physicalAxis = pc.PhysicalXAxis1Cache; //pc.plotCursor = CursorType.LeftRight; } else if (pc.PhysicalXAxis2Cache.Axis == axis) { physicalAxis = pc.PhysicalXAxis2Cache; //ps.plotCursor = CursorType.LeftRight; } else if (pc.PhysicalYAxis1Cache.Axis == axis) { physicalAxis = pc.PhysicalYAxis1Cache; //pc.plotCursor = CursorType.UpDown; } else if (pc.PhysicalYAxis2Cache.Axis == axis) { physicalAxis = pc.PhysicalYAxis2Cache; //pc.plotCursor = CursorType.UpDown; } startPoint = new Point (args.X, args.Y); lastPoint = startPoint; // evaluate focusRatio about which axis is expanded double x = startPoint.X - physicalAxis.PhysicalMin.X; double y = startPoint.Y - physicalAxis.PhysicalMin.Y; double r = Math.Sqrt(x*x + y*y); focusRatio = r/physicalAxis.PhysicalLength; return false; } } } return false; }
void Init() { drawables = new ArrayList (); xAxisPositions = new ArrayList (); yAxisPositions = new ArrayList (); zPositions = new ArrayList (); ordering = new SortedList (); PlotBackColor = Colors.White; TitleFont = Font.SystemSansSerifFont.WithSize (14); TitleColor = Colors.Black; Padding = 10; Title = ""; Legend = null; autoScaletitle = false; autoScaleAutoGeneratedAxes = false; XAxis1 = null; XAxis2 = null; YAxis1 = null; YAxis2 = null; pXAxis1Cache = null; pYAxis1Cache = null; pXAxis2Cache = null; pYAxis2Cache = null; axesConstraints = new ArrayList (); }
/// <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 XwPlotException( "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> /// 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> /// Draw using the Drawing Context and Axes supplied /// </summary> /// <remarks>TODO: block positions may be off by a pixel or so. maybe. Re-think calculations</remarks> public void Draw(Context ctx, 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 / xStep; double worldHeight = yAxis.Axis.WorldMax - yAxis.Axis.WorldMin; double numBlocksVertical = worldHeight / 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; ctx.Save(); for (int i = 0; i < data.GetLength(0); ++i) { for (int j = 0; j < data.GetLength(1); ++j) { double wX = (double)j * xStep + xStart; double wY = (double)i * yStep + yStart; if (!hPositive) { wY += yStep; } if (!wPositive) { wX += xStep; } if (Center) { wX -= xStep / 2.0; wY -= yStep / 2.0; } Color color = Gradient.GetColor((data[i, j] - DataMin) / (DataMax - DataMin)); ctx.SetColor(color); double x = xAxis.WorldToPhysical(wX, false).X; double y = yAxis.WorldToPhysical(wY, false).Y; ctx.Rectangle(x, y, blockWidth, blockHeight); ctx.Fill(); } } ctx.Restore(); }
/// <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> /// Draw the the PlotSurface and contents (axes, drawables, and legend) using the /// Drawing Context supplied and the bounding rectangle for the PlotSurface to cover /// </summary> /// <param name="ctx">The Drawing Context with which to draw.</param> /// <param name="bounds">The rectangle within which to draw</param> public void Draw(Context ctx, Rectangle bounds) { Point titleOrigin = Point.Zero; ctx.Save(); // determine font sizes and tick scale factor. double scale = DetermineScaleFactor(bounds.Width, bounds.Height); // if there is nothing to plot, draw title and return. if (drawables.Count == 0) { // draw title //TODO: Title should be centred here - not its origin Point origin = Point.Zero; titleOrigin.X = bounds.Width / 2; titleOrigin.Y = bounds.Height / 2; DrawTitle(ctx, titleOrigin, scale); ctx.Restore(); return; } // determine the [non physical] axes to draw based on the axis properties set. Axis XAxis1 = null; Axis XAxis2 = null; Axis YAxis1 = null; Axis YAxis2 = null; DetermineAxesToDraw(out XAxis1, out XAxis2, out YAxis1, out YAxis2); // apply scale factor to axes as desired. if (XAxis1.AutoScaleTicks) { XAxis1.TickScale = scale; } if (XAxis1.AutoScaleText) { XAxis1.FontScale = scale; } if (YAxis1.AutoScaleTicks) { YAxis1.TickScale = scale; } if (YAxis1.AutoScaleText) { YAxis1.FontScale = scale; } if (XAxis2.AutoScaleTicks) { XAxis2.TickScale = scale; } if (XAxis2.AutoScaleText) { XAxis2.FontScale = scale; } if (YAxis2.AutoScaleTicks) { YAxis2.TickScale = scale; } if (YAxis2.AutoScaleText) { YAxis2.FontScale = scale; } // determine the default physical positioning of those axes. PhysicalAxis pXAxis1 = null; PhysicalAxis pYAxis1 = null; PhysicalAxis pXAxis2 = null; PhysicalAxis pYAxis2 = null; DeterminePhysicalAxesToDraw( bounds, XAxis1, XAxis2, YAxis1, YAxis2, out pXAxis1, out pXAxis2, out pYAxis1, out pYAxis2); double oldXAxis2Height = pXAxis2.PhysicalMin.Y; // Apply axes constraints for (int i = 0; i < axesConstraints.Count; ++i) { ((AxesConstraint)axesConstraints[i]).ApplyConstraint( pXAxis1, pYAxis1, pXAxis2, pYAxis2); } // draw legend if have one. // Note: this will update axes if necessary. Point legendPosition = new Point(0, 0); if (legend != null) { legend.UpdateAxesPositions( pXAxis1, pYAxis1, pXAxis2, pYAxis2, drawables, scale, Padding, bounds, out legendPosition); } double newXAxis2Height = pXAxis2.PhysicalMin.Y; double titleExtraOffset = oldXAxis2Height - newXAxis2Height; // now we are ready to define the clipping region plotAreaBoundingBoxCache = new Rectangle( Math.Min(pXAxis1.PhysicalMin.X, pXAxis1.PhysicalMax.X), Math.Min(pYAxis1.PhysicalMax.Y, pYAxis1.PhysicalMin.Y), Math.Abs(pXAxis1.PhysicalMax.X - pXAxis1.PhysicalMin.X + 1), Math.Abs(pYAxis1.PhysicalMin.Y - pYAxis1.PhysicalMax.Y + 1) ); bbXAxis1Cache = pXAxis1.GetBoundingBox(); bbXAxis2Cache = pXAxis2.GetBoundingBox(); bbYAxis1Cache = pYAxis1.GetBoundingBox(); bbYAxis2Cache = pYAxis2.GetBoundingBox(); Rectangle plotBounds = (Rectangle)plotAreaBoundingBoxCache; // set the clipping region.. (necessary for zoom) // Note: although clipping is enforced by the clip region, it is probably more efficient // for each Drawable to check against the plotBounds and not draw if points are outside. // This hasn't yet been implemented ctx.Save(); ctx.Rectangle(plotBounds); ctx.Clip(); // Fill in the plot background. if (plotBackImage != null) { // Ensure plotBounds has integer size for correct tiling/drawing plotBounds.Width = Math.Truncate(plotBounds.Width); plotBounds.Height = Math.Truncate(plotBounds.Height); ctx.DrawImage(Utils.TiledImage(plotBackImage, plotBounds.Size), plotBounds); } else if (plotBackGradient != null) { // Scale plotBackGradient to plotBounds double startX = plotBounds.X + (plotBackGradient.StartPoint.X * plotBounds.Width); double startY = plotBounds.Y + (plotBackGradient.StartPoint.Y * plotBounds.Height); double endX = plotBounds.X + (plotBackGradient.EndPoint.X * plotBounds.Width); double endY = plotBounds.Y + (plotBackGradient.EndPoint.Y * plotBounds.Height); LinearGradient g = new LinearGradient(startX, startY, endX, endY); g.AddColorStop(0, plotBackGradient.StartColor); g.AddColorStop(1, plotBackGradient.EndColor); ctx.Rectangle(plotBounds); ctx.Pattern = g; ctx.Fill(); } else { ctx.Rectangle(plotBounds); ctx.SetColor(plotBackColor); ctx.Fill(); } // draw title at centre of Physical X-axis and at top of plot titleOrigin.X = (pXAxis2.PhysicalMax.X + pXAxis2.PhysicalMin.X) / 2.0; titleOrigin.Y = bounds.Top + Padding - titleExtraOffset; Size s = DrawTitle(ctx, titleOrigin, scale); bbTitleCache = new Rectangle(titleOrigin.X - s.Width / 2, titleOrigin.Y, s.Width, s.Height); // draw drawables.. bool legendDrawn = false; for (int i_o = 0; i_o < ordering.Count; ++i_o) { int i = (int)ordering.GetByIndex(i_o); double zOrder = (double)ordering.GetKey(i_o); if (zOrder > legendZOrder) { // draw legend. if (!legendDrawn && legend != null) { legend.Draw(ctx, legendPosition, drawables, scale); legendDrawn = true; } } IDrawable drawable = (IDrawable)drawables[i]; XAxisPosition xap = (XAxisPosition)xAxisPositions[i]; YAxisPosition yap = (YAxisPosition)yAxisPositions[i]; PhysicalAxis drawXAxis; PhysicalAxis drawYAxis; if (xap == XAxisPosition.Bottom) { drawXAxis = pXAxis1; } else { drawXAxis = pXAxis2; } if (yap == YAxisPosition.Left) { drawYAxis = pYAxis1; } else { drawYAxis = pYAxis2; } drawable.Draw(ctx, drawXAxis, drawYAxis); } if (!legendDrawn && legend != null) { legend.Draw(ctx, legendPosition, drawables, scale); } ctx.Restore(); // end of clipping region // cache the physical axes we used on this draw; pXAxis1Cache = pXAxis1; pYAxis1Cache = pYAxis1; pXAxis2Cache = pXAxis2; pYAxis2Cache = pYAxis2; // now draw axes. Rectangle axisBounds; pXAxis1.Draw(ctx, out axisBounds); pXAxis2.Draw(ctx, out axisBounds); pYAxis1.Draw(ctx, out axisBounds); pYAxis2.Draw(ctx, out axisBounds); #if DEBUG_BOUNDING_BOXES ctx.SetColor(Colors.Orange); ctx.Rectangle((Rectangle)bbXAxis1Cache); ctx.Rectangle((Rectangle)bbXAxis2Cache); ctx.Rectangle((Rectangle)bbYAxis1Cache); ctx.Rectangle((Rectangle)bbYAxis2Cache); ctx.Stroke(); ctx.SetColor(Colors.Red); ctx.Rectangle((Rectangle)plotAreaBoundingBoxCache); ctx.Rectangle((Rectangle)bbTitleCache); ctx.Stroke(); #endif ctx.Restore(); }
/// <summary> /// Draw the filled region /// </summary> /// <param name="g">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) { ITransform2D t = Transform2D.GetTransformer (xAxis, yAxis); ctx.Save (); if (hl1 != null && hl2 != null) { ctx.MoveTo (t.Transform (xAxis.Axis.WorldMin, hl1.OrdinateValue)); ctx.LineTo (t.Transform (xAxis.Axis.WorldMax, hl1.OrdinateValue)); ctx.LineTo (t.Transform (xAxis.Axis.WorldMax, hl2.OrdinateValue)); ctx.LineTo (t.Transform (xAxis.Axis.WorldMin, hl2.OrdinateValue)); ctx.ClosePath (); } else if (vl1 != null && vl2 != null) { ctx.MoveTo (t.Transform (vl1.AbscissaValue, yAxis.Axis.WorldMin)); ctx.LineTo (t.Transform (vl1.AbscissaValue, yAxis.Axis.WorldMax)); ctx.LineTo (t.Transform (vl2.AbscissaValue, yAxis.Axis.WorldMax)); ctx.LineTo (t.Transform (vl2.AbscissaValue, yAxis.Axis.WorldMin)); ctx.ClosePath (); } else if (lp1 != null && lp2 != null) { SequenceAdapter a1 = new SequenceAdapter (lp1.DataSource, lp1.DataMember, lp1.OrdinateData, lp1.AbscissaData); SequenceAdapter a2 = new SequenceAdapter (lp2.DataSource, lp2.DataMember, lp2.OrdinateData, lp2.AbscissaData); // Start at first point of LinePlot 1 within plot bounds int start = 0; while (t.Transform (a1 [start]).X < xAxis.PhysicalMin.X) { ++start; } Point first = t.Transform (a1 [start]); ctx.MoveTo (first); // Join LinePlot 1 points in ascending order Point next; for (int i = start+1; i < a1.Count-1; ++i) { next = t.Transform (a1 [i]); if (next.X > xAxis.PhysicalMax.X) break; ctx.LineTo (next); } // Then join LinePlot 2 points in descending order int end = a2.Count-1; while (t.Transform (a2 [end]).X > xAxis.PhysicalMax.X) { --end; } for (int i = end; i > 0; --i) { next = t.Transform (a2 [i]); if (next.X < xAxis.PhysicalMin.X) break; ctx.LineTo (next); } ctx.LineTo (first); ctx.ClosePath (); } else { throw new XwPlotException ("Filled Region bounds not defined"); } ctx.SetColor (FillColor); ctx.Fill (); ctx.Restore (); }
/// <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 ) { if (xAxisPosition_ != null) { if ((XAxisPosition)xAxisPosition_ == XAxisPosition.Bottom) { pXAxis1.PhysicalMin = new Point (pXAxis1.PhysicalMin.X, position_ ); pXAxis1.PhysicalMax = new Point (pXAxis1.PhysicalMax.X, position_ ); pYAxis1.PhysicalMin = new Point (pYAxis1.PhysicalMin.X, position_ ); pYAxis2.PhysicalMin = new Point (pYAxis2.PhysicalMin.X, position_ ); } else { pXAxis2.PhysicalMin = new Point (pXAxis2.PhysicalMin.X, position_ ); pXAxis2.PhysicalMax = new Point (pXAxis2.PhysicalMax.X, position_ ); pYAxis1.PhysicalMax = new Point (pYAxis1.PhysicalMax.X, position_ ); pYAxis2.PhysicalMax = new Point (pYAxis2.PhysicalMax.X, position_ ); } } else if (yAxisPosition_ != null) { if ((YAxisPosition)yAxisPosition_ == YAxisPosition.Left) { pYAxis1.PhysicalMin = new Point (position_, pYAxis1.PhysicalMin.Y ); pYAxis1.PhysicalMax = new Point (position_, pYAxis1.PhysicalMax.Y ); pXAxis1.PhysicalMin = new Point (position_, pXAxis1.PhysicalMin.Y ); pXAxis2.PhysicalMin = new Point (position_, pXAxis2.PhysicalMin.Y ); } else { pYAxis2.PhysicalMin = new Point (position_, pYAxis2.PhysicalMin.Y ); pYAxis2.PhysicalMax = new Point (position_, pYAxis2.PhysicalMax.Y ); pXAxis1.PhysicalMax = new Point (position_, pXAxis1.PhysicalMax.Y ); pXAxis2.PhysicalMax = new Point (position_, pXAxis2.PhysicalMax.Y ); } } }
/// <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); ctx.Save(); ctx.SetColor(Color); ctx.SetLineWidth(1); 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 { p2 = p1; } double offset = p1.X - p2.X; p2.X = p1.X + offset; p2.Y = p1.Y; p3 = p2; } if (centre) { 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; } 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> /// 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 desiredLength = pYAxis1.Axis.WorldLength/pWorldLength_; double currentLength = pYAxis1.PhysicalLength; double delta = currentLength - desiredLength; double changeBottom = -delta / 2; double changeTop = -delta / 2; if (holdFixedX_ != null) { if ((XAxisPosition)holdFixedX_ == 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> /// 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> /// Draws the line plot using the Context and Physical Axes provided /// </summary> /// <param name="ctx">The Drawing Context with which to draw.</param> /// <param name="xAxis">The X-Axis to draw against.</param> /// <param name="yAxis">The Y-Axis to draw against.</param> public void Draw(Context ctx, PhysicalAxis xAxis, PhysicalAxis yAxis) { if (shadow) { DrawLineOrShadow (ctx, xAxis, yAxis, true); } DrawLineOrShadow (ctx, xAxis, yAxis, false); }
/// <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 XwPlotException("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 grid /// </summary> /// <param name="ctx">The Drawing Context with 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(Context ctx, PhysicalAxis xAxis, PhysicalAxis yAxis) { ArrayList xLargePositions = null; ArrayList yLargePositions = null; ArrayList xSmallPositions = null; ArrayList ySmallPositions = null; ctx.Save (); // Draw MajorGrid gridColor = MajorGridColor; gridDash = majorGridDash; if (horizontalGridType != GridType.None) { xAxis.Axis.WorldTickPositions_FirstPass (xAxis.PhysicalMin, xAxis.PhysicalMax, out xLargePositions, out xSmallPositions); DrawGridLines (ctx, xAxis, yAxis, xLargePositions, true); } if (verticalGridType != GridType.None) { yAxis.Axis.WorldTickPositions_FirstPass (yAxis.PhysicalMin, yAxis.PhysicalMax, out yLargePositions, out ySmallPositions); DrawGridLines (ctx, yAxis, xAxis, yLargePositions, false); } // Draw MinorGrid gridColor = MinorGridColor; gridDash = minorGridDash; if (horizontalGridType == GridType.Fine) { xAxis.Axis.WorldTickPositions_SecondPass (xAxis.PhysicalMin, xAxis.PhysicalMax, xLargePositions, ref xSmallPositions); DrawGridLines(ctx, xAxis, yAxis, xSmallPositions, true); } if (verticalGridType == GridType.Fine) { yAxis.Axis.WorldTickPositions_SecondPass (yAxis.PhysicalMin, yAxis.PhysicalMax, yLargePositions, ref ySmallPositions); DrawGridLines (ctx, yAxis, xAxis, ySmallPositions, false); } ctx.Restore (); }
/// <summary> /// OnButtonReleased method for AxisScale interaction /// </summary> public override bool OnButtonReleased(ButtonEventArgs args, PlotCanvas pc) { if (dragging) { dragging = false; axis = null; physicalAxis = null; lastPoint = new Point(); //pc.plotCursor = CursorType.LeftPointer; } return false; }
/// <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); } ctx.SetLineWidth (1); ctx.SetColor (gridColor); ctx.SetLineDash (0, gridDash); ctx.Stroke (); }
/// <summary> /// Updates the PlotSurface 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, double scale, double padding, Rectangle bounds, out Point position) { double leftIndent = 0; double rightIndent = 0; double bottomIndent = 0; double 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 = 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 (xAttach_ == XAxisPosition.Bottom) { position.Y += pYAxis1.PhysicalMin.Y; if (horizontalEdgePlacement_ == Legend.Placement.Inside) { position.Y -= legendWidthHeight.Height; } } else { position.Y += pYAxis1.PhysicalMax.Y; if (horizontalEdgePlacement_ == Legend.Placement.Outside) { position.Y -= legendWidthHeight.Height; } } // x position.X = xOffset_; if (yAttach_ == YAxisPosition.Left) { if (verticalEdgePlacement_ == Legend.Placement.Outside) { position.X -= legendWidthHeight.Width; } position.X += pXAxis1.PhysicalMin.X; } else { if (verticalEdgePlacement_ == Legend.Placement.Inside) { position.X -= legendWidthHeight.Width; } position.X += pXAxis1.PhysicalMax.X; } // determine update amounts for axes if (!neverShiftAxes_) { if (position.X < padding) { double 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) { double changeAmount = (position.X - bounds.Right + legendWidthHeight.Width + padding); // only allow axes to move away from bounds. if (changeAmount > 0) { rightIndent = changeAmount; } position.X -= changeAmount; } if (position.Y < padding) { double changeAmount = -position.Y + padding; // only allow axes to move away from bounds. if (changeAmount > 0) { topIndent = changeAmount; } position.Y += changeAmount; } if (position.Y + legendWidthHeight.Height > bounds.Bottom - padding) { double changeAmount = (position.Y - bounds.Bottom + legendWidthHeight.Height + padding); // only allow axes to move away from bounds. if (changeAmount > 0) { 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 using the Drawing Context and Axes supplied /// </summary> /// <remarks>TODO: block positions may be off by a pixel or so. maybe. Re-think calculations</remarks> public void Draw(Context ctx, 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 / xStep; double worldHeight = yAxis.Axis.WorldMax - yAxis.Axis.WorldMin; double numBlocksVertical = worldHeight / 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; ctx.Save (); for (int i=0; i<data.GetLength(0); ++i) { for (int j=0; j<data.GetLength(1); ++j) { double wX = (double)j*xStep + xStart; double wY = (double)i*yStep + yStart; if (!hPositive) { wY += yStep; } if (!wPositive ) { wX += xStep; } if (Center) { wX -= xStep/2.0; wY -= yStep/2.0; } Color color = Gradient.GetColor ((data[i,j]-DataMin)/(DataMax-DataMin)); ctx.SetColor (color); double x = xAxis.WorldToPhysical(wX,false).X; double y = yAxis.WorldToPhysical(wY,false).Y; ctx.Rectangle (x, y, blockWidth, blockHeight); ctx.Fill (); } } ctx.Restore (); }
/// <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 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 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> /// 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> /// 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 double CalculatePhysicalSeparation(CandleDataAdapter cd, PhysicalAxis xAxis) { if (cd.Count > 1) { double xPos1 = (xAxis.WorldToPhysical( ((PointOLHC)cd[0]).X, false )).X; double xPos2 = (xAxis.WorldToPhysical( ((PointOLHC)cd[1]).X, false )).X; double minDist = xPos2 - xPos1; if (cd.Count > 2) { // to be pretty sure we get the smallest gap. double xPos3 = (xAxis.WorldToPhysical(((PointOLHC)cd[2]).X, false)).X; if (xPos3 - xPos2 < minDist) { minDist = xPos3 - xPos2; } if (cd.Count > 3) { double xPos4 = (xAxis.WorldToPhysical(((PointOLHC)cd[3]).X, false)).X; if (xPos4 - xPos3 < minDist) { minDist = xPos4 - xPos3; } } } return minDist; } return 0; }
/// <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 bar = new Rectangle (xPos1.X+xoff, yPos1.Y, width-2*xoff, height); ctx.Rectangle (bar); if (Filled) { if (bar.Height != 0 && bar.Width != 0) { if (FillGradient != null) { // Scale FillGradient to bar rectangle double sX = bar.X + fillGradient.StartPoint.X * bar.Width; double sY = bar.Y + fillGradient.StartPoint.Y * bar.Height; double eX = bar.X + fillGradient.EndPoint.X * bar.Width; double eY = bar.Y + fillGradient.EndPoint.Y * bar.Height; LinearGradient g = new LinearGradient (sX, sY, eX, eY); g.AddColorStop (0, FillGradient.StartColor); g.AddColorStop (1, FillGradient.EndColor); ctx.Pattern = g; } else { ctx.SetColor (FillColor); } ctx.FillPreserve (); } } ctx.SetColor (BorderColor); ctx.Stroke (); } ctx.Restore (); }
/// <summary> /// Draw the filled region /// </summary> /// <param name="g">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) { ITransform2D t = Transform2D.GetTransformer(xAxis, yAxis); ctx.Save(); if (hl1 != null && hl2 != null) { ctx.MoveTo(t.Transform(xAxis.Axis.WorldMin, hl1.OrdinateValue)); ctx.LineTo(t.Transform(xAxis.Axis.WorldMax, hl1.OrdinateValue)); ctx.LineTo(t.Transform(xAxis.Axis.WorldMax, hl2.OrdinateValue)); ctx.LineTo(t.Transform(xAxis.Axis.WorldMin, hl2.OrdinateValue)); ctx.ClosePath(); } else if (vl1 != null && vl2 != null) { ctx.MoveTo(t.Transform(vl1.AbscissaValue, yAxis.Axis.WorldMin)); ctx.LineTo(t.Transform(vl1.AbscissaValue, yAxis.Axis.WorldMax)); ctx.LineTo(t.Transform(vl2.AbscissaValue, yAxis.Axis.WorldMax)); ctx.LineTo(t.Transform(vl2.AbscissaValue, yAxis.Axis.WorldMin)); ctx.ClosePath(); } else if (lp1 != null && lp2 != null) { SequenceAdapter a1 = new SequenceAdapter(lp1.DataSource, lp1.DataMember, lp1.OrdinateData, lp1.AbscissaData); SequenceAdapter a2 = new SequenceAdapter(lp2.DataSource, lp2.DataMember, lp2.OrdinateData, lp2.AbscissaData); // Start at first point of LinePlot 1 within plot bounds int start = 0; while (t.Transform(a1 [start]).X < xAxis.PhysicalMin.X) { ++start; } Point first = t.Transform(a1 [start]); ctx.MoveTo(first); // Join LinePlot 1 points in ascending order Point next; for (int i = start + 1; i < a1.Count - 1; ++i) { next = t.Transform(a1 [i]); if (next.X > xAxis.PhysicalMax.X) { break; } ctx.LineTo(next); } // Then join LinePlot 2 points in descending order int end = a2.Count - 1; while (t.Transform(a2 [end]).X > xAxis.PhysicalMax.X) { --end; } for (int i = end; i > 0; --i) { next = t.Transform(a2 [i]); if (next.X < xAxis.PhysicalMin.X) { break; } ctx.LineTo(next); } ctx.LineTo(first); ctx.ClosePath(); } else { throw new XwPlotException("Filled Region bounds not defined"); } ctx.SetColor(FillColor); ctx.Fill(); ctx.Restore(); }
/// <summary> /// Updates the PlotSurface 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, double scale, double padding, Rectangle bounds, out Point position) { double leftIndent = 0; double rightIndent = 0; double bottomIndent = 0; double 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 = 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 (xAttach_ == XAxisPosition.Bottom) { position.Y += pYAxis1.PhysicalMin.Y; if (horizontalEdgePlacement_ == Legend.Placement.Inside) { position.Y -= legendWidthHeight.Height; } } else { position.Y += pYAxis1.PhysicalMax.Y; if (horizontalEdgePlacement_ == Legend.Placement.Outside) { position.Y -= legendWidthHeight.Height; } } // x position.X = xOffset_; if (yAttach_ == YAxisPosition.Left) { if (verticalEdgePlacement_ == Legend.Placement.Outside) { position.X -= legendWidthHeight.Width; } position.X += pXAxis1.PhysicalMin.X; } else { if (verticalEdgePlacement_ == Legend.Placement.Inside) { position.X -= legendWidthHeight.Width; } position.X += pXAxis1.PhysicalMax.X; } // determine update amounts for axes if (!neverShiftAxes_) { if (position.X < padding) { double 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) { double changeAmount = (position.X - bounds.Right + legendWidthHeight.Width + padding); // only allow axes to move away from bounds. if (changeAmount > 0) { rightIndent = changeAmount; } position.X -= changeAmount; } if (position.Y < padding) { double changeAmount = -position.Y + padding; // only allow axes to move away from bounds. if (changeAmount > 0) { topIndent = changeAmount; } position.Y += changeAmount; } if (position.Y + legendWidthHeight.Height > bounds.Bottom - padding) { double changeAmount = (position.Y - bounds.Bottom + legendWidthHeight.Height + padding); // only allow axes to move away from bounds. if (changeAmount > 0) { 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> /// 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. }
void DeterminePhysicalAxesToDraw(Rectangle bounds, Axis XAxis1, Axis XAxis2, Axis YAxis1, Axis YAxis2, out PhysicalAxis pXAxis1, out PhysicalAxis pXAxis2, out PhysicalAxis pYAxis1, out PhysicalAxis pYAxis2) { Rectangle cb = bounds; pXAxis1 = new PhysicalAxis(XAxis1, new Point(cb.Left, cb.Bottom), new Point(cb.Right, cb.Bottom)); pYAxis1 = new PhysicalAxis(YAxis1, new Point(cb.Left, cb.Bottom), new Point(cb.Left, cb.Top)); pXAxis2 = new PhysicalAxis(XAxis2, new Point(cb.Left, cb.Top), new Point(cb.Right, cb.Top)); pYAxis2 = new PhysicalAxis(YAxis2, new Point(cb.Right, cb.Bottom), new Point(cb.Right, cb.Top)); double bottomIndent = Padding; if (!pXAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis1.GetBoundingBox(); // finally determine its indentation from the bottom bottomIndent = bottomIndent + bb.Bottom - cb.Bottom; } double leftIndent = Padding; if (!pYAxis1.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis1.GetBoundingBox(); // finally determine its indentation from the left leftIndent = leftIndent - bb.Left + cb.Left; } // determine title size double scale = DetermineScaleFactor(bounds.Width, bounds.Height); Font scaled_font; if (AutoScaleTitle) { scaled_font = TitleFont.WithScaledSize(scale); } else { scaled_font = TitleFont; } Size titleSize; using (TextLayout layout = new TextLayout()) { layout.Font = scaled_font; layout.Text = Title; titleSize = layout.GetSize(); }; double topIndent = Padding; if (!pXAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pXAxis2.GetBoundingBox(); topIndent = topIndent - bb.Top + cb.Top; // finally determine its indentation from the top // correct top indendation to take into account plot title if (Title != "") { topIndent += titleSize.Height * 1.3; } } double rightIndent = Padding; if (!pYAxis2.Axis.Hidden) { // evaluate its bounding box Rectangle bb = pYAxis2.GetBoundingBox(); // finally determine its indentation from the right rightIndent += (bb.Right - cb.Right); } // now we have all the default calculated positions and we can proceed to // "move" the axes to their right places // primary axes (bottom, left) pXAxis1.PhysicalMin = new Point(cb.Left + leftIndent, cb.Bottom - bottomIndent); pXAxis1.PhysicalMax = new Point(cb.Right - rightIndent, cb.Bottom - bottomIndent); pYAxis1.PhysicalMin = new Point(cb.Left + leftIndent, cb.Bottom - bottomIndent); pYAxis1.PhysicalMax = new Point(cb.Left + leftIndent, cb.Top + topIndent); // secondary axes (top, right) pXAxis2.PhysicalMin = new Point(cb.Left + leftIndent, cb.Top + topIndent); pXAxis2.PhysicalMax = new Point(cb.Right - rightIndent, cb.Top + topIndent); pYAxis2.PhysicalMin = new Point(cb.Right - rightIndent, cb.Bottom - bottomIndent); pYAxis2.PhysicalMax = new Point(cb.Right - rightIndent, cb.Top + topIndent); }