1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! Polygon geometry utilities for regular polygon rendering.
use std::f32::consts::TAU;
use glam::Vec2;
use crate::sprite::Sprite;
/// Polygon geometry builder with automatic bounds calculation and UV mapping.
///
/// This struct consolidates polygon vertex generation, bounds calculation,
/// and UV coordinate mapping into a single reusable type.
pub(crate) struct PolygonBuilder {
vertices: Vec<Vec2>,
min: Vec2,
max: Vec2,
}
impl PolygonBuilder {
/// Create a regular polygon with the given number of sides.
///
/// # Arguments
/// * `center` - Center point of the polygon
/// * `radius` - Distance from center to each vertex
/// * `sides` - Number of sides (must be >= 3)
/// * `rotation` - Rotation angle in radians
pub fn regular(center: Vec2, radius: f32, sides: u32, rotation: f32) -> Self {
let step = TAU / sides as f32;
let vertices: Vec<Vec2> = (0..sides)
.map(|i| {
let angle = rotation + step * i as f32;
center + Vec2::new(angle.cos(), angle.sin()) * radius
})
.collect();
debug_assert!(vertices.len() >= 3);
let (min, max) = Self::compute_bounds(&vertices);
Self { vertices, min, max }
}
fn compute_bounds(vertices: &[Vec2]) -> (Vec2, Vec2) {
let mut min = vertices[0];
let mut max = vertices[0];
for v in &vertices[1..] {
min = min.min(*v);
max = max.max(*v);
}
(min, max)
}
/// Get the polygon vertices.
pub fn vertices(&self) -> &[Vec2] {
&self.vertices
}
/// Get the minimum bounds corner.
pub fn min(&self) -> Vec2 {
self.min
}
/// Get the maximum bounds corner.
pub fn max(&self) -> Vec2 {
self.max
}
/// Get the bounding box size.
pub fn size(&self) -> Vec2 {
self.max - self.min
}
/// Compute UV coordinates for texturing the polygon with a sprite.
///
/// Maps each vertex to UV coordinates based on its position within
/// the polygon's bounding box, respecting sprite source rect and flip flags.
pub fn uvs_for_sprite(&self, sprite: &Sprite) -> Vec<Vec2> {
let tex_size = sprite.size.as_vec2().max(Vec2::splat(1.0));
let (tex_min, tex_max) = sprite
.src
.as_ref()
.map(|rect| (rect.min, rect.max))
.unwrap_or((Vec2::ZERO, tex_size));
let tex_extent = tex_max - tex_min;
let size = self.size();
self.vertices
.iter()
.map(|v| {
let rel_x = if size.x.abs() <= f32::EPSILON {
0.5
} else {
(v.x - self.min.x) / size.x
};
let rel_y = if size.y.abs() <= f32::EPSILON {
0.5
} else {
(v.y - self.min.y) / size.y
};
let mut rel = Vec2::new(rel_x, rel_y);
if sprite.flip_x {
rel.x = 1.0 - rel.x;
}
if sprite.flip_y {
rel.y = 1.0 - rel.y;
}
let uv_pixels = tex_min + tex_extent * rel;
uv_pixels / tex_size
})
.collect()
}
}