private Point InverseTransform(Point point) { if (XAxis != null) { return(XAxis.InverseTransform(point.X, point.Y, YAxis)); } if (YAxis != null) { return(new Point(0, YAxis.InverseTransform(point.Y))); } return(new Point()); }
/// <summary> /// Calculates the actual minimums and maximums. /// </summary> protected virtual void CalculateActualMinimumsMaximums() { this.actualMinimumX = Math.Max(this.MinimumX, this.XAxis.ActualMinimum); this.actualMaximumX = Math.Min(this.MaximumX, this.XAxis.ActualMaximum); this.actualMinimumY = Math.Max(this.MinimumY, this.YAxis.ActualMinimum); this.actualMaximumY = Math.Min(this.MaximumY, this.YAxis.ActualMaximum); if (!this.ClipByXAxis) { double right = XAxis.InverseTransform(PlotModel.PlotArea.Right); double left = XAxis.InverseTransform(PlotModel.PlotArea.Left); this.actualMaximumX = Math.Max(left, right); this.actualMinimumX = Math.Min(left, right); } if (!this.ClipByYAxis) { double bottom = YAxis.InverseTransform(PlotModel.PlotArea.Bottom); double top = YAxis.InverseTransform(PlotModel.PlotArea.Top); this.actualMaximumY = Math.Max(top, bottom); this.actualMinimumY = Math.Min(top, bottom); } }
/// <summary> /// Renders the line annotation. /// </summary> /// <param name="rc"> /// The render context. /// </param> /// <param name="model"> /// The plot model. /// </param> public override void Render(IRenderContext rc, PlotModel model) { base.Render(rc, model); bool aliased = false; double actualMinimumX = Math.Max(this.MinimumX, this.XAxis.ActualMinimum); double actualMaximumX = Math.Min(this.MaximumX, this.XAxis.ActualMaximum); double actualMinimumY = Math.Max(this.MinimumY, this.YAxis.ActualMinimum); double actualMaximumY = Math.Min(this.MaximumY, this.YAxis.ActualMaximum); if (!this.ClipByXAxis) { double right = XAxis.InverseTransform(PlotModel.PlotArea.Right); double left = XAxis.InverseTransform(PlotModel.PlotArea.Left); actualMaximumX = Math.Max(left, right); actualMinimumX = Math.Min(left, right); } if (!this.ClipByYAxis) { double bottom = YAxis.InverseTransform(PlotModel.PlotArea.Bottom); double top = YAxis.InverseTransform(PlotModel.PlotArea.Top); actualMaximumY = Math.Max(top, bottom); actualMinimumY = Math.Min(top, bottom); } // y=f(x) Func <double, double> fx = null; // x=f(y) Func <double, double> fy = null; switch (this.Type) { case LineAnnotationType.Horizontal: fx = x => this.Y; break; case LineAnnotationType.Vertical: fy = y => this.X; break; case LineAnnotationType.EquationY: fx = this.Equation; break; case LineAnnotationType.EquationX: fy = this.Equation; break; default: fx = x => (this.Slope * x) + this.Intercept; break; } var points = new List <DataPoint>(); bool isCurvedLine = !(this.XAxis is LinearAxis) || !(this.YAxis is LinearAxis) || this.Type == LineAnnotationType.EquationY; if (!isCurvedLine) { // we only need to calculate two points if it is a straight line if (fx != null) { points.Add(new DataPoint(actualMinimumX, fx(actualMinimumX))); points.Add(new DataPoint(actualMaximumX, fx(actualMaximumX))); } else if (fy != null) { points.Add(new DataPoint(fy(actualMinimumY), actualMinimumY)); points.Add(new DataPoint(fy(actualMaximumY), actualMaximumY)); } if (this.Type == LineAnnotationType.Horizontal || this.Type == LineAnnotationType.Vertical) { // use aliased line drawing for horizontal and vertical lines aliased = true; } } else { if (fx != null) { double x = actualMinimumX; // todo: the step size should be adaptive double dx = (actualMaximumX - actualMinimumX) / 100; while (true) { points.Add(new DataPoint(x, fx(x))); if (x > actualMaximumX) { break; } x += dx; } } else if (fy != null) { double y = actualMinimumY; // todo: the step size should be adaptive double dy = (actualMaximumY - actualMinimumY) / 100; while (true) { points.Add(new DataPoint(fy(y), y)); if (y > actualMaximumY) { break; } y += dy; } } } // transform to screen coordinates this.screenPoints = points.Select(p => this.Transform(p)).ToList(); // clip to the area defined by the axes var clippingRectangle = OxyRect.Create( this.ClipByXAxis ? this.XAxis.ScreenMin.X : PlotModel.PlotArea.Left, this.ClipByYAxis ? this.YAxis.ScreenMin.Y : PlotModel.PlotArea.Top, this.ClipByXAxis ? this.XAxis.ScreenMax.X : PlotModel.PlotArea.Right, this.ClipByYAxis ? this.YAxis.ScreenMax.Y : PlotModel.PlotArea.Bottom); const double MinimumSegmentLength = 4; IList <ScreenPoint> clippedPoints = null; rc.DrawClippedLine( this.screenPoints, clippingRectangle, MinimumSegmentLength * MinimumSegmentLength, this.GetSelectableColor(this.Color), this.StrokeThickness, this.LineStyle, this.LineJoin, aliased, pts => clippedPoints = pts); ScreenPoint position; double angle; double margin = this.TextMargin; if (this.TextHorizontalAlignment == HorizontalAlignment.Center) { margin = 0; } else { margin *= this.TextPosition < 0.5 ? 1 : -1; } if (clippedPoints != null && GetPointAtRelativeDistance(clippedPoints, this.TextPosition, margin, out position, out angle)) { if (angle < -90) { angle += 180; } if (angle > 90) { angle -= 180; } switch (this.TextOrientation) { case AnnotationTextOrientation.Horizontal: angle = 0; break; case AnnotationTextOrientation.Vertical: angle = -90; break; } // Apply 'padding' to the position var angleInRadians = angle / 180 * Math.PI; var f = 1; if (this.TextHorizontalAlignment == HorizontalAlignment.Right) { f = -1; } if (this.TextHorizontalAlignment == HorizontalAlignment.Center) { f = 0; } position.X += f * this.TextPadding * Math.Cos(angleInRadians); position.Y += f * this.TextPadding * Math.Sin(angleInRadians); var cs = new CohenSutherlandClipping(clippingRectangle); if (!string.IsNullOrEmpty(this.Text) && cs.IsInside(position)) { rc.DrawClippedText( clippingRectangle, position, this.Text, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, angle, this.TextHorizontalAlignment, this.TextVerticalAlignment); } } }