1 | use glam::{DVec3, dvec2};
|
2 | use rand::{SeedableRng, seq::SliceRandom};
|
3 | use 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]
|
9 | struct BubbleSortScene(pub usize);
|
10 |
|
11 | impl 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 |
|
128 | fn 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 |
|