1use bevy::color::{color_difference::EuclideanDistance, palettes::css};
9use bevy::prelude::*;
10use bevy::render::{
11 render_asset::RenderAssetUsages,
12 render_resource::{Extent3d, TextureDimension, TextureFormat},
13};
14use rand::{Rng, SeedableRng};
15use rand_chacha::ChaCha8Rng;
16
17const IMAGE_WIDTH: u32 = 256;
18const IMAGE_HEIGHT: u32 = 256;
19
20fn main() {
21 App::new()
22 .add_plugins(DefaultPlugins)
23 .insert_resource(Time::<Fixed>::from_hz(1024.0))
28 .add_systems(Startup, setup)
29 .add_systems(FixedUpdate, draw)
30 .run();
31}
32
33#[derive(Resource)]
35struct MyProcGenImage(Handle<Image>);
36
37#[derive(Resource)]
38struct SeededRng(ChaCha8Rng);
39
40fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
41 commands.spawn(Camera2d);
42
43 let mut image = Image::new_fill(
45 Extent3d {
47 width: IMAGE_WIDTH,
48 height: IMAGE_HEIGHT,
49 depth_or_array_layers: 1,
50 },
51 TextureDimension::D2,
52 &(css::BEIGE.to_u8_array()),
54 TextureFormat::Rgba8UnormSrgb,
56 RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
57 );
58
59 for y in 0..IMAGE_HEIGHT {
62 for x in 0..IMAGE_WIDTH {
63 let center = Vec2::new(IMAGE_WIDTH as f32 / 2.0, IMAGE_HEIGHT as f32 / 2.0);
64 let max_radius = IMAGE_HEIGHT.min(IMAGE_WIDTH) as f32 / 2.0;
65 let r = Vec2::new(x as f32, y as f32).distance(center);
66 let a = 1.0 - (r / max_radius).clamp(0.0, 1.0);
67
68 let pixel_bytes = image.pixel_bytes_mut(UVec3::new(x, y, 0)).unwrap();
73 pixel_bytes[3] = (a * u8::MAX as f32) as u8;
75 }
76 }
77
78 let handle = images.add(image);
82
83 commands.spawn(Sprite::from_image(handle.clone()));
85 commands.insert_resource(MyProcGenImage(handle));
86
87 let seeded_rng = ChaCha8Rng::seed_from_u64(19878367467712);
90 commands.insert_resource(SeededRng(seeded_rng));
91}
92
93fn draw(
95 my_handle: Res<MyProcGenImage>,
96 mut images: ResMut<Assets<Image>>,
97 mut i: Local<u32>,
99 mut draw_color: Local<Color>,
100 mut seeded_rng: ResMut<SeededRng>,
101) {
102 if *i == 0 {
103 *draw_color = Color::linear_rgb(
105 seeded_rng.0.r#gen(),
106 seeded_rng.0.r#gen(),
107 seeded_rng.0.r#gen(),
108 );
109 }
110
111 let image = images.get_mut(&my_handle.0).expect("Image not found");
113
114 let center = Vec2::new(IMAGE_WIDTH as f32 / 2.0, IMAGE_HEIGHT as f32 / 2.0);
117 let max_radius = IMAGE_HEIGHT.min(IMAGE_WIDTH) as f32 / 2.0;
118 let rot_speed = 0.0123;
119 let period = 0.12345;
120
121 let r = ops::sin(*i as f32 * period) * max_radius;
122 let xy = Vec2::from_angle(*i as f32 * rot_speed) * r + center;
123 let (x, y) = (xy.x as u32, xy.y as u32);
124
125 let old_color = image.get_color_at(x, y).unwrap();
127
128 let tolerance = 1.0 / 255.0;
130 if old_color.distance(&draw_color) <= tolerance {
131 *draw_color = Color::linear_rgb(
132 seeded_rng.0.r#gen(),
133 seeded_rng.0.r#gen(),
134 seeded_rng.0.r#gen(),
135 );
136 }
137
138 image
140 .set_color_at(x, y, draw_color.with_alpha(old_color.alpha()))
141 .unwrap();
142
143 *i += 1;
144}