/// <summary> /// Draws clipped line segments. /// </summary> /// <param name="rc">The render context.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="points">The points defining the line segments. Lines are drawn from point 0 to 1, point 2 to 3 and so on.</param> /// <param name="stroke">The stroke color.</param> /// <param name="strokeThickness">The stroke thickness.</param> /// <param name="dashArray">The dash array (in device independent units, 1/96 inch).</param> /// <param name="lineJoin">The line join.</param> /// <param name="aliased">Set to <c>true</c> to draw as an aliased line.</param> public static void DrawClippedLineSegments( this IRenderContext rc, OxyRect clippingRectangle, IList <ScreenPoint> points, OxyColor stroke, double strokeThickness, double[] dashArray, LineJoin lineJoin, bool aliased) { if (rc.SetClip(clippingRectangle)) { rc.DrawLineSegments(points, stroke, strokeThickness, dashArray, lineJoin, aliased); rc.ResetClip(); return; } var clipping = new CohenSutherlandClipping(clippingRectangle); var clippedPoints = new List <ScreenPoint>(points.Count); for (int i = 0; i + 1 < points.Count; i += 2) { var s0 = points[i]; var s1 = points[i + 1]; if (clipping.ClipLine(ref s0, ref s1)) { clippedPoints.Add(s0); clippedPoints.Add(s1); } } rc.DrawLineSegments(clippedPoints, stroke, strokeThickness, dashArray, lineJoin, aliased); }
/// <summary> /// Draws the clipped line segments. /// </summary> /// <param name="rc">The render context.</param> /// <param name="points">The points.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="stroke">The stroke.</param> /// <param name="strokeThickness">The stroke thickness.</param> /// <param name="lineStyle">The line style.</param> /// <param name="lineJoin">The line join.</param> /// <param name="aliased">if set to <c>true</c> [aliased].</param> public static void DrawClippedLineSegments( this IRenderContext rc, IList <ScreenPoint> points, OxyRect clippingRectangle, OxyColor stroke, double strokeThickness, LineStyle lineStyle, OxyPenLineJoin lineJoin, bool aliased) { var clipping = new CohenSutherlandClipping(clippingRectangle.Left, clippingRectangle.Right, clippingRectangle.Top, clippingRectangle.Bottom); var clippedPoints = new List <ScreenPoint>(points.Count); for (int i = 0; i + 1 < points.Count; i += 2) { var s0 = points[i]; var s1 = points[i + 1]; if (clipping.ClipLine(ref s0, ref s1)) { clippedPoints.Add(s0); clippedPoints.Add(s1); } } rc.DrawLineSegments(clippedPoints, stroke, strokeThickness, LineStyleHelper.GetDashArray(lineStyle), lineJoin, aliased); }
/// <summary> /// Draws a clipped polyline through the specified points. /// </summary> /// <param name="rc">The render context.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="points">The points.</param> /// <param name="minDistSquared">The minimum line segment length (squared).</param> /// <param name="stroke">The stroke color.</param> /// <param name="strokeThickness">The stroke thickness.</param> /// <param name="dashArray">The dash array (in device independent units, 1/96 inch).</param> /// <param name="lineJoin">The line join.</param> /// <param name="aliased">Set to <c>true</c> to draw as an aliased line.</param> /// <param name="outputBuffer">The output buffer.</param> /// <param name="pointsRendered">The points rendered callback.</param> public static void DrawClippedLine( this IRenderContext rc, OxyRect clippingRectangle, IList <ScreenPoint> points, double minDistSquared, OxyColor stroke, double strokeThickness, double[] dashArray, LineJoin lineJoin, bool aliased, List <ScreenPoint> outputBuffer = null, Action <IList <ScreenPoint> > pointsRendered = null) { var n = points.Count; if (n == 0) { return; } if (rc.SetClip(clippingRectangle)) { rc.DrawLine(points, stroke, strokeThickness, dashArray, lineJoin, aliased); rc.ResetClip(); if (outputBuffer != null) { outputBuffer.Clear(); outputBuffer.AddRange(points); } if (pointsRendered != null) { pointsRendered(points); } return; } if (outputBuffer != null) { outputBuffer.Clear(); } else { outputBuffer = new List <ScreenPoint>(n); } // draws the points in the output buffer and calls the callback (if specified) Action drawLine = () => { EnsureNonEmptyLineIsVisible(outputBuffer); rc.DrawLine(outputBuffer, stroke, strokeThickness, dashArray, lineJoin, aliased); // Execute the 'callback' if (pointsRendered != null) { pointsRendered(outputBuffer); } }; var clipping = new CohenSutherlandClipping(clippingRectangle); if (n == 1 && clipping.IsInside(points[0])) { outputBuffer.Add(points[0]); } int lastPointIndex = 0; for (int i = 1; i < n; i++) { // Calculate the clipped version of previous and this point. var sc0 = points[i - 1]; var sc1 = points[i]; bool isInside = clipping.ClipLine(ref sc0, ref sc1); if (!isInside) { // the line segment is outside the clipping rectangle // keep the previous coordinate for minimum distance comparison continue; } // length calculation (inlined for performance) var dx = sc1.X - points[lastPointIndex].X; var dy = sc1.Y - points[lastPointIndex].Y; if ((dx * dx) + (dy * dy) > minDistSquared || outputBuffer.Count == 0 || i == n - 1) { // point comparison inlined for performance // ReSharper disable CompareOfFloatsByEqualityOperator if (sc0.X != points[lastPointIndex].X || sc0.Y != points[lastPointIndex].Y || outputBuffer.Count == 0) // ReSharper restore disable CompareOfFloatsByEqualityOperator { outputBuffer.Add(new ScreenPoint(sc0.X, sc0.Y)); } outputBuffer.Add(new ScreenPoint(sc1.X, sc1.Y)); lastPointIndex = i; } if (clipping.IsInside(points[i]) || outputBuffer.Count == 0) { continue; } // we are leaving the clipping region - render the line drawLine(); outputBuffer.Clear(); } if (outputBuffer.Count > 0) { drawLine(); } }
/// <summary> /// Draws the clipped line. /// </summary> /// <param name="rc">The render context.</param> /// <param name="points">The points.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="minDistSquared">The squared minimum distance.</param> /// <param name="stroke">The stroke.</param> /// <param name="strokeThickness">The stroke thickness.</param> /// <param name="lineStyle">The line style.</param> /// <param name="lineJoin">The line join.</param> /// <param name="aliased">if set to <c>true</c> [aliased].</param> /// <param name="pointsRendered">The points rendered callback.</param> public static void DrawClippedLine( this IRenderContext rc, IList <ScreenPoint> points, OxyRect clippingRectangle, double minDistSquared, OxyColor stroke, double strokeThickness, LineStyle lineStyle, OxyPenLineJoin lineJoin, bool aliased, Action <IList <ScreenPoint> > pointsRendered = null) { var clipping = new CohenSutherlandClipping(clippingRectangle.Left, clippingRectangle.Right, clippingRectangle.Top, clippingRectangle.Bottom); var pts = new List <ScreenPoint>(); int n = points.Count; if (n > 0) { if (n == 1) { pts.Add(points[0]); } var last = points[0]; for (int i = 1; i < n; i++) { var s0 = points[i - 1]; var s1 = points[i]; // Clipped version of this and next point. var sc0 = s0; var sc1 = s1; bool isInside = clipping.ClipLine(ref sc0, ref sc1); if (!isInside) { // keep the previous coordinate continue; } // render from s0c-s1c double dx = sc1.x - last.x; double dy = sc1.y - last.y; if ((dx * dx) + (dy * dy) > minDistSquared || i == 1 || i == n - 1) { if (!sc0.Equals(last) || i == 1) { pts.Add(sc0); } pts.Add(sc1); last = sc1; } // render the line if we are leaving the clipping region);); if (!clipping.IsInside(s1)) { if (pts.Count > 0) { EnsureNonEmptyLineIsVisible(pts); rc.DrawLine(pts, stroke, strokeThickness, lineStyle.GetDashArray(), lineJoin, aliased); if (pointsRendered != null) { pointsRendered(pts); } pts = new List <ScreenPoint>(); } } } if (pts.Count > 0) { EnsureNonEmptyLineIsVisible(pts); rc.DrawLine(pts, stroke, strokeThickness, lineStyle.GetDashArray(), lineJoin, aliased); // Execute the 'callback'. if (pointsRendered != null) { pointsRendered(pts); } } } }
/// <summary> /// Draws the clipped line. /// </summary> /// <param name="rc">The render context.</param> /// <param name="points">The points.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="minDistSquared">The min dist squared.</param> /// <param name="stroke">The stroke.</param> /// <param name="strokeThickness">The stroke thickness.</param> /// <param name="lineStyle">The line style.</param> /// <param name="lineJoin">The line join.</param> /// <param name="aliased">if set to <c>true</c> [aliased].</param> /// <param name="pointsRendered">The points rendered callback.</param> public static void DrawClippedLine( this IRenderContext rc, IList<ScreenPoint> points, OxyRect clippingRectangle, double minDistSquared, OxyColor stroke, double strokeThickness, LineStyle lineStyle, OxyPenLineJoin lineJoin, bool aliased, Action<IList<ScreenPoint>> pointsRendered = null) { var clipping = new CohenSutherlandClipping( clippingRectangle.Left, clippingRectangle.Right, clippingRectangle.Top, clippingRectangle.Bottom); var pts = new List<ScreenPoint>(); int n = points.Count; if (n > 0) { if (n == 1) { pts.Add(points[0]); } var last = points[0]; for (int i = 1; i < n; i++) { var s0 = points[i - 1]; var s1 = points[i]; // Clipped version of this and next point. var s0c = s0; var s1c = s1; bool isInside = clipping.ClipLine(ref s0c, ref s1c); s0 = s1; if (!isInside) { // keep the previous coordinate continue; } // render from s0c-s1c double dx = s1c.x - last.x; double dy = s1c.y - last.y; if (dx * dx + dy * dy > minDistSquared || i == 1 || i == n - 1) { if (!s0c.Equals(last) || i == 1) { pts.Add(s0c); } pts.Add(s1c); last = s1c; } // render the line if we are leaving the clipping region);); if (!clipping.IsInside(s1)) { if (pts.Count > 0) { rc.DrawLine( pts, stroke, strokeThickness, LineStyleHelper.GetDashArray(lineStyle), lineJoin, aliased); if (pointsRendered != null) { pointsRendered(pts); } pts = new List<ScreenPoint>(); } } } // Check if the line contains two points and they are at the same point if (pts.Count == 2) { if (pts[0].DistanceTo(pts[1]) < 1) { // Modify to a small horizontal line to make sure it is being rendered pts[1] = new ScreenPoint(pts[0].X + 1, pts[0].Y); pts[0] = new ScreenPoint(pts[0].X - 1, pts[0].Y); } } // Check if the line contains a single point if (pts.Count == 1) { // Add a second point to make sure the line is being rendered as a small dot pts.Add(new ScreenPoint(pts[0].X + 1, pts[0].Y)); pts[0] = new ScreenPoint(pts[0].X - 1, pts[0].Y); } if (pts.Count > 0) { rc.DrawLine(pts, stroke, strokeThickness, LineStyleHelper.GetDashArray(lineStyle), lineJoin, aliased); // Execute the 'callback'. if (pointsRendered != null) { pointsRendered(pts); } } } }
/// <summary> /// Draws the clipped line segments. /// </summary> /// <param name="rc">The render context.</param> /// <param name="points">The points.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="stroke">The stroke.</param> /// <param name="strokeThickness">The stroke thickness.</param> /// <param name="lineStyle">The line style.</param> /// <param name="lineJoin">The line join.</param> /// <param name="aliased">if set to <c>true</c> [aliased].</param> public static void DrawClippedLineSegments( this IRenderContext rc, IList<ScreenPoint> points, OxyRect clippingRectangle, OxyColor stroke, double strokeThickness, LineStyle lineStyle, OxyPenLineJoin lineJoin, bool aliased) { var clipping = new CohenSutherlandClipping(clippingRectangle.Left, clippingRectangle.Right, clippingRectangle.Top, clippingRectangle.Bottom); var clippedPoints = new List<ScreenPoint>(points.Count); for (int i = 0; i + 1 < points.Count; i += 2) { var s0 = points[i]; var s1 = points[i + 1]; if (clipping.ClipLine(ref s0, ref s1)) { clippedPoints.Add(s0); clippedPoints.Add(s1); } } rc.DrawLineSegments(clippedPoints, stroke, strokeThickness, LineStyleHelper.GetDashArray(lineStyle), lineJoin, aliased); }
/// <summary> /// Draws the clipped line. /// </summary> /// <param name="rc">The render context.</param> /// <param name="points">The points.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="minDistSquared">The min dist squared.</param> /// <param name="stroke">The stroke.</param> /// <param name="strokeThickness">The stroke thickness.</param> /// <param name="lineStyle">The line style.</param> /// <param name="lineJoin">The line join.</param> /// <param name="aliased">if set to <c>true</c> [aliased].</param> /// <param name="pointsRendered">The points rendered callback.</param> public static void DrawClippedLine( this IRenderContext rc, IList <ScreenPoint> points, OxyRect clippingRectangle, double minDistSquared, OxyColor stroke, double strokeThickness, LineStyle lineStyle, OxyPenLineJoin lineJoin, bool aliased, Action <IList <ScreenPoint> > pointsRendered = null) { var clipping = new CohenSutherlandClipping( clippingRectangle.Left, clippingRectangle.Right, clippingRectangle.Top, clippingRectangle.Bottom); var pts = new List <ScreenPoint>(); int n = points.Count; if (n > 0) { if (n == 1) { pts.Add(points[0]); } var last = points[0]; for (int i = 1; i < n; i++) { var s0 = points[i - 1]; var s1 = points[i]; // Clipped version of this and next point. var s0c = s0; var s1c = s1; bool isInside = clipping.ClipLine(ref s0c, ref s1c); s0 = s1; if (!isInside) { // keep the previous coordinate continue; } // render from s0c-s1c double dx = s1c.x - last.x; double dy = s1c.y - last.y; if (dx * dx + dy * dy > minDistSquared || i == 1 || i == n - 1) { if (!s0c.Equals(last) || i == 1) { pts.Add(s0c); } pts.Add(s1c); last = s1c; } // render the line if we are leaving the clipping region);); if (!clipping.IsInside(s1)) { if (pts.Count > 0) { rc.DrawLine( pts, stroke, strokeThickness, LineStyleHelper.GetDashArray(lineStyle), lineJoin, aliased); if (pointsRendered != null) { pointsRendered(pts); } pts = new List <ScreenPoint>(); } } } // Check if the line contains two points and they are at the same point if (pts.Count == 2) { if (pts[0].DistanceTo(pts[1]) < 1) { // Modify to a small horizontal line to make sure it is being rendered pts[1] = new ScreenPoint(pts[0].X + 1, pts[0].Y); pts[0] = new ScreenPoint(pts[0].X - 1, pts[0].Y); } } // Check if the line contains a single point if (pts.Count == 1) { // Add a second point to make sure the line is being rendered as a small dot pts.Add(new ScreenPoint(pts[0].X + 1, pts[0].Y)); pts[0] = new ScreenPoint(pts[0].X - 1, pts[0].Y); } if (pts.Count > 0) { rc.DrawLine(pts, stroke, strokeThickness, LineStyleHelper.GetDashArray(lineStyle), lineJoin, aliased); // Execute the 'callback'. if (pointsRendered != null) { pointsRendered(pts); } } } }
/// <summary> /// Draws the clipped line. /// </summary> /// <param name="rc">The render context.</param> /// <param name="points">The points.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="minDistSquared">The squared minimum distance.</param> /// <param name="stroke">The stroke.</param> /// <param name="strokeThickness">The stroke thickness.</param> /// <param name="lineStyle">The line style.</param> /// <param name="lineJoin">The line join.</param> /// <param name="aliased">if set to <c>true</c> [aliased].</param> /// <param name="pointsRendered">The points rendered callback.</param> public static void DrawClippedLine( this IRenderContext rc, IList<ScreenPoint> points, OxyRect clippingRectangle, double minDistSquared, OxyColor stroke, double strokeThickness, LineStyle lineStyle, OxyPenLineJoin lineJoin, bool aliased, Action<IList<ScreenPoint>> pointsRendered = null) { var clipping = new CohenSutherlandClipping(clippingRectangle.Left, clippingRectangle.Right, clippingRectangle.Top, clippingRectangle.Bottom); var pts = new List<ScreenPoint>(); int n = points.Count; if (n > 0) { if (n == 1) { pts.Add(points[0]); } var last = points[0]; for (int i = 1; i < n; i++) { var s0 = points[i - 1]; var s1 = points[i]; // Clipped version of this and next point. var sc0 = s0; var sc1 = s1; bool isInside = clipping.ClipLine(ref sc0, ref sc1); if (!isInside) { // keep the previous coordinate continue; } // render from s0c-s1c double dx = sc1.x - last.x; double dy = sc1.y - last.y; if ((dx * dx) + (dy * dy) > minDistSquared || i == 1 || i == n - 1) { if (!sc0.Equals(last) || i == 1) { pts.Add(sc0); } pts.Add(sc1); last = sc1; } // render the line if we are leaving the clipping region);); if (!clipping.IsInside(s1)) { if (pts.Count > 0) { EnsureNonEmptyLineIsVisible(pts); rc.DrawLine(pts, stroke, strokeThickness, lineStyle.GetDashArray(), lineJoin, aliased); if (pointsRendered != null) { pointsRendered(pts); } pts = new List<ScreenPoint>(); } } } if (pts.Count > 0) { EnsureNonEmptyLineIsVisible(pts); rc.DrawLine(pts, stroke, strokeThickness, lineStyle.GetDashArray(), lineJoin, aliased); // Execute the 'callback'. if (pointsRendered != null) { pointsRendered(pts); } } } }
/// <summary> /// Draws clipped line segments. /// </summary> /// <param name="rc">The render context.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="points">The points defining the line segments. Lines are drawn from point 0 to 1, point 2 to 3 and so on.</param> /// <param name="stroke">The stroke color.</param> /// <param name="strokeThickness">The stroke thickness.</param> /// <param name="dashArray">The dash array (in device independent units, 1/96 inch).</param> /// <param name="lineJoin">The line join.</param> /// <param name="aliased">Set to <c>true</c> to draw as an aliased line.</param> public static void DrawClippedLineSegments( this IRenderContext rc, OxyRect clippingRectangle, IList<ScreenPoint> points, OxyColor stroke, double strokeThickness, double[] dashArray, LineJoin lineJoin, bool aliased) { if (rc.SetClip(clippingRectangle)) { rc.DrawLineSegments(points, stroke, strokeThickness, dashArray, lineJoin, aliased); rc.ResetClip(); return; } var clipping = new CohenSutherlandClipping(clippingRectangle); var clippedPoints = new List<ScreenPoint>(points.Count); for (int i = 0; i + 1 < points.Count; i += 2) { var s0 = points[i]; var s1 = points[i + 1]; if (clipping.ClipLine(ref s0, ref s1)) { clippedPoints.Add(s0); clippedPoints.Add(s1); } } rc.DrawLineSegments(clippedPoints, stroke, strokeThickness, dashArray, lineJoin, aliased); }
/// <summary> /// Draws a clipped polyline through the specified points. /// </summary> /// <param name="rc">The render context.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="points">The points.</param> /// <param name="minDistSquared">The minimum line segment length (squared).</param> /// <param name="stroke">The stroke color.</param> /// <param name="strokeThickness">The stroke thickness.</param> /// <param name="dashArray">The dash array (in device independent units, 1/96 inch).</param> /// <param name="lineJoin">The line join.</param> /// <param name="aliased">Set to <c>true</c> to draw as an aliased line.</param> /// <param name="outputBuffer">The output buffer.</param> /// <param name="pointsRendered">The points rendered callback.</param> public static void DrawClippedLine( this IRenderContext rc, OxyRect clippingRectangle, IList<ScreenPoint> points, double minDistSquared, OxyColor stroke, double strokeThickness, double[] dashArray, LineJoin lineJoin, bool aliased, List<ScreenPoint> outputBuffer = null, Action<IList<ScreenPoint>> pointsRendered = null) { var n = points.Count; if (n == 0) { return; } if (rc.SetClip(clippingRectangle)) { rc.DrawLine(points, stroke, strokeThickness, dashArray, lineJoin, aliased); rc.ResetClip(); if (outputBuffer != null) { outputBuffer.Clear(); outputBuffer.AddRange(points); } if (pointsRendered != null) { pointsRendered(points); } return; } if (outputBuffer != null) { outputBuffer.Clear(); } else { outputBuffer = new List<ScreenPoint>(n); } // draws the points in the output buffer and calls the callback (if specified) Action drawLine = () => { EnsureNonEmptyLineIsVisible(outputBuffer); rc.DrawLine(outputBuffer, stroke, strokeThickness, dashArray, lineJoin, aliased); // Execute the 'callback' if (pointsRendered != null) { pointsRendered(outputBuffer); } }; var clipping = new CohenSutherlandClipping(clippingRectangle); if (n == 1 && clipping.IsInside(points[0])) { outputBuffer.Add(points[0]); } int lastPointIndex = 0; for (int i = 1; i < n; i++) { // Calculate the clipped version of previous and this point. var sc0 = points[i - 1]; var sc1 = points[i]; bool isInside = clipping.ClipLine(ref sc0, ref sc1); if (!isInside) { // the line segment is outside the clipping rectangle // keep the previous coordinate for minimum distance comparison continue; } // length calculation (inlined for performance) var dx = sc1.X - points[lastPointIndex].X; var dy = sc1.Y - points[lastPointIndex].Y; if ((dx * dx) + (dy * dy) > minDistSquared || outputBuffer.Count == 0 || i == n - 1) { // point comparison inlined for performance // ReSharper disable CompareOfFloatsByEqualityOperator if (sc0.X != points[lastPointIndex].X || sc0.Y != points[lastPointIndex].Y || outputBuffer.Count == 0) // ReSharper restore disable CompareOfFloatsByEqualityOperator { outputBuffer.Add(new ScreenPoint(sc0.X, sc0.Y)); } outputBuffer.Add(new ScreenPoint(sc1.X, sc1.Y)); lastPointIndex = i; } if (clipping.IsInside(points[i]) || outputBuffer.Count == 0) { continue; } // we are leaving the clipping region - render the line drawLine(); outputBuffer.Clear(); } if (outputBuffer.Count > 0) { drawLine(); } }
/// <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 == HorizontalTextAlign.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 == HorizontalTextAlign.Right) { f = -1; } if (this.TextHorizontalAlignment == HorizontalTextAlign.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); } } }