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