using System; using System.Windows; using System.Windows.Media; using System.Windows.Shapes; namespace LiveCharts.Wpf.Points { //special thanks to Colin Eberhardt for the article. //http://www.codeproject.com/Articles/28098/A-WPF-Pie-Chart-with-Data-Binding-Support /// /// /// /// public class PieSlice : Shape { #region dependency properties /// /// The radius property /// public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("RadiusProperty", typeof(double), typeof(PieSlice), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// The radius of this pie piece /// public double Radius { get { return (double)GetValue(RadiusProperty); } set { SetValue(RadiusProperty, value); } } /// /// The push out property /// public static readonly DependencyProperty PushOutProperty = DependencyProperty.Register("PushOutProperty", typeof(double), typeof(PieSlice), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// The distance to 'push' this pie piece out from the centre. /// public double PushOut { get { return (double)GetValue(PushOutProperty); } set { SetValue(PushOutProperty, value); } } /// /// The inner radius property /// public static readonly DependencyProperty InnerRadiusProperty = DependencyProperty.Register("InnerRadiusProperty", typeof(double), typeof(PieSlice), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// The inner radius of this pie piece /// public double InnerRadius { get { return (double)GetValue(InnerRadiusProperty); } set { SetValue(InnerRadiusProperty, value); } } /// /// The wedge angle property /// public static readonly DependencyProperty WedgeAngleProperty = DependencyProperty.Register("WedgeAngleProperty", typeof(double), typeof(PieSlice), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// The wedge angle of this pie piece in degrees /// public double WedgeAngle { get { return (double)GetValue(WedgeAngleProperty); } set { SetValue(WedgeAngleProperty, value); Percentage = (value / 360.0); } } /// /// The rotation angle property /// public static readonly DependencyProperty RotationAngleProperty = DependencyProperty.Register("RotationAngleProperty", typeof(double), typeof(PieSlice), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// The rotation, in degrees, from the Y axis vector of this pie piece. /// public double RotationAngle { get { return (double)GetValue(RotationAngleProperty); } set { SetValue(RotationAngleProperty, value); } } /// /// The centre x property /// public static readonly DependencyProperty CentreXProperty = DependencyProperty.Register("CentreXProperty", typeof(double), typeof(PieSlice), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// The X coordinate of centre of the circle from which this pie piece is cut. /// public double CentreX { get { return (double)GetValue(CentreXProperty); } set { SetValue(CentreXProperty, value); } } /// /// The centre y property /// public static readonly DependencyProperty CentreYProperty = DependencyProperty.Register("CentreYProperty", typeof(double), typeof(PieSlice), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure)); /// /// The Y coordinate of centre of the circle from which this pie piece is cut. /// public double CentreY { get { return (double)GetValue(CentreYProperty); } set { SetValue(CentreYProperty, value); } } /// /// The percentage property /// public static readonly DependencyProperty PercentageProperty = DependencyProperty.Register("PercentageProperty", typeof(double), typeof(PieSlice), new FrameworkPropertyMetadata(0.0)); /// /// The percentage of a full pie that this piece occupies. /// public double Percentage { get { return (double)GetValue(PercentageProperty); } private set { SetValue(PercentageProperty, value); } } /// /// The piece value property /// public static readonly DependencyProperty PieceValueProperty = DependencyProperty.Register("PieceValueProperty", typeof(double), typeof(PieSlice), new FrameworkPropertyMetadata(0.0)); /// /// The value that this pie piece represents. /// public double PieceValue { get { return (double)GetValue(PieceValueProperty); } set { SetValue(PieceValueProperty, value); } } #endregion /// /// Gets a value that represents the of the . /// protected override Geometry DefiningGeometry { get { // Create a StreamGeometry for describing the shape var geometry = new StreamGeometry { FillRule = FillRule.EvenOdd }; using (var context = geometry.Open()) { DrawGeometry(context); } // Freeze the geometry for performance benefits geometry.Freeze(); return geometry; } } /// /// Draws the pie piece /// private void DrawGeometry(StreamGeometryContext context) { var innerArcStartPoint = PieUtils.ComputeCartesianCoordinate(RotationAngle, InnerRadius); innerArcStartPoint.Offset(CentreX, CentreY); var innerArcEndPoint = PieUtils.ComputeCartesianCoordinate(RotationAngle + WedgeAngle, InnerRadius); innerArcEndPoint.Offset(CentreX, CentreY); var outerArcStartPoint = PieUtils.ComputeCartesianCoordinate(RotationAngle, Radius); outerArcStartPoint.Offset(CentreX, CentreY); var outerArcEndPoint = PieUtils.ComputeCartesianCoordinate(RotationAngle + WedgeAngle, Radius); outerArcEndPoint.Offset(CentreX, CentreY); var innerArcMidPoint = PieUtils.ComputeCartesianCoordinate(RotationAngle + WedgeAngle * .5, InnerRadius); innerArcMidPoint.Offset(CentreX, CentreY); var outerArcMidPoint = PieUtils.ComputeCartesianCoordinate(RotationAngle + WedgeAngle * .5, Radius); outerArcMidPoint.Offset(CentreX, CentreY); var largeArc = WedgeAngle > 180.0d; var requiresMidPoint = Math.Abs(WedgeAngle - 360) < .01; if (PushOut > 0 && !requiresMidPoint) { var offset = PieUtils.ComputeCartesianCoordinate(RotationAngle + WedgeAngle / 2, PushOut); innerArcStartPoint.Offset(offset.X, offset.Y); innerArcEndPoint.Offset(offset.X, offset.Y); outerArcStartPoint.Offset(offset.X, offset.Y); outerArcEndPoint.Offset(offset.X, offset.Y); } var outerArcSize = new Size(Radius, Radius); var innerArcSize = new Size(InnerRadius, InnerRadius); if (requiresMidPoint) { context.BeginFigure(innerArcStartPoint, true, true); context.LineTo(outerArcStartPoint, true, true); context.ArcTo(outerArcMidPoint, outerArcSize, 0, false, SweepDirection.Clockwise, true, true); context.ArcTo(outerArcEndPoint, outerArcSize, 0, false, SweepDirection.Clockwise, true, true); context.LineTo(innerArcEndPoint, true, true); context.ArcTo(innerArcMidPoint, innerArcSize, 0, false, SweepDirection.Counterclockwise, true, true); context.ArcTo(innerArcStartPoint, innerArcSize, 0, false, SweepDirection.Counterclockwise, true, true); return; } context.BeginFigure(innerArcStartPoint, true, true); context.LineTo(outerArcStartPoint, true, true); context.ArcTo(outerArcEndPoint, outerArcSize, 0, largeArc, SweepDirection.Clockwise, true, true); context.LineTo(innerArcEndPoint, true, true); context.ArcTo(innerArcStartPoint, innerArcSize, 0, largeArc, SweepDirection.Counterclockwise, true, true); } } /// /// /// public static class PieUtils { /// /// Converts a coordinate from the polar coordinate system to the cartesian coordinate system. /// /// /// /// public static Point ComputeCartesianCoordinate(double angle, double radius) { // convert to radians var angleRad = (Math.PI / 180.0) * (angle - 90); var x = radius * Math.Cos(angleRad); var y = radius * Math.Sin(angleRad); return new Point(x, y); } } }