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