1use std::f64::consts::{PI, TAU};
2
3use ranim_core::{
4 Extract,
5 anchor::{Aabb, AabbPoint, Locate},
6 color,
7 core_item::CoreItem,
8 glam::{DVec2, DVec3, dvec2, dvec3},
9 traits::{Discard, RotateTransform, ScaleTransform, ShiftTransform, ShiftTransformExt},
10};
11
12use color::{AlphaColor, Srgb};
13use itertools::Itertools;
14
15use crate::vitem::{DEFAULT_STROKE_WIDTH, VItem, geometry::Circle};
16use ranim_core::core_item::vitem::Basis2d;
17use ranim_core::traits::{Alignable, FillColor, Opacity, StrokeColor, StrokeWidth, With};
18
19#[derive(Clone, Debug, ranim_macros::Interpolatable)]
22pub struct Square {
23 pub basis: Basis2d,
25 pub center: DVec3,
27 pub size: f64,
29
30 pub stroke_rgba: AlphaColor<Srgb>,
32 pub stroke_width: f32,
34 pub fill_rgba: AlphaColor<Srgb>,
36}
37
38impl Square {
39 pub fn new(size: f64) -> Self {
41 Self {
42 basis: Basis2d::default(),
43 center: dvec3(0.0, 0.0, 0.0),
44 size,
45
46 stroke_rgba: AlphaColor::WHITE,
47 stroke_width: DEFAULT_STROKE_WIDTH,
48 fill_rgba: AlphaColor::TRANSPARENT,
49 }
50 }
51 pub fn scale(&mut self, scale: f64) -> &mut Self {
56 self.scale_at(scale, AabbPoint::CENTER)
57 }
58 pub fn scale_at<T>(&mut self, scale: f64, anchor: T) -> &mut Self
63 where
64 T: Locate<Self>,
65 {
66 let anchor = anchor.locate(self);
67 self.size *= scale;
68 self.center
69 .shift(-anchor)
70 .scale(DVec3::splat(scale))
71 .shift(anchor);
72 self
73 }
74}
75
76impl Aabb for Square {
78 fn aabb(&self) -> [DVec3; 2] {
79 let (u, v) = self.basis.uv();
80 [
81 self.center + self.size / 2.0 * (u + v),
82 self.center - self.size / 2.0 * (u + v),
83 ]
84 .aabb()
85 }
86}
87
88impl ShiftTransform for Square {
89 fn shift(&mut self, shift: DVec3) -> &mut Self {
90 self.center.shift(shift);
91 self
92 }
93}
94
95impl RotateTransform for Square {
96 fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
97 self.center.rotate_on_axis(axis, angle);
98 self.basis.rotate_on_axis(axis, angle);
99 self
100 }
101}
102
103impl Alignable for Square {
104 fn is_aligned(&self, _other: &Self) -> bool {
105 true
106 }
107 fn align_with(&mut self, _other: &mut Self) {}
108}
109
110impl Opacity for Square {
111 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
112 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
113 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
114 self
115 }
116}
117
118impl StrokeColor for Square {
119 fn stroke_color(&self) -> AlphaColor<Srgb> {
120 self.stroke_rgba
121 }
122 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
123 self.stroke_rgba = color;
124 self
125 }
126 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
127 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
128 self
129 }
130}
131
132impl FillColor for Square {
133 fn fill_color(&self) -> AlphaColor<Srgb> {
134 self.fill_rgba
135 }
136 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
137 self.fill_rgba = color;
138 self
139 }
140 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
141 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
142 self
143 }
144}
145
146impl Extract for Square {
147 type Target = CoreItem;
148 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
149 VItem::from(self.clone()).extract_into(buf);
150 }
151}
152
153impl From<Square> for Rectangle {
155 fn from(value: Square) -> Self {
156 let Square {
157 basis,
158 center,
159 size: width,
160 stroke_rgba,
161 stroke_width,
162 fill_rgba,
163 } = value;
164 let (u, v) = basis.uv();
165 let p0 = center - width / 2.0 * u - width / 2.0 * v;
166 Rectangle {
167 basis,
168 p0,
169 size: dvec2(width, width),
170 stroke_rgba,
171 stroke_width,
172 fill_rgba,
173 }
174 }
175}
176
177impl From<Square> for RegularPolygon {
178 fn from(value: Square) -> Self {
179 RegularPolygon::new(4, value.size / 2.0 * 2.0f64.sqrt()).with(|x| {
180 x.basis = value.basis;
181 x.stroke_rgba = value.stroke_rgba;
182 x.stroke_width = value.stroke_width;
183 x.fill_rgba = value.fill_rgba;
184 })
185 }
186}
187
188impl From<Square> for Polygon {
189 fn from(value: Square) -> Self {
190 Rectangle::from(value).into()
191 }
192}
193
194impl From<Square> for VItem {
195 fn from(value: Square) -> Self {
196 Rectangle::from(value).into()
197 }
198}
199
200#[derive(Clone, Debug, ranim_macros::Interpolatable)]
203pub struct Rectangle {
204 pub basis: Basis2d,
206 pub p0: DVec3,
208 pub size: DVec2,
210
211 pub stroke_rgba: AlphaColor<Srgb>,
213 pub stroke_width: f32,
215 pub fill_rgba: AlphaColor<Srgb>,
217}
218
219impl Rectangle {
220 pub fn new(width: f64, height: f64) -> Self {
222 let half_width = width / 2.0;
223 let half_height = height / 2.0;
224 let p0 = dvec3(-half_width, -half_height, 0.0);
225 let size = dvec2(width, height);
226 Self::from_min_size(p0, size)
227 }
228 pub fn from_min_size(p0: DVec3, size: DVec2) -> Self {
230 Self {
231 basis: Basis2d::default(),
232 p0,
233 size,
234 stroke_rgba: AlphaColor::WHITE,
235 stroke_width: DEFAULT_STROKE_WIDTH,
236 fill_rgba: AlphaColor::TRANSPARENT,
237 }
238 }
239 pub fn width(&self) -> f64 {
241 self.size.x.abs()
242 }
243 pub fn height(&self) -> f64 {
245 self.size.y.abs()
246 }
247}
248
249impl Aabb for Rectangle {
251 fn aabb(&self) -> [DVec3; 2] {
252 let (u, v) = self.basis.uv();
253 let p1 = self.p0;
254 let p2 = self.p0 + self.size.x * u + self.size.y * v;
255 [p1, p2].aabb()
256 }
257}
258
259impl ShiftTransform for Rectangle {
260 fn shift(&mut self, shift: DVec3) -> &mut Self {
261 self.p0.shift(shift);
262 self
263 }
264}
265
266impl RotateTransform for Rectangle {
267 fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
268 self.p0.rotate_on_axis(axis, angle);
269 self.basis.rotate_on_axis(axis, angle);
270 self
271 }
272}
273
274impl ScaleTransform for Rectangle {
275 fn scale(&mut self, scale: DVec3) -> &mut Self {
276 self.p0.scale(scale);
277 let (u, v) = self.basis.uv();
278 let scale_u = scale.dot(u);
279 let scale_v = scale.dot(v);
280 self.size *= dvec2(scale_u, scale_v);
281 self
282 }
283}
284
285impl Opacity for Rectangle {
286 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
287 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
288 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
289 self
290 }
291}
292
293impl Alignable for Rectangle {
294 fn align_with(&mut self, _other: &mut Self) {}
295 fn is_aligned(&self, _other: &Self) -> bool {
296 true
297 }
298}
299
300impl StrokeColor for Rectangle {
301 fn stroke_color(&self) -> AlphaColor<Srgb> {
302 self.stroke_rgba
303 }
304 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
305 self.stroke_rgba = color;
306 self
307 }
308 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
309 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
310 self
311 }
312}
313
314impl FillColor for Rectangle {
315 fn fill_color(&self) -> AlphaColor<Srgb> {
316 self.fill_rgba
317 }
318 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
319 self.fill_rgba = color;
320 self
321 }
322 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
323 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
324 self
325 }
326}
327
328impl From<Rectangle> for Polygon {
330 fn from(value: Rectangle) -> Self {
331 let p0 = value.p0;
332 let (u, v) = value.basis.uv();
333 let DVec2 { x: w, y: h } = value.size;
334 let points = vec![p0, p0 + u * w, p0 + u * w + v * h, p0 + v * h];
335 Polygon {
336 basis: value.basis,
337 points,
338 stroke_rgba: value.stroke_rgba,
339 stroke_width: value.stroke_width,
340 fill_rgba: value.fill_rgba,
341 }
342 }
343}
344
345impl From<Rectangle> for VItem {
346 fn from(value: Rectangle) -> Self {
347 Polygon::from(value).into()
348 }
349}
350
351impl Extract for Rectangle {
352 type Target = CoreItem;
353 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
354 VItem::from(self.clone()).extract_into(buf);
355 }
356}
357
358#[derive(Clone, Debug, ranim_macros::Interpolatable)]
361pub struct Polygon {
362 pub basis: Basis2d,
364 pub points: Vec<DVec3>,
366 pub stroke_rgba: AlphaColor<Srgb>,
368 pub stroke_width: f32,
370 pub fill_rgba: AlphaColor<Srgb>,
372}
373
374impl Polygon {
375 pub fn new(points: Vec<DVec3>) -> Self {
377 Self {
378 basis: Basis2d::default(),
379 points,
380 stroke_rgba: AlphaColor::WHITE,
381 stroke_width: DEFAULT_STROKE_WIDTH,
382 fill_rgba: AlphaColor::TRANSPARENT,
383 }
384 }
385}
386
387impl Aabb for Polygon {
389 fn aabb(&self) -> [DVec3; 2] {
390 self.points.aabb()
391 }
392}
393
394impl ShiftTransform for Polygon {
395 fn shift(&mut self, shift: DVec3) -> &mut Self {
396 self.points.shift(shift);
397 self
398 }
399}
400
401impl RotateTransform for Polygon {
402 fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
403 self.points.rotate_on_axis(axis, angle);
404 self.basis.rotate_on_axis(axis, angle);
405 self
406 }
407}
408
409impl ScaleTransform for Polygon {
410 fn scale(&mut self, scale: DVec3) -> &mut Self {
411 self.points.scale(scale);
412 self
413 }
414}
415
416impl Alignable for Polygon {
425 fn is_aligned(&self, other: &Self) -> bool {
426 self.points.len() == other.points.len()
427 }
428 fn align_with(&mut self, other: &mut Self) {
429 if self.points.len() > other.points.len() {
430 return other.align_with(self);
431 }
432 self.points
434 .resize(other.points.len(), self.points.last().cloned().unwrap());
435 }
436}
437
438impl Opacity for Polygon {
439 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
440 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
441 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
442 self
443 }
444}
445
446impl StrokeColor for Polygon {
447 fn stroke_color(&self) -> AlphaColor<Srgb> {
448 self.stroke_rgba
449 }
450 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
451 self.stroke_rgba = color;
452 self
453 }
454 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
455 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
456 self
457 }
458}
459
460impl FillColor for Polygon {
461 fn fill_color(&self) -> AlphaColor<Srgb> {
462 self.fill_rgba
463 }
464 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
465 self.fill_rgba = color;
466 self
467 }
468 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
469 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
470 self
471 }
472}
473
474impl From<Polygon> for VItem {
476 fn from(value: Polygon) -> Self {
477 let Polygon {
478 mut points,
479 stroke_rgba,
480 stroke_width,
481 fill_rgba,
482 basis,
483 ..
484 } = value;
485 assert!(points.len() > 2);
486
487 points.push(points[0]);
489
490 let anchors = points;
491 let handles = anchors
492 .iter()
493 .tuple_windows()
494 .map(|(&a, &b)| 0.5 * (a + b))
495 .collect::<Vec<_>>();
496
497 let vpoints = anchors.into_iter().interleave(handles).collect::<Vec<_>>();
499 VItem::from_vpoints(vpoints)
500 .with_basis(basis)
501 .with(|vitem| {
502 vitem
503 .set_fill_color(fill_rgba)
504 .set_stroke_color(stroke_rgba)
505 .set_stroke_width(stroke_width);
506 })
507 }
508}
509
510impl Extract for Polygon {
511 type Target = CoreItem;
512 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
513 VItem::from(self.clone()).extract_into(buf);
514 }
515}
516
517#[derive(Debug, Clone, ranim_macros::Interpolatable)]
518pub struct RegularPolygon {
520 pub basis: Basis2d,
522 pub center: DVec3,
524 pub sides: usize,
526 pub radius: f64,
528 pub stroke_rgba: AlphaColor<Srgb>,
530 pub stroke_width: f32,
532 pub fill_rgba: AlphaColor<Srgb>,
534}
535
536impl Alignable for RegularPolygon {
537 fn is_aligned(&self, _other: &Self) -> bool {
538 true
539 }
540 fn align_with(&mut self, _other: &mut Self) {}
541}
542
543impl RegularPolygon {
544 pub fn new(sides: usize, radius: f64) -> Self {
546 assert!(sides >= 3);
547 Self {
548 basis: Basis2d::default(),
549 center: DVec3::ZERO,
550 sides,
551 radius,
552 stroke_rgba: AlphaColor::WHITE,
553 stroke_width: DEFAULT_STROKE_WIDTH,
554 fill_rgba: AlphaColor::TRANSPARENT,
555 }
556 }
557 pub fn points(&self) -> Vec<DVec3> {
559 let &Self {
560 sides,
561 radius,
562 center,
563 ..
564 } = self;
565 let u = self.basis.u();
566 let normal = self.basis.normal();
567 (0..sides)
568 .map(|i| TAU * (i as f64 / sides as f64))
569 .map(|angle| u.rotate_axis(normal, angle) * radius + center)
570 .collect()
571 }
572 pub fn outer_circle(&self) -> Circle {
574 Circle::new(self.radius).with(|x| x.move_to(self.center).discard())
575 }
576 pub fn inner_circle(&self) -> Circle {
578 Circle::new(self.radius * (PI / self.sides as f64).cos())
579 .with(|x| x.move_to(self.center).discard())
580 }
581}
582
583impl Aabb for RegularPolygon {
584 fn aabb(&self) -> [DVec3; 2] {
585 self.points().aabb()
586 }
587}
588
589impl ShiftTransform for RegularPolygon {
590 fn shift(&mut self, offset: DVec3) -> &mut Self {
591 self.center.shift(offset);
592 self
593 }
594}
595
596impl RotateTransform for RegularPolygon {
597 fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
598 self.basis.rotate_on_axis(axis, angle);
599 self.center.rotate_on_axis(axis, angle);
600 self
601 }
602}
603
604impl Opacity for RegularPolygon {
605 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
606 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
607 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
608 self
609 }
610}
611
612impl FillColor for RegularPolygon {
613 fn fill_color(&self) -> AlphaColor<Srgb> {
614 self.fill_rgba
615 }
616
617 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
618 self.fill_rgba = color;
619 self
620 }
621
622 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
623 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
624 self
625 }
626}
627
628impl StrokeColor for RegularPolygon {
629 fn stroke_color(&self) -> AlphaColor<Srgb> {
630 self.stroke_rgba
631 }
632
633 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
634 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
635 self
636 }
637
638 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
639 self.stroke_rgba = color;
640 self
641 }
642}
643
644impl From<RegularPolygon> for Polygon {
645 fn from(value: RegularPolygon) -> Self {
646 Polygon::new(value.points()).with(|x| {
647 x.basis = value.basis;
648 x.fill_rgba = value.fill_rgba;
649 x.stroke_rgba = value.stroke_rgba;
650 x.stroke_width = value.stroke_width;
651 })
652 }
653}
654
655impl Extract for RegularPolygon {
656 type Target = CoreItem;
657
658 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
659 Polygon::from(self.clone()).extract_into(buf);
660 }
661}