Нарисовать правильный многоугольник (Ngon)

Правильный многоугольник - это многоугольник все стороны и все внутренние углы которого равны между собой. Примерами правильных многоугольников являются квадрат и равносторонний треугольник.

Любой правильный многоугольник может быть вписан в окружность. Воспользуемся этим свойством и нарисуем произвольный правильный многоугольник (например 6-угольник) с заданным радиусом.

Создадим новый пустой проект со статичной отрисовкой

 public Form1()
 {
     InitializeComponent();
     Paint += Form1_Paint; // подпишемся на событие отрисовки формы
     ClientSize = new Size(500, 500);  // зададим размер пользовательской области для рисования 500 на 500 пикселей
     StartPosition = FormStartPosition.CenterScreen;  // (опционально) зададим стартовую позицию окна по центру экрана
 }

 

Чтобы вывести многоугольник достаточно пройтись циклом по всем его углам и соеденить их линиями между собой. 

Подводящее упражнение 1

Для начала давайте отрисуем маркеры (маленькие прямоугольники) в каждой точке нашего нового правильного многоугольника:

  private void Form1_Paint(object? sender, PaintEventArgs e)
  {
      var gr = e.Graphics;
      gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;  // (опционально) включаем сглаживание, чтобы улучшить качество картинки
      gr.Clear(Color.Black);  // заливаем всю область рисования черным цветом

      double radius = 160;   // радиус описанной окружности в которую вписан наш многоугольник
      
      var centerX = ClientRectangle.Width / 2;   // X центра области рисования (в этом примере это буде 250)
      var centerY = ClientRectangle.Height / 2;  // Y центра

      int n = 6;  // количество углов в фигуре 
      var step = 360.0 / n;  // шаг приращения градусов на каждый угол фигуры

      const int cornerSize = 3; // размера маркера деленный на два (половина размера)

      for (int i = 0; i <= n; i ++)  // совершим n+1 шагов, на каждом шаге увеличиваем угол на step
      {
          var angle = i * step;
          var radians = angle * Math.PI / 180.0;   // переведем наш угол i в радианы

          var x = centerX + radius * Math.Cos(radians);  // вычислим координаты на описанной окружности
          var y = centerY + radius * Math.Sin(radians);

          PointF point = new PointF((float)x, (float)y); // создадим точку PointF

           // нарисуем маркер
          gr.DrawRectangle(Pens.Yellow, (float)x - cornerSize, (float)y - cornerSize, 2 * cornerSize, 2 * cornerSize);
      }
  }

Запустив программу получим следюущий результат:

Обратите внимание что первая точка которая будет отрисована расположена справа. Это связано с тем что при 0° косинус возращает 1, т.е. cos(0) = 1 (ось X максимальна), а sin(0) = 0 (ось Y равна 0). Поэтому если требуется вывести повернутую фигуру, то требуется поворачивать угол перед расчетом координат каждой точки. Например, чтобы повернуть на 90° достаточно добавить 90 к переменной angle (из примера выше)

Теперь чтобы получить шестиугольник нам осталось только соеденить полученные точки. Для этого нам нужно ввести новую переменную prevPoint, которая будет хранить предыдущую точку. Первую линию мы сможем нарисовать когда вычислим минимум две точки, поэтому на первом шаге мы линию не рисуем.

  private void Form1_Paint(object? sender, PaintEventArgs e)
  {
      var gr = e.Graphics;
      gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;  // (опционально) включаем сглаживание, чтобы улучшить качество картинки
      gr.Clear(Color.Black);  // заливаем всю область рисования черным цветом
      PointF? prevPoint = null;
      double radius = 160;   // радиус описанной окружности в которую вписан наш многоугольник
      
      var centerX = ClientRectangle.Width / 2;   // X центра области рисования (в этом примере это буде 250)
      var centerY = ClientRectangle.Height / 2;  // Y центра

      int n = 6;  // количество углов в фигуре 
      var step = 360.0 / n;  // шаг приращение градусов на каждый угол фигуры
      
      for (int i = 0; i <= n; i ++)  // совершим n+1 шагов, на каждом шаге увеличиваем угол на step
      {
          var angle = i * step;
          var radians = angle * Math.PI / 180.0;   // переведем наш угол i в радианы
          var x = centerX + radius * Math.Cos(radians);  // вычисляим координаты на описанной окружности
          var y = centerY + radius * Math.Sin(radians);

          PointF point = new PointF((float)x, (float)y); // создадим точку PointF

          if (prevPoint != null)  // проверяем что предыдущая точка была заполнена ранее
              gr.DrawLine(Pens.White, prevPoint.Value, point);  // рисуем белую линия из точки prevPoint до точки point

          prevPoint = point;  // обновляем prevPoint
      }
  }

При запуске программы получим следующий результат:

 


Аппроксимация окружности многоугольником 

Часто отрисовку окружностей заменяют отрисовкой предрасчитанной полилинией - правильным многоугольником с большим шагом разбиения (достаточным для визуального восприятия полилинии как окружности). Давайте попробуем поставить n = 60 и убедиться в этом.

Как вы видите при аппроксимации окружности правильным многоугольником с 60 углами визуально мы получаем хорошую гладкую окружность.  Более подробнее читай здесь 

 

Упражнения:

1. Совместите две отрисовки и нарисуйте маркеры поверх многоугольника

2. Попробуйте поменять n чтобы нарисовать многоугольники с другим количеством углов

3. Нарисуйте ромб. Подумайте как сделать так чтобы из него получился квадрат 

4. Нарисовать пчелиные соты (гексагоны стоящие встык с друг другом)

5. Сделать чтобы радиус многоугольника подбирался автоматически в зависимости от размеров области отрисовки (ClientSize), сделать чтобы при изменении размеров окна происходила перерисовка многоугольника с новым размером.