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