Уроки · 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 разницы
почти не видно, а при большом она снова становится заметна.
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 в этом непоследователен: у поворота текста или текстуры угол, наоборот, в радианах, так что при случае легко перепутать.