Нарисуйте аннотированную круговую диаграмму в C#

Пример Нарисуйте отмеченную круговую диаграмму в C# объясняет, как рисовать круговую диаграмму с помеченными фрагментами. В этом примере добавляются аннотации, прикрепленные к срезам по строкам. Следующий код показывает, как программа рисует, накладывает метки и комментирует фрагменты.

// Рисуем круговую диаграмму.
private static void DrawAnnotatedPieChart(Graphics gr,
    Rectangle ellipse_rect, Rectangle left_rect,
    Rectangle right_rect, float annotation_radius_scale,
    float initial_angle, Brush[] brushes, Pen[] pens,
    float[] values, string[] annotations, string label_format,
    Font label_font, Brush label_brush, Font annotation_font,
    Pen annotation_pen, Brush annotation_brush,
    Brush rectangle_brush, Pen rectangle_pen)
{
    // Получите общее количество всех углов.
    float total = values.Sum();

    // Рисуем срезы.
    float start_angle = initial_angle;
    for (int i = 0; i < values.Length; i++)
    {
        float sweep_angle = values[i] * 360f / total;

        // Заполните и нарисуйте кусочек пирога.
        gr.FillPie(brushes[i % brushes.Length], ellipse_rect,
            start_angle, sweep_angle);
        gr.DrawPie(pens[i % pens.Length], ellipse_rect,
            start_angle, sweep_angle);

        start_angle += sweep_angle;
    }

    // При необходимости нарисуем прямоугольники.
    if (rectangle_brush != null)
    {
        gr.FillRectangle(rectangle_brush, left_rect);
        gr.FillRectangle(rectangle_brush, right_rect);
    }
    if (rectangle_pen != null)
    {
        gr.DrawRectangle(rectangle_pen, left_rect);
        gr.DrawRectangle(rectangle_pen, right_rect);
    }

    // Ярлык и аннотирование фрагментов.
    // Мы маркируем срезы после их рисования так, чтобы один
    // slice не покрывает метку на другом очень тонком срезе.
    using (StringFormat string_format = new StringFormat())
    {
        // Найдите центр прямоугольника.
        float cx = (ellipse_rect.Left + ellipse_rect.Right) / 2;
        float cy = (ellipse_rect.Top + ellipse_rect.Bottom) / 2;

        // Поместите метку примерно на 2/3 пути к краю.
        float radius =
            (ellipse_rect.Width + ellipse_rect.Height) / 2f * 0.33f;

        // Расстояния для строк аннотаций.
        float annotation_rx1 = ellipse_rect.Width / 2;
        float annotation_ry1 = ellipse_rect.Height / 2;
        float annotation_rx2 =
            annotation_rx1 * annotation_radius_scale;
        float annotation_ry2 =
            annotation_ry1 * annotation_radius_scale;

        start_angle = start_angle = initial_angle;
        for (int i = 0; i < values.Length; i++)
        {
            float sweep_angle = values[i] * 360f / total;

            // Ярлык фрагмента.
            string_format.Alignment = StringAlignment.Center;
            string_format.LineAlignment = StringAlignment.Center;
            double label_angle =
                Math.PI * (start_angle + sweep_angle / 2) / 180;
            float x = cx + (float)(radius * Math.Cos(label_angle));
            float y = cy + (float)(radius * Math.Sin(label_angle));
            gr.DrawString(values[i].ToString(label_format),
                label_font, label_brush, x, y, string_format);

                        float x1 = cx +
                (float)(annotation_rx1 * Math.Cos(label_angle));
            float y1 = cy +
                (float)(annotation_rx1 * Math.Sin(label_angle));
            float x2 = cx +
                (float)(annotation_rx2 * Math.Cos(label_angle));
            float y2 = cy +
                (float)(annotation_rx2 * Math.Sin(label_angle));
            gr.DrawLine(annotation_pen, x1, y1, x2, y2);

                        if (x2 < x1)
            {
                                gr.DrawLine(annotation_pen, x2, y2,
                    left_rect.Right, y2);

                                string_format.Alignment = StringAlignment.Far;
                gr.DrawString(annotations[i], annotation_font,
                    annotation_brush, left_rect.Right, y2,
                    string_format);
            }
            else
            {
                                gr.DrawLine(annotation_pen, x2, y2,
                    right_rect.Left, y2);

                                string_format.Alignment = StringAlignment.Near;
                gr.DrawString(annotations[i], annotation_font,
                    annotation_brush, right_rect.Left, y2,
                    string_format);
            }

            start_angle += sweep_angle;
        }
    }
}

См. предыдущие сообщения для получения подробной информации о коде, который рисует и маркирует срезы. Этот пост концентрируется на коде, который рисует радиальные и горизонтальные линии от срезов до их аннотаций.

Прежде чем он начнет цикл через срезы, чтобы нарисовать свои метки и аннотации, код вычисляет значения annotation_rx1 и annotation_ry1. Они дают радиусы, что код может умножаться на синусы и косинусы углов, чтобы получить точку на краю эллипса среза пирога.

Код также вычисляет annotation_rx2 и annotation_ry2, который он может использовать для получения точек под тем же углом, что и точки на краю эллипса, но немного вне эллипса.

Затем программа перебирает фрагменты, обозначая их как в предыдущем примере. Для каждого среза он также рисует короткий радиальный сегмент между точками, рассчитанными с помощью annotation_rx1, annotation_ry1, annotation_rx2 и annotation_ry2 .

Затем, в зависимости от того, больше ли радиальная линия располагается слева или справа, код рисует горизонтальную линию в левую или правую область аннотации. Он заканчивает срез, нарисуя текст аннотации фрагмента как вправо, так и влево.

Этот пример немного хулиган и не обязательно дает хорошие результаты для некоторых наборов данных. Например, если в верхней или нижней части эллипса слишком много очень тонких срезов, их текст аннотации может перекрываться. В этом случае вы можете попытаться нарисовать линии для аннотаций, которые распространяют их отдельно, но это было бы сложно сделать в общем виде и могло бы также привести к уродливым результатам. В качестве альтернативы вы можете изменить значения, чтобы тонкие не были размещены вместе, или вы могли бы изменить начальный угол для первого среза, чтобы группы тонких срезов не были сверху или снизу.

Наконец, из-за того, как методы FillPie и DrawPie измеряют углы, этот пример работает только хорошо, если эллипс представляет собой круг. Если эллипс очень эллиптический, радиальные линии срезов не будут находиться в центрах их срезов и в крайних случаях могут вообще отсутствовать в их срезах.

Источник: http://csharphelper.com/blog/2016/03/draw-an-annotated-pie-chart-in-c/

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Adblock
detector