How to choose dark color always with Random?

Emon Haque 3,176 Reputation points
2021-05-29T01:35:52.233+00:00

I've a PieChart, a custom Panel, and it adds a number of PieSlice, a custom FrameworkElement, based on the number of int in the IEnumerable I provide as its ItemSource, a DependecnyProperty. When it adds slices it chooses color randomly. Sometimes those colors are pleasant and sometimes those hurt my eyes. Here's the two relevant lines in the PropertyChangedCallback of PieChart's ItemsSource:

var color = Color.FromRgb((byte)rand.Next(0, 256), (byte)rand.Next(0, 256), (byte)rand.Next(0, 256));  
var slice = new PieSlice(new SolidColorBrush(color), item);  

that sometimes hurts! When the app is loaded it looks like this:

100655-test.gif

one more issue I've at the point where PieSlices meet. Looks like there's stripe in between slices and its apparent when the colors are light. Here's the code for PieChart:

public class PieChart : Panel  
{  
    Popup pop;  
    TextBlock value, percentage;  
    Path ellipse;  
    int total;  
    DoubleAnimation ellipseAnim;  
    BooleanAnimationUsingKeyFrames popOpenAnim, popCloseAnim;  

    public PieChart() {  
        Margin = new Thickness(10);  
        value = new TextBlock() {  
            FontSize = 32,  
            FontWeight = FontWeights.Bold,  
            Foreground = Brushes.Gray,  
            HorizontalAlignment = HorizontalAlignment.Center,  
            TextAlignment = TextAlignment.Center  
        };  
        percentage = new TextBlock() {  
            Foreground = Brushes.Blue,  
            HorizontalAlignment = HorizontalAlignment.Center,  
            TextAlignment = TextAlignment.Center  
        };  
        pop = new Popup() {  
            AllowsTransparency = true,  
            PlacementTarget = this,  
            Placement = PlacementMode.Center,  
            Child = new StackPanel() {  
                Children = {  
                    value,  
                    new Separator(),  
                    percentage  
                }  
            }  
        };  
        ellipseAnim = new DoubleAnimation() {  
            BeginTime = TimeSpan.FromSeconds(1),  
            Duration = TimeSpan.FromSeconds(1),  
            EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut },  
        };  
        popOpenAnim = new BooleanAnimationUsingKeyFrames() {  
            KeyFrames = {  
                new DiscreteBooleanKeyFrame(false, TimeSpan.FromSeconds(0)),  
                new DiscreteBooleanKeyFrame(true, TimeSpan.FromSeconds(1.5)),  
            }  
        };  
        popCloseAnim = new BooleanAnimationUsingKeyFrames() {  
            KeyFrames = {  
                new DiscreteBooleanKeyFrame(false, TimeSpan.FromSeconds(1))  
            }  
        };  
    }  
    protected override Size ArrangeOverride(Size finalSize) {  
        if (ItemSource != null) {  
            var list = ItemSource.Cast<int>().ToList();  
            double cx = finalSize.Width / 2;  
            double cy = finalSize.Height / 2;  
            double radius, startAngle, sweepAngle, endAngle;  
            startAngle = sweepAngle = endAngle = 0d;  
            radius = cx > cy ? cy : cx;  
            var total = list.Sum();  
            int index = 0;  
            foreach (PieSlice item in Children.OfType<PieSlice>()) {  
                var value = list[index];  
                startAngle += sweepAngle;  
                sweepAngle = 2 * Math.PI * value / total;  
                endAngle = startAngle + sweepAngle;  
                bool isLarge = (double)value / total > 0.5;  
                item.SetParameters(cx, cy, radius, startAngle, sweepAngle, isLarge);  
                item.Arrange(new Rect(item.DesiredSize));  
                index++;  
            }  
            ellipse = Children.OfType<Path>().First();  
            var geo = ellipse.Data as EllipseGeometry;  
            geo.RadiusX = geo.RadiusY = radius - 100;  
            geo.Center = new Point(cx, cy);  
            ellipse.Measure(finalSize);  
            ellipse.Arrange(new Rect(ellipse.DesiredSize));  
            ellipse.RenderTransform = new ScaleTransform(0, 0);  
        }  
        return finalSize;  
    }  
    void animateEllipse(double scale) {  
        double x, y, cx, cy;  
        cx = ActualWidth / 2;  
        cy = ActualHeight / 2;  
        if (scale == 1) {  
            x = y = 0;  
            pop.BeginAnimation(Popup.IsOpenProperty, popOpenAnim);  
        }  
        else {  
            var transfrom = ellipse.RenderTransform as ScaleTransform;  
            x = transfrom.ScaleX;  
            y = transfrom.ScaleY;  
            pop.BeginAnimation(Popup.IsOpenProperty, popCloseAnim);  
        }  
        ellipse.RenderTransform = new ScaleTransform(x, y, cx, cy);  
        ellipseAnim.To = scale;  
        ellipse.RenderTransform.BeginAnimation(ScaleTransform.ScaleXProperty, ellipseAnim);  
        ellipse.RenderTransform.BeginAnimation(ScaleTransform.ScaleYProperty, ellipseAnim);  
    }  
    void showPopup(object sender, MouseEventArgs e) {  
        double val = (sender as PieSlice).value;  
        value.Text = val.ToString();  
        percentage.Text = (val / total * 100).ToString("N2") + " %";  
        animateEllipse(1);  
    }  
    void hidePopup(object sender, MouseEventArgs e) => animateEllipse(0);  

    public IEnumerable ItemSource {  
        get { return (IEnumerable)GetValue(ItemSourceProperty); }  
        set { SetValue(ItemSourceProperty, value); }  
    }  
    public static readonly DependencyProperty ItemSourceProperty =  
        DependencyProperty.Register("ItemSource", typeof(IEnumerable), typeof(PieChart), new PropertyMetadata(null, onSourceChanged));  

    static void onSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {  
        var o = d as PieChart;  
        if (e.OldValue != null) o.Children.Clear();  
        if (o.ItemSource != null) {  
            Random rand = new();  
            foreach (int item in o.ItemSource) {  
                o.total += item;  
                var color = Color.FromRgb((byte)rand.Next(0, 256), (byte)rand.Next(0, 256), (byte)rand.Next(0, 256));  
                var slice = new PieSlice(new SolidColorBrush(color), item);  
                o.Children.Add(slice);  
                slice.MouseEnter += o.showPopup;  
                slice.MouseLeave += o.hidePopup;  
            }  
            var path = new Path() {  
                Fill = Brushes.White,  
                Data = new EllipseGeometry()  
            };  
            o.Children.Add(path);  
        }  
    }  
}  

