/// <summary> /// Renders a chunk of points on the screen. /// </summary> /// <param name="context">Render context.</param> /// <param name="points">Screen points.</param> /// <returns>The list of resampled points.</returns> protected virtual List <ScreenPoint> RenderScreenPoints(AreaRenderContext context, List <ScreenPoint> points) { var final = points; if (context.Reverse) { final.Reverse(); } if (this.Smooth) { var resampled = ScreenPointHelper.ResamplePoints(final, this.MinimumSegmentLength); final = CanonicalSplineHelper.CreateSpline(resampled, 0.5, null, false, 0.25); } context.RenderContext.DrawClippedLine( context.ClippingRect, final, context.MinDistSquared, this.GetSelectableColor(context.Color), this.StrokeThickness, context.DashArray, this.LineJoin, false); return(final); }
/// <summary> /// Renders the transformed points as a line (smoothed if <see cref="LineSeries.Smooth"/> is <c>true</c>) and markers (if <see cref="MarkerType"/> is not <c>None</c>). /// </summary> /// <param name="rc">The render context.</param> /// <param name="clippingRect">The clipping rectangle.</param> /// <param name="pointsToRender">The points to render.</param> protected virtual void RenderLineAndMarkers(IRenderContext rc, OxyRect clippingRect, IList <ScreenPoint> pointsToRender) { var screenPoints = pointsToRender; if (this.Smooth) { // spline smoothing (should only be used on small datasets) var resampledPoints = ScreenPointHelper.ResamplePoints(pointsToRender, this.MinimumSegmentLength); screenPoints = CanonicalSplineHelper.CreateSpline(resampledPoints, 0.5, null, false, 0.25); } // clip the line segments with the clipping rectangle if (this.StrokeThickness > 0 && this.ActualLineStyle != LineStyle.None) { this.RenderLine(rc, clippingRect, screenPoints); } if (this.MarkerType != MarkerType.None) { var markerBinOffset = this.MarkerResolution > 0 ? this.Transform(this.MinX, this.MinY) : default(ScreenPoint); rc.DrawMarkers( clippingRect, pointsToRender, this.MarkerType, this.MarkerOutline, new[] { this.MarkerSize }, this.ActualMarkerFill, this.MarkerStroke, this.MarkerStrokeThickness, this.MarkerResolution, markerBinOffset); } }
/// <summary> /// Renders the transformed points. /// </summary> /// <param name="rc"> /// The render context. /// </param> /// <param name="clippingRect"> /// The clipping rectangle. /// </param> /// <param name="pointsToRender"> /// The points to render. /// </param> protected void RenderPoints(IRenderContext rc, OxyRect clippingRect, IList <ScreenPoint> pointsToRender) { var screenPoints = pointsToRender; if (this.Smooth) { // spline smoothing (should only be used on small datasets) var resampledPoints = ScreenPointHelper.ResamplePoints(pointsToRender, this.MinimumSegmentLength); screenPoints = CanonicalSplineHelper.CreateSpline(resampledPoints, 0.5, null, false, 0.25); } // clip the line segments with the clipping rectangle if (this.StrokeThickness > 0 && this.ActualLineStyle != LineStyle.None) { this.RenderSmoothedLine(rc, clippingRect, screenPoints); } if (this.MarkerType != MarkerType.None) { rc.DrawMarkers( pointsToRender, clippingRect, this.MarkerType, this.MarkerOutline, new[] { this.MarkerSize }, this.MarkerFill, this.MarkerStroke, this.MarkerStrokeThickness); } }
protected sealed override void CreateImage() { base.CreateImage(); Rect bounds = new Rect(StrokeWidth / 2, StrokeWidth / 2, CalculatedWidth - StrokeWidth, CalculatedHeight - StrokeWidth); Brush brush = Fill.GetBrush(); PointCollection points = GetPoints(bounds); PathGeometry geometry = CanonicalSplineHelper.CreateSpline(points, Roundness / 100.0, null, true, true, 0.25); Pen pen = GetPen(); DrawingVisual dv = new DrawingVisual(); DrawingContext dc = dv.RenderOpen(); // Draw polygon. dc.DrawGeometry(brush, pen, geometry); dc.Close(); RenderTargetBitmap rtb = RenderTargetBitmapUtility.CreateRenderTargetBitmap(CalculatedWidth, CalculatedHeight); rtb.Render(dv); Bitmap = new FastBitmap(rtb); }
/// <summary> /// The create solution geometry. /// </summary> /// <param name="solution">The solution.</param> /// <param name="height">The height.</param> /// <param name="diameter">The diameter.</param> /// <returns>The solution tube geometry.</returns> private MeshGeometry3D CreateSolutionGeometry(IEnumerable <Cell> solution, double height = 0.25, double diameter = 0.25) { var builder = new MeshBuilder(); var path = solution.Select(cell => this.GetPosition(cell, height)).ToList(); var spline = CanonicalSplineHelper.CreateSpline(path, 0.7, null, false, 0.05); builder.AddTube(spline, diameter, 13, false); return(builder.ToMesh()); }
/// <summary> /// Calculate smooth points to detect Y-Values (WORKAROUND -> there should be better solution???) /// </summary> /// <param name="dataPoints"></param> /// <returns></returns> private List <DataPoint> CalculateSmoothedDataPoints(List <DataPoint> dataPoints) { //var wpfSeries = this.fanSpeedChart.Series[0] as OxyPlot.Wpf.LineSeries; //OxyPlot.Series.LineSeries s = wpfSeries.InternalSeries as OxyPlot.Series.LineSeries; double ToleranceDivisor = 200; //double tolerance = Math.Abs(Math.Max(s.MaxX - s.MinX, s.MaxY - s.MinY) / ToleranceDivisor); double tolerance = Math.Abs(Math.Max(100.0 - 0.0, 100.0 - 0.0) / ToleranceDivisor); return(CanonicalSplineHelper.CreateSpline(dataPoints, 0.5, null, false, tolerance)); }
/// <summary> /// Gets the screen points. /// </summary> /// <returns>The list of points to display on screen for this path.</returns> protected override IList <ScreenPoint> GetScreenPoints() { var screenPoints = this.Points.Select(this.Transform).ToList(); if (this.Smooth) { var resampledPoints = ScreenPointHelper.ResamplePoints(screenPoints, this.MinimumSegmentLength); return(CanonicalSplineHelper.CreateSpline(resampledPoints, 0.5, null, false, 0.25)); } return(this.Points.Select(this.Transform).ToList()); }
/// <summary> /// Renders data points from the specified index to the specified maximum x, skipping NaN values. /// </summary> /// <param name="actualPoints">Points list.</param> /// <param name="chunksOfPoints">A list where chunks will be placed.</param> /// <param name="startIndex">The start index of the points to render.</param> /// <param name="maximumX">The maximum X coordinate to render.</param> /// <param name="rc">Render context</param> /// <param name="clippingRect">Clipping rectangle.</param> /// <param name="minDistSquared">Minimal distance squared.</param> /// <param name="reverse">Reverse points.</param> /// <param name="color">Stroke color.</param> private void RenderChunkedPoints(List <DataPoint> actualPoints, List <List <ScreenPoint> > chunksOfPoints, int startIndex, double maximumX, IRenderContext rc, OxyRect clippingRect, double minDistSquared, bool reverse, OxyColor color) { var dashArray = this.ActualDashArray; var screenPoints = new List <ScreenPoint>(); Action <List <ScreenPoint> > finalizeChunk = list => { var final = list; if (reverse) { final.Reverse(); } if (this.Smooth) { var resampled = ScreenPointHelper.ResamplePoints(final, this.MinimumSegmentLength); final = CanonicalSplineHelper.CreateSpline(resampled, 0.5, null, false, 0.25); } chunksOfPoints.Add(final); rc.DrawClippedLine( clippingRect, final, minDistSquared, this.GetSelectableColor(color), this.StrokeThickness, dashArray, this.LineJoin, false); }; int clipCount = 0; for (int i = startIndex; i < actualPoints.Count; i++) { var point = actualPoints[i]; if (double.IsNaN(point.Y)) { if (screenPoints.Count == 0) { continue; } finalizeChunk(screenPoints); screenPoints = new List <ScreenPoint>(); } else { var sp = this.XAxis.Transform(point.X, point.Y, this.YAxis); screenPoints.Add(sp); } // We break after two points were seen beyond xMax to ensure glitch-free rendering. clipCount += point.x > maximumX ? 1 : 0; if (clipCount > 1) { break; } } if (screenPoints.Count > 0) { finalizeChunk(screenPoints); } }
/// <summary> /// Renders the series on the specified rendering context. /// </summary> /// <param name="rc">The rendering context.</param> /// <param name="model">The owner plot model.</param> public override void Render(IRenderContext rc, PlotModel model) { var actualPoints = this.ActualPoints; var actualPoints2 = this.ActualPoints2; int n0 = actualPoints.Count; if (n0 == 0) { return; } this.VerifyAxes(); double minDistSquared = this.MinimumSegmentLength * this.MinimumSegmentLength; var clippingRect = this.GetClippingRect(); rc.SetClip(clippingRect); // Transform all points to screen coordinates IList <ScreenPoint> pts0 = new ScreenPoint[n0]; for (int i = 0; i < n0; i++) { pts0[i] = this.XAxis.Transform(actualPoints[i].X, actualPoints[i].Y, this.YAxis); } int n1 = actualPoints2.Count; IList <ScreenPoint> pts1 = new ScreenPoint[n1]; for (int i = 0; i < n1; i++) { int j = this.Reverse2 ? n1 - 1 - i : i; pts1[j] = this.XAxis.Transform(actualPoints2[i].X, actualPoints2[i].Y, this.YAxis); } if (this.Smooth) { var rpts0 = ScreenPointHelper.ResamplePoints(pts0, this.MinimumSegmentLength); var rpts1 = ScreenPointHelper.ResamplePoints(pts1, this.MinimumSegmentLength); pts0 = CanonicalSplineHelper.CreateSpline(rpts0, 0.5, null, false, 0.25); pts1 = CanonicalSplineHelper.CreateSpline(rpts1, 0.5, null, false, 0.25); } var dashArray = this.ActualDashArray; // draw the clipped lines rc.DrawClippedLine( clippingRect, pts0, minDistSquared, this.GetSelectableColor(this.ActualColor), this.StrokeThickness, dashArray, this.LineJoin, false); rc.DrawClippedLine( clippingRect, pts1, minDistSquared, this.GetSelectableColor(this.ActualColor2), this.StrokeThickness, dashArray, this.LineJoin, false); // combine the two lines and draw the clipped area var pts = new List <ScreenPoint>(); pts.AddRange(pts1); pts.AddRange(pts0); // pts = SutherlandHodgmanClipping.ClipPolygon(clippingRect, pts); rc.DrawClippedPolygon(clippingRect, pts, minDistSquared, this.GetSelectableFillColor(this.ActualFill), OxyColors.Undefined); var markerSizes = new[] { this.MarkerSize }; // draw the markers on top rc.DrawMarkers( clippingRect, pts0, this.MarkerType, null, markerSizes, this.MarkerFill, this.MarkerStroke, this.MarkerStrokeThickness, 1); rc.DrawMarkers( clippingRect, pts1, this.MarkerType, null, markerSizes, this.MarkerFill, this.MarkerStroke, this.MarkerStrokeThickness, 1); rc.ResetClip(); }
/// <summary> /// Force the smoothed points to be re-evaluated. /// </summary> protected virtual void ResetSmoothedPoints() { double tolerance = Math.Abs(Math.Max(this.MaxX - this.MinX, this.MaxY - this.MinY) / ToleranceDivisor); this.smoothedPoints = CanonicalSplineHelper.CreateSpline(this.ActualPoints, 0.5, null, false, tolerance); }
/// <summary> /// Renders the series on the specified rendering context. /// </summary> /// <param name="rc">The rendering context.</param> public override void Render(IRenderContext rc) { var actualPoints = this.ActualPoints; var actualPoints2 = this.points2; int n0 = actualPoints.Count; if (n0 == 0) { return; } this.VerifyAxes(); double minDistSquared = this.MinimumSegmentLength * this.MinimumSegmentLength; var clippingRect = this.GetClippingRect(); rc.SetClip(clippingRect); // Transform all points to screen coordinates IList <ScreenPoint> pts0 = new List <ScreenPoint>(); for (int i = 0; i < n0; i++) { pts0.Add(this.XAxis.Transform(actualPoints[i].X, actualPoints[i].Y, this.YAxis)); } int n1 = actualPoints2.Count; IList <ScreenPoint> pts1 = new ScreenPoint[n1]; for (int i = 0; i < n1; i++) { int j = n1 - 1 - i; pts1[j] = this.XAxis.Transform(actualPoints2[i].X, actualPoints2[i].Y, this.YAxis); } if (this.Smooth) { var rpts0 = ScreenPointHelper.ResamplePoints(pts0, this.MinimumSegmentLength); // var rpts1 = ScreenPointHelper.ResamplePoints(pts1, this.MinimumSegmentLength); pts0 = CanonicalSplineHelper.CreateSpline(rpts0, 0.5, null, false, 0.25); // pts1 = CanonicalSplineHelper.CreateSpline(rpts1, 0.5, null, false, 0.25); } var dashArray = this.ActualDashArray; var dashArray2 = this.ActualDashArray2; var limit = this.YAxis.Transform(this.Limit); if (limit < clippingRect.Top) { limit = clippingRect.Top; } if (limit > clippingRect.Bottom) { limit = clippingRect.Bottom; } var markerSizes = new[] { this.MarkerSize }; var bottom = clippingRect.Bottom; var top = clippingRect.Top; clippingRect.Top = limit; clippingRect.Height = bottom - limit; // draw the clipped lines belove the limit line rc.DrawClippedLine( clippingRect, pts0, minDistSquared, this.GetSelectableColor(this.ActualColor2), this.StrokeThickness, dashArray2, this.LineJoin, false); // combine the two lines and draw the clipped area var pts = new List <ScreenPoint>(); pts.AddRange(pts1); pts.AddRange(pts0); // fill the area belove the limit line rc.DrawClippedPolygon(clippingRect, pts, minDistSquared, this.GetSelectableFillColor(this.ActuallFill2), OxyColors.Undefined); // draw the markers on line belove the limit line rc.DrawMarkers( clippingRect, pts0, this.MarkerType, null, markerSizes, this.MarkerFill2, this.MarkerStroke2, this.MarkerStrokeThickness, 1); clippingRect.Top = top; clippingRect.Bottom = limit; // draw the clipped lines above the limit line rc.DrawClippedLine( clippingRect, pts0, minDistSquared, this.GetSelectableColor(this.ActualColor), this.StrokeThickness, dashArray, this.LineJoin, false); // fill the area above the limit line rc.DrawClippedPolygon(clippingRect, pts, minDistSquared, this.GetSelectableFillColor(this.ActualFill), OxyColors.Undefined); // draw the markers on line above the limit line rc.DrawMarkers( clippingRect, pts0, this.MarkerType, null, markerSizes, this.MarkerFill, this.MarkerStroke, this.MarkerStrokeThickness, 1); rc.ResetClip(); }
/// <summary> /// Creates the positions for the specified line segments. /// </summary> /// <param name="points"> /// The key points of the spline. /// </param> /// <param name="thickness"> /// The thickness of the line. /// </param> /// <param name="depthOffset"> /// The depth offset. A positive number (e.g. 0.0001) moves the point towards the camera. /// </param> /// <param name="clipping"> /// The clipping. /// </param> /// <param name="tension"> /// The tension. /// </param> /// <param name="tolerance"> /// The tolerance. /// </param> /// <returns> /// The positions collection. /// </returns> public Point3DCollection CreatePositions( IList <Point3D> points, double thickness = 1.0, double depthOffset = 0.0, CohenSutherlandClipping clipping = null, double tension = 0.5, double tolerance = 0.25, bool isClosed = false ) { var halfThickness = thickness * 0.5; var spoints = CanonicalSplineHelper.CreateSpline(points, tension, null, isClosed, tolerance); var segmentCount = spoints.Count - 1; var positions = new Point3DCollection(segmentCount * 4); for (int i = 0; i < segmentCount; i++) { // int startIndex = i * 2; var startPoint = spoints[i]; var endPoint = spoints[i + 1]; // Transform the start and end points to screen space var s0 = (Point4D)startPoint * this.visualToScreen; var s1 = (Point4D)endPoint * this.visualToScreen; if (clipping != null) { // Apply a clipping rectangle var x0 = s0.X / s0.W; var y0 = s0.Y / s0.W; var x1 = s1.X / s1.W; var y1 = s1.Y / s1.W; if (!clipping.ClipLine(ref x0, ref y0, ref x1, ref y1)) { continue; } s0.X = x0 * s0.W; s0.Y = y0 * s0.W; s1.X = x1 * s1.W; s1.Y = y1 * s1.W; } var lx = (s1.X / s1.W) - (s0.X / s0.W); var ly = (s1.Y / s1.W) - (s0.Y / s0.W); var l2 = (lx * lx) + (ly * ly); var p00 = s0; var p01 = s0; var p10 = s1; var p11 = s1; if (l2.Equals(0)) { // coinciding points (in world space or screen space) var dz = halfThickness; // TODO: make a square with the thickness as side length p00.X -= dz * p00.W; p00.Y -= dz * p00.W; p01.X -= dz * p01.W; p01.Y += dz * p01.W; p10.X += dz * p10.W; p10.Y -= dz * p10.W; p11.X += dz * p11.W; p11.Y += dz * p11.W; } else { var m = halfThickness / Math.Sqrt(l2); // the normal (dx,dy) var dx = -ly * m; var dy = lx * m; // segment start points p00.X += dx * p00.W; p00.Y += dy * p00.W; p01.X -= dx * p01.W; p01.Y -= dy * p01.W; // segment end points p10.X += dx * p10.W; p10.Y += dy * p10.W; p11.X -= dx * p11.W; p11.Y -= dy * p11.W; } if (!depthOffset.Equals(0)) { // Adjust the z-coordinate by the depth offset p00.Z -= depthOffset; p01.Z -= depthOffset; p10.Z -= depthOffset; p11.Z -= depthOffset; // Transform from screen space to world space p00 *= this.screenToVisual; p01 *= this.screenToVisual; p10 *= this.screenToVisual; p11 *= this.screenToVisual; positions.Add(new Point3D(p00.X / p00.W, p00.Y / p00.W, p00.Z / p00.W)); positions.Add(new Point3D(p01.X / p00.W, p01.Y / p01.W, p01.Z / p01.W)); positions.Add(new Point3D(p10.X / p00.W, p10.Y / p10.W, p10.Z / p10.W)); positions.Add(new Point3D(p11.X / p00.W, p11.Y / p11.W, p11.Z / p11.W)); } else { // Transform from screen space to world space p00 *= this.screenToVisual; p01 *= this.screenToVisual; p10 *= this.screenToVisual; p11 *= this.screenToVisual; positions.Add(new Point3D(p00.X, p00.Y, p00.Z)); positions.Add(new Point3D(p01.X, p01.Y, p01.Z)); positions.Add(new Point3D(p10.X, p10.Y, p10.Z)); positions.Add(new Point3D(p11.X, p11.Y, p11.Z)); } } positions.Freeze(); return(positions); }