ranim_items/vitem/geometry/
polygon.rs1#[cfg(feature = "render")]
2use ranim_render::extract::{Extract, ExtractContext};
3
4use std::f64::consts::{PI, TAU};
5
6use color::{AlphaColor, Srgb};
7use itertools::Itertools;
8use ranim_core::{
9 anchor::Aabb,
10 color,
11 glam::{DVec2, DVec3},
12 traits::{Alignable, ApplyTransform, FillColor, Opacity, StrokeColor, StrokeWidth, With},
13};
14
15use crate::vitem::{DEFAULT_STROKE_WIDTH, VItem, geometry::Circle};
16
17#[derive(Clone, Debug, ranim_macros::Interpolatable)]
19#[allow(missing_docs)]
20pub struct Square {
21 pub size: f64,
22 pub stroke_rgba: AlphaColor<Srgb>,
23 pub stroke_width: f32,
24 pub fill_rgba: AlphaColor<Srgb>,
25}
26
27#[allow(missing_docs)]
28impl Square {
29 pub fn new(size: f64) -> Self {
30 Self {
31 size,
32 stroke_rgba: AlphaColor::WHITE,
33 stroke_width: DEFAULT_STROKE_WIDTH,
34 fill_rgba: AlphaColor::TRANSPARENT,
35 }
36 }
37
38 pub fn scale(&mut self, scale: f64) -> &mut Self {
40 self.size *= scale;
41 self
42 }
43}
44
45impl Aabb for Square {
46 fn aabb(&self) -> [DVec3; 2] {
47 let h = self.size / 2.0;
48 [DVec3::new(-h, -h, 0.0), DVec3::new(h, h, 0.0)].aabb()
49 }
50}
51
52impl Alignable for Square {
53 fn is_aligned(&self, _other: &Self) -> bool {
54 true
55 }
56 fn align_with(&mut self, _other: &mut Self) {}
57}
58
59impl Opacity for Square {
60 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
61 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
62 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
63 self
64 }
65}
66
67impl StrokeColor for Square {
68 fn stroke_color(&self) -> AlphaColor<Srgb> {
69 self.stroke_rgba
70 }
71 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
72 self.stroke_rgba = color;
73 self
74 }
75 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
76 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
77 self
78 }
79}
80
81impl FillColor for Square {
82 fn fill_color(&self) -> AlphaColor<Srgb> {
83 self.fill_rgba
84 }
85 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
86 self.fill_rgba = color;
87 self
88 }
89 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
90 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
91 self
92 }
93}
94
95impl From<Square> for Rectangle {
96 fn from(value: Square) -> Self {
97 let Square {
98 size,
99 stroke_rgba,
100 stroke_width,
101 fill_rgba,
102 } = value;
103 Rectangle {
104 size: DVec2::splat(size),
105 stroke_rgba,
106 stroke_width,
107 fill_rgba,
108 }
109 }
110}
111
112impl From<Square> for RegularPolygon {
113 fn from(value: Square) -> Self {
114 RegularPolygon::new(4, value.size / 2.0 * 2.0f64.sqrt()).with(|x| {
115 x.stroke_rgba = value.stroke_rgba;
116 x.stroke_width = value.stroke_width;
117 x.fill_rgba = value.fill_rgba;
118 })
119 }
120}
121
122impl From<Square> for Polygon {
123 fn from(value: Square) -> Self {
124 Rectangle::from(value).into()
125 }
126}
127impl From<Square> for VItem {
128 fn from(value: Square) -> Self {
129 Rectangle::from(value).into()
130 }
131}
132#[cfg(feature = "render")]
133impl Extract for Square {
134 fn extract(&self, ctx: &mut ExtractContext<'_>) {
135 VItem::from(self.clone()).extract(ctx);
136 }
137}
138
139#[derive(Clone, Debug, ranim_macros::Interpolatable)]
141#[allow(missing_docs)]
142pub struct Rectangle {
143 pub size: DVec2,
144 pub stroke_rgba: AlphaColor<Srgb>,
145 pub stroke_width: f32,
146 pub fill_rgba: AlphaColor<Srgb>,
147}
148
149#[allow(missing_docs)]
150impl Rectangle {
151 pub fn new(width: f64, height: f64) -> Self {
152 Self {
153 size: DVec2::new(width, height),
154 stroke_rgba: AlphaColor::WHITE,
155 stroke_width: DEFAULT_STROKE_WIDTH,
156 fill_rgba: AlphaColor::TRANSPARENT,
157 }
158 }
159 pub fn width(&self) -> f64 {
160 self.size.x.abs()
161 }
162 pub fn height(&self) -> f64 {
163 self.size.y.abs()
164 }
165 pub fn scale_axes(&mut self, scale: DVec2) -> &mut Self {
167 self.size *= scale;
168 self
169 }
170}
171
172impl Aabb for Rectangle {
173 fn aabb(&self) -> [DVec3; 2] {
174 let h = self.size / 2.0;
175 [h.extend(0.0), (-h).extend(0.0)].aabb()
176 }
177}
178impl Alignable for Rectangle {
179 fn align_with(&mut self, _other: &mut Self) {}
180 fn is_aligned(&self, _other: &Self) -> bool {
181 true
182 }
183}
184impl Opacity for Rectangle {
185 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
186 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
187 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
188 self
189 }
190}
191impl StrokeColor for Rectangle {
192 fn stroke_color(&self) -> AlphaColor<Srgb> {
193 self.stroke_rgba
194 }
195 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
196 self.stroke_rgba = color;
197 self
198 }
199 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
200 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
201 self
202 }
203}
204impl FillColor for Rectangle {
205 fn fill_color(&self) -> AlphaColor<Srgb> {
206 self.fill_rgba
207 }
208 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
209 self.fill_rgba = color;
210 self
211 }
212 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
213 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
214 self
215 }
216}
217
218impl From<Rectangle> for Polygon {
219 fn from(value: Rectangle) -> Self {
220 let h = value.size / 2.0;
221 Self {
222 points: vec![
223 DVec3::new(-h.x, -h.y, 0.0),
224 DVec3::new(h.x, -h.y, 0.0),
225 DVec3::new(h.x, h.y, 0.0),
226 DVec3::new(-h.x, h.y, 0.0),
227 ],
228 stroke_rgba: value.stroke_rgba,
229 stroke_width: value.stroke_width,
230 fill_rgba: value.fill_rgba,
231 }
232 }
233}
234impl From<Rectangle> for VItem {
235 fn from(value: Rectangle) -> Self {
236 Polygon::from(value).into()
237 }
238}
239#[cfg(feature = "render")]
240impl Extract for Rectangle {
241 fn extract(&self, ctx: &mut ExtractContext<'_>) {
242 VItem::from(self.clone()).extract(ctx);
243 }
244}
245
246#[derive(Clone, Debug, ranim_macros::Interpolatable)]
248#[allow(missing_docs)]
249pub struct Polygon {
250 pub points: Vec<DVec3>,
251 pub stroke_rgba: AlphaColor<Srgb>,
252 pub stroke_width: f32,
253 pub fill_rgba: AlphaColor<Srgb>,
254}
255#[allow(missing_docs)]
256impl Polygon {
257 pub fn new(points: Vec<DVec3>) -> Self {
258 Self {
259 points,
260 stroke_rgba: AlphaColor::WHITE,
261 stroke_width: DEFAULT_STROKE_WIDTH,
262 fill_rgba: AlphaColor::TRANSPARENT,
263 }
264 }
265}
266impl Aabb for Polygon {
267 fn aabb(&self) -> [DVec3; 2] {
268 self.points.aabb()
269 }
270}
271impl<G: Into<ranim_core::glam::DAffine3>> ApplyTransform<G> for Polygon {
272 fn apply(&mut self, transform: G) -> &mut Self {
273 self.points.apply(transform.into());
274 self
275 }
276}
277impl Alignable for Polygon {
278 fn is_aligned(&self, other: &Self) -> bool {
279 self.points.len() == other.points.len()
280 }
281 fn align_with(&mut self, other: &mut Self) {
282 if self.points.len() > other.points.len() {
283 return other.align_with(self);
284 }
285 self.points
286 .resize(other.points.len(), *self.points.last().unwrap());
287 }
288}
289impl Opacity for Polygon {
290 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
291 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
292 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
293 self
294 }
295}
296impl StrokeColor for Polygon {
297 fn stroke_color(&self) -> AlphaColor<Srgb> {
298 self.stroke_rgba
299 }
300 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
301 self.stroke_rgba = color;
302 self
303 }
304 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
305 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
306 self
307 }
308}
309impl FillColor for Polygon {
310 fn fill_color(&self) -> AlphaColor<Srgb> {
311 self.fill_rgba
312 }
313 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
314 self.fill_rgba = color;
315 self
316 }
317 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
318 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
319 self
320 }
321}
322impl From<Polygon> for VItem {
323 fn from(value: Polygon) -> Self {
324 let Polygon {
325 mut points,
326 stroke_rgba,
327 stroke_width,
328 fill_rgba,
329 } = value;
330 assert!(points.len() > 2);
331 points.push(points[0]);
332 let handles = points
333 .iter()
334 .tuple_windows()
335 .map(|(&a, &b)| 0.5 * (a + b))
336 .collect::<Vec<_>>();
337 let vpoints = points.into_iter().interleave(handles).collect();
338 VItem::from_vpoints(vpoints).with(|v| {
339 v.set_fill_color(fill_rgba)
340 .set_stroke_color(stroke_rgba)
341 .set_stroke_width(stroke_width);
342 })
343 }
344}
345#[cfg(feature = "render")]
346impl Extract for Polygon {
347 fn extract(&self, ctx: &mut ExtractContext<'_>) {
348 VItem::from(self.clone()).extract(ctx);
349 }
350}
351
352#[derive(Debug, Clone, ranim_macros::Interpolatable)]
354#[allow(missing_docs)]
355pub struct RegularPolygon {
356 pub sides: usize,
357 pub radius: f64,
358 pub stroke_rgba: AlphaColor<Srgb>,
359 pub stroke_width: f32,
360 pub fill_rgba: AlphaColor<Srgb>,
361}
362impl Alignable for RegularPolygon {
363 fn is_aligned(&self, _other: &Self) -> bool {
364 true
365 }
366 fn align_with(&mut self, _other: &mut Self) {}
367}
368#[allow(missing_docs)]
369impl RegularPolygon {
370 pub fn new(sides: usize, radius: f64) -> Self {
371 assert!(sides >= 3);
372 Self {
373 sides,
374 radius,
375 stroke_rgba: AlphaColor::WHITE,
376 stroke_width: DEFAULT_STROKE_WIDTH,
377 fill_rgba: AlphaColor::TRANSPARENT,
378 }
379 }
380 pub fn points(&self) -> Vec<DVec3> {
381 (0..self.sides)
382 .map(|i| {
383 let a = TAU * i as f64 / self.sides as f64;
384 DVec3::new(a.cos() * self.radius, a.sin() * self.radius, 0.0)
385 })
386 .collect()
387 }
388 pub fn outer_circle(&self) -> Circle {
389 Circle::new(self.radius)
390 }
391 pub fn inner_circle(&self) -> Circle {
392 Circle::new(self.radius * (PI / self.sides as f64).cos())
393 }
394}
395impl Aabb for RegularPolygon {
396 fn aabb(&self) -> [DVec3; 2] {
397 self.points().aabb()
398 }
399}
400impl Opacity for RegularPolygon {
401 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
402 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
403 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
404 self
405 }
406}
407impl FillColor for RegularPolygon {
408 fn fill_color(&self) -> AlphaColor<Srgb> {
409 self.fill_rgba
410 }
411 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
412 self.fill_rgba = color;
413 self
414 }
415 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
416 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
417 self
418 }
419}
420impl StrokeColor for RegularPolygon {
421 fn stroke_color(&self) -> AlphaColor<Srgb> {
422 self.stroke_rgba
423 }
424 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
425 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
426 self
427 }
428 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
429 self.stroke_rgba = color;
430 self
431 }
432}
433impl From<RegularPolygon> for Polygon {
434 fn from(value: RegularPolygon) -> Self {
435 Polygon::new(value.points()).with(|x| {
436 x.fill_rgba = value.fill_rgba;
437 x.stroke_rgba = value.stroke_rgba;
438 x.stroke_width = value.stroke_width;
439 })
440 }
441}
442#[cfg(feature = "render")]
443impl Extract for RegularPolygon {
444 fn extract(&self, ctx: &mut ExtractContext<'_>) {
445 Polygon::from(self.clone()).extract(ctx);
446 }
447}