/// <summary> /// Renders the series on the specified render context. /// </summary> /// <param name="rc"> /// The rendering context. /// </param> /// <param name="model"> /// The model. /// </param> public override void Render(IRenderContext rc, PlotModel model) { if (this.Data == null) { this.image = null; return; } int m = this.Data.GetLength(0); int n = this.Data.GetLength(1); double dx = (this.X1 - this.X0) / m; double left = this.X0 - (dx * 0.5); double right = this.X1 + (dx * 0.5); double dy = (this.Y1 - this.Y0) / n; double bottom = this.Y0 - (dy * 0.5); double top = this.Y1 + (dy * 0.5); var s00 = this.Transform(left, bottom); var s11 = this.Transform(right, top); var rect = OxyRect.Create(s00, s11); if (this.image == null || this.Data.GetHashCode() != this.dataHash) { this.UpdateImage(); this.dataHash = this.Data.GetHashCode(); } if (this.image != null) { var clip = this.GetClippingRect(); rc.DrawClippedImage(clip, this.image, rect.Left, rect.Top, rect.Width, rect.Height, 1, true); } }
/// <summary> /// Renders the polygon 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); this.screenRectangle = OxyRect.Create(this.Transform(this.X - (Width / 2), Y - (Height / 2)), this.Transform(X + (Width / 2), Y + (Height / 2))); // clip to the area defined by the axes var clipping = this.GetClippingRect(); rc.DrawClippedEllipse(clipping, this.screenRectangle, this.Fill, this.Stroke, this.StrokeThickness); if (!string.IsNullOrEmpty(this.Text)) { var textPosition = this.screenRectangle.Center; rc.DrawClippedText( clipping, textPosition, this.Text, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, this.TextRotation, HorizontalAlignment.Center, VerticalAlignment.Middle); } }
/// <summary> /// Renders the polygon 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); double x0 = double.IsNaN(this.MinimumX) || this.MinimumX.Equals(double.MinValue) ? this.XAxis.ActualMinimum : this.MinimumX; double x1 = double.IsNaN(this.MaximumX) || this.MaximumX.Equals(double.MaxValue) ? this.XAxis.ActualMaximum : this.MaximumX; double y0 = double.IsNaN(this.MinimumY) || this.MinimumY.Equals(double.MinValue) ? this.YAxis.ActualMinimum : this.MinimumY; double y1 = double.IsNaN(this.MaximumY) || this.MaximumY.Equals(double.MaxValue) ? this.YAxis.ActualMaximum : this.MaximumY; this.screenRectangle = OxyRect.Create(this.Transform(x0, y0), this.Transform(x1, y1)); // clip to the area defined by the axes var clipping = this.GetClippingRect(); rc.DrawClippedRectangle(this.screenRectangle, clipping, this.Fill, null, 0); if (!string.IsNullOrEmpty(this.Text)) { var textPosition = this.screenRectangle.Center; rc.DrawClippedText( clipping, textPosition, this.Text, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, this.TextRotation, HorizontalTextAlign.Center, VerticalTextAlign.Middle); } }
/// <summary> /// Renders the Series on the specified rendering context. /// </summary> /// <param name="rc"> /// The rendering context. /// </param> /// <param name="model"> /// The model. /// </param> public override void Render(IRenderContext rc, PlotModel model) { this.ActualBarRectangles = new List <OxyRect>(); if (this.ValidItems.Count == 0) { return; } var clippingRect = this.GetClippingRect(); var categoryAxis = this.GetCategoryAxis(); var actualBarWidth = this.GetActualBarWidth(); var stackIndex = categoryAxis.StackIndexMapping[this.StackGroup]; for (var i = 0; i < this.ValidItems.Count; i++) { var item = this.ValidItems[i]; var categoryIndex = item.GetCategoryIndex(i); double categoryValue = categoryAxis.GetCategoryValue(categoryIndex, stackIndex, actualBarWidth); var p0 = this.Transform(item.Start, categoryValue); var p1 = this.Transform(item.End, categoryValue + actualBarWidth); var rectangle = OxyRect.Create(p0.X, p0.Y, p1.X, p1.Y); this.ActualBarRectangles.Add(rectangle); rc.DrawClippedRectangleAsPolygon( rectangle, clippingRect, this.GetSelectableFillColor(item.Color ?? this.ActualFillColor), this.StrokeColor, this.StrokeThickness); if (this.LabelFormatString != null) { var s = StringHelper.Format( this.ActualCulture, this.LabelFormatString, this.GetItem(i), item.Start, item.End, item.Title); var pt = new ScreenPoint( (rectangle.Left + rectangle.Right) / 2, (rectangle.Top + rectangle.Bottom) / 2); rc.DrawClippedText( clippingRect, pt, s, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, 0, HorizontalAlignment.Center, VerticalAlignment.Middle); } } }
/// <summary> /// Renders the rectangle annotation. /// </summary> /// <param name="rc">The render context.</param> public override void Render(IRenderContext rc) { base.Render(rc); double x0 = double.IsNaN(this.MinimumX) || this.MinimumX.Equals(double.MinValue) ? this.ClipByXAxis ? this.XAxis.ActualMinimum : this.XAxis.InverseTransform(this.PlotModel.PlotArea.Left) : this.MinimumX; double x1 = double.IsNaN(this.MaximumX) || this.MaximumX.Equals(double.MaxValue) ? this.ClipByXAxis ? this.XAxis.ActualMaximum : this.XAxis.InverseTransform(this.PlotModel.PlotArea.Right) : this.MaximumX; double y0 = double.IsNaN(this.MinimumY) || this.MinimumY.Equals(double.MinValue) ? this.ClipByYAxis ? this.YAxis.ActualMinimum : this.YAxis.InverseTransform(this.PlotModel.PlotArea.Bottom) : this.MinimumY; double y1 = double.IsNaN(this.MaximumY) || this.MaximumY.Equals(double.MaxValue) ? this.ClipByYAxis ? this.YAxis.ActualMaximum : this.YAxis.InverseTransform(this.PlotModel.PlotArea.Top) : this.MaximumY; this.screenRectangle = new OxyRect(this.Transform(x0, y0), this.Transform(x1, y1)); // clip to the area defined by the axes var clippingRectangle = OxyRect.Create( this.ClipByXAxis ? this.XAxis.ScreenMin.X : this.PlotModel.PlotArea.Left, this.ClipByYAxis ? this.YAxis.ScreenMin.Y : this.PlotModel.PlotArea.Top, this.ClipByXAxis ? this.XAxis.ScreenMax.X : this.PlotModel.PlotArea.Right, this.ClipByYAxis ? this.YAxis.ScreenMax.Y : this.PlotModel.PlotArea.Bottom); rc.DrawClippedRectangle( clippingRectangle, this.screenRectangle, this.GetSelectableFillColor(this.Fill), this.GetSelectableColor(this.Stroke), this.StrokeThickness); if (!string.IsNullOrEmpty(this.Text)) { var textPosition = this.GetActualTextPosition(() => this.screenRectangle.Center); rc.DrawClippedText( clippingRectangle, textPosition, this.Text, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, this.TextRotation, HorizontalAlignment.Center, VerticalAlignment.Middle); } }
public void Render_MultipleNonEmptyLine_RendersTheLines(LineStyle?style, double[] dashes) { // Setup var random = new Random(21); int lineCount = random.Next(5, 455); var series = new MultipleLineSeries(); if (style.HasValue) { series.LineStyle = style.Value; } series.Dashes = dashes; var model = new PlotModel(); model.Series.Add(series); var mocks = new MockRepository(); var renderContext = mocks.StrictMock <IRenderContext>(); renderContext.Stub(rc => rc.SetClip(OxyRect.Create(0, 0, 0, 0))).Return(true); renderContext.Stub(rc => rc.ResetClip()); renderContext.Expect(rc => rc.DrawLine( Arg <ScreenPoint[]> .Matches(sp => sp.Length == 1), Arg <OxyColor> .Is.Equal(series.Color), Arg <double> .Is.Equal(series.StrokeThickness), Arg <double[]> .Is.Equal(dashes ?? style.Value.GetDashArray()), Arg <LineJoin> .Is.Anything, Arg <bool> .Is.Anything)).Repeat.Times(lineCount); mocks.ReplayAll(); for (var i = 0; i < lineCount; i++) { series.Lines.Add(new[] { new DataPoint(random.Next(-50, 50), random.Next(-50, 50)) }); } ((IPlotModel)model).Update(false); // Call series.Render(renderContext); // Assert mocks.VerifyAll(); }
public void Render_MultipleNonEmptyArea_RendersTheAreas() { // Setup var random = new Random(21); int areaCount = random.Next(5, 455); var series = new MultipleAreaSeries(); var model = new PlotModel(); model.Series.Add(series); var mocks = new MockRepository(); var renderContext = mocks.StrictMock <IRenderContext>(); renderContext.Stub(rc => rc.SetClip(OxyRect.Create(0, 0, 0, 0))).Return(true); renderContext.Stub(rc => rc.ResetClip()); renderContext.Expect(rc => rc.DrawPolygon( Arg <ScreenPoint[]> .Matches(sp => sp.Length == 1), Arg <OxyColor> .Is.Equal(series.Fill), Arg <OxyColor> .Is.Equal(series.Color), Arg <double> .Is.Equal(series.StrokeThickness), Arg <double[]> .Is.Anything, Arg <LineJoin> .Is.Anything, Arg <bool> .Is.Anything)).Repeat.Times(areaCount); mocks.ReplayAll(); for (var i = 0; i < areaCount; i++) { series.Areas.Add(new[] { new DataPoint(random.Next(-50, 50), random.Next(-50, 50)) }); } ((IPlotModel)model).Update(false); // Call series.Render(renderContext); // Assert mocks.VerifyAll(); }
/// <summary> /// Renders the annotation on the specified context. /// </summary> /// <param name="rc">The render context.</param> /// <param name="model">The model.</param> public override void Render(IRenderContext rc, PlotModel model) { base.Render(rc, model); var lon0 = this.XAxis.ActualMinimum; var lon1 = this.XAxis.ActualMaximum; var lat0 = this.YAxis.ActualMinimum; var lat1 = this.YAxis.ActualMaximum; // the desired number of tiles horizontally double tilesx = model.Width / this.TileSize; // calculate the desired zoom level var n = tilesx / (((lon1 + 180) / 360) - ((lon0 + 180) / 360)); var zoom = (int)Math.Round(Math.Log(n) / Math.Log(2)); if (zoom < this.MinZoomLevel) { zoom = this.MinZoomLevel; } if (zoom > this.MaxZoomLevel) { zoom = this.MaxZoomLevel; } // find tile coordinates for the corners double x0, y0; LatLonToTile(lat0, lon0, zoom, out x0, out y0); double x1, y1; LatLonToTile(lat1, lon1, zoom, out x1, out y1); double xmax = Math.Max(x0, x1); double xmin = Math.Min(x0, x1); double ymax = Math.Max(y0, y1); double ymin = Math.Min(y0, y1); var clippingRectangle = this.GetClippingRect(); // Add the tiles for (var x = (int)xmin; x < xmax; x++) { for (var y = (int)ymin; y < ymax; y++) { string uri = this.GetTileUri(x, y, zoom); var img = this.GetImage(uri, rc.RendersToScreen); if (img == null) { continue; } // transform from tile coordinates to lat/lon double latitude0, latitude1, longitude0, longitude1; TileToLatLon(x, y, zoom, out latitude0, out longitude0); TileToLatLon(x + 1, y + 1, zoom, out latitude1, out longitude1); // transform from lat/lon to screen coordinates var s00 = this.Transform(longitude0, latitude0); var s11 = this.Transform(longitude1, latitude1); var r = OxyRect.Create(s00.X, s00.Y, s11.X, s11.Y); // draw the image rc.DrawClippedImage(clippingRectangle, img, r.Left, r.Top, r.Width, r.Height, this.Opacity, true); } } // draw the copyright notice var p = new ScreenPoint(clippingRectangle.Right - 5, clippingRectangle.Bottom - 5); var textSize = rc.MeasureText(this.CopyrightNotice, this.ActualFont, this.ActualFontSize, this.ActualFontWeight); rc.DrawRectangle(new OxyRect(p.X - textSize.Width - 2, p.Y - textSize.Height - 2, textSize.Width + 4, textSize.Height + 4), OxyColor.FromAColor(200, OxyColors.White), OxyColors.Undefined); rc.DrawText( p, this.CopyrightNotice, OxyColors.Black, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, 0, HorizontalAlignment.Right, VerticalAlignment.Bottom); }
/// <summary> /// Renders the annotation on the specified context. /// </summary> /// <param name="rc">The render context.</param> public override void Render(IRenderContext rc) { base.Render(rc); this.CalculateActualMinimumsMaximums(); this.screenPoints = this.GetScreenPoints(); // clip to the area defined by the axes var clippingRectangle = OxyRect.Create( this.ClipByXAxis ? this.XAxis.ScreenMin.X : this.PlotModel.PlotArea.Left, this.ClipByYAxis ? this.YAxis.ScreenMin.Y : this.PlotModel.PlotArea.Top, this.ClipByXAxis ? this.XAxis.ScreenMax.X : this.PlotModel.PlotArea.Right, this.ClipByYAxis ? this.YAxis.ScreenMax.Y : this.PlotModel.PlotArea.Bottom); const double MinimumSegmentLength = 4; var clippedPoints = new List <ScreenPoint>(); var dashArray = this.LineStyle.GetDashArray(); if (this.StrokeThickness > 0 && this.LineStyle != LineStyle.None) { rc.DrawClippedLine( clippingRectangle, this.screenPoints, MinimumSegmentLength * MinimumSegmentLength, this.GetSelectableColor(this.Color), this.StrokeThickness, dashArray, this.LineJoin, this.Aliased, null, clippedPoints.AddRange); } ScreenPoint position; double angle; double margin = this.TextMargin; if (this.TextHorizontalAlignment == HorizontalAlignment.Center) { margin = 0; } else { margin *= this.TextLinePosition < 0.5 ? 1 : -1; } if (GetPointAtRelativeDistance(clippedPoints, this.TextLinePosition, 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 += new ScreenVector(f * this.TextPadding * Math.Cos(angleInRadians), f * this.TextPadding * Math.Sin(angleInRadians)); if (!string.IsNullOrEmpty(this.Text)) { var textPosition = this.GetActualTextPosition(() => position); if (this.TextPosition.IsDefined()) { angle = this.TextRotation; } if (this.ClipText) { var cs = new CohenSutherlandClipping(clippingRectangle); if (cs.IsInside(position)) { rc.DrawClippedText( clippingRectangle, textPosition, this.Text, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, angle, this.TextHorizontalAlignment, this.TextVerticalAlignment); } } else { rc.DrawText( textPosition, this.Text, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, angle, this.TextHorizontalAlignment, this.TextVerticalAlignment); } } } }
/// <summary> /// Renders the series on the specified rendering context. /// </summary> /// <param name="rc">The rendering context.</param> /// <param name="model">The model.</param> public override void Render(IRenderContext rc, PlotModel model) { if (this.Items.Count == 0) { return; } var clippingRect = this.GetClippingRect(); int i = 0; this.ActualBarRectangles = new List <OxyRect>(); foreach (var item in this.Items) { if (!this.IsValid(item.X0) || !this.IsValid(item.X1) || !this.IsValid(item.Y0) || !this.IsValid(item.Y1)) { continue; } var p0 = this.Transform(item.X0, item.Y0); var p1 = this.Transform(item.X1, item.Y1); var rectangle = OxyRect.Create(p0.X, p0.Y, p1.X, p1.Y); this.ActualBarRectangles.Add(rectangle); rc.DrawClippedRectangleAsPolygon( clippingRect, rectangle, this.GetSelectableFillColor(item.Color.GetActualColor(this.ActualFillColor)), this.StrokeColor, this.StrokeThickness); if (this.LabelFormatString != null) { var s = this.Format( this.LabelFormatString, this.GetItem(i), item.X0, item.X1, item.Y0, item.Y1, item.Title); var pt = new ScreenPoint( (rectangle.Left + rectangle.Right) / 2, (rectangle.Top + rectangle.Bottom) / 2); rc.DrawClippedText( clippingRect, pt, s, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, 0, HorizontalAlignment.Center, VerticalAlignment.Middle); } i++; } }
/// <summary> /// Renders the Series on the specified rendering context. /// </summary> /// <param name="rc">The rendering context.</param> /// <param name="model">The model.</param> public override void Render(IRenderContext rc, PlotModel model) { this.ActualMinimumBarRectangles = new List <OxyRect>(); this.ActualMaximumBarRectangles = new List <OxyRect>(); if (this.ValidItems.Count == 0) { return; } var clippingRect = this.GetClippingRect(); var categoryAxis = this.GetCategoryAxis(); var actualBarWidth = this.GetActualBarWidth(); for (var i = 0; i < this.ValidItems.Count; i++) { var item = this.ValidItems[i]; var categoryIndex = item.GetCategoryIndex(i); var baseValue = double.IsNaN(item.BaseValue) ? this.BaseValue : item.BaseValue; var barOffset = categoryAxis.GetCurrentBarOffset(categoryIndex); var p0 = this.Transform(item.Minimum, categoryIndex - 0.5 + barOffset); var p1 = this.Transform(item.Maximum, categoryIndex - 0.5 + barOffset + actualBarWidth); var p2 = this.Transform(baseValue, categoryIndex - 0.5 + barOffset); p2 = new ScreenPoint((int)p2.X, p2.Y); var minimumRectangle = OxyRect.Create(p0.X, p0.Y, p2.X, p1.Y); var maximumRectangle = OxyRect.Create(p2.X, p0.Y, p1.X, p1.Y); this.ActualMinimumBarRectangles.Add(minimumRectangle); this.ActualMaximumBarRectangles.Add(maximumRectangle); rc.DrawClippedRectangleAsPolygon( clippingRect, minimumRectangle, item.MinimumColor.GetActualColor(this.ActualMinimumFillColor), this.StrokeColor, this.StrokeThickness); rc.DrawClippedRectangleAsPolygon( clippingRect, maximumRectangle, item.MaximumColor.GetActualColor(this.ActualMaximumFillColor), this.StrokeColor, this.StrokeThickness); if (this.MinimumLabelFormatString != null) { var s = this.Format( this.MinimumLabelFormatString, this.GetItem(this.ValidItemsIndexInversion[i]), item.Minimum); var pt = new ScreenPoint( minimumRectangle.Left - this.LabelMargin, (minimumRectangle.Top + minimumRectangle.Bottom) / 2); rc.DrawClippedText( clippingRect, pt, s, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, 0, HorizontalAlignment.Right, VerticalAlignment.Middle); } if (this.MaximumLabelFormatString != null) { var s = this.Format( this.MaximumLabelFormatString, this.GetItem(this.ValidItemsIndexInversion[i]), item.Maximum); var pt = new ScreenPoint( maximumRectangle.Right + this.LabelMargin, (maximumRectangle.Top + maximumRectangle.Bottom) / 2); rc.DrawClippedText( clippingRect, pt, s, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, 0, HorizontalAlignment.Left, VerticalAlignment.Middle); } } }
/// <summary> /// Renders the series on the specified rendering context. /// </summary> /// <param name="rc">The rendering context.</param> public override void Render(IRenderContext rc) { if (this.Items.Count == 0) { return; } var clippingRect = this.GetClippingRect(); int startIdx = 0; double xmax = double.MaxValue; this.ActualBarRectangles = new List <OxyRect>(); if (this.IsXMonotonic) { var xmin = this.XAxis.ActualMinimum; xmax = this.XAxis.ActualMaximum; this.WindowStartIndex = this.UpdateWindowStartIndex(this.Items, rect => rect.X0, xmin, this.WindowStartIndex); startIdx = this.WindowStartIndex; } int clipCount = 0; for (int i = startIdx; i < this.Items.Count; i++) { var item = this.Items[i]; if (!this.IsValid(item.X0) || !this.IsValid(item.X1) || !this.IsValid(item.Y0) || !this.IsValid(item.Y1)) { continue; } var p0 = this.Transform(item.X0, item.Y0); var p1 = this.Transform(item.X1, item.Y1); var rectangle = OxyRect.Create(p0.X, p0.Y, p1.X, p1.Y); this.ActualBarRectangles.Add(rectangle); rc.DrawClippedRectangleAsPolygon( clippingRect, rectangle, this.GetSelectableFillColor(item.Color.GetActualColor(this.ActualFillColor)), this.StrokeColor, this.StrokeThickness); if (this.LabelFormatString != null) { var s = StringHelper.Format( this.ActualCulture, this.LabelFormatString, this.GetItem(i), item.X0, item.X1, item.Y0, item.Y1, item.Title); var pt = new ScreenPoint( (rectangle.Left + rectangle.Right) / 2, (rectangle.Top + rectangle.Bottom) / 2); rc.DrawClippedText( clippingRect, pt, s, this.ActualTextColor, this.ActualFont, this.ActualFontSize, this.ActualFontWeight, 0, HorizontalAlignment.Center, VerticalAlignment.Middle); } clipCount += item.X0 > xmax ? 1 : 0; if (clipCount > 1) { break; } } }
/// <summary> /// Gets the rectangle for the specified values. /// </summary> /// <param name="baseValue"> /// The base value of the bar /// </param> /// <param name="topValue"> /// The top value of the bar /// </param> /// <param name="beginValue"> /// The begin value of the bar /// </param> /// <param name="endValue"> /// The end value of the bar /// </param> /// <returns> /// The rectangle. /// </returns> protected override OxyRect GetRectangle(double baseValue, double topValue, double beginValue, double endValue) { return(OxyRect.Create(this.Transform(baseValue, beginValue), this.Transform(topValue, endValue))); }
/// <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); } } }