1use ranim_core::{Extract, color, glam, traits::Anchor};
2
3use color::{AlphaColor, Srgb};
4use glam::{DVec3, dvec3};
5use itertools::Itertools;
6
7use ranim_core::{
8 primitives::vitem::{DEFAULT_STROKE_WIDTH, VItemPrimitive},
9 traits::{
10 Alignable, BoundingBox, FillColor, Interpolatable, Opacity, Rotate, Scale, Shift,
11 StrokeColor, StrokeWidth, With,
12 },
13};
14
15use crate::vitem::VItem;
16
17#[derive(Clone, Debug)]
20pub struct Square {
21 pub center: DVec3,
23 pub size: f64,
25 pub up: DVec3,
27 pub normal: DVec3,
29
30 pub stroke_rgba: AlphaColor<Srgb>,
32 pub stroke_width: f32,
34 pub fill_rgba: AlphaColor<Srgb>,
36}
37
38impl Interpolatable for Square {
39 fn lerp(&self, target: &Self, t: f64) -> Self {
40 Self {
41 center: Interpolatable::lerp(&self.center, &target.center, t),
42 size: Interpolatable::lerp(&self.size, &target.size, t),
43 up: Interpolatable::lerp(&self.up, &target.up, t),
44 normal: Interpolatable::lerp(&self.normal, &target.normal, t),
45 stroke_rgba: Interpolatable::lerp(&self.stroke_rgba, &target.stroke_rgba, t),
46 stroke_width: Interpolatable::lerp(&self.stroke_width, &target.stroke_width, t),
47 fill_rgba: Interpolatable::lerp(&self.fill_rgba, &target.fill_rgba, t),
48 }
49 }
50}
51
52impl Square {
53 pub fn new(size: f64) -> Self {
55 Self {
56 center: dvec3(0.0, 0.0, 0.0),
57 size,
58 up: dvec3(0.0, 1.0, 0.0),
59 normal: dvec3(0.0, 0.0, 1.0),
60
61 stroke_rgba: AlphaColor::WHITE,
62 stroke_width: DEFAULT_STROKE_WIDTH,
63 fill_rgba: AlphaColor::TRANSPARENT,
64 }
65 }
66 pub fn scale(&mut self, scale: f64) -> &mut Self {
71 self.scale_by_anchor(scale, Anchor::CENTER)
72 }
73 pub fn scale_by_anchor(&mut self, scale: f64, anchor: Anchor) -> &mut Self {
78 let anchor = Anchor::Point(match anchor {
79 Anchor::Point(point) => point,
80 Anchor::Edge(edge) => self.get_bounding_box_point(edge),
81 });
82 self.size *= scale;
83 self.center.scale_by_anchor(DVec3::splat(scale), anchor);
84 self
85 }
86}
87
88impl BoundingBox for Square {
90 fn get_bounding_box(&self) -> [DVec3; 3] {
91 let right = -self.normal.cross(self.up).normalize();
92 [
93 self.center - self.size / 2.0 * right + self.size / 2.0 * self.up,
94 self.center + self.size / 2.0 * right - self.size / 2.0 * self.up,
95 ]
96 .get_bounding_box()
97 }
98}
99
100impl Shift for Square {
101 fn shift(&mut self, shift: DVec3) -> &mut Self {
102 self.center.shift(shift);
103 self
104 }
105}
106
107impl Rotate for Square {
108 fn rotate_by_anchor(&mut self, angle: f64, axis: DVec3, anchor: Anchor) -> &mut Self {
109 let anchor = Anchor::Point(match anchor {
110 Anchor::Point(point) => point,
111 Anchor::Edge(edge) => self.get_bounding_box_point(edge),
112 });
113 self.center.rotate_by_anchor(angle, axis, anchor);
114 self.up.rotate_by_anchor(angle, axis, Anchor::ORIGIN);
115 self.normal.rotate_by_anchor(angle, axis, Anchor::ORIGIN);
116 self
117 }
118}
119
120impl Alignable for Square {
121 fn is_aligned(&self, _other: &Self) -> bool {
122 true
123 }
124 fn align_with(&mut self, _other: &mut Self) {}
125}
126
127impl Opacity for Square {
128 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
129 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
130 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
131 self
132 }
133}
134
135impl StrokeColor for Square {
136 fn stroke_color(&self) -> AlphaColor<Srgb> {
137 self.stroke_rgba
138 }
139 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
140 self.stroke_rgba = color;
141 self
142 }
143 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
144 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
145 self
146 }
147}
148
149impl FillColor for Square {
150 fn fill_color(&self) -> AlphaColor<Srgb> {
151 self.fill_rgba
152 }
153 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
154 self.fill_rgba = color;
155 self
156 }
157 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
158 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
159 self
160 }
161}
162
163impl Extract for Square {
164 type Target = VItemPrimitive;
165 fn extract(&self) -> Vec<Self::Target> {
166 VItem::from(self.clone()).extract()
167 }
168}
169
170impl From<Square> for Rectangle {
172 fn from(value: Square) -> Self {
173 let Square {
174 center,
175 size: width,
176 up,
177 normal,
178 stroke_rgba,
179 stroke_width,
180 fill_rgba,
181 } = value;
182 let right = up.cross(normal).normalize();
183 let p1 = center - width / 2.0 * right + width / 2.0 * up;
184 let p2 = center + width / 2.0 * right - width / 2.0 * up;
185 Rectangle {
186 p1,
187 p2,
188 up,
189 normal,
190 stroke_rgba,
191 stroke_width,
192 fill_rgba,
193 }
194 }
195}
196
197impl From<Square> for Polygon {
198 fn from(value: Square) -> Self {
199 Rectangle::from(value).into()
200 }
201}
202
203impl From<Square> for VItem {
204 fn from(value: Square) -> Self {
205 Rectangle::from(value).into()
206 }
207}
208
209#[derive(Clone, Debug)]
212pub struct Rectangle {
213 pub p1: DVec3,
215 pub p2: DVec3,
217 up: DVec3,
218 pub normal: DVec3,
220
221 pub stroke_rgba: AlphaColor<Srgb>,
223 pub stroke_width: f32,
225 pub fill_rgba: AlphaColor<Srgb>,
227}
228
229impl Interpolatable for Rectangle {
230 fn lerp(&self, target: &Self, t: f64) -> Self {
231 Self {
232 p1: Interpolatable::lerp(&self.p1, &target.p1, t),
233 p2: Interpolatable::lerp(&self.p2, &target.p2, t),
234 up: Interpolatable::lerp(&self.up, &target.up, t),
235 normal: Interpolatable::lerp(&self.normal, &target.normal, t),
236 stroke_rgba: Interpolatable::lerp(&self.stroke_rgba, &target.stroke_rgba, t),
237 stroke_width: Interpolatable::lerp(&self.stroke_width, &target.stroke_width, t),
238 fill_rgba: Interpolatable::lerp(&self.fill_rgba, &target.fill_rgba, t),
239 }
240 }
241}
242
243impl Rectangle {
244 pub fn new(width: f64, height: f64) -> Self {
246 let half_width = width / 2.0;
247 let half_height = height / 2.0;
248 Self {
249 p1: dvec3(-half_width, half_height, 0.0),
250 p2: dvec3(half_width, -half_height, 0.0),
251 up: DVec3::Y,
252 normal: DVec3::Z,
253 stroke_rgba: AlphaColor::WHITE,
254 stroke_width: DEFAULT_STROKE_WIDTH,
255 fill_rgba: AlphaColor::TRANSPARENT,
256 }
257 }
258 pub fn width(&self) -> f64 {
260 let right = self.up.cross(self.normal).normalize();
261 (self.p2 - self.p1).dot(right).abs()
262 }
263 pub fn height(&self) -> f64 {
265 (self.p2 - self.p1).dot(self.up).abs()
266 }
267}
268
269impl BoundingBox for Rectangle {
271 fn get_bounding_box(&self) -> [DVec3; 3] {
272 [self.p1, self.p2].get_bounding_box()
273 }
274}
275
276impl Shift for Rectangle {
277 fn shift(&mut self, shift: DVec3) -> &mut Self {
278 self.p1.shift(shift);
279 self.p2.shift(shift);
280 self
281 }
282}
283
284impl Rotate for Rectangle {
285 fn rotate_by_anchor(&mut self, angle: f64, axis: DVec3, anchor: Anchor) -> &mut Self {
286 let anchor = Anchor::Point(anchor.get_pos(self));
287 self.p1.rotate_by_anchor(angle, axis, anchor);
288 self.p2.rotate_by_anchor(angle, axis, anchor);
289 self.up.rotate_by_anchor(angle, axis, Anchor::ORIGIN);
290 self.normal.rotate_by_anchor(angle, axis, Anchor::ORIGIN);
291 self
292 }
293}
294
295impl Scale for Rectangle {
296 fn scale_by_anchor(&mut self, scale: DVec3, anchor: Anchor) -> &mut Self {
297 let anchor = Anchor::Point(anchor.get_pos(self));
298 self.p1.scale_by_anchor(scale, anchor);
299 self.p2.scale_by_anchor(scale, anchor);
300 self
301 }
302}
303
304impl Opacity for Rectangle {
305 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
306 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
307 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
308 self
309 }
310}
311
312impl Alignable for Rectangle {
313 fn align_with(&mut self, _other: &mut Self) {}
314 fn is_aligned(&self, _other: &Self) -> bool {
315 true
316 }
317}
318
319impl StrokeColor for Rectangle {
320 fn stroke_color(&self) -> AlphaColor<Srgb> {
321 self.stroke_rgba
322 }
323 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
324 self.stroke_rgba = color;
325 self
326 }
327 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
328 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
329 self
330 }
331}
332
333impl FillColor for Rectangle {
334 fn fill_color(&self) -> AlphaColor<Srgb> {
335 self.fill_rgba
336 }
337 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
338 self.fill_rgba = color;
339 self
340 }
341 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
342 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
343 self
344 }
345}
346
347impl From<Rectangle> for Polygon {
349 fn from(value: Rectangle) -> Self {
350 let points = vec![
351 value.p1,
352 value.p1 - value.up * value.height(),
353 value.p2,
354 value.p2 + value.up * value.height(),
355 ];
356 Polygon {
357 points,
358 stroke_rgba: value.stroke_rgba,
359 stroke_width: value.stroke_width,
360 fill_rgba: value.fill_rgba,
361 }
362 }
363}
364
365impl From<Rectangle> for VItem {
366 fn from(value: Rectangle) -> Self {
367 Polygon::from(value).into()
368 }
369}
370
371impl Extract for Rectangle {
372 type Target = VItemPrimitive;
373 fn extract(&self) -> Vec<Self::Target> {
374 VItem::from(self.clone()).extract()
375 }
376}
377
378#[derive(Clone, Debug)]
381pub struct Polygon {
382 pub points: Vec<DVec3>,
384 pub stroke_rgba: AlphaColor<Srgb>,
386 pub stroke_width: f32,
388 pub fill_rgba: AlphaColor<Srgb>,
390 }
393
394impl Polygon {
395 pub fn new(points: Vec<DVec3>) -> Self {
397 Self {
398 points,
399 stroke_rgba: AlphaColor::WHITE,
400 stroke_width: DEFAULT_STROKE_WIDTH,
401 fill_rgba: AlphaColor::TRANSPARENT,
402 }
405 }
406}
407
408impl BoundingBox for Polygon {
410 fn get_bounding_box(&self) -> [DVec3; 3] {
411 self.points.get_bounding_box()
412 }
413}
414
415impl Shift for Polygon {
416 fn shift(&mut self, shift: DVec3) -> &mut Self {
417 self.points.shift(shift);
418 self
419 }
420}
421
422impl Rotate for Polygon {
423 fn rotate_by_anchor(&mut self, angle: f64, axis: DVec3, anchor: Anchor) -> &mut Self {
424 self.points.rotate_by_anchor(angle, axis, anchor);
425 self
426 }
427}
428
429impl Scale for Polygon {
430 fn scale_by_anchor(&mut self, scale: DVec3, anchor: Anchor) -> &mut Self {
431 self.points.scale_by_anchor(scale, anchor);
432 self
433 }
434}
435
436impl Alignable for Polygon {
437 fn is_aligned(&self, other: &Self) -> bool {
438 self.points.len() == other.points.len()
439 }
440 fn align_with(&mut self, other: &mut Self) {
441 if self.points.len() > other.points.len() {
442 return other.align_with(self);
443 }
444 self.points
446 .resize(other.points.len(), self.points.last().cloned().unwrap());
447 }
448}
449
450impl Interpolatable for Polygon {
451 fn lerp(&self, target: &Self, t: f64) -> Self {
452 Self {
453 points: self
454 .points
455 .iter()
456 .zip(target.points.iter())
457 .map(|(a, b)| a.lerp(b, t))
458 .collect(),
459 stroke_rgba: Interpolatable::lerp(&self.stroke_rgba, &target.stroke_rgba, t),
460 stroke_width: self.stroke_width.lerp(&target.stroke_width, t),
461 fill_rgba: Interpolatable::lerp(&self.fill_rgba, &target.fill_rgba, t),
462 }
465 }
466}
467
468impl Opacity for Polygon {
469 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
470 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
471 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
472 self
473 }
474}
475
476impl StrokeColor for Polygon {
477 fn stroke_color(&self) -> AlphaColor<Srgb> {
478 self.stroke_rgba
479 }
480 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
481 self.stroke_rgba = color;
482 self
483 }
484 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
485 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
486 self
487 }
488}
489
490impl FillColor for Polygon {
491 fn fill_color(&self) -> AlphaColor<Srgb> {
492 self.fill_rgba
493 }
494 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
495 self.fill_rgba = color;
496 self
497 }
498 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
499 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
500 self
501 }
502}
503
504impl From<Polygon> for VItem {
506 fn from(value: Polygon) -> Self {
507 let Polygon {
508 mut points,
509 stroke_rgba,
510 stroke_width,
511 fill_rgba,
512 ..
513 } = value;
514 assert!(points.len() > 2);
515
516 points.push(points[0]);
518
519 let anchors = points;
520 let handles = anchors
521 .iter()
522 .tuple_windows()
523 .map(|(&a, &b)| 0.5 * (a + b))
524 .collect::<Vec<_>>();
525
526 let vpoints = anchors.into_iter().interleave(handles).collect::<Vec<_>>();
528 VItem::from_vpoints(vpoints).with(|vitem| {
529 vitem
530 .set_fill_color(fill_rgba)
531 .set_stroke_color(stroke_rgba)
532 .set_stroke_width(stroke_width);
533 })
534 }
535}
536
537impl Extract for Polygon {
538 type Target = VItemPrimitive;
539 fn extract(&self) -> Vec<Self::Target> {
540 VItem::from(self.clone()).extract()
550 }
551}
552
553#[cfg(test)]
554mod tests {
555 use assert_float_eq::assert_float_absolute_eq;
556
557 use super::*;
558 #[test]
559 fn test_square() {
560 let square = Square::new(2.0).with(|data| {
561 data.shift(DVec3::NEG_Y)
562 .rotate(std::f64::consts::PI / 2.0, DVec3::X);
563 });
564 assert_float_absolute_eq!(square.center.distance_squared(DVec3::NEG_Y), 0.0, 1e-10);
565 assert_float_absolute_eq!(square.up.distance_squared(DVec3::Z), 0.0, 1e-10);
566 assert_float_absolute_eq!(square.normal.distance_squared(DVec3::NEG_Y), 0.0, 1e-10);
567 let square = Square::new(2.0).with(|data| {
568 data.shift(DVec3::X)
569 .rotate(std::f64::consts::PI / 2.0, DVec3::Y);
570 });
571 assert_float_absolute_eq!(square.center.distance_squared(DVec3::X), 0.0, 1e-10);
572 assert_float_absolute_eq!(square.up.distance_squared(DVec3::Y), 0.0, 1e-10);
573 assert_float_absolute_eq!(square.normal.distance_squared(DVec3::X), 0.0, 1e-10);
574 let square = Square::new(2.0).with(|data| {
575 data.shift(DVec3::NEG_Z);
576 });
577 assert_float_absolute_eq!(square.center.distance_squared(DVec3::NEG_Z), 0.0, 1e-10);
578 assert_float_absolute_eq!(square.up.distance_squared(DVec3::Y), 0.0, 1e-10);
579 assert_float_absolute_eq!(square.normal.distance_squared(DVec3::Z), 0.0, 1e-10);
580 let square = Square::new(2.0).with(|data| {
581 data.shift(DVec3::Y)
582 .rotate(-std::f64::consts::PI / 2.0, DVec3::X);
583 });
584 assert_float_absolute_eq!(square.center.distance_squared(DVec3::Y), 0.0, 1e-10);
585 assert_float_absolute_eq!(square.up.distance_squared(DVec3::NEG_Z), 0.0, 1e-10);
586 assert_float_absolute_eq!(square.normal.distance_squared(DVec3::Y), 0.0, 1e-10);
587 let square = Square::new(2.0).with(|data| {
588 data.shift(DVec3::Z);
589 });
590 assert_float_absolute_eq!(square.center.distance_squared(DVec3::Z), 0.0, 1e-10);
591 assert_float_absolute_eq!(square.up.distance_squared(DVec3::Y), 0.0, 1e-10);
592 assert_float_absolute_eq!(square.normal.distance_squared(DVec3::Z), 0.0, 1e-10);
593 let square = Square::new(2.0).with(|data| {
594 data.shift(DVec3::NEG_X)
595 .rotate(-std::f64::consts::PI / 2.0, DVec3::Y);
596 });
597 assert_float_absolute_eq!(square.center.distance_squared(DVec3::NEG_X), 0.0, 1e-10);
598 assert_float_absolute_eq!(square.up.distance_squared(DVec3::Y), 0.0, 1e-10);
599 assert_float_absolute_eq!(square.normal.distance_squared(DVec3::NEG_X), 0.0, 1e-10);
600 }
601}