and here's PieSlice:

public class PieSlice : FrameworkElement  
{  
    PathGeometry geometry, arcGeo;  
    PathFigure figure;  
    LineSegment startLine;  
    ArcSegment arc;  
    SolidColorBrush original, background;  
    DoubleAnimation translateX, translateY, scaleAndRotate;  
    ColorAnimation colorAnim;  
    double cx, dx, cy, dy, radius, rotation;  
    Point start, end;  
    bool isLargeArc;  
    public int value;  

    public PieSlice(SolidColorBrush brush, int value) {  
        this.value = value;  
        original = brush;  
        background = new SolidColorBrush(original.Color);  
        startLine = new LineSegment();  
        arc = new ArcSegment() { SweepDirection = SweepDirection.Clockwise };  
        figure = new PathFigure() {  
            IsClosed = true,  
            Segments = { startLine, arc }  
        };  
        geometry = new PathGeometry() { Figures = { figure } };  
        Loaded += animateSlice;  
        initAnimations();  
    }  
    void initAnimations() {  
        scaleAndRotate = new DoubleAnimation() {  
            From = 0,  
            Duration = TimeSpan.FromSeconds(3),  
            EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut }  
        };  
        translateX = new DoubleAnimation() {  
            Duration = TimeSpan.FromSeconds(2),  
            EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut }  
        };  
        translateY = new DoubleAnimation() {  
            Duration = TimeSpan.FromSeconds(2),  
            EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut }  
        };  
        colorAnim = new ColorAnimation() {  
            Duration = TimeSpan.FromSeconds(2),  
            EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut }  
        };  
    }  
    void animateSlice(object sender, RoutedEventArgs e) {  
        arcGeo.Freeze();  
        var arcPointAnim = new PointAnimationUsingPath() {  
            PathGeometry = arcGeo,  
            Duration = TimeSpan.FromSeconds(2),  
            FillBehavior = FillBehavior.Stop  
        };  
        RenderTransform = new TransformGroup() {  
            Children = {  
                new ScaleTransform(1, 1) {  
                    CenterX = cx,  
                    CenterY = cy  
                },  
                new RotateTransform(360) {  
                    CenterX = cx,  
                    CenterY = cy  
                }  
            }  
        };  
        var scale = (RenderTransform as TransformGroup).Children[0];  
        var rotate = (RenderTransform as TransformGroup).Children[1];  
        scale.BeginAnimation(ScaleTransform.ScaleXProperty, scaleAndRotate);  
        scale.BeginAnimation(ScaleTransform.ScaleYProperty, scaleAndRotate);  
        rotate.BeginAnimation(RotateTransform.AngleProperty, scaleAndRotate);  
        rotate.Changed += updateAngle;  
        arc.BeginAnimation(ArcSegment.PointProperty, arcPointAnim);  
    }  
    void updateAngle(object sender, EventArgs e) {  
        rotation = (sender as RotateTransform).Angle;  
        if (rotation == 360) {  
            RenderTransform = new TranslateTransform(0, 0);  
        }  
    }  
    public void SetParameters(double cx, double cy, double radius, double startAngle, double sweepAngle, bool isLargeArc) {  
        this.cx = cx;  
        this.cy = cy;  
        this.radius = radius;  
        this.isLargeArc = isLargeArc;  
        start = new Point(cx + radius * Math.Cos(startAngle), cy + radius * Math.Sin(startAngle));  
        end = new Point(cx + radius * Math.Cos(startAngle + sweepAngle), cy + radius * Math.Sin(startAngle + sweepAngle));  
        dx = 10 * Math.Cos(startAngle + sweepAngle / 2);  
        dy = 10 * Math.Sin(startAngle + sweepAngle / 2);  
        arcGeo = new PathGeometry() {  
            Figures = {  
                new PathFigure() {  
                    StartPoint = start,  
                    Segments = {  
                        new ArcSegment() {  
                            SweepDirection = SweepDirection.Clockwise,  
                            Point = end,  
                            Size = new Size(radius, radius),  
                            IsLargeArc = isLargeArc  
                        }  
                    }  
                }  
            }  
        };  

        Draw();  
    }  
    void Draw() {  
        figure.StartPoint = new Point(cx, cy);  
        startLine.Point = start;  
        arc.Point = end;  
        arc.Size = new Size(radius, radius);  
        arc.IsLargeArc = isLargeArc;  
    }  
    protected override void OnRender(DrawingContext dc) => dc.DrawGeometry(background, null, geometry);  
    protected override void OnMouseEnter(MouseEventArgs e) {  
        if (rotation < 360) return;  
        var t = (RenderTransform as TranslateTransform);  
        translateX.By = dx - t.X;  
        translateY.By = dy - t.Y;  
        RenderTransform.BeginAnimation(TranslateTransform.XProperty, translateX);  
        RenderTransform.BeginAnimation(TranslateTransform.YProperty, translateY);  
        colorAnim.To = Colors.Gray;  
        background.BeginAnimation(SolidColorBrush.ColorProperty, colorAnim);  
    }  
    protected override void OnMouseLeave(MouseEventArgs e) {  
        if (rotation < 360) return;  
        var t = (RenderTransform as TranslateTransform);  
        translateX.By = -t.X;  
        translateY.By = -t.Y;  
        RenderTransform.BeginAnimation(TranslateTransform.XProperty, translateX);  
        RenderTransform.BeginAnimation(TranslateTransform.YProperty, translateY);  
        colorAnim.To = original.Color;  
        background.BeginAnimation(SolidColorBrush.ColorProperty, colorAnim);  
    }  
}  

just add PieChart in MainWindow.xaml and give it a List<int>/ObservableCollection<int> as its ItemSource and you'll be able to see it clearly. In the PieSlice I've an ArcSegment and a LineSegment at start. I've tried by adding another LineSegment at the end BUT I see those stripes.

Developer technologies Windows Presentation Foundation
0 comments No comments
{count} votes

Accepted answer
  1. DaisyTian-1203 11,646 Reputation points
    2021-06-01T06:24:38.477+00:00

    We can get below color referring to Colors Class:

     var white = Color.FromRgb(255, 255, 255);  
     var black = Color.FromRgb(0, 0, 0);  
     var red = Color.FromRgb(255, 0, 0);  
     var green = Color.FromRgb(0, 255, 0);  
     var blue = Color.FromRgb(0, 0, 255);  
    

    Analyze the color table and test a lot, I find that if I change your color like below, it will always get dark color.

    var color = Color.FromRgb((byte)rand.Next(0, 128), (byte)rand.Next(0, 128), (byte)rand.Next(0, 128));  
    

    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.