Перейти к содержимому

Уроки · 56

Многоугольники

До сих пор мы рисовали только круги — draw_circle сам решает, как это сделать. А если захочется свой собственный многоугольник — с любым числом сторон, треугольник или шестиугольник? Тут как раз пригодятся cos и sin, та же парочка, что и в уроке «Вращение».

Вершины правильного многоугольника — это просто несколько точек на окружности, расставленных через равный угол одна от другой. Раз полный оборот делится поровну между всеми сторонами, угол между соседними вершинами — это просто полный оборот, поделённый на их количество:

let step = (360.0 / sides as f32).to_radians();

Дальше — цикл на каждую сторону: у вершины i угол равен step, умноженному на i, а у следующей вершины — на i + 1. Сама точка на окружности считается так же, как и раньше: центр плюс направление (cos, sin), растянутое на радиус. Между двумя такими точками — текущей и следующей — рисуем отрезок, и так по кругу для каждой стороны.

Последняя сторона замыкает фигуру сама, без всяких хитростей: у последней вершины угол равен step, умноженному на количество сторон, — то есть ровно полному обороту. А полный оборот — это то же самое направление, что и нулевой угол, та же самая точка на окружности. Поэтому последний отрезок сам возвращается точно в первую вершину, и ничего досчитывать отдельно не нужно.

Сделай sides небольшим числом — 3-4-5 — увидишь треугольник, квадрат, пятиугольник. Сделай сильно больше — фигура станет неотличима от круга. Кнопка ниже рисует рядом настоящую окружность того же радиуса для сравнения — при небольшом radius разницы почти не видно, а при большом она снова становится заметна.

src/main.rs
use macroquad::prelude::*;
#[macroquad::main("Многоугольники")]
async fn main() {
loop {
clear_background(BLACK);
let center_x = screen_width() / 2.0;
let center_y = screen_height() / 2.0;
// Сколько сторон у многоугольника и какой у него радиус.
let sides = 6;
let radius = 150.0;
// Угол между соседними вершинами — полный оборот, поделённый
// на количество сторон (как в уроке «Радианы»).
let step = (360.0 / sides as f32).to_radians();
for i in 0..sides {
// Угол этой вершины и угол следующей. У последней стороны
// следующий угол равен полному обороту — той же самой
// точке, что и нулевой угол, поэтому многоугольник сам
// замыкается.
let angle = step * i as f32;
let next_angle = step * (i as f32 + 1.0);
// Точка на окружности — центр плюс направление (cos, sin),
// растянутое на радиус (как в уроке «Синус и косинус»).
let x1 = center_x + angle.cos() * radius;
let y1 = center_y + angle.sin() * radius;
let x2 = center_x + next_angle.cos() * radius;
let y2 = center_y + next_angle.sin() * radius;
draw_line(x1, y1, x2, y2, 4.0, YELLOW);
}
next_frame().await;
}
}
  • step — угол между соседними вершинами, полный оборот (360°), поделённый на количество сторон и переведённый в радианы — cos и sin в Rust работают только с ними (подробнее о том, что такое радианы на самом деле, — в дополнительном уроке «Радианы»).
  • angle.cos() и angle.sin(), растянутые на radius и прибавленные к центру, — та же самая точка на окружности, что и в уроке «Вращение».
  • Последняя сторона замыкается сама: угол последней вершины равен полному обороту, а это та же самая точка, что и угол 0 у самой первой вершины.
  • Сделай sides небольшим числом — 3, 4, 5 — увидишь треугольник, квадрат, пятиугольник. Сделай сильно больше — многоугольник перестаёт быть отличим от круга на глаз.
  • И это не иллюзия: draw_circle всё это время рисовал ровно то же самое, просто с очень большим числом сторон, спрятанным внутри самой функции. Экран рисует пикселями, а у пикселя есть предел мелкости — начиная с какого-то числа сторон соседние вершины и так уже ближе друг к другу, чем один пиксель, и никакой разницы с настоящей окружностью просто не видно.
  • Кнопка “Показать окружность” рисует рядом настоящую окружность того же радиуса. При маленьком radius она сливается с многоугольником даже при небольшом числе сторон — а при большом radius та же самая разница между сторонами становится заметна снова, потому что каждая сторона теперь растянута на больше пикселей.
  • В macroquad вообще-то уже есть готовая функция для того же самого — draw_poly(x, y, sides, radius, rotation, color). Делает она ровно то, что мы только что написали сами, только ещё и умеет сразу развернуть многоугольник на угол rotation — в нашей версии вершины всегда начинаются строго с угла 0. rotation здесь задаётся в градусах, а не в радианах — сам macroquad в этом непоследователен: у поворота текста или текстуры угол, наоборот, в радианах, так что при случае легко перепутать.