Fill in the rest of the owl (basic impl done)
And that's the program, all finished and working as intended. Step 1: boiler plate. Step 2: Everything else. The program isn't that complicated, so I didn't really feel the need to spread it out over several commits.
This commit is contained in:
@@ -5,3 +5,8 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.16"
|
bevy = "0.16"
|
||||||
|
rand = "0.9.2"
|
||||||
|
rand_chacha = "0.9.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
dynamic_linking = ["bevy/dynamic_linking"]
|
||||||
129
src/lib.rs
129
src/lib.rs
@@ -1 +1,130 @@
|
|||||||
|
use bevy::{
|
||||||
|
asset::RenderAssetUsages,
|
||||||
|
color::palettes::css::*,
|
||||||
|
prelude::*,
|
||||||
|
render::render_resource::{Extent3d, TextureDimension, TextureFormat},
|
||||||
|
};
|
||||||
|
|
||||||
|
use rand::{Rng, SeedableRng};
|
||||||
|
use rand_chacha::ChaCha8Rng;
|
||||||
|
|
||||||
|
const IMAGE_WIDTH: u32 = 800;
|
||||||
|
const IMAGE_HEIGHT: u32 = 600;
|
||||||
|
|
||||||
|
pub struct ChaosGamePlugin;
|
||||||
|
|
||||||
|
impl Plugin for ChaosGamePlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.insert_resource(ClearColor(BLACK.into()))
|
||||||
|
.insert_resource(Attractor::triangle())
|
||||||
|
.insert_resource(Time::<Fixed>::from_hz(1024.0)) // make it draw really fast
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.add_systems(FixedUpdate, walk_point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>, att: Res<Attractor>) {
|
||||||
|
commands.spawn(Camera2d);
|
||||||
|
|
||||||
|
commands.insert_resource(SeededRng(ChaCha8Rng::seed_from_u64(0)));
|
||||||
|
|
||||||
|
let mut image = Image::new_fill(
|
||||||
|
Extent3d {
|
||||||
|
width: IMAGE_WIDTH,
|
||||||
|
height: IMAGE_HEIGHT,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
TextureDimension::D2,
|
||||||
|
&(BLACK.to_u8_array()),
|
||||||
|
TextureFormat::Rgba8UnormSrgb,
|
||||||
|
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Paint the target points so they can be seen
|
||||||
|
for idx in 0..3 {
|
||||||
|
let point = att.points[idx];
|
||||||
|
let color = att.colors[idx];
|
||||||
|
let pixel_bytes = image
|
||||||
|
.pixel_bytes_mut(UVec3::new(point.x as u32, point.y as u32, 0))
|
||||||
|
.unwrap();
|
||||||
|
fill_color(pixel_bytes, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn a sprite with this image and store the handle in the [`Drawing`]
|
||||||
|
// resource for lookup during the walk function.
|
||||||
|
let handle = images.add(image);
|
||||||
|
commands.spawn(Sprite::from_image(handle.clone()));
|
||||||
|
commands.insert_resource(Drawing(handle));
|
||||||
|
|
||||||
|
// Spawn the Walker entity
|
||||||
|
// TODO: Pull in an RNG and randomly place the walker.
|
||||||
|
commands.spawn(Walker(Vec2::new(IMAGE_WIDTH as f32 / 2., IMAGE_HEIGHT as f32 / 2.)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_point(
|
||||||
|
drawing: Res<Drawing>,
|
||||||
|
mut images: ResMut<Assets<Image>>,
|
||||||
|
mut walker: Single<&mut Walker>,
|
||||||
|
att: Res<Attractor>,
|
||||||
|
mut rng: ResMut<SeededRng>,
|
||||||
|
) {
|
||||||
|
// Walk towards the next point. Select at random, step half the distance
|
||||||
|
// between the walker and that point.
|
||||||
|
let tgt = rng.0.random_range(0..3usize);
|
||||||
|
let delta_p = (att.points[tgt] - walker.0) / 2.0;
|
||||||
|
walker.0 = walker.0 + delta_p;
|
||||||
|
|
||||||
|
// Paint the pixel at the new walker position with the color of the chosen
|
||||||
|
// target.
|
||||||
|
|
||||||
|
let image = images.get_mut(&drawing.0).expect("Image not found");
|
||||||
|
fill_color(
|
||||||
|
image
|
||||||
|
.pixel_bytes_mut(UVec3::new(walker.0.x as u32, walker.0.y as u32, 0))
|
||||||
|
.unwrap(),
|
||||||
|
att.colors[tgt],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility function to fill in a pixel by reaching through it's byte slice.
|
||||||
|
fn fill_color(pixel_slice: &mut [u8], color: Color) {
|
||||||
|
pixel_slice[0] = (color.to_linear().red * u8::MAX as f32) as u8;
|
||||||
|
pixel_slice[1] = (color.to_linear().green * u8::MAX as f32) as u8;
|
||||||
|
pixel_slice[2] = (color.to_linear().blue * u8::MAX as f32) as u8;
|
||||||
|
pixel_slice[3] = u8::MAX; // hard-code alpha to be fully opaque.
|
||||||
|
// I'm not dealing with transparency bugs today.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct SeededRng(ChaCha8Rng);
|
||||||
|
|
||||||
|
/// The entity that walks around leaving colored dots along the way.
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Walker(Vec2);
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct Drawing(Handle<Image>);
|
||||||
|
|
||||||
|
/// An "attractor" is the set of points that will be walked between to draw the
|
||||||
|
/// shape.
|
||||||
|
///
|
||||||
|
/// TODO: Support other shapes.
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct Attractor {
|
||||||
|
points: [Vec2; 3],
|
||||||
|
colors: [Color; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attractor {
|
||||||
|
/// Create a triangle whose verticies will be used as the target locations.
|
||||||
|
pub fn triangle() -> Self {
|
||||||
|
Self {
|
||||||
|
points: [
|
||||||
|
Vec2::new(10., 10.), // bottom left
|
||||||
|
Vec2::new((IMAGE_WIDTH / 2) as f32, (IMAGE_HEIGHT - 10) as f32), // top middle
|
||||||
|
Vec2::new((IMAGE_WIDTH - 10) as f32, 10.), // bottom right
|
||||||
|
],
|
||||||
|
colors: [RED.into(), GREEN.into(), BLUE.into()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use bevy::{prelude::*, winit::WinitSettings};
|
use bevy::{prelude::*, winit::WinitSettings};
|
||||||
|
|
||||||
|
use chaos_game_rs::ChaosGamePlugin;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.insert_resource(WinitSettings::desktop_app())
|
.insert_resource(WinitSettings::desktop_app())
|
||||||
@@ -10,5 +12,6 @@ fn main() {
|
|||||||
}),
|
}),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
|
.add_plugins(ChaosGamePlugin)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user