protected void DrawHorizontalBezier(ILineDataSet dataSet) { float phaseY = Animator.PhaseY; var trans = Chart.GetTransformer(dataSet.AxisDependency); XBounds.Set(Chart, dataSet, Animator); CubicPath.Reset(); if (XBounds.Range >= 1) { var prev = ((IDataSet <Entry>)dataSet)[XBounds.Min]; var cur = prev; // let the spline start CubicPath.MoveTo(cur.X, cur.Y * phaseY); for (int j = XBounds.Min + 1; j <= XBounds.Range + XBounds.Min; j++) { prev = cur; cur = ((IDataSet <Entry>)dataSet)[j]; float cpx = (prev.X) + (cur.X - prev.X) / 2.0f; CubicPath.CubicTo( cpx, prev.Y * phaseY, cpx, cur.Y * phaseY, cur.X, cur.Y * phaseY); } } // if filled is enabled, close the path if (dataSet.DrawFilled) { CubicFillPath.Reset(); CubicFillPath.AddPath(CubicPath); // create a new path, this is bad for performance DrawCubicFill(BitmapCanvas, dataSet, CubicFillPath, trans, XBounds); } RenderPaint.Color = dataSet.Color; RenderPaint.Style = SKPaintStyle.Stroke; trans.PathValueToPixel(CubicPath); BitmapCanvas.DrawPath(CubicPath, RenderPaint); RenderPaint.PathEffect = null; }
protected void DrawCircles(SKCanvas c) { RenderPaint.Style = SKPaintStyle.Fill; float phaseY = Animator.PhaseY; var dataSets = ((Interfaces.DataProvider.ILineChartProvider)Chart).Data.DataSets; for (int i = 0; i < dataSets.Count; i++) { ILineDataSet dataSet = dataSets[i]; if (!dataSet.IsVisible || !dataSet.IsDrawCirclesEnabled || dataSet.EntryCount == 0) { continue; } CirclePaintInner.Color = dataSet.CircleHoleColor; Transformer trans = Chart.GetTransformer(dataSet.AxisDependency); XBounds.Set(Chart, dataSet, Animator); float circleRadius = dataSet.CircleRadius; float circleHoleRadius = dataSet.CircleHoleRadius; var drawCircleHole = dataSet.IsDrawCircleHoleEnabled && circleHoleRadius <circleRadius && circleHoleRadius> 0.0f; var drawTransparentCircleHole = drawCircleHole && dataSet.CircleHoleColor == SKColors.Empty; if (imageCaches.TryGetValue(dataSet, out DataSetImageCache imageCache) == false) { imageCache = new DataSetImageCache(); imageCaches.Add(dataSet, imageCache); } // only fill the cache with new bitmaps if a change is required if (imageCache.Init(dataSet)) { Fill(imageCache, dataSet, drawCircleHole, drawTransparentCircleHole); } int boundsRangeCount = XBounds.Range + XBounds.Min; for (int j = XBounds.Min; j <= boundsRangeCount; j++) { Entry e = ((IDataSet)dataSet)[j]; if (e == null) { break; } var pt = trans.PointValueToPixel(e.X, e.Y * phaseY); if (!ViewPortHandler.IsInBoundsRight(pt.X)) { break; } if (!ViewPortHandler.IsInBoundsLeft(pt.X) || !ViewPortHandler.IsInBoundsY(pt.Y)) { continue; } var circleBitmap = imageCache.GetBitmap(j); if (circleBitmap != null) { c.DrawBitmap(circleBitmap, pt.X - circleRadius, pt.Y - circleRadius, null); } } } }
public override void DrawValues(SKCanvas c) { if (IsDrawingValuesAllowed(Chart)) { var dataSets = ((Interfaces.DataProvider.ILineChartProvider)Chart).Data.DataSets; for (int i = 0; i < dataSets.Count; i++) { ILineDataSet dataSet = dataSets[i]; if (!ShouldDrawValues(dataSet) || dataSet.EntryCount < 1) { continue; } // apply the text-styling defined by the DataSet ApplyValueTextStyle(dataSet); Transformer trans = Chart.GetTransformer(dataSet.AxisDependency); // make sure the values do not interfear with the circles int valOffset = (int)(dataSet.CircleRadius * 1.75f); if (!dataSet.IsDrawCirclesEnabled) { valOffset /= 2; } XBounds.Set(Chart, dataSet, Animator); var positions = trans.GenerateTransformedValuesLine(dataSet, Animator.PhaseX, Animator .PhaseY, XBounds.Min, XBounds.Max); var iconsOffset = dataSet.IconsOffset.DpToPixel(); for (int j = 0; j < positions.Length; j++) { var pos = positions[j]; if (!ViewPortHandler.IsInBoundsRight(pos.X)) { break; } if (!ViewPortHandler.IsInBoundsLeft(pos.X) || !ViewPortHandler.IsInBoundsY(pos.Y)) { continue; } Entry entry = ((IDataSet)dataSet)[j + XBounds.Min]; if (dataSet.IsDrawValuesEnabled) { DrawValue(c, dataSet.ValueFormatter, entry.Y, entry, i, pos.X, pos.Y - valOffset, dataSet.ValueTextColorAt(j)); } if (entry.Icon != null && dataSet.IsDrawIconsEnabled) { entry.Icon.Draw( c, (int)(pos.X + iconsOffset.X), (int)(pos.Y + iconsOffset.Y)); } } } } }
protected void DrawCubicBezier(ILineDataSet lineDataSet) { float phaseY = Animator.PhaseY; Transformer trans = Chart.GetTransformer(lineDataSet.AxisDependency); XBounds.Set(Chart, lineDataSet, Animator); float intensity = lineDataSet.CubicIntensity; CubicPath.Reset(); IDataSet <Entry> dataSet = lineDataSet; if (XBounds.Range >= 1) { // Take an extra point from the left, and an extra from the right. // That's because we need 4 points for a cubic bezier (cubic=4), otherwise we get lines moving and doing weird stuff on the edges of the chart. // So in the starting `prev` and `cur`, go -2, -1 // And in the `lastIndex`, add +1 int firstIndex = XBounds.Min + 1; Entry prevPrev; Entry prev = dataSet[Math.Max(firstIndex - 2, 0)]; Entry cur = dataSet[Math.Max(firstIndex - 1, 0)]; Entry next = cur; int nextIndex = -1; if (cur == null) { return; } // let the spline start CubicPath.MoveTo(cur.X, cur.Y * phaseY); for (int j = XBounds.Min + 1; j <= XBounds.Range + XBounds.Min; j++) { prevPrev = prev; prev = cur; cur = nextIndex == j ? next : dataSet[j]; nextIndex = j + 1 < lineDataSet.EntryCount ? j + 1 : j; next = dataSet[nextIndex]; float prevDx = (cur.X - prevPrev.X) * intensity; float prevDy = (cur.Y - prevPrev.Y) * intensity; float curDx = (next.X - prev.X) * intensity; float curDy = (next.Y - prev.Y) * intensity; CubicPath.CubicTo(prev.X + prevDx, (prev.Y + prevDy) * phaseY, cur.X - curDx, (cur.Y - curDy) * phaseY, cur.X, cur.Y * phaseY); } } // if filled is enabled, close the path if (lineDataSet.DrawFilled) { CubicFillPath.Reset(); CubicFillPath.AddPath(CubicPath); DrawCubicFill(BitmapCanvas, lineDataSet, CubicFillPath, trans, XBounds); } RenderPaint.Color = lineDataSet.Color; RenderPaint.Style = SKPaintStyle.Stroke; trans.PathValueToPixel(CubicPath); BitmapCanvas.DrawPath(CubicPath, RenderPaint); RenderPaint.PathEffect = null; }
protected void DrawLinear(SKCanvas c, ILineDataSet lineDataSet) { int entryCount = lineDataSet.EntryCount; bool isDrawSteppedEnabled = lineDataSet.Mode == LineDataSet.LineMode.Stepped; int pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2; Transformer trans = Chart.GetTransformer(lineDataSet.AxisDependency); float phaseY = Animator.PhaseY; RenderPaint.Style = SKPaintStyle.Stroke; SKCanvas canvas; // if the data-set is dashed, draw on bitmap-canvas if (lineDataSet.IsDashedLineEnabled) { canvas = BitmapCanvas; } else { canvas = c; } XBounds.Set(Chart, lineDataSet, Animator); // if drawing filled is enabled if (lineDataSet.DrawFilled && entryCount > 0) { DrawLinearFill(c, lineDataSet, trans, XBounds); } // more than 1 color if (lineDataSet.Colors.Count > 1) { int numberOfFloats = pointsPerEntryPair; if (_lineBuffer.Length <= numberOfFloats) { _lineBuffer = new SKPoint[numberOfFloats]; } int max = XBounds.Min + XBounds.Range; IDataSet <Entry> dataSet = lineDataSet; for (int j = XBounds.Min; j < max; j++) { Entry e = dataSet[j]; if (e == null) { continue; } _lineBuffer[0] = new SKPoint(e.X, e.Y * phaseY); if (j < XBounds.Max) { e = dataSet[j + 1]; if (e == null) { break; } if (isDrawSteppedEnabled) { _lineBuffer[1] = new SKPoint(e.X, _lineBuffer[0].Y); _lineBuffer[2] = _lineBuffer[1]; _lineBuffer[3] = new SKPoint(e.X, e.Y * phaseY); } else { _lineBuffer[1] = new SKPoint(e.X, e.Y * phaseY); } } else { _lineBuffer[1] = _lineBuffer[0]; } var pts = trans.PointValuesToPixel(_lineBuffer); // Determine the start and end coordinates of the line, and make sure they differ. var firstCoordinate = pts[0]; var lastCoordinate = pts[numberOfFloats - 1]; if (firstCoordinate == lastCoordinate) { continue; } if (!ViewPortHandler.IsInBoundsRight(firstCoordinate.X)) { break; } // make sure the lines don't do shitty things outside // bounds if (!ViewPortHandler.IsInBoundsLeft(lastCoordinate.X) || !ViewPortHandler.IsInBoundsTop(Math.Max(firstCoordinate.Y, lastCoordinate.Y)) || !ViewPortHandler.IsInBoundsBottom(Math.Min(firstCoordinate.Y, lastCoordinate.Y))) { continue; } // get the color that is set for this line-segment RenderPaint.Color = lineDataSet.ColorAt(j); for (int i = 0; i < numberOfFloats; i += 2) { canvas.DrawLine(pts[i], pts[i + 1], RenderPaint); } } } else { // only one color per dataset if (_lineBuffer.Length < Math.Max(entryCount * pointsPerEntryPair, pointsPerEntryPair)) { _lineBuffer = new SKPoint[Math.Max(entryCount * pointsPerEntryPair, pointsPerEntryPair) * 2]; } Entry e1, e2; IDataSet <Entry> dataSet = lineDataSet; e1 = dataSet[XBounds.Min]; if (e1 != null) { int j = 0; for (int x = XBounds.Min; x <= XBounds.Range + XBounds.Min; x++) { e1 = dataSet[x == 0 ? 0 : (x - 1)]; e2 = dataSet[x]; if (e1 == null || e2 == null) { continue; } _lineBuffer[j++] = new SKPoint(e1.X, e1.Y * phaseY); if (isDrawSteppedEnabled) { _lineBuffer[j++] = new SKPoint(e2.X, e1.Y * phaseY); _lineBuffer[j++] = new SKPoint(e2.X, e1.Y * phaseY); } _lineBuffer[j++] = new SKPoint(e2.X, e2.Y * phaseY); } if (j > 0) { var pts = trans.PointValuesToPixel(_lineBuffer); int size = Math.Max((XBounds.Range + 1) * pointsPerEntryPair, pointsPerEntryPair); RenderPaint.Color = lineDataSet.Color; for (int i = 0; i < size; i += 2) { canvas.DrawLine(pts[i], pts[i + 1], RenderPaint); } } } } RenderPaint.PathEffect = null; }