/// <summary> /// Gets called whenever figures get added/removed from the figure collection. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnFigureListEntriesChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: foreach (var o in e.NewItems) { FigureAccess access = new FigureAccess(o, OnFigureValueChanged, easeInValue: true); FigureAccesses.Add(access); var entryAnimation = new Animation((v) => { access.Entrance = (float)v; Redraw(); }, 0, 1, Easing.CubicInOut); entryAnimation.Commit(this, access.EntranceHandle, _animationFramerate, 500u); } break; case NotifyCollectionChangedAction.Move: break; case NotifyCollectionChangedAction.Remove: foreach (var o in e.OldItems) { var access = FigureAccesses[o]; if (access == null) { continue; } if (this.AnimationIsRunning(access.EntranceHandle)) { this.AbortAnimation(access.EntranceHandle); } access.Unsubscribe(); var leaveAnimation = new Animation((v) => { access.Entrance = (float)v; Redraw(); }, access.Entrance, 0, Easing.CubicInOut); leaveAnimation.Commit(this, access.EntranceHandle, _animationFramerate, 500u, finished: (v, c) => { FigureAccesses.Remove(o); Redraw(force: true); }); } break; case NotifyCollectionChangedAction.Replace: break; case NotifyCollectionChangedAction.Reset: break; default: break; } }
protected override void DrawSpecific(SKCanvas canvas, int width, int height) { float widthCut = 0f; float startPositionX = 0f; float rotationPivotX = 0f; float rotationPivotY = 0f; switch (DescribtionPosition) { case DescribtionArea.Default: widthCut = width - 2 * Padding; rotationPivotX = width / 2; rotationPivotY = height / 2; break; case DescribtionArea.LeftAndRight: widthCut = width - 2 * Padding - 2 * DescribtionSpace; startPositionX = DescribtionSpace; rotationPivotX = width / 2; rotationPivotY = height / 2; break; case DescribtionArea.Left: widthCut = width - 2 * Padding - DescribtionSpace; startPositionX = DescribtionSpace; rotationPivotX = (widthCut / 2) + startPositionX; rotationPivotY = height / 2; break; case DescribtionArea.Right: widthCut = width - 2 * Padding - DescribtionSpace; rotationPivotX = widthCut / 2; rotationPivotY = height / 2; break; } if (Rotation != 0) { canvas.RotateDegrees(Rotation, rotationPivotX, rotationPivotY); } //the sum of all values, adjusted by their animation float maxValue = 0; if (FigureAccesses == null || FigureAccesses.Count == 0) { return; } maxValue = FigureAccesses.Max(fig => fig.AnimatedValue); //The starting position as portion of the full circle angle, going from 0 to 1 //Gets adjusted after every figure by the portion of the radius that figure demands float position = 0; float barWidth = widthCut / FigureAccesses.Count; float barHeight; foreach (FigureAccess figure in FigureAccesses) { if (BarOrientation == BarOrientation.Up || BarOrientation == BarOrientation.Down) { barHeight = (height - 2 * Padding) * (figure.AnimatedValue / maxValue); } else { barHeight = (widthCut) * (figure.AnimatedValue / maxValue); barWidth = (height - 2 * Padding) / FigureAccesses.Count; } using (var paint = new SKPaint() { Color = figure.Color, IsAntialias = IsAntiAliased, Style = SKPaintStyle.Fill }) { switch (BarOrientation) { case BarOrientation.Up: canvas.DrawRect( x: startPositionX + Padding + BarPadding + position * barWidth, y: height - Padding - barHeight, w: barWidth - 2 * BarPadding, h: barHeight, paint: paint); break; case BarOrientation.Down: canvas.DrawRect( x: startPositionX + Padding + BarPadding + position * barWidth, y: Padding, w: barWidth - 2 * BarPadding, h: barHeight, paint: paint); break; case BarOrientation.Left: canvas.DrawRect( x: widthCut - Padding - barHeight, y: Padding + position * barWidth, w: barHeight, h: barWidth - 2 * BarPadding, paint: paint); break; case BarOrientation.Right: canvas.DrawRect( x: startPositionX + Padding, y: Padding + position * barWidth, w: barHeight, h: barWidth - 2 * BarPadding, paint: paint); break; default: break; } } //Increase the position for the next figure position += 1; } }
protected override void DrawSpecific(SKCanvas canvas, int width, int height) { using (new SKAutoCanvasRestore(canvas)) { float radius = 0f; switch (DescribtionPosition) { case DescribtionArea.Default: radius = Math.Min((width / 2f) - Padding, (height / 2f) - Padding); canvas.Translate(width / 2, height / 2); break; case DescribtionArea.LeftAndRight: radius = Math.Min((width / 2f) - Padding - DescribtionSpace, (height / 2f) - Padding); canvas.Translate(width / 2, height / 2); break; case DescribtionArea.Left: radius = Math.Min((width / 2f) - Padding - DescribtionSpace / 2f, (height / 2f) - Padding); canvas.Translate(DescribtionSpace / 2f + (width / 2f), height / 2f); break; case DescribtionArea.Right: radius = Math.Min((width / 2f) - Padding - DescribtionSpace / 2f, (height / 2f) - Padding); canvas.Translate(-DescribtionSpace / 2f + (width / 2f), height / 2f); break; } if (Rotation != 0) { canvas.RotateDegrees(Rotation); } //Create the inner and outer clip. //If its a polygon if (IsPolygon) { canvas.ClipPath(CircleMath.GetPolygonPath(InnerCirclePropoertionAnimated * radius, InnerCorners), SKClipOperation.Difference, antialias: IsAntiAliased); canvas.ClipPath(CircleMath.GetPolygonPath(radius, OuterCorners), SKClipOperation.Intersect, antialias: IsAntiAliased); } //If its a circle else { canvas.ClipPath(CircleMath.GetCirclePath(InnerCirclePropoertionAnimated * radius), SKClipOperation.Difference, antialias: IsAntiAliased); canvas.ClipPath(CircleMath.GetCirclePath(radius), SKClipOperation.Intersect, antialias: IsAntiAliased); } //If there are no Figures, draw a placeholder and return if (FigureAccesses == null || FigureAccesses.Count == 0) { using (var paint = new SKPaint() { Color = PlaceHolderColor, IsAntialias = IsAntiAliased, Style = SKPaintStyle.Fill }) canvas.DrawPath(CircleMath.GetPiePath(radius, 0, CircleRadius), paint); return; } //Draw the Pie pieces for every figure //the sum of all values, adjusted by their animation float valueSum = FigureAccesses.Sum(x => x.AnimatedValue); //if the sum is 0, then the valuesum is set to 1 because the circle needs a valuesum > 0 if (valueSum == 0) { valueSum = Math.Max(valueSum, 1); } //The starting position as portion of the full circle angle, going from 0 to 1 //Gets adjusted after every figure by the portion of the radius that figure demands float position = 0; foreach (FigureAccess figure in FigureAccesses) { //Create The pie shaped path float circlePortion = figure.AnimatedValue / valueSum; SKPath path = CircleMath.GetPiePath ( radius: radius, fromRad: position * CircleRadius, toRad: (position + circlePortion) * CircleRadius ); //Draw the clipped pie shape using (var paint = new SKPaint() { Color = figure.Color, IsAntialias = IsAntiAliased, Style = SKPaintStyle.Fill }) canvas.DrawPath(path, paint); //Increase the position for the next figure position += circlePortion; } } }
/// <summary> /// Draw the describtions. So far, this is chart unspecific. /// </summary> protected void DrawValueLabels(SKCanvas canvas, int width, int height) { if (FigureAccesses == null || FigureAccesses.Count == 0) { return; } using (var describtionPaint = new SKPaint() { TextSize = 20f, IsAntialias = IsAntiAliased, Color = SKColors.Black, IsStroke = false }) { //Calculating the maximum line height, that is then used for every line. float maxDescribtionHeight = 0f; foreach (FigureAccess fig in FigureAccesses) { if (!string.IsNullOrWhiteSpace(fig.Describtion)) { var describtionBounds = new SKRect(); describtionPaint.MeasureText(fig.Describtion, ref describtionBounds); maxDescribtionHeight = Math.Max(maxDescribtionHeight, describtionBounds.Height); } } DrawDescribtionBackGround(canvas, width, height, maxDescribtionHeight, FigureAccesses.Count); for (int i = 0; i < this.FigureAccesses.Count(); i++) { FigureAccess figure = FigureAccesses.ElementAt(i); if (!string.IsNullOrEmpty(figure.Describtion)) { var describtionBounds = new SKRect(); string text = figure.Describtion; describtionPaint.MeasureText(text, ref describtionBounds); maxDescribtionHeight = Math.Max(maxDescribtionHeight, describtionBounds.Height); float xOffset = 0; float yOffset = 0; float xStep = 0; float yStep = 0; int adjustedIndex = i; switch (DescribtionPosition) { case DescribtionArea.Default: case DescribtionArea.LeftAndRight: bool halfDone = i < (this.FigureAccesses.Count() + 1) / 2; adjustedIndex = halfDone ? i : i - this.FigureAccesses.Count() % 2 - this.FigureAccesses.Count() / 2; xOffset = halfDone ? width - Padding - DescribtionSpace + DescribtionPadding : +Padding + DescribtionPadding; yOffset = halfDone ? Padding + DescribtionPadding : +Padding + DescribtionPadding + ((height - 2 * Padding) - Math.Min((this.FigureAccesses.Count() / 2) * maxDescribtionHeight * DescribtionSpacing + DescribtionPadding, height - 2 * Padding)); xStep = 0; yStep = maxDescribtionHeight * DescribtionSpacing; break; case DescribtionArea.Right: xOffset = width - Padding - DescribtionSpace + DescribtionPadding; yOffset = Padding + DescribtionPadding; xStep = 0; yStep = maxDescribtionHeight * DescribtionSpacing; break; case DescribtionArea.Left: xOffset = +Padding + DescribtionPadding; yOffset = +Padding + DescribtionPadding; xStep = 0; yStep = maxDescribtionHeight * DescribtionSpacing; break; case DescribtionArea.Top: break; case DescribtionArea.Bottom: break; default: break; } SKRect colorRect = SKRect.Create( x: xOffset + xStep * adjustedIndex, y: yOffset + yStep * adjustedIndex, width: maxDescribtionHeight, height: maxDescribtionHeight ); using (var paint = new SKPaint() { IsAntialias = IsAntiAliased, Color = figure.Color, Style = SKPaintStyle.Fill }) canvas.DrawRect(colorRect, paint); canvas.DrawText(text, x: colorRect.Right + 3f, y: colorRect.Bottom, paint: describtionPaint); float valToShow = AnimateVisibleValues ? figure.AnimatedValue : figure.Value; string valText = Math.Round(valToShow, FractionalDigits).ToString("n" + FractionalDigits); canvas.DrawText( text: valText, x: colorRect.Right + describtionBounds.Width + 9f, y: colorRect.Bottom, paint: describtionPaint); } } } }