Preview
App
use rand::{SeedableRng, seq::SliceRandom};
use ranim::{
anims::morph::MorphAnim,
color::palettes::manim,
core::animation::{compose::sequence::AnimSequence, eval::StaticAnim},
glam::{DVec3, dvec2, dvec3},
items::vitem::{VItem, geometry::Rectangle},
prelude::*,
utils::rate_functions::linear,
};
fn bubble_sort(r: &mut RanimScene, num: usize) {
let frame_size = dvec2(8.0 * 16.0 / 9.0, 8.0);
let padded_frame_size = frame_size * 0.9;
let anim_step_duration = 15.0 / num.pow(2) as f64;
let width_unit = padded_frame_size.x / num as f64;
let height_unit = padded_frame_size.y / num as f64;
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(114514);
let mut heights = (1..=num)
.map(|x| x as f64 * height_unit)
.collect::<Vec<f64>>();
heights.shuffle(&mut rng);
let padded_frame_bl = dvec2(padded_frame_size.x / -2.0, padded_frame_size.y / -2.0);
let mut rects = heights
.iter()
.enumerate()
.map(|(i, &height)| {
let target_bc_coord =
padded_frame_bl.extend(0.0) + DVec3::X * (width_unit * i as f64 + width_unit / 2.0);
let rect = Rectangle::new(width_unit, height).with(|rect| {
rect.stroke_width = 0.0;
rect.set_fill_color(manim::WHITE.with_alpha(0.5))
.scale_axes(dvec2(0.8, 0.8));
});
let mut rect = VItem::from(rect);
rect.move_anchor_to(AabbPoint(dvec3(0.0, -1.0, 0.0)), target_bc_coord);
let sequence = seq![rect.show()];
(sequence, rect)
})
.collect::<Vec<_>>();
let sync = |rects: &mut [(AnimSequence, VItem)]| {
let target = rects
.iter()
.map(|(sequence, _)| sequence.cursor_sec())
.fold(0.0, f64::max);
rects.iter_mut().for_each(|(sequence, _)| {
sequence.hold_to(target);
});
};
let shift_right = DVec3::X * width_unit;
for i in (1..num).rev() {
for j in 0..i {
for (sequence, rect) in rects.get_disjoint_mut([j, j + 1]).unwrap() {
sequence.push(
rect.morph(|rect| {
rect.set_fill_color(manim::BLUE_C.with_alpha(0.5));
})
.with_duration(anim_step_duration)
.with_rate_func(linear),
);
}
if heights[j] > heights[j + 1] {
for ((sequence, rect), shift) in rects
.get_disjoint_mut([j, j + 1])
.unwrap()
.into_iter()
.zip([shift_right, -shift_right])
{
sequence.push(
rect.morph(|rect| {
rect.shift(shift);
})
.with_duration(anim_step_duration)
.with_rate_func(linear),
);
}
sync(&mut rects);
heights.swap(j, j + 1);
rects.swap(j, j + 1);
}
for (sequence, rect) in rects.get_disjoint_mut([j, j + 1]).unwrap() {
sequence.push(
rect.morph(|rect| {
rect.set_fill_color(manim::WHITE.with_alpha(0.5));
})
.with_duration(anim_step_duration)
.with_rate_func(linear),
);
}
sync(&mut rects);
}
}
let total_secs = rects[0].0.cursor_sec();
let mut content = AnimStack::new();
for (sequence, _) in rects {
content.push(sequence);
}
r.play(CameraFrame::default().show().with_duration(total_secs));
r.play(content);
r.insert_time_mark(total_secs, TimeMark::Capture(format!("preview-{num}.png")));
}
#[scene]
#[wasm_demo_doc]
#[output(dir = "./output/bubble_sort")]
fn bubble_sort_10(r: &mut RanimScene) {
bubble_sort(r, 10);
}
#[scene(name = "bubble_sort")]
#[wasm_demo_doc]
#[output(dir = "./output/bubble_sort")]
fn bubble_sort_100(r: &mut RanimScene) {
bubble_sort(r, 100);
}