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 ..
491 } = value;
492 assert!(points.len() > 2);
493
494 points.push(points[0]);
496
497 let anchors = points;
498 let handles = anchors
499 .iter()
500 .tuple_windows()
501 .map(|(&a, &b)| 0.5 * (a + b))
502 .collect::<Vec<_>>();
503
504 let vpoints = anchors.into_iter().interleave(handles).collect::<Vec<_>>();
506 VItem::from_vpoints(vpoints).with(|vitem| {
507 vitem
508 .set_fill_color(fill_rgba)
509 .set_stroke_color(stroke_rgba)
510 .set_stroke_width(stroke_width);
511 })
512 }
513}
514
515impl Extract for Polygon {
516 type Target = CoreItem;
517 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
518 VItem::from(self.clone()).extract_into(buf);
519 }
520}
521
522#[derive(Debug, Clone, ranim_macros::Interpolatable)]
523pub struct RegularPolygon {
525 pub axes: (DVec3, DVec3),
527 pub center: DVec3,
529 pub sides: usize,
531 pub radius: f64,
533 pub stroke_rgba: AlphaColor<Srgb>,
535 pub stroke_width: f32,
537 pub fill_rgba: AlphaColor<Srgb>,
539}
540
541impl Alignable for RegularPolygon {
542 fn is_aligned(&self, _other: &Self) -> bool {
543 true
544 }
545 fn align_with(&mut self, _other: &mut Self) {}
546}
547
548impl RegularPolygon {
549 pub fn new(sides: usize, radius: f64) -> Self {
551 assert!(sides >= 3);
552 Self {
553 axes: (DVec3::X, DVec3::Y),
554 center: DVec3::ZERO,
555 sides,
556 radius,
557 stroke_rgba: AlphaColor::WHITE,
558 stroke_width: DEFAULT_STROKE_WIDTH,
559 fill_rgba: AlphaColor::TRANSPARENT,
560 }
561 }
562 pub fn points(&self) -> Vec<DVec3> {
564 let &Self {
565 sides,
566 radius,
567 center,
568 ..
569 } = self;
570 let u = self.axes.0.normalize();
571 let normal = self.axes.0.cross(self.axes.1).normalize();
572 (0..sides)
573 .map(|i| TAU * (i as f64 / sides as f64))
574 .map(|angle| u.rotate_axis(normal, angle) * radius + center)
575 .collect()
576 }
577 pub fn outer_circle(&self) -> Circle {
579 Circle::new(self.radius).with(|x| x.move_to(self.center).discard())
580 }
581 pub fn inner_circle(&self) -> Circle {
583 Circle::new(self.radius * (PI / self.sides as f64).cos())
584 .with(|x| x.move_to(self.center).discard())
585 }
586}
587
588impl Aabb for RegularPolygon {
589 fn aabb(&self) -> [DVec3; 2] {
590 self.points().aabb()
591 }
592}
593
594impl ShiftTransform for RegularPolygon {
595 fn shift(&mut self, offset: DVec3) -> &mut Self {
596 self.center.shift(offset);
597 self
598 }
599}
600
601impl RotateTransform for RegularPolygon {
602 fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
603 self.axes.0.rotate_on_axis(axis, angle);
604 self.axes.0 = self.axes.0.normalize();
605 self.axes.1.rotate_on_axis(axis, angle);
606 self.axes.1 = self.axes.1.normalize();
607 self.center.rotate_on_axis(axis, angle);
608 self
609 }
610}
611
612impl Opacity for RegularPolygon {
613 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
614 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
615 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
616 self
617 }
618}
619
620impl FillColor for RegularPolygon {
621 fn fill_color(&self) -> AlphaColor<Srgb> {
622 self.fill_rgba
623 }
624
625 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
626 self.fill_rgba = color;
627 self
628 }
629
630 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
631 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
632 self
633 }
634}
635
636impl StrokeColor for RegularPolygon {
637 fn stroke_color(&self) -> AlphaColor<Srgb> {
638 self.stroke_rgba
639 }
640
641 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
642 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
643 self
644 }
645
646 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
647 self.stroke_rgba = color;
648 self
649 }
650}
651
652impl From<RegularPolygon> for Polygon {
653 fn from(value: RegularPolygon) -> Self {
654 Polygon::new(value.points()).with(|x| {
655 x.axes = value.axes;
656 x.fill_rgba = value.fill_rgba;
657 x.stroke_rgba = value.stroke_rgba;
658 x.stroke_width = value.stroke_width;
659 })
660 }
661}
662
663impl Extract for RegularPolygon {
664 type Target = CoreItem;
665
666 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
667 Polygon::from(self.clone()).extract_into(buf);
668 }
669}