Koch snowflake
This commit is contained in:
parent
41ad2cabee
commit
07d05aba84
4 changed files with 126 additions and 35 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -8,6 +8,12 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "c_vec"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdd7a427adc0135366d99db65b36dae9237130997e560ed61118041fb72be6e8"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -40,6 +46,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3b498da7d14d1ad6c839729bd4ad6fc11d90a57583605f3b4df2cd709a9cd380"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"c_vec",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"sdl2-sys",
|
||||
|
|
|
@ -6,4 +6,4 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
sdl2 = "0.37"
|
||||
sdl2 = { version = "0.37", features = ["gfx"] }
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
packageBuildInputs = with pkgs; [
|
||||
SDL2
|
||||
SDL2_gfx
|
||||
];
|
||||
|
||||
inherit (import "${crate2nix}/tools.nix" { inherit pkgs; })
|
||||
|
|
151
src/main.rs
151
src/main.rs
|
@ -1,50 +1,133 @@
|
|||
extern crate sdl2;
|
||||
|
||||
use sdl2::pixels::Color;
|
||||
use std::cmp::min;
|
||||
|
||||
use sdl2::event::Event;
|
||||
use sdl2::keyboard::Keycode;
|
||||
use std::time::Duration;
|
||||
use sdl2::pixels::{self, Color};
|
||||
|
||||
pub fn main() {
|
||||
println!("Hello, world!");
|
||||
let sdl_context = sdl2::init().unwrap();
|
||||
let video_subsystem = sdl_context.video().unwrap();
|
||||
use sdl2::gfx::primitives::DrawRenderer;
|
||||
use sdl2::render::WindowCanvas;
|
||||
|
||||
let window = match video_subsystem.window("fractals", 800, 600)
|
||||
const SCREEN_WIDTH: i16 = 800; // i15
|
||||
const SCREEN_HEIGHT: i16 = 600; // i15
|
||||
|
||||
fn atan(a: f32, b: f32) -> f32 {
|
||||
if b == 0.0 {
|
||||
return 0.0
|
||||
} else if b < 0.0 {
|
||||
return (a/b).atan() + std::f32::consts::PI;
|
||||
} else {
|
||||
return (a/b).atan();
|
||||
}
|
||||
}
|
||||
|
||||
/** for each zoomlevel, lines are replaced by 4 new lines of length a/3 **/
|
||||
fn koch_line(canvas: &WindowCanvas, x0: i16, y0: i16, x4: i16, y4: i16, color: Color, zoom: i16) {
|
||||
|
||||
if zoom == 1 {
|
||||
let _ = canvas.line(x0, y0, x4, y4, color);
|
||||
} else {
|
||||
/* the outer two lines */
|
||||
let x1 = x0 + (x4-x0) / 3;
|
||||
let y1 = y0 + (y4-y0) / 3;
|
||||
let x3 = x0 + 2* (x4-x0) / 3;
|
||||
let y3 = y0 + 2* (y4-y0) / 3;
|
||||
koch_line(canvas, x0, y0, x1, y1, color, zoom-1);
|
||||
koch_line(canvas, x3, y3, x4, y4, color, zoom-1);
|
||||
|
||||
/* the inner two lines (arms of an equilateral triangle) */
|
||||
let dx = x3 - x1;
|
||||
let dy = y3 - y1;
|
||||
let a = ((dx*dx + dy*dy) as f32).sqrt();
|
||||
let angle = atan((-dy).into(), dx.into()); // angle of the original line
|
||||
const INTERNAL_ANGLE: f32 = std::f32::consts::FRAC_PI_3; // 60_f32.to_radians();
|
||||
let x2 = x1 + ((a * (angle - INTERNAL_ANGLE).cos()) as i16);
|
||||
let y2 = y1 - ((a * (angle - INTERNAL_ANGLE).sin()) as i16);
|
||||
koch_line(canvas, x1, y1, x2, y2, color, zoom-1);
|
||||
koch_line(canvas, x2, y2, x3, y3, color, zoom-1);
|
||||
}
|
||||
}
|
||||
|
||||
fn koch_snowflake(canvas: &WindowCanvas, x: i16, y: i16, a: i16, color: Option<Color>, zoom: i16) {
|
||||
let max_zoom = (a as f32).log(3.0).ceil() as i16;
|
||||
let effective_zoom = min(zoom, max_zoom);
|
||||
println!("zoom: {}/{}", effective_zoom, max_zoom);
|
||||
|
||||
let c = (255 * (effective_zoom-1) / (max_zoom-1)) as u8;
|
||||
let color = color.unwrap_or(Color::RGB(c, c, 127+c/2));
|
||||
|
||||
let h = (a as f32 * 3.0_f32.sqrt() / 2.0) as i16; // height of equilateral triangle
|
||||
|
||||
let x1 = x;
|
||||
let y1 = y - h/2;
|
||||
let x2 = x - a/2;
|
||||
let y2 = y + h/2;
|
||||
let x3 = x + a/2;
|
||||
let y3 = y + h/2;
|
||||
koch_line(&canvas, x1, y1, x2, y2, color, effective_zoom);
|
||||
koch_line(&canvas, x2, y2, x3, y3, color, effective_zoom);
|
||||
koch_line(&canvas, x3, y3, x1, y1, color, effective_zoom);
|
||||
}
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let sdl_context = sdl2::init()?;
|
||||
let video_subsys = sdl_context.video()?;
|
||||
let window = video_subsys
|
||||
.window(
|
||||
"rust-sdl2_gfx: draw line & FPSManager",
|
||||
(SCREEN_WIDTH as u16).into(),
|
||||
(SCREEN_HEIGHT as u16).into(),
|
||||
)
|
||||
.position_centered()
|
||||
.build() {
|
||||
Ok(w) => { w },
|
||||
Err(_) => { panic!("Can not build window!") }
|
||||
};
|
||||
.opengl()
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let mut canvas = window.into_canvas().build().unwrap();
|
||||
let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?;
|
||||
|
||||
let mut event_pump = sdl_context.event_pump().unwrap();
|
||||
let mut i = 0;
|
||||
let mut flip = true;
|
||||
canvas.set_draw_color(pixels::Color::RGB(0, 0, 0));
|
||||
canvas.clear();
|
||||
canvas.present();
|
||||
println!("Welcome :)");
|
||||
println!("[Klick] into canvas to draw a koch snowflake…");
|
||||
println!("Press [Space] to clear the canvas…");
|
||||
println!("Press [Backspace] to reset the zoom…");
|
||||
|
||||
'running: loop {
|
||||
if i % 255 == 0 {
|
||||
flip = !flip;
|
||||
}
|
||||
if flip && i > 0 {
|
||||
i = i - 1;
|
||||
} else {
|
||||
i = i + 1;
|
||||
}
|
||||
canvas.set_draw_color(Color::RGB(i, 64, 255 - i));
|
||||
canvas.clear();
|
||||
for event in event_pump.poll_iter() {
|
||||
let mut events = sdl_context.event_pump()?;
|
||||
|
||||
let mut zoom = 1;
|
||||
|
||||
'main: loop {
|
||||
for event in events.poll_iter() {
|
||||
match event {
|
||||
Event::Quit { .. } |
|
||||
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
|
||||
break 'running
|
||||
},
|
||||
Event::Quit { .. } => break 'main,
|
||||
|
||||
Event::KeyDown {
|
||||
keycode: Some(keycode),
|
||||
..
|
||||
} => {
|
||||
if keycode == Keycode::Escape {
|
||||
break 'main;
|
||||
} else if keycode == Keycode::Space {
|
||||
canvas.set_draw_color(pixels::Color::RGB(0, 0, 0));
|
||||
canvas.clear();
|
||||
canvas.present();
|
||||
} else if keycode == Keycode::BACKSPACE {
|
||||
zoom = 1
|
||||
}
|
||||
}
|
||||
|
||||
Event::MouseButtonDown { x, y, .. } => {
|
||||
koch_snowflake(&canvas, x.try_into().unwrap(), y.try_into().unwrap(), SCREEN_HEIGHT/3, None, zoom);
|
||||
canvas.present();
|
||||
zoom += 1;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
canvas.present();
|
||||
::std::thread::sleep(Duration::from_millis(1000 / 60));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue