1use glam::{DVec3, dvec2};
2use rand::{SeedableRng, seq::SliceRandom};
3use ranim::{
4 animation::transform::TransformAnimSchedule, color::palettes::manim, components::Anchor,
5 items::vitem::Rectangle, prelude::*, timeline::TimeMark, utils::rate_functions::linear,
6};
7
8#[scene]
9struct BubbleSortScene(pub usize);
10
11impl TimelineConstructor for BubbleSortScene {
12 fn construct(self, timeline: &RanimTimeline, _camera: &mut Rabject<CameraFrame>) {
13 let num = self.0;
14
15 let frame_size = dvec2(8.0 * 16.0 / 9.0, 8.0);
16 let padded_frame_size = frame_size * 0.9;
17
18 let anim_step_duration = 15.0 / num.pow(2) as f64;
19
20 let width_unit = padded_frame_size.x / num as f64;
21 let height_unit = padded_frame_size.y / num as f64;
22
23 let mut rng = rand_chacha::ChaChaRng::seed_from_u64(114514);
24 let mut heights = (1..=num)
25 .map(|x| x as f64 * height_unit)
26 .collect::<Vec<f64>>();
27 heights.shuffle(&mut rng);
28
29 let padded_frame_bl = dvec2(padded_frame_size.x / -2.0, padded_frame_size.y / -2.0);
30 let mut rects = heights
31 .iter()
32 .enumerate()
33 .map(|(i, &height)| {
34 let mut rect = Rectangle(width_unit, height).build();
35 let target_bc_coord = padded_frame_bl.extend(0.0)
36 + DVec3::X * (width_unit * i as f64 + width_unit / 2.0);
37 rect.scale(DVec3::splat(0.8))
38 .put_anchor_on(Anchor::edge(0, -1, 0), target_bc_coord)
39 .set_color(manim::WHITE)
40 .set_stroke_width(0.0)
41 .set_fill_opacity(0.5);
42 timeline.insert(rect)
43 })
44 .collect::<Vec<_>>();
45
46 let shift_right = DVec3::X * width_unit;
47 for i in (1..num).rev() {
48 for j in 0..i {
49 timeline.play(
50 rects[j]
51 .transform(|data| {
52 data.set_color(manim::BLUE_C).set_fill_opacity(0.5);
53 })
54 .with_duration(anim_step_duration)
55 .with_rate_func(linear)
56 .apply(),
57 );
58 timeline.play(
59 rects[j + 1]
60 .transform(|data| {
61 data.set_color(manim::BLUE_C).set_fill_opacity(0.5);
62 })
63 .with_duration(anim_step_duration)
64 .with_rate_func(linear)
65 .apply(),
66 );
67 timeline.sync();
68
69 if heights[j] > heights[j + 1] {
70 timeline.play(
71 rects[j]
72 .transform(|data| {
73 data.shift(shift_right)
74 .set_color(manim::BLUE_C)
75 .set_fill_opacity(0.5);
76 })
77 .with_duration(anim_step_duration)
78 .with_rate_func(linear)
79 .apply(),
80 );
81 timeline.play(
82 rects[j + 1]
83 .transform(|data| {
84 data.shift(-shift_right)
85 .set_color(manim::BLUE_C)
86 .set_fill_opacity(0.5);
87 })
88 .with_duration(anim_step_duration)
89 .with_rate_func(linear)
90 .apply(),
91 );
92 timeline.sync();
93 heights.swap(j, j + 1);
94 rects.swap(j, j + 1);
95 }
96 timeline.play(
97 rects[j]
98 .transform(|data| {
99 data.set_color(manim::WHITE).set_fill_opacity(0.5);
100 })
101 .with_duration(anim_step_duration)
102 .with_rate_func(linear)
103 .apply(),
104 );
105 timeline.play(
106 rects[j + 1]
107 .transform(|data| {
108 data.set_color(manim::WHITE).set_fill_opacity(0.5);
109 })
110 .with_duration(anim_step_duration)
111 .with_rate_func(linear)
112 .apply(),
113 );
114 timeline.sync();
115 }
116 }
117
118 timeline.insert_time_mark(
119 timeline.duration_secs() / 2.0,
120 TimeMark::Capture(format!("preview-{num}.png")),
121 );
122 }
123}
124
125fn main() {
126 #[cfg(feature = "app")]
127 run_scene_app(BubbleSortScene(100));
128 #[cfg(not(feature = "app"))]
129 {
130 render_scene(
131 BubbleSortScene(10),
132 &AppOptions {
133 output_filename: "output-10.mp4",
134 ..Default::default()
135 },
136 );
137 render_scene(
138 BubbleSortScene(100),
139 &AppOptions {
140 output_filename: "output-100.mp4",
141 ..Default::default()
142 },
143 );
144 }
145}
146