formatting
note that i'm intentionally not using rustfmt. i've tried to like that tool, but in the end i just really don't like it. too many edge cases and subjectivity and not enough customization. which is probably the intent. which makes me hate it that much more. fuck you, rustfmt.
This commit is contained in:
parent
43333687a8
commit
eb6a363afd
|
@ -9,195 +9,193 @@ use libretrogd::utils::rnd_value;
|
|||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct AudioChannelStatus {
|
||||
size: usize,
|
||||
position: usize,
|
||||
playing: bool
|
||||
size: usize,
|
||||
position: usize,
|
||||
playing: bool,
|
||||
}
|
||||
|
||||
fn load_and_convert_wav(path: &Path, target_spec: &AudioSpec) -> Result<AudioBuffer> {
|
||||
let sound = AudioBuffer::load_wav_file(path)?;
|
||||
let original_spec = *sound.spec();
|
||||
let sound = sound.convert(target_spec)?;
|
||||
let final_spec = *sound.spec();
|
||||
if original_spec != final_spec {
|
||||
println!("{:?} was converted from {:?} to {:?}", path, original_spec, final_spec);
|
||||
} else {
|
||||
println!("{:?} did not need to be converted from {:?}", path, original_spec);
|
||||
}
|
||||
Ok(sound)
|
||||
let sound = AudioBuffer::load_wav_file(path)?;
|
||||
let original_spec = *sound.spec();
|
||||
let sound = sound.convert(target_spec)?;
|
||||
let final_spec = *sound.spec();
|
||||
if original_spec != final_spec {
|
||||
println!("{:?} was converted from {:?} to {:?}", path, original_spec, final_spec);
|
||||
} else {
|
||||
println!("{:?} did not need to be converted from {:?}", path, original_spec);
|
||||
}
|
||||
Ok(sound)
|
||||
}
|
||||
|
||||
pub struct SineWaveGenerator {
|
||||
t: usize,
|
||||
t: usize,
|
||||
}
|
||||
|
||||
impl SineWaveGenerator {
|
||||
pub fn new() -> Self {
|
||||
SineWaveGenerator {
|
||||
t: 0
|
||||
}
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
SineWaveGenerator {
|
||||
t: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioGenerator for SineWaveGenerator {
|
||||
fn gen_sample(&mut self, position: usize) -> Option<u8> {
|
||||
const MAX_TIME: usize = AUDIO_FREQUENCY_22KHZ as usize * 3; // 3 seconds
|
||||
if self.t < MAX_TIME {
|
||||
let sample = (self.t as f64 * 0.25).sin() * 80.0;
|
||||
self.t += 1;
|
||||
Some((sample + 128.0) as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn gen_sample(&mut self, position: usize) -> Option<u8> {
|
||||
const MAX_TIME: usize = AUDIO_FREQUENCY_22KHZ as usize * 3; // 3 seconds
|
||||
if self.t < MAX_TIME {
|
||||
let sample = (self.t as f64 * 0.25).sin() * 80.0;
|
||||
self.t += 1;
|
||||
Some((sample + 128.0) as u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut system = SystemBuilder::new().window_title("Audio Playback").vsync(true).build()?;
|
||||
let mut system = SystemBuilder::new().window_title("Audio Playback").vsync(true).build()?;
|
||||
|
||||
let mut using_queue_commands = false;
|
||||
let mut volume = 1.0;
|
||||
let mut using_queue_commands = false;
|
||||
let mut volume = 1.0;
|
||||
|
||||
let sounds = [
|
||||
load_and_convert_wav(Path::new("./assets/pickup-coin.wav"), system.audio.spec())?,
|
||||
load_and_convert_wav(Path::new("./assets/powerup.wav"), system.audio.spec())?,
|
||||
load_and_convert_wav(Path::new("./assets/explosion.wav"), system.audio.spec())?,
|
||||
load_and_convert_wav(Path::new("./assets/jump.wav"), system.audio.spec())?,
|
||||
load_and_convert_wav(Path::new("./assets/laser-shoot.wav"), system.audio.spec())?,
|
||||
];
|
||||
let sounds = [
|
||||
load_and_convert_wav(Path::new("./assets/pickup-coin.wav"), system.audio.spec())?,
|
||||
load_and_convert_wav(Path::new("./assets/powerup.wav"), system.audio.spec())?,
|
||||
load_and_convert_wav(Path::new("./assets/explosion.wav"), system.audio.spec())?,
|
||||
load_and_convert_wav(Path::new("./assets/jump.wav"), system.audio.spec())?,
|
||||
load_and_convert_wav(Path::new("./assets/laser-shoot.wav"), system.audio.spec())?,
|
||||
];
|
||||
|
||||
let mut statuses = [AudioChannelStatus { size: 0, position: 0, playing: false }; NUM_CHANNELS];
|
||||
let mut statuses = [AudioChannelStatus { size: 0, position: 0, playing: false }; NUM_CHANNELS];
|
||||
|
||||
while !system.do_events() {
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
break;
|
||||
}
|
||||
while !system.do_events() {
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut audio_device = system.audio.lock();
|
||||
audio_device.volume = volume;
|
||||
let mut audio_device = system.audio.lock();
|
||||
audio_device.volume = volume;
|
||||
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num1) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer(&sounds[0], false);
|
||||
} else {
|
||||
audio_device.play_buffer(&sounds[0], false)?;
|
||||
}
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num1) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer(&sounds[0], false);
|
||||
} else {
|
||||
audio_device.play_buffer(&sounds[0], false)?;
|
||||
}
|
||||
}
|
||||
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num2) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer(&sounds[1], false);
|
||||
} else {
|
||||
audio_device.play_buffer(&sounds[1], false)?;
|
||||
}
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num2) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer(&sounds[1], false);
|
||||
} else {
|
||||
audio_device.play_buffer(&sounds[1], false)?;
|
||||
}
|
||||
}
|
||||
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num3) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer(&sounds[2], false);
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num3) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer(&sounds[2], false);
|
||||
} else {
|
||||
audio_device.play_buffer(&sounds[2], false)?;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
audio_device.play_buffer(&sounds[2], false)?;
|
||||
}
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num4) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer(&sounds[3], false);
|
||||
} else {
|
||||
audio_device.play_buffer(&sounds[3], false)?;
|
||||
}
|
||||
}
|
||||
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num4) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer(&sounds[3], false);
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num5) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer(&sounds[4], false);
|
||||
} else {
|
||||
audio_device.play_buffer(&sounds[4], false)?;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
audio_device.play_buffer(&sounds[3], false)?;
|
||||
}
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num6) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_generator(Box::new(SineWaveGenerator::new()), false);
|
||||
} else {
|
||||
audio_device.play_generator(Box::new(SineWaveGenerator::new()), false);
|
||||
}
|
||||
}
|
||||
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num5) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer(&sounds[4], false);
|
||||
} else {
|
||||
audio_device.play_buffer(&sounds[4], false)?;
|
||||
}
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num7) {
|
||||
let index = rnd_value(0, sounds.len() - 1);
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer_on_channel(7, &sounds[index], false)?;
|
||||
} else {
|
||||
audio_device.play_buffer_on_channel(7, &sounds[index], false)?;
|
||||
}
|
||||
}
|
||||
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num6) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_generator(Box::new(SineWaveGenerator::new()), false);
|
||||
} else {
|
||||
audio_device.play_generator(Box::new(SineWaveGenerator::new()), false);
|
||||
}
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::S) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.stop_all();
|
||||
} else {
|
||||
audio_device.stop_all();
|
||||
}
|
||||
}
|
||||
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Num7) {
|
||||
let index = rnd_value(0, sounds.len() - 1);
|
||||
if using_queue_commands {
|
||||
system.audio_queue.play_buffer_on_channel(7, &sounds[index], false)?;
|
||||
} else {
|
||||
audio_device.play_buffer_on_channel(7, &sounds[index], false)?;
|
||||
}
|
||||
}
|
||||
system.audio_queue.apply_to_device(&mut audio_device)?;
|
||||
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::S) {
|
||||
if using_queue_commands {
|
||||
system.audio_queue.stop_all();
|
||||
} else {
|
||||
audio_device.stop_all();
|
||||
}
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::KpMinus) {
|
||||
volume -= 0.1;
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::KpPlus) {
|
||||
volume += 0.1;
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Q) {
|
||||
using_queue_commands = !using_queue_commands;
|
||||
}
|
||||
|
||||
system.audio_queue.apply_to_device(&mut audio_device)?;
|
||||
for index in 0..NUM_CHANNELS {
|
||||
let channel = &audio_device[index];
|
||||
let mut status = &mut statuses[index];
|
||||
status.playing = channel.playing;
|
||||
status.position = channel.position;
|
||||
status.size = channel.data.len();
|
||||
}
|
||||
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::KpMinus) {
|
||||
volume -= 0.1;
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::KpPlus) {
|
||||
volume += 0.1;
|
||||
}
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Q) {
|
||||
using_queue_commands = !using_queue_commands;
|
||||
}
|
||||
drop(audio_device);
|
||||
|
||||
for index in 0..NUM_CHANNELS {
|
||||
let channel = &audio_device[index];
|
||||
let mut status = &mut statuses[index];
|
||||
status.playing = channel.playing;
|
||||
status.position = channel.position;
|
||||
status.size = channel.data.len();
|
||||
}
|
||||
system.video.clear(0);
|
||||
|
||||
drop(audio_device);
|
||||
system.video.print_string(&format!("Volume: {:2.2}", volume), 16, 16, FontRenderOpts::Color(10), &system.font);
|
||||
system.video.print_string(
|
||||
if using_queue_commands {
|
||||
"Queueing Commands"
|
||||
} else {
|
||||
"Direct Commands"
|
||||
},
|
||||
160, 16, FontRenderOpts::Color(9), &system.font,
|
||||
);
|
||||
|
||||
system.video.clear(0);
|
||||
system.video.print_string("Audio Channels", 16, 32, FontRenderOpts::Color(14), &system.font);
|
||||
|
||||
system.video.print_string(&format!("Volume: {:2.2}", volume), 16, 16, FontRenderOpts::Color(10), &system.font);
|
||||
system.video.print_string(
|
||||
if using_queue_commands {
|
||||
"Queueing Commands"
|
||||
} else {
|
||||
"Direct Commands"
|
||||
},
|
||||
160, 16, FontRenderOpts::Color(9), &system.font
|
||||
);
|
||||
let mut y = 48;
|
||||
for index in 0..NUM_CHANNELS {
|
||||
let status = &statuses[index];
|
||||
system.video.print_string(
|
||||
&format!(
|
||||
"channel {} - {} {}",
|
||||
index,
|
||||
if status.playing { "playing" } else { "not playing" },
|
||||
if status.playing { String::from(format!("{} / {}", status.position, status.size)) } else { String::new() }
|
||||
),
|
||||
16, y,
|
||||
FontRenderOpts::Color(15),
|
||||
&system.font,
|
||||
);
|
||||
y += 16;
|
||||
}
|
||||
|
||||
system.video.print_string("Audio Channels", 16, 32, FontRenderOpts::Color(14), &system.font);
|
||||
system.display()?;
|
||||
}
|
||||
|
||||
let mut y = 48;
|
||||
for index in 0..NUM_CHANNELS {
|
||||
let status = &statuses[index];
|
||||
system.video.print_string(
|
||||
&format!(
|
||||
"channel {} - {} {}",
|
||||
index,
|
||||
if status.playing { "playing" } else { "not playing" },
|
||||
if status.playing { String::from(format!("{} / {}", status.position, status.size)) } else { String::new() }
|
||||
),
|
||||
16, y,
|
||||
FontRenderOpts::Color(15),
|
||||
&system.font
|
||||
);
|
||||
y += 16;
|
||||
}
|
||||
|
||||
system.display()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -14,105 +14,105 @@ const BALL_WIDTH: u32 = 8;
|
|||
const BALL_HEIGHT: u32 = 8;
|
||||
|
||||
struct Ball {
|
||||
x: i32,
|
||||
y: i32,
|
||||
dir_x: i32,
|
||||
dir_y: i32,
|
||||
sprite: usize,
|
||||
x: i32,
|
||||
y: i32,
|
||||
dir_x: i32,
|
||||
dir_y: i32,
|
||||
sprite: usize,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut system = SystemBuilder::new()
|
||||
.window_title("Flying Balls!")
|
||||
.vsync(true)
|
||||
.build()?;
|
||||
let mut system = SystemBuilder::new()
|
||||
.window_title("Flying Balls!")
|
||||
.vsync(true)
|
||||
.build()?;
|
||||
|
||||
let font = BitmaskFont::new_vga_font()?;
|
||||
let font = BitmaskFont::new_vga_font()?;
|
||||
|
||||
let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
|
||||
system.palette = balls_palette.clone();
|
||||
let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
|
||||
system.palette = balls_palette.clone();
|
||||
|
||||
let mut sprites = Vec::<Bitmap>::new();
|
||||
let mut balls = Vec::<Ball>::new();
|
||||
let mut sprites = Vec::<Bitmap>::new();
|
||||
let mut balls = Vec::<Ball>::new();
|
||||
|
||||
for i in 0..NUM_BALL_SPRITES {
|
||||
let mut sprite = Bitmap::new(BALL_WIDTH, BALL_HEIGHT)?;
|
||||
sprite.blit_region(
|
||||
BlitMethod::Solid,
|
||||
&balls_bmp,
|
||||
&Rect::new(i as i32 * BALL_WIDTH as i32, 0, BALL_WIDTH, BALL_HEIGHT),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
sprites.push(sprite);
|
||||
}
|
||||
for i in 0..NUM_BALL_SPRITES {
|
||||
let mut sprite = Bitmap::new(BALL_WIDTH, BALL_HEIGHT)?;
|
||||
sprite.blit_region(
|
||||
BlitMethod::Solid,
|
||||
&balls_bmp,
|
||||
&Rect::new(i as i32 * BALL_WIDTH as i32, 0, BALL_WIDTH, BALL_HEIGHT),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
sprites.push(sprite);
|
||||
}
|
||||
|
||||
for _ in 0..NUM_BALLS {
|
||||
let speed = rnd_value(1, 3);
|
||||
let ball = Ball {
|
||||
x: rnd_value(0, SCREEN_WIDTH as i32 - 1),
|
||||
y: rnd_value(0, SCREEN_HEIGHT as i32 - 1),
|
||||
dir_x: if rnd_value(0, 1) == 0 { -speed } else { speed },
|
||||
dir_y: if rnd_value(0, 1) == 0 { -speed } else { speed },
|
||||
sprite: rnd_value(0, NUM_BALL_SPRITES - 1),
|
||||
};
|
||||
balls.push(ball);
|
||||
}
|
||||
for _ in 0..NUM_BALLS {
|
||||
let speed = rnd_value(1, 3);
|
||||
let ball = Ball {
|
||||
x: rnd_value(0, SCREEN_WIDTH as i32 - 1),
|
||||
y: rnd_value(0, SCREEN_HEIGHT as i32 - 1),
|
||||
dir_x: if rnd_value(0, 1) == 0 { -speed } else { speed },
|
||||
dir_y: if rnd_value(0, 1) == 0 { -speed } else { speed },
|
||||
sprite: rnd_value(0, NUM_BALL_SPRITES - 1),
|
||||
};
|
||||
balls.push(ball);
|
||||
}
|
||||
|
||||
while !system.do_events() {
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
break;
|
||||
}
|
||||
while !system.do_events() {
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
break;
|
||||
}
|
||||
|
||||
if system.input_devices.keyboard.is_key_up(Scancode::S) {
|
||||
for i in 0..NUM_BALLS {
|
||||
let ball = &mut balls[i];
|
||||
ball.x += ball.dir_x;
|
||||
ball.y += ball.dir_y;
|
||||
if system.input_devices.keyboard.is_key_up(Scancode::S) {
|
||||
for i in 0..NUM_BALLS {
|
||||
let ball = &mut balls[i];
|
||||
ball.x += ball.dir_x;
|
||||
ball.y += ball.dir_y;
|
||||
|
||||
if ball.dir_x < 0 {
|
||||
if ball.x <= 0 {
|
||||
ball.dir_x = -ball.dir_x;
|
||||
ball.x = 0;
|
||||
}
|
||||
} else {
|
||||
if ball.x >= (SCREEN_WIDTH - BALL_WIDTH) as i32 {
|
||||
ball.dir_x = -ball.dir_x;
|
||||
ball.x = (SCREEN_WIDTH - BALL_WIDTH) as i32;
|
||||
}
|
||||
}
|
||||
if ball.dir_x < 0 {
|
||||
if ball.x <= 0 {
|
||||
ball.dir_x = -ball.dir_x;
|
||||
ball.x = 0;
|
||||
}
|
||||
} else {
|
||||
if ball.x >= (SCREEN_WIDTH - BALL_WIDTH) as i32 {
|
||||
ball.dir_x = -ball.dir_x;
|
||||
ball.x = (SCREEN_WIDTH - BALL_WIDTH) as i32;
|
||||
}
|
||||
}
|
||||
|
||||
if ball.dir_y < 0 {
|
||||
if ball.y <= 0 {
|
||||
ball.dir_y = -ball.dir_y;
|
||||
ball.y = 0;
|
||||
}
|
||||
} else {
|
||||
if ball.y >= (SCREEN_HEIGHT - BALL_HEIGHT) as i32 {
|
||||
ball.dir_y = -ball.dir_y;
|
||||
ball.y = (SCREEN_HEIGHT - BALL_HEIGHT) as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ball.dir_y < 0 {
|
||||
if ball.y <= 0 {
|
||||
ball.dir_y = -ball.dir_y;
|
||||
ball.y = 0;
|
||||
}
|
||||
} else {
|
||||
if ball.y >= (SCREEN_HEIGHT - BALL_HEIGHT) as i32 {
|
||||
ball.dir_y = -ball.dir_y;
|
||||
ball.y = (SCREEN_HEIGHT - BALL_HEIGHT) as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
system.video.clear(2);
|
||||
system.video.clear(2);
|
||||
|
||||
system
|
||||
.video
|
||||
.print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &font);
|
||||
system
|
||||
.video
|
||||
.print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &font);
|
||||
|
||||
for i in 0..NUM_BALLS {
|
||||
system.video.blit(
|
||||
BlitMethod::Transparent(0),
|
||||
&sprites[balls[i].sprite],
|
||||
balls[i].x,
|
||||
balls[i].y,
|
||||
);
|
||||
}
|
||||
for i in 0..NUM_BALLS {
|
||||
system.video.blit(
|
||||
BlitMethod::Transparent(0),
|
||||
&sprites[balls[i].sprite],
|
||||
balls[i].x,
|
||||
balls[i].y,
|
||||
);
|
||||
}
|
||||
|
||||
system.display()?;
|
||||
}
|
||||
system.display()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -27,236 +27,236 @@ pub struct Particle;
|
|||
pub struct Color(u8);
|
||||
|
||||
pub struct LifeLeft {
|
||||
pub life: f32,
|
||||
pub initial: f32,
|
||||
pub life: f32,
|
||||
pub initial: f32,
|
||||
}
|
||||
|
||||
pub struct LeavesTrail {
|
||||
pub timer: f32,
|
||||
pub timer: f32,
|
||||
}
|
||||
|
||||
pub struct ColorByLifeTime(u8, u8, u8, u8, u8);
|
||||
|
||||
pub enum Event {
|
||||
CollideAgainstEdge(EntityId),
|
||||
Kill(EntityId),
|
||||
LeaveTrail(Vector2),
|
||||
CollideAgainstEdge(EntityId),
|
||||
Kill(EntityId),
|
||||
LeaveTrail(Vector2),
|
||||
}
|
||||
|
||||
fn new_basic_particle_entity(entities: &mut Entities, x: f32, y: f32, color: u8, lifetime: f32, angle: f32, speed: i32) {
|
||||
let id = entities.new_entity();
|
||||
entities.add_component(id, Particle);
|
||||
entities.add_component(id, Color(color));
|
||||
entities.add_component(id, LifeLeft { life: lifetime, initial: lifetime });
|
||||
entities.add_component(id, Position(Vector2::new(x, y)));
|
||||
entities.add_component(id, Velocity(Vector2::from_angle(angle) * speed as f32));
|
||||
let id = entities.new_entity();
|
||||
entities.add_component(id, Particle);
|
||||
entities.add_component(id, Color(color));
|
||||
entities.add_component(id, LifeLeft { life: lifetime, initial: lifetime });
|
||||
entities.add_component(id, Position(Vector2::new(x, y)));
|
||||
entities.add_component(id, Velocity(Vector2::from_angle(angle) * speed as f32));
|
||||
}
|
||||
|
||||
fn new_trail_particle_entity(entities: &mut Entities, x: f32, y: f32, lifetime: f32) {
|
||||
let id = entities.new_entity();
|
||||
entities.add_component(id, Particle);
|
||||
entities.add_component(id, ColorByLifeTime(33, 26, 21, 16, 10));
|
||||
entities.add_component(id, LifeLeft { life: lifetime, initial: lifetime });
|
||||
entities.add_component(id, Position(Vector2::new(x, y)));
|
||||
let id = entities.new_entity();
|
||||
entities.add_component(id, Particle);
|
||||
entities.add_component(id, ColorByLifeTime(33, 26, 21, 16, 10));
|
||||
entities.add_component(id, LifeLeft { life: lifetime, initial: lifetime });
|
||||
entities.add_component(id, Position(Vector2::new(x, y)));
|
||||
}
|
||||
|
||||
fn new_bounce_particles(entities: &mut Entities, x: f32, y: f32) {
|
||||
for direction in 0..6 {
|
||||
let angle = direction as f32 * (RADIANS_360 / 6.0);
|
||||
new_basic_particle_entity(
|
||||
entities,
|
||||
x,
|
||||
y,
|
||||
BOUNCE_PARTICLE_COLOR,
|
||||
BOUNCE_PARTICLE_LIFETIME,
|
||||
angle,
|
||||
BOUNCE_PARTICLE_SPEED
|
||||
);
|
||||
}
|
||||
for direction in 0..6 {
|
||||
let angle = direction as f32 * (RADIANS_360 / 6.0);
|
||||
new_basic_particle_entity(
|
||||
entities,
|
||||
x,
|
||||
y,
|
||||
BOUNCE_PARTICLE_COLOR,
|
||||
BOUNCE_PARTICLE_LIFETIME,
|
||||
angle,
|
||||
BOUNCE_PARTICLE_SPEED,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn new_ball_entity(entities: &mut Entities) {
|
||||
let id = entities.new_entity();
|
||||
let id = entities.new_entity();
|
||||
|
||||
let x: i32 = rnd_value(SCREEN_LEFT as i32 + 1, SCREEN_RIGHT as i32 - BALL_SIZE - 1);
|
||||
let y: i32 = rnd_value(SCREEN_TOP as i32 + 1, SCREEN_BOTTOM as i32 - BALL_SIZE - 1);
|
||||
let x: i32 = rnd_value(SCREEN_LEFT as i32 + 1, SCREEN_RIGHT as i32 - BALL_SIZE - 1);
|
||||
let y: i32 = rnd_value(SCREEN_TOP as i32 + 1, SCREEN_BOTTOM as i32 - BALL_SIZE - 1);
|
||||
|
||||
let speed = rnd_value(1, 3) * BALL_BASE_SPEED;
|
||||
let vx = if rnd_value(0, 1) == 0 { -speed } else { speed };
|
||||
let vy = if rnd_value(0, 1) == 0 { -speed } else { speed };
|
||||
let speed = rnd_value(1, 3) * BALL_BASE_SPEED;
|
||||
let vx = if rnd_value(0, 1) == 0 { -speed } else { speed };
|
||||
let vy = if rnd_value(0, 1) == 0 { -speed } else { speed };
|
||||
|
||||
let sprite_index = rnd_value(0, NUM_BALL_SPRITES - 1);
|
||||
let sprite_index = rnd_value(0, NUM_BALL_SPRITES - 1);
|
||||
|
||||
entities.add_component(id, Position(Vector2::new(x as f32, y as f32)));
|
||||
entities.add_component(id, Velocity(Vector2::new(vx as f32, vy as f32)));
|
||||
entities.add_component(id, SpriteIndex(sprite_index));
|
||||
entities.add_component(id, BouncesAgainstEdge);
|
||||
entities.add_component(id, LeavesTrail { timer: BALL_TRAIL_PARTICLE_INTERVAL });
|
||||
entities.add_component(id, Position(Vector2::new(x as f32, y as f32)));
|
||||
entities.add_component(id, Velocity(Vector2::new(vx as f32, vy as f32)));
|
||||
entities.add_component(id, SpriteIndex(sprite_index));
|
||||
entities.add_component(id, BouncesAgainstEdge);
|
||||
entities.add_component(id, LeavesTrail { timer: BALL_TRAIL_PARTICLE_INTERVAL });
|
||||
}
|
||||
|
||||
fn update_system_movement(context: &mut Context) {
|
||||
let mut positions = context.entities.components_mut::<Position>().unwrap();
|
||||
let velocities = context.entities.components::<Velocity>();
|
||||
let mut positions = context.entities.components_mut::<Position>().unwrap();
|
||||
let velocities = context.entities.components::<Velocity>();
|
||||
|
||||
for (entity, position) in positions.iter_mut() {
|
||||
if let Some(velocity) = velocities.get(&entity) {
|
||||
position.0 += velocity.0 * context.delta;
|
||||
}
|
||||
}
|
||||
for (entity, position) in positions.iter_mut() {
|
||||
if let Some(velocity) = velocities.get(&entity) {
|
||||
position.0 += velocity.0 * context.delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_system_collision(context: &mut Context) {
|
||||
let bounceables = context.entities.components::<BouncesAgainstEdge>().unwrap();
|
||||
let mut positions = context.entities.components_mut::<Position>();
|
||||
let mut velocities = context.entities.components_mut::<Velocity>();
|
||||
let bounceables = context.entities.components::<BouncesAgainstEdge>().unwrap();
|
||||
let mut positions = context.entities.components_mut::<Position>();
|
||||
let mut velocities = context.entities.components_mut::<Velocity>();
|
||||
|
||||
for (entity, _) in bounceables.iter() {
|
||||
let mut position = positions.get_mut(&entity).unwrap();
|
||||
let mut velocity = velocities.get_mut(&entity).unwrap();
|
||||
for (entity, _) in bounceables.iter() {
|
||||
let mut position = positions.get_mut(&entity).unwrap();
|
||||
let mut velocity = velocities.get_mut(&entity).unwrap();
|
||||
|
||||
let mut bounced = false;
|
||||
if position.0.x as i32 <= SCREEN_LEFT as i32 || position.0.x as i32 + BALL_SIZE >= SCREEN_RIGHT as i32 {
|
||||
position.0.x -= velocity.0.x * context.delta;
|
||||
velocity.0.x = -velocity.0.x;
|
||||
bounced = true;
|
||||
}
|
||||
if position.0.y as i32 <= SCREEN_TOP as i32 || position.0.y as i32 + BALL_SIZE >= SCREEN_BOTTOM as i32 {
|
||||
position.0.y -= velocity.0.y * context.delta;
|
||||
velocity.0.y = -velocity.0.y;
|
||||
bounced = true;
|
||||
}
|
||||
if bounced {
|
||||
context.event_publisher.queue(Event::CollideAgainstEdge(*entity));
|
||||
}
|
||||
}
|
||||
let mut bounced = false;
|
||||
if position.0.x as i32 <= SCREEN_LEFT as i32 || position.0.x as i32 + BALL_SIZE >= SCREEN_RIGHT as i32 {
|
||||
position.0.x -= velocity.0.x * context.delta;
|
||||
velocity.0.x = -velocity.0.x;
|
||||
bounced = true;
|
||||
}
|
||||
if position.0.y as i32 <= SCREEN_TOP as i32 || position.0.y as i32 + BALL_SIZE >= SCREEN_BOTTOM as i32 {
|
||||
position.0.y -= velocity.0.y * context.delta;
|
||||
velocity.0.y = -velocity.0.y;
|
||||
bounced = true;
|
||||
}
|
||||
if bounced {
|
||||
context.event_publisher.queue(Event::CollideAgainstEdge(*entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_system_lifetime(context: &mut Context) {
|
||||
let mut lifetimes = context.entities.components_mut::<LifeLeft>().unwrap();
|
||||
for (entity, lifetime) in lifetimes.iter_mut() {
|
||||
lifetime.life -= context.delta;
|
||||
if lifetime.life < 0.0 {
|
||||
context.event_publisher.queue(Event::Kill(*entity));
|
||||
}
|
||||
}
|
||||
let mut lifetimes = context.entities.components_mut::<LifeLeft>().unwrap();
|
||||
for (entity, lifetime) in lifetimes.iter_mut() {
|
||||
lifetime.life -= context.delta;
|
||||
if lifetime.life < 0.0 {
|
||||
context.event_publisher.queue(Event::Kill(*entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_system_leave_particle_trail(context: &mut Context) {
|
||||
let mut leaves_trails = context.entities.components_mut::<LeavesTrail>().unwrap();
|
||||
let positions = context.entities.components::<Position>();
|
||||
let mut leaves_trails = context.entities.components_mut::<LeavesTrail>().unwrap();
|
||||
let positions = context.entities.components::<Position>();
|
||||
|
||||
for (entity, leaves_trail) in leaves_trails.iter_mut() {
|
||||
leaves_trail.timer -= context.delta;
|
||||
for (entity, leaves_trail) in leaves_trails.iter_mut() {
|
||||
leaves_trail.timer -= context.delta;
|
||||
|
||||
if leaves_trail.timer <= 0.0 {
|
||||
leaves_trail.timer = BALL_TRAIL_PARTICLE_INTERVAL;
|
||||
let position = positions.get(&entity).unwrap();
|
||||
let mut trail_position = position.0;
|
||||
trail_position.x += (BALL_SIZE / 2) as f32;
|
||||
trail_position.y += (BALL_SIZE / 2) as f32;
|
||||
context.event_publisher.queue(Event::LeaveTrail(trail_position));
|
||||
}
|
||||
}
|
||||
if leaves_trail.timer <= 0.0 {
|
||||
leaves_trail.timer = BALL_TRAIL_PARTICLE_INTERVAL;
|
||||
let position = positions.get(&entity).unwrap();
|
||||
let mut trail_position = position.0;
|
||||
trail_position.x += (BALL_SIZE / 2) as f32;
|
||||
trail_position.y += (BALL_SIZE / 2) as f32;
|
||||
context.event_publisher.queue(Event::LeaveTrail(trail_position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_system_sprites(context: &mut Context) {
|
||||
let sprite_indices = context.entities.components::<SpriteIndex>().unwrap();
|
||||
let positions = context.entities.components::<Position>();
|
||||
let sprite_indices = context.entities.components::<SpriteIndex>().unwrap();
|
||||
let positions = context.entities.components::<Position>();
|
||||
|
||||
for (entity, sprite_index) in sprite_indices.iter() {
|
||||
let position = positions.get(&entity).unwrap();
|
||||
context.system.video.blit(
|
||||
BlitMethod::Transparent(0),
|
||||
&context.sprites[sprite_index.0],
|
||||
position.0.x as i32,
|
||||
position.0.y as i32
|
||||
);
|
||||
}
|
||||
for (entity, sprite_index) in sprite_indices.iter() {
|
||||
let position = positions.get(&entity).unwrap();
|
||||
context.system.video.blit(
|
||||
BlitMethod::Transparent(0),
|
||||
&context.sprites[sprite_index.0],
|
||||
position.0.x as i32,
|
||||
position.0.y as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_system_particles(context: &mut Context) {
|
||||
let particles = context.entities.components::<Particle>().unwrap();
|
||||
let positions = context.entities.components::<Position>();
|
||||
let colors = context.entities.components::<Color>();
|
||||
let colors_by_lifetime = context.entities.components::<ColorByLifeTime>();
|
||||
let lifetimes = context.entities.components::<LifeLeft>();
|
||||
let particles = context.entities.components::<Particle>().unwrap();
|
||||
let positions = context.entities.components::<Position>();
|
||||
let colors = context.entities.components::<Color>();
|
||||
let colors_by_lifetime = context.entities.components::<ColorByLifeTime>();
|
||||
let lifetimes = context.entities.components::<LifeLeft>();
|
||||
|
||||
for (entity, _) in particles.iter() {
|
||||
let position = positions.get(&entity).unwrap();
|
||||
for (entity, _) in particles.iter() {
|
||||
let position = positions.get(&entity).unwrap();
|
||||
|
||||
let pixel_color;
|
||||
if let Some(color) = colors.get(&entity) {
|
||||
pixel_color = Some(color.0);
|
||||
} else if let Some(color_by_lifetime) = colors_by_lifetime.get(&entity) {
|
||||
let lifetime = lifetimes.get(&entity).unwrap();
|
||||
let percent_life = lifetime.life / lifetime.initial;
|
||||
pixel_color = Some(if percent_life >= 0.8 {
|
||||
color_by_lifetime.0
|
||||
} else if percent_life >= 0.6 {
|
||||
color_by_lifetime.1
|
||||
} else if percent_life >= 0.4 {
|
||||
color_by_lifetime.2
|
||||
} else if percent_life >= 0.2 {
|
||||
color_by_lifetime.3
|
||||
} else {
|
||||
color_by_lifetime.4
|
||||
});
|
||||
} else {
|
||||
pixel_color = None;
|
||||
}
|
||||
let pixel_color;
|
||||
if let Some(color) = colors.get(&entity) {
|
||||
pixel_color = Some(color.0);
|
||||
} else if let Some(color_by_lifetime) = colors_by_lifetime.get(&entity) {
|
||||
let lifetime = lifetimes.get(&entity).unwrap();
|
||||
let percent_life = lifetime.life / lifetime.initial;
|
||||
pixel_color = Some(if percent_life >= 0.8 {
|
||||
color_by_lifetime.0
|
||||
} else if percent_life >= 0.6 {
|
||||
color_by_lifetime.1
|
||||
} else if percent_life >= 0.4 {
|
||||
color_by_lifetime.2
|
||||
} else if percent_life >= 0.2 {
|
||||
color_by_lifetime.3
|
||||
} else {
|
||||
color_by_lifetime.4
|
||||
});
|
||||
} else {
|
||||
pixel_color = None;
|
||||
}
|
||||
|
||||
if let Some(color) = pixel_color {
|
||||
context.system.video.set_pixel(position.0.x as i32, position.0.y as i32, color);
|
||||
}
|
||||
}
|
||||
if let Some(color) = pixel_color {
|
||||
context.system.video.set_pixel(position.0.x as i32, position.0.y as i32, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn event_handler(event: &Event, context: &mut Context) -> bool {
|
||||
match event {
|
||||
Event::Kill(entity) => {
|
||||
context.entities.remove_entity(*entity);
|
||||
},
|
||||
Event::CollideAgainstEdge(entity) => {
|
||||
let positions = context.entities.components::<Position>();
|
||||
let position = positions.get(entity).unwrap();
|
||||
let x = position.0.x + (BALL_SIZE / 2) as f32;
|
||||
let y = position.0.y + (BALL_SIZE / 2) as f32;
|
||||
drop(positions);
|
||||
new_bounce_particles(&mut context.entities, x, y);
|
||||
},
|
||||
Event::LeaveTrail(position) => {
|
||||
new_trail_particle_entity(&mut context.entities, position.x, position.y, TRAIL_PARTICLE_LIFETIME);
|
||||
}
|
||||
}
|
||||
false
|
||||
match event {
|
||||
Event::Kill(entity) => {
|
||||
context.entities.remove_entity(*entity);
|
||||
}
|
||||
Event::CollideAgainstEdge(entity) => {
|
||||
let positions = context.entities.components::<Position>();
|
||||
let position = positions.get(entity).unwrap();
|
||||
let x = position.0.x + (BALL_SIZE / 2) as f32;
|
||||
let y = position.0.y + (BALL_SIZE / 2) as f32;
|
||||
drop(positions);
|
||||
new_bounce_particles(&mut context.entities, x, y);
|
||||
}
|
||||
Event::LeaveTrail(position) => {
|
||||
new_trail_particle_entity(&mut context.entities, position.x, position.y, TRAIL_PARTICLE_LIFETIME);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn init_entities(entities: &mut Entities) {
|
||||
entities.init_components::<Position>();
|
||||
entities.init_components::<Velocity>();
|
||||
entities.init_components::<SpriteIndex>();
|
||||
entities.init_components::<BouncesAgainstEdge>();
|
||||
entities.init_components::<Particle>();
|
||||
entities.init_components::<Color>();
|
||||
entities.init_components::<LifeLeft>();
|
||||
entities.init_components::<LeavesTrail>();
|
||||
entities.init_components::<ColorByLifeTime>();
|
||||
entities.init_components::<Position>();
|
||||
entities.init_components::<Velocity>();
|
||||
entities.init_components::<SpriteIndex>();
|
||||
entities.init_components::<BouncesAgainstEdge>();
|
||||
entities.init_components::<Particle>();
|
||||
entities.init_components::<Color>();
|
||||
entities.init_components::<LifeLeft>();
|
||||
entities.init_components::<LeavesTrail>();
|
||||
entities.init_components::<ColorByLifeTime>();
|
||||
|
||||
entities.remove_all_entities();
|
||||
for _ in 0..NUM_BALLS {
|
||||
new_ball_entity(entities);
|
||||
}
|
||||
entities.remove_all_entities();
|
||||
for _ in 0..NUM_BALLS {
|
||||
new_ball_entity(entities);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_component_system(cs: &mut ComponentSystems<Context, Context>) {
|
||||
cs.add_update_system(update_system_movement);
|
||||
cs.add_update_system(update_system_collision);
|
||||
cs.add_update_system(update_system_lifetime);
|
||||
cs.add_update_system(update_system_leave_particle_trail);
|
||||
cs.add_render_system(render_system_particles);
|
||||
cs.add_render_system(render_system_sprites);
|
||||
cs.add_update_system(update_system_movement);
|
||||
cs.add_update_system(update_system_collision);
|
||||
cs.add_update_system(update_system_lifetime);
|
||||
cs.add_update_system(update_system_leave_particle_trail);
|
||||
cs.add_render_system(render_system_particles);
|
||||
cs.add_render_system(render_system_sprites);
|
||||
}
|
||||
|
||||
pub fn init_event_listeners(event_listeners: &mut EventListeners<Event, Context>) {
|
||||
event_listeners.add(event_handler);
|
||||
event_listeners.add(event_handler);
|
||||
}
|
||||
|
|
|
@ -12,27 +12,27 @@ mod entities;
|
|||
mod states;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let system = SystemBuilder::new().window_title("Flying Balls").vsync(true).build()?;
|
||||
let mut game = Game::new(system)?;
|
||||
let mut states = States::new();
|
||||
states.push(SimulationState)?;
|
||||
let system = SystemBuilder::new().window_title("Flying Balls").vsync(true).build()?;
|
||||
let mut game = Game::new(system)?;
|
||||
let mut states = States::new();
|
||||
states.push(SimulationState)?;
|
||||
|
||||
let tick_frequency = game.context.system.tick_frequency();
|
||||
let mut last_ticks = game.context.system.ticks();
|
||||
let tick_frequency = game.context.system.tick_frequency();
|
||||
let mut last_ticks = game.context.system.ticks();
|
||||
|
||||
while !game.context.system.do_events() && !states.is_empty() {
|
||||
let ticks = game.context.system.ticks();
|
||||
let elapsed = ticks - last_ticks;
|
||||
last_ticks = ticks;
|
||||
game.context.delta = (elapsed as f64 / tick_frequency as f64) as f32;
|
||||
while !game.context.system.do_events() && !states.is_empty() {
|
||||
let ticks = game.context.system.ticks();
|
||||
let elapsed = ticks - last_ticks;
|
||||
last_ticks = ticks;
|
||||
game.context.delta = (elapsed as f64 / tick_frequency as f64) as f32;
|
||||
|
||||
states.update(&mut game)?;
|
||||
states.update(&mut game)?;
|
||||
|
||||
game.context.system.video.clear(0);
|
||||
states.render(&mut game);
|
||||
game.context.system.video.clear(0);
|
||||
states.render(&mut game);
|
||||
|
||||
game.context.system.display()?;
|
||||
}
|
||||
game.context.system.display()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -12,97 +12,97 @@ pub const NUM_BALLS: usize = 32;
|
|||
pub const NUM_BALL_SPRITES: usize = 16;
|
||||
|
||||
pub struct Context {
|
||||
pub delta: f32,
|
||||
pub system: System,
|
||||
pub font: BitmaskFont,
|
||||
pub sprites: Vec<Bitmap>,
|
||||
pub entities: Entities,
|
||||
pub event_publisher: EventPublisher<Event>,
|
||||
pub delta: f32,
|
||||
pub system: System,
|
||||
pub font: BitmaskFont,
|
||||
pub sprites: Vec<Bitmap>,
|
||||
pub entities: Entities,
|
||||
pub event_publisher: EventPublisher<Event>,
|
||||
}
|
||||
|
||||
pub struct Game {
|
||||
pub context: Context,
|
||||
pub component_systems: ComponentSystems<Context, Context>,
|
||||
pub event_listeners: EventListeners<Event, Context>,
|
||||
pub context: Context,
|
||||
pub component_systems: ComponentSystems<Context, Context>,
|
||||
pub event_listeners: EventListeners<Event, Context>,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new(mut system: System) -> Result<Self> {
|
||||
let font = BitmaskFont::new_vga_font()?;
|
||||
pub fn new(mut system: System) -> Result<Self> {
|
||||
let font = BitmaskFont::new_vga_font()?;
|
||||
|
||||
let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
|
||||
system.palette = balls_palette.clone();
|
||||
let (balls_bmp, balls_palette) = Bitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?;
|
||||
system.palette = balls_palette.clone();
|
||||
|
||||
let mut sprites = Vec::new();
|
||||
for i in 0..NUM_BALL_SPRITES {
|
||||
let mut sprite = Bitmap::new(BALL_SIZE as u32, BALL_SIZE as u32)?;
|
||||
sprite.blit_region(
|
||||
BlitMethod::Solid,
|
||||
&balls_bmp,
|
||||
&Rect::new(i as i32 * BALL_SIZE as i32, 0, BALL_SIZE as u32, BALL_SIZE as u32),
|
||||
0,
|
||||
0
|
||||
);
|
||||
sprites.push(sprite);
|
||||
}
|
||||
let mut sprites = Vec::new();
|
||||
for i in 0..NUM_BALL_SPRITES {
|
||||
let mut sprite = Bitmap::new(BALL_SIZE as u32, BALL_SIZE as u32)?;
|
||||
sprite.blit_region(
|
||||
BlitMethod::Solid,
|
||||
&balls_bmp,
|
||||
&Rect::new(i as i32 * BALL_SIZE as i32, 0, BALL_SIZE as u32, BALL_SIZE as u32),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
sprites.push(sprite);
|
||||
}
|
||||
|
||||
let entities = Entities::new();
|
||||
let mut component_systems = ComponentSystems::new();
|
||||
let event_publisher = EventPublisher::new();
|
||||
let mut event_listeners = EventListeners::new();
|
||||
let entities = Entities::new();
|
||||
let mut component_systems = ComponentSystems::new();
|
||||
let event_publisher = EventPublisher::new();
|
||||
let mut event_listeners = EventListeners::new();
|
||||
|
||||
init_component_system(&mut component_systems);
|
||||
init_event_listeners(&mut event_listeners);
|
||||
init_component_system(&mut component_systems);
|
||||
init_event_listeners(&mut event_listeners);
|
||||
|
||||
Ok(Game {
|
||||
context: Context {
|
||||
delta: 0.0,
|
||||
system,
|
||||
font,
|
||||
sprites,
|
||||
entities,
|
||||
event_publisher
|
||||
},
|
||||
component_systems,
|
||||
event_listeners
|
||||
})
|
||||
}
|
||||
Ok(Game {
|
||||
context: Context {
|
||||
delta: 0.0,
|
||||
system,
|
||||
font,
|
||||
sprites,
|
||||
entities,
|
||||
event_publisher,
|
||||
},
|
||||
component_systems,
|
||||
event_listeners,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn do_events(&mut self) {
|
||||
self.event_listeners.take_queue_from(&mut self.context.event_publisher);
|
||||
self.event_listeners.dispatch_queue(&mut self.context);
|
||||
}
|
||||
pub fn do_events(&mut self) {
|
||||
self.event_listeners.take_queue_from(&mut self.context.event_publisher);
|
||||
self.event_listeners.dispatch_queue(&mut self.context);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SimulationState;
|
||||
|
||||
impl AppState<Game> for SimulationState {
|
||||
fn update(&mut self, _state: State, context: &mut Game) -> Option<StateChange<Game>> {
|
||||
if context.context.system.input_devices.keyboard.is_key_up(Scancode::S) {
|
||||
context.do_events();
|
||||
context.component_systems.update(&mut context.context);
|
||||
}
|
||||
fn update(&mut self, _state: State, context: &mut Game) -> Option<StateChange<Game>> {
|
||||
if context.context.system.input_devices.keyboard.is_key_up(Scancode::S) {
|
||||
context.do_events();
|
||||
context.component_systems.update(&mut context.context);
|
||||
}
|
||||
|
||||
if context.context.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
return Some(StateChange::Pop(1));
|
||||
}
|
||||
if context.context.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
return Some(StateChange::Pop(1));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn render(&mut self, _state: State, context: &mut Game) {
|
||||
context.context.system.video.clear(2);
|
||||
context.component_systems.render(&mut context.context);
|
||||
context.context.system.video.print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &context.context.font);
|
||||
}
|
||||
fn render(&mut self, _state: State, context: &mut Game) {
|
||||
context.context.system.video.clear(2);
|
||||
context.component_systems.render(&mut context.context);
|
||||
context.context.system.video.print_string("hello, world!", 10, 10, FontRenderOpts::Color(15), &context.context.font);
|
||||
}
|
||||
|
||||
fn transition(&mut self, _state: State, _context: &mut Game) -> bool {
|
||||
true
|
||||
}
|
||||
fn transition(&mut self, _state: State, _context: &mut Game) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn state_change(&mut self, new_state: State, _old_state: State, context: &mut Game) {
|
||||
if new_state == State::Pending {
|
||||
init_entities(&mut context.context.entities);
|
||||
}
|
||||
}
|
||||
fn state_change(&mut self, new_state: State, _old_state: State, context: &mut Game) {
|
||||
if new_state == State::Pending {
|
||||
init_entities(&mut context.context.entities);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,88 +6,88 @@ use crate::entities::*;
|
|||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Event {
|
||||
TurnAndMove(EntityId, Direction),
|
||||
MoveForward(EntityId),
|
||||
Remove(EntityId),
|
||||
RemoveAttachment(EntityId),
|
||||
AnimationFinished(EntityId),
|
||||
Spawn(EntityId),
|
||||
SpawnSlimeRandomly,
|
||||
SetActivity(EntityId, EntityActivity),
|
||||
Attack(EntityId),
|
||||
Hit(EntityId, EntityId, i32, Vector2),
|
||||
Kill(EntityId),
|
||||
Pickup(EntityId, EntityId),
|
||||
TurnAndMove(EntityId, Direction),
|
||||
MoveForward(EntityId),
|
||||
Remove(EntityId),
|
||||
RemoveAttachment(EntityId),
|
||||
AnimationFinished(EntityId),
|
||||
Spawn(EntityId),
|
||||
SpawnSlimeRandomly,
|
||||
SetActivity(EntityId, EntityActivity),
|
||||
Attack(EntityId),
|
||||
Hit(EntityId, EntityId, i32, Vector2),
|
||||
Kill(EntityId),
|
||||
Pickup(EntityId, EntityId),
|
||||
}
|
||||
|
||||
fn event_handler(event: &Event, context: &mut Core) -> bool {
|
||||
match event {
|
||||
Event::Remove(entity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
remove_entity(&mut context.entities, *entity);
|
||||
}
|
||||
},
|
||||
Event::RemoveAttachment(entity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
remove_entity_attachment(&mut context.entities, *entity);
|
||||
}
|
||||
}
|
||||
Event::TurnAndMove(entity, direction) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
turn_and_move_entity(context, *entity, *direction);
|
||||
}
|
||||
},
|
||||
Event::MoveForward(entity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
move_entity_forward(context, *entity);
|
||||
}
|
||||
},
|
||||
Event::Spawn(entity) => {
|
||||
// todo
|
||||
},
|
||||
Event::AnimationFinished(entity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
// if the entity's 'attack' animation just finished, move them back to 'idle'
|
||||
let activities = context.entities.components::<Activity>();
|
||||
if let Some(activity) = activities.get(entity) {
|
||||
if activity.0 == EntityActivity::Attacking {
|
||||
drop(activities);
|
||||
stop_attack(context, *entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::SpawnSlimeRandomly => {
|
||||
spawn_slime_randomly(context);
|
||||
},
|
||||
Event::SetActivity(entity, activity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
set_entity_activity(&mut context.entities, *entity, *activity);
|
||||
}
|
||||
},
|
||||
Event::Attack(entity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
attack(context, *entity);
|
||||
}
|
||||
},
|
||||
Event::Hit(target, source, damage, damage_position) => {
|
||||
if context.entities.has_entity(*target) {
|
||||
hit_entity(context, *target, *source, *damage, *damage_position);
|
||||
}
|
||||
},
|
||||
Event::Kill(entity) => {
|
||||
kill_entity(context, *entity);
|
||||
},
|
||||
Event::Pickup(picked_up_by, picked_up) => {
|
||||
if context.entities.has_entity(*picked_up_by) && context.entities.has_entity(*picked_up) {
|
||||
pickup(context, *picked_up_by, *picked_up);
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
match event {
|
||||
Event::Remove(entity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
remove_entity(&mut context.entities, *entity);
|
||||
}
|
||||
}
|
||||
Event::RemoveAttachment(entity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
remove_entity_attachment(&mut context.entities, *entity);
|
||||
}
|
||||
}
|
||||
Event::TurnAndMove(entity, direction) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
turn_and_move_entity(context, *entity, *direction);
|
||||
}
|
||||
}
|
||||
Event::MoveForward(entity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
move_entity_forward(context, *entity);
|
||||
}
|
||||
}
|
||||
Event::Spawn(entity) => {
|
||||
// todo
|
||||
}
|
||||
Event::AnimationFinished(entity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
// if the entity's 'attack' animation just finished, move them back to 'idle'
|
||||
let activities = context.entities.components::<Activity>();
|
||||
if let Some(activity) = activities.get(entity) {
|
||||
if activity.0 == EntityActivity::Attacking {
|
||||
drop(activities);
|
||||
stop_attack(context, *entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::SpawnSlimeRandomly => {
|
||||
spawn_slime_randomly(context);
|
||||
}
|
||||
Event::SetActivity(entity, activity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
set_entity_activity(&mut context.entities, *entity, *activity);
|
||||
}
|
||||
}
|
||||
Event::Attack(entity) => {
|
||||
if context.entities.has_entity(*entity) {
|
||||
attack(context, *entity);
|
||||
}
|
||||
}
|
||||
Event::Hit(target, source, damage, damage_position) => {
|
||||
if context.entities.has_entity(*target) {
|
||||
hit_entity(context, *target, *source, *damage, *damage_position);
|
||||
}
|
||||
}
|
||||
Event::Kill(entity) => {
|
||||
kill_entity(context, *entity);
|
||||
}
|
||||
Event::Pickup(picked_up_by, picked_up) => {
|
||||
if context.entities.has_entity(*picked_up_by) && context.entities.has_entity(*picked_up) {
|
||||
pickup(context, *picked_up_by, *picked_up);
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn init_events(event_listener: &mut EventListeners<Event, Core>) {
|
||||
event_listener.clear();
|
||||
event_listener.add(event_handler);
|
||||
event_listener.clear();
|
||||
event_listener.add(event_handler);
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -27,176 +27,176 @@ pub const TILE_WIDTH: u32 = 16;
|
|||
pub const TILE_HEIGHT: u32 = 16;
|
||||
|
||||
pub struct Core {
|
||||
pub delta: f32,
|
||||
pub system: System,
|
||||
pub font: BitmaskFont,
|
||||
pub entities: Entities,
|
||||
pub event_publisher: EventPublisher<Event>,
|
||||
pub palette: Palette,
|
||||
pub fade_out_palette: Palette,
|
||||
pub tiles: Rc<BitmapAtlas>,
|
||||
pub hero_male: Rc<BitmapAtlas>,
|
||||
pub hero_female: Rc<BitmapAtlas>,
|
||||
pub green_slime: Rc<BitmapAtlas>,
|
||||
pub blue_slime: Rc<BitmapAtlas>,
|
||||
pub orange_slime: Rc<BitmapAtlas>,
|
||||
pub fist: Rc<BitmapAtlas>,
|
||||
pub sword: Rc<BitmapAtlas>,
|
||||
pub particles: Rc<BitmapAtlas>,
|
||||
pub items: Rc<BitmapAtlas>,
|
||||
pub ui: Rc<BitmapAtlas>,
|
||||
pub tilemap: TileMap,
|
||||
pub slime_activity_states: Rc<HashMap<EntityActivity, Rc<AnimationDef>>>,
|
||||
pub hero_activity_states: Rc<HashMap<EntityActivity, Rc<AnimationDef>>>,
|
||||
pub poof1_animation_def: Rc<AnimationDef>,
|
||||
pub poof2_animation_def: Rc<AnimationDef>,
|
||||
pub sparkles_animation_def: Rc<AnimationDef>,
|
||||
pub sprite_render_list: Vec<(EntityId, Vector2, BlitMethod)>,
|
||||
pub delta: f32,
|
||||
pub system: System,
|
||||
pub font: BitmaskFont,
|
||||
pub entities: Entities,
|
||||
pub event_publisher: EventPublisher<Event>,
|
||||
pub palette: Palette,
|
||||
pub fade_out_palette: Palette,
|
||||
pub tiles: Rc<BitmapAtlas>,
|
||||
pub hero_male: Rc<BitmapAtlas>,
|
||||
pub hero_female: Rc<BitmapAtlas>,
|
||||
pub green_slime: Rc<BitmapAtlas>,
|
||||
pub blue_slime: Rc<BitmapAtlas>,
|
||||
pub orange_slime: Rc<BitmapAtlas>,
|
||||
pub fist: Rc<BitmapAtlas>,
|
||||
pub sword: Rc<BitmapAtlas>,
|
||||
pub particles: Rc<BitmapAtlas>,
|
||||
pub items: Rc<BitmapAtlas>,
|
||||
pub ui: Rc<BitmapAtlas>,
|
||||
pub tilemap: TileMap,
|
||||
pub slime_activity_states: Rc<HashMap<EntityActivity, Rc<AnimationDef>>>,
|
||||
pub hero_activity_states: Rc<HashMap<EntityActivity, Rc<AnimationDef>>>,
|
||||
pub poof1_animation_def: Rc<AnimationDef>,
|
||||
pub poof2_animation_def: Rc<AnimationDef>,
|
||||
pub sparkles_animation_def: Rc<AnimationDef>,
|
||||
pub sprite_render_list: Vec<(EntityId, Vector2, BlitMethod)>,
|
||||
}
|
||||
|
||||
impl CoreState for Core {
|
||||
fn system(&self) -> &System {
|
||||
&self.system
|
||||
}
|
||||
fn system(&self) -> &System {
|
||||
&self.system
|
||||
}
|
||||
|
||||
fn system_mut(&mut self) -> &mut System {
|
||||
&mut self.system
|
||||
}
|
||||
fn system_mut(&mut self) -> &mut System {
|
||||
&mut self.system
|
||||
}
|
||||
|
||||
fn delta(&self) -> f32 {
|
||||
self.delta
|
||||
}
|
||||
fn delta(&self) -> f32 {
|
||||
self.delta
|
||||
}
|
||||
|
||||
fn set_delta(&mut self, delta: f32) {
|
||||
self.delta = delta;
|
||||
}
|
||||
fn set_delta(&mut self, delta: f32) {
|
||||
self.delta = delta;
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreStateWithEvents<Event> for Core {
|
||||
fn event_publisher(&mut self) -> &mut EventPublisher<Event> {
|
||||
&mut self.event_publisher
|
||||
}
|
||||
fn event_publisher(&mut self) -> &mut EventPublisher<Event> {
|
||||
&mut self.event_publisher
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Support {
|
||||
pub component_systems: ComponentSystems<Core, Core>,
|
||||
pub event_listeners: EventListeners<Event, Core>,
|
||||
pub component_systems: ComponentSystems<Core, Core>,
|
||||
pub event_listeners: EventListeners<Event, Core>,
|
||||
}
|
||||
|
||||
impl SupportSystems for Support {}
|
||||
|
||||
impl SupportSystemsWithEvents<Event> for Support {
|
||||
type ContextType = Core;
|
||||
type ContextType = Core;
|
||||
|
||||
fn event_listeners(&mut self) -> &mut EventListeners<Event, Self::ContextType> {
|
||||
&mut self.event_listeners
|
||||
}
|
||||
fn event_listeners(&mut self) -> &mut EventListeners<Event, Self::ContextType> {
|
||||
&mut self.event_listeners
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Game {
|
||||
pub core: Core,
|
||||
pub support: Support,
|
||||
pub core: Core,
|
||||
pub support: Support,
|
||||
}
|
||||
|
||||
impl AppContext for Game {
|
||||
type CoreType = Core;
|
||||
type SupportType = Support;
|
||||
type CoreType = Core;
|
||||
type SupportType = Support;
|
||||
|
||||
fn core(&mut self) -> &mut Self::CoreType {
|
||||
&mut self.core
|
||||
}
|
||||
fn core(&mut self) -> &mut Self::CoreType {
|
||||
&mut self.core
|
||||
}
|
||||
|
||||
fn support(&mut self) -> &mut Self::SupportType {
|
||||
&mut self.support
|
||||
}
|
||||
fn support(&mut self) -> &mut Self::SupportType {
|
||||
&mut self.support
|
||||
}
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new(mut system: System) -> Result<Self> {
|
||||
let palette = load_palette(Path::new("./assets/db16.pal"))?;
|
||||
system.palette = palette.clone();
|
||||
pub fn new(mut system: System) -> Result<Self> {
|
||||
let palette = load_palette(Path::new("./assets/db16.pal"))?;
|
||||
system.palette = palette.clone();
|
||||
|
||||
let font = load_font(Path::new("./assets/dp.fnt"))?;
|
||||
let font = load_font(Path::new("./assets/dp.fnt"))?;
|
||||
|
||||
let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?);
|
||||
let hero_male = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_male.pcx"))?);
|
||||
let hero_female = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_female.pcx"))?);
|
||||
let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?);
|
||||
let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?);
|
||||
let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?);
|
||||
let fist = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/fist.pcx"))?);
|
||||
let sword = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/sword.pcx"))?);
|
||||
let particles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/particles.pcx"))?);
|
||||
let items = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/items.pcx"))?);
|
||||
let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?);
|
||||
let hero_male = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_male.pcx"))?);
|
||||
let hero_female = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_female.pcx"))?);
|
||||
let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?);
|
||||
let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?);
|
||||
let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?);
|
||||
let fist = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/fist.pcx"))?);
|
||||
let sword = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/sword.pcx"))?);
|
||||
let particles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/particles.pcx"))?);
|
||||
let items = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/items.pcx"))?);
|
||||
|
||||
let mut ui = load_bitmap_atlas(Path::new("./assets/ui.pcx"))?;
|
||||
ui.add(Rect::new(0, 0, 16, 16))?;
|
||||
ui.add(Rect::new(16, 0, 16, 16))?;
|
||||
for i in 0..8 {
|
||||
ui.add(Rect::new(i * 8, 16, 8, 8))?;
|
||||
}
|
||||
let mut ui = load_bitmap_atlas(Path::new("./assets/ui.pcx"))?;
|
||||
ui.add(Rect::new(0, 0, 16, 16))?;
|
||||
ui.add(Rect::new(16, 0, 16, 16))?;
|
||||
for i in 0..8 {
|
||||
ui.add(Rect::new(i * 8, 16, 8, 8))?;
|
||||
}
|
||||
|
||||
let tilemap = TileMap::load_from(Path::new("./assets/title_screen.map.json"))?;
|
||||
let tilemap = TileMap::load_from(Path::new("./assets/title_screen.map.json"))?;
|
||||
|
||||
let entities = Entities::new();
|
||||
let component_systems = ComponentSystems::new();
|
||||
let event_publisher = EventPublisher::new();
|
||||
let event_listeners = EventListeners::new();
|
||||
let entities = Entities::new();
|
||||
let component_systems = ComponentSystems::new();
|
||||
let event_publisher = EventPublisher::new();
|
||||
let event_listeners = EventListeners::new();
|
||||
|
||||
let slime_activity_states = HashMap::from([
|
||||
(EntityActivity::Idle, Rc::new(AnimationDef::new(&[1, 2], true, 1.0, Some(3)))),
|
||||
(EntityActivity::Walking, Rc::new(AnimationDef::new(&[1, 0, 2, 0], true, 0.25, Some(3)))),
|
||||
(EntityActivity::Attacking, Rc::new(AnimationDef::new(&[0], false, 0.3, Some(3)))),
|
||||
(EntityActivity::Dead, Rc::new(AnimationDef::new(&[12], false, 1.0, None))),
|
||||
]);
|
||||
let hero_activity_states = HashMap::from([
|
||||
(EntityActivity::Idle, Rc::new(AnimationDef::new(&[0], true, 0.5, Some(4)))),
|
||||
(EntityActivity::Walking, Rc::new(AnimationDef::new(&[0, 1, 0, 2], true, 0.15, Some(4)))),
|
||||
(EntityActivity::Attacking, Rc::new(AnimationDef::new(&[3], false, 0.3, Some(4)))),
|
||||
(EntityActivity::Dead, Rc::new(AnimationDef::new(&[16], false, 1.0, None))),
|
||||
]);
|
||||
let poof1_animation_def = Rc::new(AnimationDef::new(&[0, 1, 2], false, 0.15, None));
|
||||
let poof2_animation_def = Rc::new(AnimationDef::new(&[3, 4, 5], false, 0.15, None));
|
||||
let sparkles_animation_def = Rc::new(AnimationDef::new(&[6, 7, 8, 9], false, 0.1, None));
|
||||
let slime_activity_states = HashMap::from([
|
||||
(EntityActivity::Idle, Rc::new(AnimationDef::new(&[1, 2], true, 1.0, Some(3)))),
|
||||
(EntityActivity::Walking, Rc::new(AnimationDef::new(&[1, 0, 2, 0], true, 0.25, Some(3)))),
|
||||
(EntityActivity::Attacking, Rc::new(AnimationDef::new(&[0], false, 0.3, Some(3)))),
|
||||
(EntityActivity::Dead, Rc::new(AnimationDef::new(&[12], false, 1.0, None))),
|
||||
]);
|
||||
let hero_activity_states = HashMap::from([
|
||||
(EntityActivity::Idle, Rc::new(AnimationDef::new(&[0], true, 0.5, Some(4)))),
|
||||
(EntityActivity::Walking, Rc::new(AnimationDef::new(&[0, 1, 0, 2], true, 0.15, Some(4)))),
|
||||
(EntityActivity::Attacking, Rc::new(AnimationDef::new(&[3], false, 0.3, Some(4)))),
|
||||
(EntityActivity::Dead, Rc::new(AnimationDef::new(&[16], false, 1.0, None))),
|
||||
]);
|
||||
let poof1_animation_def = Rc::new(AnimationDef::new(&[0, 1, 2], false, 0.15, None));
|
||||
let poof2_animation_def = Rc::new(AnimationDef::new(&[3, 4, 5], false, 0.15, None));
|
||||
let sparkles_animation_def = Rc::new(AnimationDef::new(&[6, 7, 8, 9], false, 0.1, None));
|
||||
|
||||
Ok(Game {
|
||||
core: Core {
|
||||
delta: 0.0,
|
||||
system,
|
||||
font,
|
||||
entities,
|
||||
event_publisher,
|
||||
palette,
|
||||
fade_out_palette: Palette::new_with_default(20, 12, 28),
|
||||
tiles,
|
||||
hero_male,
|
||||
hero_female,
|
||||
green_slime,
|
||||
blue_slime,
|
||||
orange_slime,
|
||||
fist,
|
||||
sword,
|
||||
particles,
|
||||
items,
|
||||
ui: Rc::new(ui),
|
||||
tilemap,
|
||||
slime_activity_states: Rc::new(slime_activity_states),
|
||||
hero_activity_states: Rc::new(hero_activity_states),
|
||||
poof1_animation_def,
|
||||
poof2_animation_def,
|
||||
sparkles_animation_def,
|
||||
sprite_render_list: Vec::with_capacity(1024),
|
||||
},
|
||||
support: Support {
|
||||
component_systems,
|
||||
event_listeners,
|
||||
},
|
||||
})
|
||||
}
|
||||
Ok(Game {
|
||||
core: Core {
|
||||
delta: 0.0,
|
||||
system,
|
||||
font,
|
||||
entities,
|
||||
event_publisher,
|
||||
palette,
|
||||
fade_out_palette: Palette::new_with_default(20, 12, 28),
|
||||
tiles,
|
||||
hero_male,
|
||||
hero_female,
|
||||
green_slime,
|
||||
blue_slime,
|
||||
orange_slime,
|
||||
fist,
|
||||
sword,
|
||||
particles,
|
||||
items,
|
||||
ui: Rc::new(ui),
|
||||
tilemap,
|
||||
slime_activity_states: Rc::new(slime_activity_states),
|
||||
hero_activity_states: Rc::new(hero_activity_states),
|
||||
poof1_animation_def,
|
||||
poof2_animation_def,
|
||||
sparkles_animation_def,
|
||||
sprite_render_list: Vec::with_capacity(1024),
|
||||
},
|
||||
support: Support {
|
||||
component_systems,
|
||||
event_listeners,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let system = SystemBuilder::new().window_title("Slime Stabbing Simulator").vsync(true).build()?;
|
||||
let game = Game::new(system)?;
|
||||
main_loop(game, MainMenuState::new()).context("Main loop error")
|
||||
let system = SystemBuilder::new().window_title("Slime Stabbing Simulator").vsync(true).build()?;
|
||||
let game = Game::new(system)?;
|
||||
main_loop(game, MainMenuState::new()).context("Main loop error")
|
||||
}
|
||||
|
|
|
@ -11,195 +11,195 @@ use crate::Game;
|
|||
use crate::support::*;
|
||||
|
||||
pub struct MainMenuState {
|
||||
fade: f32,
|
||||
selection: i32,
|
||||
fade: f32,
|
||||
selection: i32,
|
||||
}
|
||||
|
||||
impl MainMenuState {
|
||||
pub fn new() -> Self {
|
||||
MainMenuState {
|
||||
fade: 0.0,
|
||||
selection: 0,
|
||||
}
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
MainMenuState {
|
||||
fade: 0.0,
|
||||
selection: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppState<Game> for MainMenuState {
|
||||
fn update(&mut self, state: State, context: &mut Game) -> Option<StateChange<Game>> {
|
||||
if state == State::Active {
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
return Some(StateChange::Pop(1));
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Up) {
|
||||
self.selection = (self.selection - 1).clamp(0, 1);
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Down) {
|
||||
self.selection = (self.selection + 1).clamp(0, 1);
|
||||
}
|
||||
fn update(&mut self, state: State, context: &mut Game) -> Option<StateChange<Game>> {
|
||||
if state == State::Active {
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
return Some(StateChange::Pop(1));
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Up) {
|
||||
self.selection = (self.selection - 1).clamp(0, 1);
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Down) {
|
||||
self.selection = (self.selection + 1).clamp(0, 1);
|
||||
}
|
||||
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Return) {
|
||||
match self.selection {
|
||||
0 => return Some(StateChange::Push(Box::new(GamePlayState::new()))),
|
||||
1 => return Some(StateChange::Pop(1)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Return) {
|
||||
match self.selection {
|
||||
0 => return Some(StateChange::Push(Box::new(GamePlayState::new()))),
|
||||
1 => return Some(StateChange::Pop(1)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.support.do_events(&mut context.core);
|
||||
context.support.component_systems.update(&mut context.core);
|
||||
context.support.do_events(&mut context.core);
|
||||
context.support.component_systems.update(&mut context.core);
|
||||
|
||||
None
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn render(&mut self, state: State, context: &mut Game) {
|
||||
context.core.tilemap.draw(&mut context.core.system.video, &context.core.tiles, 0, 0);
|
||||
context.support.component_systems.render(&mut context.core);
|
||||
fn render(&mut self, state: State, context: &mut Game) {
|
||||
context.core.tilemap.draw(&mut context.core.system.video, &context.core.tiles, 0, 0);
|
||||
context.support.component_systems.render(&mut context.core);
|
||||
|
||||
let x = 32;
|
||||
let y = 160;
|
||||
let width = 48;
|
||||
let height = 40;
|
||||
const SPACER: i32 = 8;
|
||||
draw_window(&mut context.core.system.video, &context.core.ui, x, y, x + width, y + height);
|
||||
let x = 32;
|
||||
let y = 160;
|
||||
let width = 48;
|
||||
let height = 40;
|
||||
const SPACER: i32 = 8;
|
||||
draw_window(&mut context.core.system.video, &context.core.ui, x, y, x + width, y + height);
|
||||
|
||||
let selection_y = y + SPACER + (self.selection as i32 * 16);
|
||||
context.core.system.video.print_string(">", x + SPACER, selection_y, FontRenderOpts::Color(15), &context.core.font);
|
||||
let selection_y = y + SPACER + (self.selection as i32 * 16);
|
||||
context.core.system.video.print_string(">", x + SPACER, selection_y, FontRenderOpts::Color(15), &context.core.font);
|
||||
|
||||
context.core.system.video.print_string("Play", x + SPACER + SPACER, y + SPACER, FontRenderOpts::Color(15), &context.core.font);
|
||||
context.core.system.video.print_string("Quit", x + SPACER + SPACER, y + SPACER + 16, FontRenderOpts::Color(15), &context.core.font);
|
||||
}
|
||||
context.core.system.video.print_string("Play", x + SPACER + SPACER, y + SPACER, FontRenderOpts::Color(15), &context.core.font);
|
||||
context.core.system.video.print_string("Quit", x + SPACER + SPACER, y + SPACER + 16, FontRenderOpts::Color(15), &context.core.font);
|
||||
}
|
||||
|
||||
fn transition(&mut self, state: State, context: &mut Game) -> bool {
|
||||
update_fade_transition(state, &mut self.fade, context.core.delta * 3.0, context)
|
||||
}
|
||||
fn transition(&mut self, state: State, context: &mut Game) -> bool {
|
||||
update_fade_transition(state, &mut self.fade, context.core.delta * 3.0, context)
|
||||
}
|
||||
|
||||
fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) {
|
||||
match new_state {
|
||||
State::Pending | State::Resume => {
|
||||
init_everything(context, Path::new("./assets/title_screen.map.json"), 0.2, 1.0, 32);
|
||||
}
|
||||
State::TransitionIn => {
|
||||
self.fade = 0.0;
|
||||
}
|
||||
State::TransitionOut(_) => {
|
||||
self.fade = 1.0;
|
||||
}
|
||||
State::Paused => {
|
||||
context.core.system.palette = context.core.palette.clone();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) {
|
||||
match new_state {
|
||||
State::Pending | State::Resume => {
|
||||
init_everything(context, Path::new("./assets/title_screen.map.json"), 0.2, 1.0, 32);
|
||||
}
|
||||
State::TransitionIn => {
|
||||
self.fade = 0.0;
|
||||
}
|
||||
State::TransitionOut(_) => {
|
||||
self.fade = 1.0;
|
||||
}
|
||||
State::Paused => {
|
||||
context.core.system.palette = context.core.palette.clone();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GamePlayState {
|
||||
fade: f32,
|
||||
in_menu: bool,
|
||||
selection: i32,
|
||||
fade: f32,
|
||||
in_menu: bool,
|
||||
selection: i32,
|
||||
}
|
||||
|
||||
impl GamePlayState {
|
||||
pub fn new() -> Self {
|
||||
GamePlayState {
|
||||
fade: 0.0,
|
||||
in_menu: false,
|
||||
selection: 0,
|
||||
}
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
GamePlayState {
|
||||
fade: 0.0,
|
||||
in_menu: false,
|
||||
selection: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppState<Game> for GamePlayState {
|
||||
fn update(&mut self, state: State, context: &mut Game) -> Option<StateChange<Game>> {
|
||||
if state == State::Active {
|
||||
if self.in_menu {
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
self.in_menu = false;
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Up) {
|
||||
self.selection = (self.selection - 1).clamp(0, 1);
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Down) {
|
||||
self.selection = (self.selection + 1).clamp(0, 1);
|
||||
}
|
||||
fn update(&mut self, state: State, context: &mut Game) -> Option<StateChange<Game>> {
|
||||
if state == State::Active {
|
||||
if self.in_menu {
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
self.in_menu = false;
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Up) {
|
||||
self.selection = (self.selection - 1).clamp(0, 1);
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Down) {
|
||||
self.selection = (self.selection + 1).clamp(0, 1);
|
||||
}
|
||||
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Return) {
|
||||
match self.selection {
|
||||
0 => self.in_menu = false,
|
||||
1 => return Some(StateChange::Pop(1)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
self.in_menu = true;
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Return) {
|
||||
match self.selection {
|
||||
0 => self.in_menu = false,
|
||||
1 => return Some(StateChange::Pop(1)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
self.in_menu = true;
|
||||
}
|
||||
|
||||
if let Some((player_entity, _)) = context.core.entities.components::<Player>().single() {
|
||||
if context.core.system.input_devices.keyboard.is_key_down(Scancode::Up) {
|
||||
context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::North));
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_down(Scancode::Down) {
|
||||
context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::South));
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_down(Scancode::Left) {
|
||||
context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::West));
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_down(Scancode::Right) {
|
||||
context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::East));
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Space) {
|
||||
context.core.event_publisher.queue(Event::Attack(*player_entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((player_entity, _)) = context.core.entities.components::<Player>().single() {
|
||||
if context.core.system.input_devices.keyboard.is_key_down(Scancode::Up) {
|
||||
context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::North));
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_down(Scancode::Down) {
|
||||
context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::South));
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_down(Scancode::Left) {
|
||||
context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::West));
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_down(Scancode::Right) {
|
||||
context.core.event_publisher.queue(Event::TurnAndMove(*player_entity, Direction::East));
|
||||
}
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Space) {
|
||||
context.core.event_publisher.queue(Event::Attack(*player_entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.support.do_events(&mut context.core);
|
||||
context.support.component_systems.update(&mut context.core);
|
||||
context.support.do_events(&mut context.core);
|
||||
context.support.component_systems.update(&mut context.core);
|
||||
|
||||
None
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn render(&mut self, state: State, context: &mut Game) {
|
||||
if let Some((_, camera)) = context.core.entities.components::<Camera>().single() {
|
||||
context.core.tilemap.draw(&mut context.core.system.video, &context.core.tiles, camera.x, camera.y);
|
||||
}
|
||||
context.support.component_systems.render(&mut context.core);
|
||||
fn render(&mut self, state: State, context: &mut Game) {
|
||||
if let Some((_, camera)) = context.core.entities.components::<Camera>().single() {
|
||||
context.core.tilemap.draw(&mut context.core.system.video, &context.core.tiles, camera.x, camera.y);
|
||||
}
|
||||
context.support.component_systems.render(&mut context.core);
|
||||
|
||||
if self.in_menu {
|
||||
let x = 32;
|
||||
let y = 160;
|
||||
let width = 80;
|
||||
let height = 40;
|
||||
const SPACER: i32 = 8;
|
||||
draw_window(&mut context.core.system.video, &context.core.ui, x, y, x + width, y + height);
|
||||
if self.in_menu {
|
||||
let x = 32;
|
||||
let y = 160;
|
||||
let width = 80;
|
||||
let height = 40;
|
||||
const SPACER: i32 = 8;
|
||||
draw_window(&mut context.core.system.video, &context.core.ui, x, y, x + width, y + height);
|
||||
|
||||
let selection_y = y + SPACER + (self.selection as i32 * 16);
|
||||
context.core.system.video.print_string(">", x + SPACER, selection_y, FontRenderOpts::Color(15), &context.core.font);
|
||||
let selection_y = y + SPACER + (self.selection as i32 * 16);
|
||||
context.core.system.video.print_string(">", x + SPACER, selection_y, FontRenderOpts::Color(15), &context.core.font);
|
||||
|
||||
context.core.system.video.print_string("Continue", x + SPACER + SPACER, y + SPACER, FontRenderOpts::Color(15), &context.core.font);
|
||||
context.core.system.video.print_string("Quit", x + SPACER + SPACER, y + SPACER + 16, FontRenderOpts::Color(15), &context.core.font);
|
||||
}
|
||||
}
|
||||
context.core.system.video.print_string("Continue", x + SPACER + SPACER, y + SPACER, FontRenderOpts::Color(15), &context.core.font);
|
||||
context.core.system.video.print_string("Quit", x + SPACER + SPACER, y + SPACER + 16, FontRenderOpts::Color(15), &context.core.font);
|
||||
}
|
||||
}
|
||||
|
||||
fn transition(&mut self, state: State, context: &mut Game) -> bool {
|
||||
update_fade_transition(state, &mut self.fade, context.core.delta * 3.0, context)
|
||||
}
|
||||
fn transition(&mut self, state: State, context: &mut Game) -> bool {
|
||||
update_fade_transition(state, &mut self.fade, context.core.delta * 3.0, context)
|
||||
}
|
||||
|
||||
fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) {
|
||||
match new_state {
|
||||
State::Pending => {
|
||||
init_everything(context, Path::new("./assets/arena.map.json"), 0.5, 2.0, 100);
|
||||
spawn_player_randomly(&mut context.core);
|
||||
},
|
||||
State::TransitionIn => {
|
||||
self.fade = 0.0;
|
||||
}
|
||||
State::TransitionOut(_) => {
|
||||
self.fade = 1.0;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) {
|
||||
match new_state {
|
||||
State::Pending => {
|
||||
init_everything(context, Path::new("./assets/arena.map.json"), 0.5, 2.0, 100);
|
||||
spawn_player_randomly(&mut context.core);
|
||||
}
|
||||
State::TransitionIn => {
|
||||
self.fade = 0.0;
|
||||
}
|
||||
State::TransitionOut(_) => {
|
||||
self.fade = 1.0;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,76 +8,76 @@ use libretrogd::states::*;
|
|||
use crate::{Game, TILE_HEIGHT, TILE_WIDTH};
|
||||
|
||||
pub fn load_palette(path: &Path) -> Result<Palette> {
|
||||
Palette::load_from_file(path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", path))
|
||||
Palette::load_from_file(path, PaletteFormat::Vga).context(format!("Loading palette: {:?}", path))
|
||||
}
|
||||
|
||||
pub fn load_font(path: &Path) -> Result<BitmaskFont> {
|
||||
BitmaskFont::load_from_file(path).context(format!("Loading font: {:?}", path))
|
||||
BitmaskFont::load_from_file(path).context(format!("Loading font: {:?}", path))
|
||||
}
|
||||
|
||||
pub fn load_bitmap_atlas_autogrid(path: &Path) -> Result<BitmapAtlas> {
|
||||
let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
|
||||
let mut atlas = BitmapAtlas::new(bmp);
|
||||
atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?;
|
||||
Ok(atlas)
|
||||
let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
|
||||
let mut atlas = BitmapAtlas::new(bmp);
|
||||
atlas.add_grid(TILE_WIDTH, TILE_HEIGHT)?;
|
||||
Ok(atlas)
|
||||
}
|
||||
|
||||
pub fn load_bitmap_atlas(path: &Path) -> Result<BitmapAtlas> {
|
||||
let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
|
||||
let atlas = BitmapAtlas::new(bmp);
|
||||
Ok(atlas)
|
||||
let (bmp, _) = Bitmap::load_file(path).context(format!("Loading bitmap atlas: {:?}", path))?;
|
||||
let atlas = BitmapAtlas::new(bmp);
|
||||
Ok(atlas)
|
||||
}
|
||||
|
||||
pub fn draw_window(dest: &mut Bitmap, ui: &BitmapAtlas, left: i32, top: i32, right: i32, bottom: i32) {
|
||||
dest.filled_rect(left + 8, top + 8, right - 8, bottom - 8, 1);
|
||||
dest.filled_rect(left + 8, top + 8, right - 8, bottom - 8, 1);
|
||||
|
||||
// corners
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[2], left, top);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[3], right - 8, top);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[4], left, bottom - 8);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[5], right - 8, bottom - 8);
|
||||
// corners
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[2], left, top);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[3], right - 8, top);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[4], left, bottom - 8);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[5], right - 8, bottom - 8);
|
||||
|
||||
// top and bottom edges
|
||||
for i in 0..((right - left) / 8) - 2 {
|
||||
let x = left + 8 + (i * 8);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[9], x, top);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[8], x, bottom - 8);
|
||||
}
|
||||
// top and bottom edges
|
||||
for i in 0..((right - left) / 8) - 2 {
|
||||
let x = left + 8 + (i * 8);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[9], x, top);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[8], x, bottom - 8);
|
||||
}
|
||||
|
||||
// left and right edges
|
||||
for i in 0..((bottom - top) / 8) - 2 {
|
||||
let y = top + 8 + (i * 8);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[6], left, y);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[7], right - 8, y);
|
||||
}
|
||||
// left and right edges
|
||||
for i in 0..((bottom - top) / 8) - 2 {
|
||||
let y = top + 8 + (i * 8);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[6], left, y);
|
||||
dest.blit_region(BlitMethod::Transparent(0), &ui.bitmap(), &ui[7], right - 8, y);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_fade_transition(state: State, fade: &mut f32, delta: f32, context: &mut Game) -> bool {
|
||||
match state {
|
||||
State::TransitionIn => {
|
||||
*fade += delta;
|
||||
if *fade >= 1.0 {
|
||||
*fade = 1.0;
|
||||
context.core.system.palette = context.core.palette.clone();
|
||||
true
|
||||
} else {
|
||||
context.core.system.palette.lerp(0..=255, &context.core.fade_out_palette, &context.core.palette, *fade);
|
||||
false
|
||||
}
|
||||
},
|
||||
State::TransitionOut(_) => {
|
||||
*fade -= delta;
|
||||
if *fade <= 0.0 {
|
||||
*fade = 0.0;
|
||||
context.core.system.palette = context.core.fade_out_palette.clone();
|
||||
true
|
||||
} else {
|
||||
context.core.system.palette.lerp(0..=255, &context.core.fade_out_palette, &context.core.palette, *fade);
|
||||
false
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
true
|
||||
}
|
||||
}
|
||||
match state {
|
||||
State::TransitionIn => {
|
||||
*fade += delta;
|
||||
if *fade >= 1.0 {
|
||||
*fade = 1.0;
|
||||
context.core.system.palette = context.core.palette.clone();
|
||||
true
|
||||
} else {
|
||||
context.core.system.palette.lerp(0..=255, &context.core.fade_out_palette, &context.core.palette, *fade);
|
||||
false
|
||||
}
|
||||
}
|
||||
State::TransitionOut(_) => {
|
||||
*fade -= delta;
|
||||
if *fade <= 0.0 {
|
||||
*fade = 0.0;
|
||||
context.core.system.palette = context.core.fade_out_palette.clone();
|
||||
true
|
||||
} else {
|
||||
context.core.system.palette.lerp(0..=255, &context.core.fade_out_palette, &context.core.palette, *fade);
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,111 +17,111 @@ pub const TILE_FLAG_SPAWNABLE: i32 = 1;
|
|||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct TileMap {
|
||||
width: u32,
|
||||
height: u32,
|
||||
layers: Vec<Box<[i32]>>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
layers: Vec<Box<[i32]>>,
|
||||
}
|
||||
|
||||
impl TileMap {
|
||||
pub fn load_from(path: &Path) -> Result<Self> {
|
||||
let f = File::open(path)?;
|
||||
let reader = BufReader::new(f);
|
||||
serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path))
|
||||
}
|
||||
pub fn load_from(path: &Path) -> Result<Self> {
|
||||
let f = File::open(path)?;
|
||||
let reader = BufReader::new(f);
|
||||
serde_json::from_reader(reader).context(format!("Loading json tilemap: {:?}", path))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn width(&self) -> u32 {
|
||||
self.width
|
||||
}
|
||||
#[inline]
|
||||
pub fn width(&self) -> u32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn height(&self) -> u32 {
|
||||
self.height
|
||||
}
|
||||
#[inline]
|
||||
pub fn height(&self) -> u32 {
|
||||
self.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn index_to(&self, x: i32, y: i32) -> Option<usize> {
|
||||
if x >= 0 && y >= 0 && x < self.width as i32 && y < self.height as i32 {
|
||||
Some(((y * self.width as i32) + x) as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn index_to(&self, x: i32, y: i32) -> Option<usize> {
|
||||
if x >= 0 && y >= 0 && x < self.width as i32 && y < self.height as i32 {
|
||||
Some(((y * self.width as i32) + x) as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn lower(&self) -> &Box<[i32]> {
|
||||
&self.layers[0]
|
||||
}
|
||||
#[inline]
|
||||
pub fn lower(&self) -> &Box<[i32]> {
|
||||
&self.layers[0]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn upper(&self) -> &Box<[i32]> {
|
||||
&self.layers[1]
|
||||
}
|
||||
#[inline]
|
||||
pub fn upper(&self) -> &Box<[i32]> {
|
||||
&self.layers[1]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn collision(&self) -> &Box<[i32]> {
|
||||
&self.layers[2]
|
||||
}
|
||||
#[inline]
|
||||
pub fn collision(&self) -> &Box<[i32]> {
|
||||
&self.layers[2]
|
||||
}
|
||||
|
||||
pub fn draw(&self, dest: &mut Bitmap, tiles: &BitmapAtlas, camera_x: i32, camera_y: i32) {
|
||||
let xt = camera_x / TILE_WIDTH as i32;
|
||||
let yt = camera_y / TILE_HEIGHT as i32;
|
||||
let xp = camera_x % TILE_WIDTH as i32;
|
||||
let yp = camera_y % TILE_HEIGHT as i32;
|
||||
pub fn draw(&self, dest: &mut Bitmap, tiles: &BitmapAtlas, camera_x: i32, camera_y: i32) {
|
||||
let xt = camera_x / TILE_WIDTH as i32;
|
||||
let yt = camera_y / TILE_HEIGHT as i32;
|
||||
let xp = camera_x % TILE_WIDTH as i32;
|
||||
let yp = camera_y % TILE_HEIGHT as i32;
|
||||
|
||||
for y in 0..=15 {
|
||||
for x in 0..=20 {
|
||||
if let Some(index) = self.index_to(x + xt, y + yt) {
|
||||
let xd = (x * TILE_WIDTH as i32) - xp;
|
||||
let yd = (y * TILE_HEIGHT as i32) - yp;
|
||||
for y in 0..=15 {
|
||||
for x in 0..=20 {
|
||||
if let Some(index) = self.index_to(x + xt, y + yt) {
|
||||
let xd = (x * TILE_WIDTH as i32) - xp;
|
||||
let yd = (y * TILE_HEIGHT as i32) - yp;
|
||||
|
||||
let lower = self.layers[0][index];
|
||||
if lower >= 0 {
|
||||
dest.blit_region(BlitMethod::Solid, tiles.bitmap(), &tiles[lower as usize], xd, yd);
|
||||
}
|
||||
let upper = self.layers[1][index];
|
||||
if upper >= 0 {
|
||||
dest.blit_region(BlitMethod::Transparent(0), tiles.bitmap(), &tiles[upper as usize], xd, yd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let lower = self.layers[0][index];
|
||||
if lower >= 0 {
|
||||
dest.blit_region(BlitMethod::Solid, tiles.bitmap(), &tiles[lower as usize], xd, yd);
|
||||
}
|
||||
let upper = self.layers[1][index];
|
||||
if upper >= 0 {
|
||||
dest.blit_region(BlitMethod::Transparent(0), tiles.bitmap(), &tiles[upper as usize], xd, yd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_colliding(&self, rect: &Rect) -> bool {
|
||||
let x1 = rect.x / TILE_WIDTH as i32;
|
||||
let y1 = rect.y / TILE_HEIGHT as i32;
|
||||
let x2 = rect.right() / TILE_WIDTH as i32;
|
||||
let y2 = rect.bottom() / TILE_HEIGHT as i32;
|
||||
pub fn is_colliding(&self, rect: &Rect) -> bool {
|
||||
let x1 = rect.x / TILE_WIDTH as i32;
|
||||
let y1 = rect.y / TILE_HEIGHT as i32;
|
||||
let x2 = rect.right() / TILE_WIDTH as i32;
|
||||
let y2 = rect.bottom() / TILE_HEIGHT as i32;
|
||||
|
||||
for y in y1..=y2 {
|
||||
for x in x1..=x2 {
|
||||
match self.index_to(x, y) {
|
||||
Some(index) => {
|
||||
if self.collision()[index] == TILE_FLAG_COLLISION {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
None => return true
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
for y in y1..=y2 {
|
||||
for x in x1..=x2 {
|
||||
match self.index_to(x, y) {
|
||||
Some(index) => {
|
||||
if self.collision()[index] == TILE_FLAG_COLLISION {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
None => return true
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_random_spawnable_coordinates(&self) -> (i32, i32) {
|
||||
// TODO: do this better
|
||||
let mut x;
|
||||
let mut y;
|
||||
pub fn get_random_spawnable_coordinates(&self) -> (i32, i32) {
|
||||
// TODO: do this better
|
||||
let mut x;
|
||||
let mut y;
|
||||
|
||||
loop {
|
||||
x = rnd_value(0, self.width as i32 - 1);
|
||||
y = rnd_value(0, self.height as i32 - 1);
|
||||
if self.collision()[self.index_to(x, y).unwrap()] == TILE_FLAG_SPAWNABLE {
|
||||
break;
|
||||
}
|
||||
}
|
||||
loop {
|
||||
x = rnd_value(0, self.width as i32 - 1);
|
||||
y = rnd_value(0, self.height as i32 - 1);
|
||||
if self.collision()[self.index_to(x, y).unwrap()] == TILE_FLAG_SPAWNABLE {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(x, y)
|
||||
}
|
||||
(x, y)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,30 +12,30 @@ use libretrogd::utils::rnd_value;
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub enum Event {
|
||||
Remove(EntityId),
|
||||
SpawnPixel,
|
||||
Remove(EntityId),
|
||||
SpawnPixel,
|
||||
}
|
||||
|
||||
pub fn event_listener(event: &Event, context: &mut Core) -> bool {
|
||||
match event {
|
||||
Event::Remove(entity) => {
|
||||
context.entities.remove_entity(*entity);
|
||||
true
|
||||
},
|
||||
Event::SpawnPixel => {
|
||||
let speed = rnd_value(1, 10) as f32 * 10.0;
|
||||
let angle = (rnd_value(0, 359) as f32).to_radians();
|
||||
let x = (SCREEN_WIDTH / 2) as f32;
|
||||
let y = (SCREEN_HEIGHT / 2) as f32;
|
||||
let color = rnd_value(0, 255);
|
||||
let id = context.entities.new_entity();
|
||||
context.entities.add_component(id, Position(Vector2::new(x, y)));
|
||||
context.entities.add_component(id, Velocity(Vector2::from_angle(angle) * speed));
|
||||
context.entities.add_component(id, Color(color));
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
}
|
||||
match event {
|
||||
Event::Remove(entity) => {
|
||||
context.entities.remove_entity(*entity);
|
||||
true
|
||||
}
|
||||
Event::SpawnPixel => {
|
||||
let speed = rnd_value(1, 10) as f32 * 10.0;
|
||||
let angle = (rnd_value(0, 359) as f32).to_radians();
|
||||
let x = (SCREEN_WIDTH / 2) as f32;
|
||||
let y = (SCREEN_HEIGHT / 2) as f32;
|
||||
let color = rnd_value(0, 255);
|
||||
let id = context.entities.new_entity();
|
||||
context.entities.add_component(id, Position(Vector2::new(x, y)));
|
||||
context.entities.add_component(id, Velocity(Vector2::from_angle(angle) * speed));
|
||||
context.entities.add_component(id, Color(color));
|
||||
true
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -49,32 +49,32 @@ pub struct Color(u8);
|
|||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub fn update_system_movement(context: &mut Core) {
|
||||
let mut positions = context.entities.components_mut::<Position>().unwrap();
|
||||
let velocities = context.entities.components::<Velocity>().unwrap();
|
||||
let mut positions = context.entities.components_mut::<Position>().unwrap();
|
||||
let velocities = context.entities.components::<Velocity>().unwrap();
|
||||
|
||||
for (entity, position) in positions.iter_mut() {
|
||||
let velocity = velocities.get(entity).unwrap();
|
||||
position.0 += velocity.0 * context.delta;
|
||||
}
|
||||
for (entity, position) in positions.iter_mut() {
|
||||
let velocity = velocities.get(entity).unwrap();
|
||||
position.0 += velocity.0 * context.delta;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_system_remove_offscreen(context: &mut Core) {
|
||||
let positions = context.entities.components::<Position>().unwrap();
|
||||
for (entity, position) in positions.iter() {
|
||||
if !context.system.video.is_xy_visible(position.0.x as i32, position.0.y as i32) {
|
||||
context.event_publisher.queue(Event::Remove(*entity));
|
||||
}
|
||||
}
|
||||
let positions = context.entities.components::<Position>().unwrap();
|
||||
for (entity, position) in positions.iter() {
|
||||
if !context.system.video.is_xy_visible(position.0.x as i32, position.0.y as i32) {
|
||||
context.event_publisher.queue(Event::Remove(*entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_system_pixels(context: &mut Core) {
|
||||
let positions = context.entities.components::<Position>().unwrap();
|
||||
let colors = context.entities.components::<Color>().unwrap();
|
||||
let positions = context.entities.components::<Position>().unwrap();
|
||||
let colors = context.entities.components::<Color>().unwrap();
|
||||
|
||||
for (entity, position) in positions.iter() {
|
||||
let color = colors.get(entity).unwrap();
|
||||
context.system.video.set_pixel(position.0.x as i32, position.0.y as i32, color.0);
|
||||
}
|
||||
for (entity, position) in positions.iter() {
|
||||
let color = colors.get(entity).unwrap();
|
||||
context.system.video.set_pixel(position.0.x as i32, position.0.y as i32, color.0);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -82,150 +82,150 @@ pub fn render_system_pixels(context: &mut Core) {
|
|||
pub struct DemoState;
|
||||
|
||||
impl DemoState {
|
||||
fn init(&mut self, context: &mut App) {
|
||||
context.core.entities.init_components::<Position>();
|
||||
context.core.entities.init_components::<Velocity>();
|
||||
context.core.entities.init_components::<Color>();
|
||||
fn init(&mut self, context: &mut App) {
|
||||
context.core.entities.init_components::<Position>();
|
||||
context.core.entities.init_components::<Velocity>();
|
||||
context.core.entities.init_components::<Color>();
|
||||
|
||||
context.support.component_systems.reset();
|
||||
context.support.component_systems.add_update_system(update_system_movement);
|
||||
context.support.component_systems.add_update_system(update_system_remove_offscreen);
|
||||
context.support.component_systems.add_render_system(render_system_pixels);
|
||||
context.support.component_systems.reset();
|
||||
context.support.component_systems.add_update_system(update_system_movement);
|
||||
context.support.component_systems.add_update_system(update_system_remove_offscreen);
|
||||
context.support.component_systems.add_render_system(render_system_pixels);
|
||||
|
||||
context.support.event_listeners.clear();
|
||||
context.support.event_listeners.add(event_listener);
|
||||
}
|
||||
context.support.event_listeners.clear();
|
||||
context.support.event_listeners.add(event_listener);
|
||||
}
|
||||
}
|
||||
|
||||
impl AppState<App> for DemoState {
|
||||
fn update(&mut self, state: State, context: &mut App) -> Option<StateChange<App>> {
|
||||
if state == State::Active {
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
return Some(StateChange::Pop(1))
|
||||
}
|
||||
}
|
||||
fn update(&mut self, state: State, context: &mut App) -> Option<StateChange<App>> {
|
||||
if state == State::Active {
|
||||
if context.core.system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
return Some(StateChange::Pop(1));
|
||||
}
|
||||
}
|
||||
|
||||
if rnd_value(0, 100) < 80 {
|
||||
context.core.event_publisher.queue(Event::SpawnPixel);
|
||||
}
|
||||
if rnd_value(0, 100) < 80 {
|
||||
context.core.event_publisher.queue(Event::SpawnPixel);
|
||||
}
|
||||
|
||||
context.support.do_events(&mut context.core);
|
||||
context.support.component_systems.update(&mut context.core);
|
||||
context.support.do_events(&mut context.core);
|
||||
context.support.component_systems.update(&mut context.core);
|
||||
|
||||
None
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn render(&mut self, state: State, context: &mut App) {
|
||||
context.core.system.video.clear(0);
|
||||
context.support.component_systems.render(&mut context.core);
|
||||
}
|
||||
fn render(&mut self, state: State, context: &mut App) {
|
||||
context.core.system.video.clear(0);
|
||||
context.support.component_systems.render(&mut context.core);
|
||||
}
|
||||
|
||||
fn transition(&mut self, state: State, context: &mut App) -> bool {
|
||||
true
|
||||
}
|
||||
fn transition(&mut self, state: State, context: &mut App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn state_change(&mut self, new_state: State, old_state: State, context: &mut App) {
|
||||
match new_state {
|
||||
State::Pending => {
|
||||
self.init(context);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn state_change(&mut self, new_state: State, old_state: State, context: &mut App) {
|
||||
match new_state {
|
||||
State::Pending => {
|
||||
self.init(context);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct Core {
|
||||
pub delta: f32,
|
||||
pub system: System,
|
||||
pub entities: Entities,
|
||||
pub event_publisher: EventPublisher<Event>,
|
||||
pub delta: f32,
|
||||
pub system: System,
|
||||
pub entities: Entities,
|
||||
pub event_publisher: EventPublisher<Event>,
|
||||
}
|
||||
|
||||
impl CoreState for Core {
|
||||
fn system(&self) -> &System {
|
||||
&self.system
|
||||
}
|
||||
fn system(&self) -> &System {
|
||||
&self.system
|
||||
}
|
||||
|
||||
fn system_mut(&mut self) -> &mut System {
|
||||
&mut self.system
|
||||
}
|
||||
fn system_mut(&mut self) -> &mut System {
|
||||
&mut self.system
|
||||
}
|
||||
|
||||
fn delta(&self) -> f32 {
|
||||
self.delta
|
||||
}
|
||||
fn delta(&self) -> f32 {
|
||||
self.delta
|
||||
}
|
||||
|
||||
fn set_delta(&mut self, delta: f32) {
|
||||
self.delta = delta;
|
||||
}
|
||||
fn set_delta(&mut self, delta: f32) {
|
||||
self.delta = delta;
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreStateWithEvents<Event> for Core {
|
||||
fn event_publisher(&mut self) -> &mut EventPublisher<Event> {
|
||||
&mut self.event_publisher
|
||||
}
|
||||
fn event_publisher(&mut self) -> &mut EventPublisher<Event> {
|
||||
&mut self.event_publisher
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Support {
|
||||
pub component_systems: ComponentSystems<Core, Core>,
|
||||
pub event_listeners: EventListeners<Event, Core>
|
||||
pub component_systems: ComponentSystems<Core, Core>,
|
||||
pub event_listeners: EventListeners<Event, Core>,
|
||||
}
|
||||
|
||||
impl SupportSystems for Support {}
|
||||
|
||||
impl SupportSystemsWithEvents<Event> for Support {
|
||||
type ContextType = Core;
|
||||
type ContextType = Core;
|
||||
|
||||
fn event_listeners(&mut self) -> &mut EventListeners<Event, Self::ContextType> {
|
||||
&mut self.event_listeners
|
||||
}
|
||||
fn event_listeners(&mut self) -> &mut EventListeners<Event, Self::ContextType> {
|
||||
&mut self.event_listeners
|
||||
}
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
pub core: Core,
|
||||
pub support: Support,
|
||||
pub core: Core,
|
||||
pub support: Support,
|
||||
}
|
||||
|
||||
impl AppContext for App {
|
||||
type CoreType = Core;
|
||||
type SupportType = Support;
|
||||
type CoreType = Core;
|
||||
type SupportType = Support;
|
||||
|
||||
fn core(&mut self) -> &mut Core {
|
||||
&mut self.core
|
||||
}
|
||||
fn core(&mut self) -> &mut Core {
|
||||
&mut self.core
|
||||
}
|
||||
|
||||
fn support(&mut self) -> &mut Support {
|
||||
&mut self.support
|
||||
}
|
||||
fn support(&mut self) -> &mut Support {
|
||||
&mut self.support
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(system: System) -> Result<Self> {
|
||||
let entities = Entities::new();
|
||||
let component_systems = ComponentSystems::new();
|
||||
let event_publisher = EventPublisher::new();
|
||||
let event_listeners = EventListeners::new();
|
||||
pub fn new(system: System) -> Result<Self> {
|
||||
let entities = Entities::new();
|
||||
let component_systems = ComponentSystems::new();
|
||||
let event_publisher = EventPublisher::new();
|
||||
let event_listeners = EventListeners::new();
|
||||
|
||||
Ok(App {
|
||||
core: Core {
|
||||
delta: 0.0,
|
||||
system,
|
||||
entities,
|
||||
event_publisher,
|
||||
},
|
||||
support: Support {
|
||||
component_systems,
|
||||
event_listeners,
|
||||
}
|
||||
})
|
||||
}
|
||||
Ok(App {
|
||||
core: Core {
|
||||
delta: 0.0,
|
||||
system,
|
||||
entities,
|
||||
event_publisher,
|
||||
},
|
||||
support: Support {
|
||||
component_systems,
|
||||
event_listeners,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let system = SystemBuilder::new().window_title("Complicated Template").vsync(true).build()?;
|
||||
let app = App::new(system)?;
|
||||
main_loop(app, DemoState).context("Main loop error")
|
||||
let system = SystemBuilder::new().window_title("Complicated Template").vsync(true).build()?;
|
||||
let app = App::new(system)?;
|
||||
main_loop(app, DemoState).context("Main loop error")
|
||||
}
|
||||
|
|
|
@ -6,25 +6,25 @@ use libretrogd::system::*;
|
|||
use libretrogd::utils::rnd_value;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let mut system = SystemBuilder::new().window_title("Minimal Template").vsync(true).build()?;
|
||||
let mut system = SystemBuilder::new().window_title("Minimal Template").vsync(true).build()?;
|
||||
|
||||
let font = BitmaskFont::new_vga_font()?;
|
||||
let font = BitmaskFont::new_vga_font()?;
|
||||
|
||||
system.video.clear(0);
|
||||
system.video.print_string("Hello, world!", 20, 20, FontRenderOpts::Color(15), &font);
|
||||
system.video.clear(0);
|
||||
system.video.print_string("Hello, world!", 20, 20, FontRenderOpts::Color(15), &font);
|
||||
|
||||
while !system.do_events() {
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
break;
|
||||
}
|
||||
while !system.do_events() {
|
||||
if system.input_devices.keyboard.is_key_pressed(Scancode::Escape) {
|
||||
break;
|
||||
}
|
||||
|
||||
let x = rnd_value(0, SCREEN_RIGHT) as i32;
|
||||
let y = rnd_value(0, SCREEN_BOTTOM) as i32;
|
||||
let color = rnd_value(0, 255);
|
||||
system.video.set_pixel(x, y, color);
|
||||
let x = rnd_value(0, SCREEN_RIGHT) as i32;
|
||||
let y = rnd_value(0, SCREEN_BOTTOM) as i32;
|
||||
let color = rnd_value(0, 255);
|
||||
system.video.set_pixel(x, y, color);
|
||||
|
||||
system.display()?;
|
||||
}
|
||||
system.display()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
||||
|
||||
use libretrogd::graphics::*;
|
||||
use libretrogd::{SCREEN_HEIGHT, SCREEN_WIDTH};
|
||||
use libretrogd::graphics::*;
|
||||
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut source = Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT).unwrap();
|
||||
let mut dest = vec![0u32; (SCREEN_WIDTH * SCREEN_HEIGHT * 4) as usize].into_boxed_slice();
|
||||
let palette = Palette::new_vga_palette().unwrap();
|
||||
let mut source = Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT).unwrap();
|
||||
let mut dest = vec![0u32; (SCREEN_WIDTH * SCREEN_HEIGHT * 4) as usize].into_boxed_slice();
|
||||
let palette = Palette::new_vga_palette().unwrap();
|
||||
|
||||
c.bench_function("deindex_bitmap_pixels", |b| {
|
||||
b.iter(|| source.copy_as_argb_to(&mut dest, &palette))
|
||||
});
|
||||
c.bench_function("deindex_bitmap_pixels", |b| {
|
||||
b.iter(|| source.copy_as_argb_to(&mut dest, &palette))
|
||||
});
|
||||
|
||||
c.bench_function("set_pixel", |b| {
|
||||
b.iter(|| source.set_pixel(black_box(100), black_box(100), black_box(42)))
|
||||
});
|
||||
c.bench_function("set_pixel", |b| {
|
||||
b.iter(|| source.set_pixel(black_box(100), black_box(100), black_box(42)))
|
||||
});
|
||||
|
||||
c.bench_function("set_pixel_unchecked", |b| {
|
||||
b.iter(|| unsafe {
|
||||
source.set_pixel_unchecked(black_box(100), black_box(100), black_box(42))
|
||||
})
|
||||
});
|
||||
c.bench_function("set_pixel_unchecked", |b| {
|
||||
b.iter(|| unsafe {
|
||||
source.set_pixel_unchecked(black_box(100), black_box(100), black_box(42))
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
|
|
|
@ -1,498 +1,497 @@
|
|||
use std::path::Path;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
||||
|
||||
use libretrogd::graphics::*;
|
||||
use libretrogd::math::*;
|
||||
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut framebuffer = Bitmap::new(320, 240).unwrap();
|
||||
let (bmp, _) = Bitmap::load_iff_file(Path::new("./test-assets/test-tiles.lbm")).unwrap();
|
||||
let mut framebuffer = Bitmap::new(320, 240).unwrap();
|
||||
let (bmp, _) = Bitmap::load_iff_file(Path::new("./test-assets/test-tiles.lbm")).unwrap();
|
||||
|
||||
let mut solid_bmp = Bitmap::new(16, 16).unwrap();
|
||||
solid_bmp.blit_region(BlitMethod::Solid, &bmp, &Rect::new(16, 16, 16, 16), 0, 0);
|
||||
let mut trans_bmp = Bitmap::new(16, 16).unwrap();
|
||||
trans_bmp.blit_region(BlitMethod::Solid, &bmp, &Rect::new(160, 0, 16, 16), 0, 0);
|
||||
let mut solid_bmp = Bitmap::new(16, 16).unwrap();
|
||||
solid_bmp.blit_region(BlitMethod::Solid, &bmp, &Rect::new(16, 16, 16, 16), 0, 0);
|
||||
let mut trans_bmp = Bitmap::new(16, 16).unwrap();
|
||||
trans_bmp.blit_region(BlitMethod::Solid, &bmp, &Rect::new(160, 0, 16, 16), 0, 0);
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_single_checked_solid", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::Solid),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_single_checked_solid", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::Solid),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_single_unchecked_solid", |b| {
|
||||
b.iter(|| unsafe {
|
||||
framebuffer.blit_unchecked(
|
||||
black_box(BlitMethod::Solid),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_single_unchecked_solid", |b| {
|
||||
b.iter(|| unsafe {
|
||||
framebuffer.blit_unchecked(
|
||||
black_box(BlitMethod::Solid),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_single_checked_transparent", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::Transparent(0)),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_single_checked_transparent", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::Transparent(0)),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_single_unchecked_transparent", |b| {
|
||||
b.iter(|| unsafe {
|
||||
framebuffer.blit_unchecked(
|
||||
black_box(BlitMethod::Transparent(0)),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_single_unchecked_transparent", |b| {
|
||||
b.iter(|| unsafe {
|
||||
framebuffer.blit_unchecked(
|
||||
black_box(BlitMethod::Transparent(0)),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_solid_flipped_not_flipped", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlipped {
|
||||
horizontal_flip: false,
|
||||
vertical_flip: false
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_solid_flipped_not_flipped", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlipped {
|
||||
horizontal_flip: false,
|
||||
vertical_flip: false,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_solid_flipped_horizontally", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlipped {
|
||||
horizontal_flip: true,
|
||||
vertical_flip: false
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_solid_flipped_horizontally", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlipped {
|
||||
horizontal_flip: true,
|
||||
vertical_flip: false,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_solid_flipped_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlipped {
|
||||
horizontal_flip: false,
|
||||
vertical_flip: true
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_solid_flipped_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlipped {
|
||||
horizontal_flip: false,
|
||||
vertical_flip: true,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_solid_flipped_horizontally_and_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlipped {
|
||||
horizontal_flip: true,
|
||||
vertical_flip: true
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_solid_flipped_horizontally_and_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlipped {
|
||||
horizontal_flip: true,
|
||||
vertical_flip: true,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_transparent_flipped_not_flipped", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlipped {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: false
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_flipped_not_flipped", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlipped {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: false,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_transparent_flipped_horizontally", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlipped {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: false
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_flipped_horizontally", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlipped {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: false,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_transparent_flipped_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlipped {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: true
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_flipped_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlipped {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: true,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_transparent_flipped_horizontally_and_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlipped {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: true
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_flipped_horizontally_and_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlipped {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: true,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_transparent_single_flipped_not_flipped", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedSingle {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: false,
|
||||
draw_color: 17,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_single_flipped_not_flipped", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedSingle {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: false,
|
||||
draw_color: 17,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_transparent_single_flipped_horizontally", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedSingle {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: false,
|
||||
draw_color: 17,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_single_flipped_horizontally", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedSingle {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: false,
|
||||
draw_color: 17,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_transparent_single_flipped_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedSingle {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: true,
|
||||
draw_color: 17,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_single_flipped_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedSingle {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: true,
|
||||
draw_color: 17,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_transparent_single_flipped_horizontally_and_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedSingle {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: true,
|
||||
draw_color: 17,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_single_flipped_horizontally_and_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedSingle {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: true,
|
||||
draw_color: 17,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_transparent_single", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentSingle {
|
||||
transparent_color: 0,
|
||||
draw_color: 17,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_single", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentSingle {
|
||||
transparent_color: 0,
|
||||
draw_color: 17,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_transparent_offset", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentOffset {
|
||||
transparent_color: 0,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_offset", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentOffset {
|
||||
transparent_color: 0,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_transparent_offset_flipped_not_flipped", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedOffset {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: false,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_offset_flipped_not_flipped", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedOffset {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: false,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_transparent_offset_flipped_horizontally", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedOffset {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: false,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_offset_flipped_horizontally", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedOffset {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: false,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_transparent_offset_flipped_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedOffset {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: true,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_offset_flipped_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedOffset {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: false,
|
||||
vertical_flip: true,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_transparent_offset_flipped_horizontally_and_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedOffset {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: true,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_transparent_offset_flipped_horizontally_and_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::TransparentFlippedOffset {
|
||||
transparent_color: 0,
|
||||
horizontal_flip: true,
|
||||
vertical_flip: true,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_solid_offset", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidOffset(42)),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_solid_offset", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidOffset(42)),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_solid_offset_flipped_not_flipped", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlippedOffset {
|
||||
horizontal_flip: false,
|
||||
vertical_flip: false,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_solid_offset_flipped_not_flipped", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlippedOffset {
|
||||
horizontal_flip: false,
|
||||
vertical_flip: false,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_solid_offset_flipped_horizontally", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlippedOffset {
|
||||
horizontal_flip: true,
|
||||
vertical_flip: false,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_solid_offset_flipped_horizontally", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlippedOffset {
|
||||
horizontal_flip: true,
|
||||
vertical_flip: false,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_solid_offset_flipped_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlippedOffset {
|
||||
horizontal_flip: false,
|
||||
vertical_flip: true,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_solid_offset_flipped_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlippedOffset {
|
||||
horizontal_flip: false,
|
||||
vertical_flip: true,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("blit_solid_offset_flipped_horizontally_and_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlippedOffset {
|
||||
horizontal_flip: true,
|
||||
vertical_flip: true,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_solid_offset_flipped_horizontally_and_vertically", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::SolidFlippedOffset {
|
||||
horizontal_flip: true,
|
||||
vertical_flip: true,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_rotozoom", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::RotoZoom {
|
||||
angle: 73.0f32.to_radians(),
|
||||
scale_x: 1.2,
|
||||
scale_y: 0.8,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_rotozoom", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::RotoZoom {
|
||||
angle: 73.0f32.to_radians(),
|
||||
scale_x: 1.2,
|
||||
scale_y: 0.8,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_rotozoom_offset", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::RotoZoomOffset {
|
||||
angle: 73.0f32.to_radians(),
|
||||
scale_x: 1.2,
|
||||
scale_y: 0.8,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_rotozoom_offset", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::RotoZoomOffset {
|
||||
angle: 73.0f32.to_radians(),
|
||||
scale_x: 1.2,
|
||||
scale_y: 0.8,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&solid_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
//////
|
||||
|
||||
c.bench_function("blit_rotozoom_transparent", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::RotoZoomTransparent {
|
||||
angle: 73.0f32.to_radians(),
|
||||
scale_x: 1.2,
|
||||
scale_y: 0.8,
|
||||
transparent_color: 0,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("blit_rotozoom_transparent", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::RotoZoomTransparent {
|
||||
angle: 73.0f32.to_radians(),
|
||||
scale_x: 1.2,
|
||||
scale_y: 0.8,
|
||||
transparent_color: 0,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
//////
|
||||
|
||||
c.bench_function("blit_rotozoom_offset_transparent", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::RotoZoomTransparentOffset {
|
||||
angle: 73.0f32.to_radians(),
|
||||
scale_x: 1.2,
|
||||
scale_y: 0.8,
|
||||
transparent_color: 0,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
//////
|
||||
|
||||
c.bench_function("blit_rotozoom_offset_transparent", |b| {
|
||||
b.iter(|| {
|
||||
framebuffer.blit(
|
||||
black_box(BlitMethod::RotoZoomTransparentOffset {
|
||||
angle: 73.0f32.to_radians(),
|
||||
scale_x: 1.2,
|
||||
scale_y: 0.8,
|
||||
transparent_color: 0,
|
||||
offset: 42,
|
||||
}),
|
||||
black_box(&trans_bmp),
|
||||
black_box(100),
|
||||
black_box(100),
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
|
|
|
@ -8,19 +8,19 @@ pub static SMALL_GIF_FILE_BYTES: &[u8] = include_bytes!("../test-assets/test.gif
|
|||
pub static LARGE_GIF_FILE_BYTES: &[u8] = include_bytes!("../test-assets/test_image.gif");
|
||||
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
c.bench_function("loading_small_gif", |b| {
|
||||
b.iter(|| {
|
||||
let mut reader = Cursor::new(SMALL_GIF_FILE_BYTES);
|
||||
Bitmap::load_gif_bytes(black_box(&mut reader)).unwrap();
|
||||
})
|
||||
});
|
||||
c.bench_function("loading_small_gif", |b| {
|
||||
b.iter(|| {
|
||||
let mut reader = Cursor::new(SMALL_GIF_FILE_BYTES);
|
||||
Bitmap::load_gif_bytes(black_box(&mut reader)).unwrap();
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function("loading_large_gif", |b| {
|
||||
b.iter(|| {
|
||||
let mut reader = Cursor::new(LARGE_GIF_FILE_BYTES);
|
||||
Bitmap::load_gif_bytes(black_box(&mut reader)).unwrap();
|
||||
})
|
||||
});
|
||||
c.bench_function("loading_large_gif", |b| {
|
||||
b.iter(|| {
|
||||
let mut reader = Cursor::new(LARGE_GIF_FILE_BYTES);
|
||||
Bitmap::load_gif_bytes(black_box(&mut reader)).unwrap();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
|
|
|
@ -6,64 +6,64 @@ pub mod wav;
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AudioBufferError {
|
||||
#[error("Error during format conversion: {0}")]
|
||||
ConversionError(String),
|
||||
#[error("Error during format conversion: {0}")]
|
||||
ConversionError(String),
|
||||
}
|
||||
|
||||
/// Holds audio sample data that can be played via [`AudioDevice`].
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct AudioBuffer {
|
||||
spec: AudioSpec,
|
||||
pub data: Vec<u8>,
|
||||
spec: AudioSpec,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for AudioBuffer {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AudioBuffer")
|
||||
.field("spec", &self.spec)
|
||||
.field("data.len()", &self.data.len())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AudioBuffer")
|
||||
.field("spec", &self.spec)
|
||||
.field("data.len()", &self.data.len())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioBuffer {
|
||||
/// Creates and returns a new, empty, [`AudioBuffer`] that will hold audio sample data in the
|
||||
/// spec/format given.
|
||||
pub fn new(spec: AudioSpec) -> Self {
|
||||
AudioBuffer {
|
||||
spec,
|
||||
data: Vec::new(),
|
||||
}
|
||||
}
|
||||
/// Creates and returns a new, empty, [`AudioBuffer`] that will hold audio sample data in the
|
||||
/// spec/format given.
|
||||
pub fn new(spec: AudioSpec) -> Self {
|
||||
AudioBuffer {
|
||||
spec,
|
||||
data: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the spec of the audio sample data that this buffer contains.
|
||||
#[inline]
|
||||
pub fn spec(&self) -> &AudioSpec {
|
||||
&self.spec
|
||||
}
|
||||
/// Returns the spec of the audio sample data that this buffer contains.
|
||||
#[inline]
|
||||
pub fn spec(&self) -> &AudioSpec {
|
||||
&self.spec
|
||||
}
|
||||
|
||||
/// Converts the audio sample data in this buffer to the spec given, returning the newly
|
||||
/// converted buffer.
|
||||
pub fn convert(self, to_spec: &AudioSpec) -> Result<Self, AudioBufferError> {
|
||||
if self.spec == *to_spec {
|
||||
Ok(self)
|
||||
} else {
|
||||
let converter = sdl2::audio::AudioCVT::new(
|
||||
self.spec.format(),
|
||||
self.spec.channels(),
|
||||
self.spec.frequency() as i32,
|
||||
to_spec.format(),
|
||||
to_spec.channels(),
|
||||
to_spec.frequency() as i32,
|
||||
);
|
||||
match converter {
|
||||
Ok(converter) => {
|
||||
let mut result = AudioBuffer::new(*to_spec);
|
||||
result.data = converter.convert(self.data);
|
||||
Ok(result)
|
||||
}
|
||||
Err(string) => Err(AudioBufferError::ConversionError(string)),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Converts the audio sample data in this buffer to the spec given, returning the newly
|
||||
/// converted buffer.
|
||||
pub fn convert(self, to_spec: &AudioSpec) -> Result<Self, AudioBufferError> {
|
||||
if self.spec == *to_spec {
|
||||
Ok(self)
|
||||
} else {
|
||||
let converter = sdl2::audio::AudioCVT::new(
|
||||
self.spec.format(),
|
||||
self.spec.channels(),
|
||||
self.spec.frequency() as i32,
|
||||
to_spec.format(),
|
||||
to_spec.channels(),
|
||||
to_spec.frequency() as i32,
|
||||
);
|
||||
match converter {
|
||||
Ok(converter) => {
|
||||
let mut result = AudioBuffer::new(*to_spec);
|
||||
result.data = converter.convert(self.data);
|
||||
Ok(result)
|
||||
}
|
||||
Err(string) => Err(AudioBufferError::ConversionError(string)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,322 +12,322 @@ use crate::utils::io::StreamSize;
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum WavError {
|
||||
#[error("Bad or unsupported WAV file: {0}")]
|
||||
BadFile(String),
|
||||
#[error("Bad or unsupported WAV file: {0}")]
|
||||
BadFile(String),
|
||||
|
||||
#[error("WAV I/O error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
#[error("WAV I/O error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct ChunkId {
|
||||
id: [u8; 4],
|
||||
id: [u8; 4],
|
||||
}
|
||||
|
||||
impl ChunkId {
|
||||
pub fn read<T: Read>(reader: &mut T) -> Result<Self, WavError> {
|
||||
let mut id = [0u8; 4];
|
||||
reader.read_exact(&mut id)?;
|
||||
Ok(ChunkId { id })
|
||||
}
|
||||
pub fn read<T: Read>(reader: &mut T) -> Result<Self, WavError> {
|
||||
let mut id = [0u8; 4];
|
||||
reader.read_exact(&mut id)?;
|
||||
Ok(ChunkId { id })
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn write<T: Write>(&self, writer: &mut T) -> Result<(), WavError> {
|
||||
writer.write_all(&self.id)?;
|
||||
Ok(())
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn write<T: Write>(&self, writer: &mut T) -> Result<(), WavError> {
|
||||
writer.write_all(&self.id)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct SubChunkHeader {
|
||||
chunk_id: ChunkId,
|
||||
size: u32,
|
||||
chunk_id: ChunkId,
|
||||
size: u32,
|
||||
}
|
||||
|
||||
impl SubChunkHeader {
|
||||
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, WavError> {
|
||||
let chunk_id = ChunkId::read(reader)?;
|
||||
let size = reader.read_u32::<LittleEndian>()?;
|
||||
Ok(SubChunkHeader { chunk_id, size })
|
||||
}
|
||||
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, WavError> {
|
||||
let chunk_id = ChunkId::read(reader)?;
|
||||
let size = reader.read_u32::<LittleEndian>()?;
|
||||
Ok(SubChunkHeader { chunk_id, size })
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), WavError> {
|
||||
self.chunk_id.write(writer)?;
|
||||
writer.write_u32::<LittleEndian>(self.size)?;
|
||||
Ok(())
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), WavError> {
|
||||
self.chunk_id.write(writer)?;
|
||||
writer.write_u32::<LittleEndian>(self.size)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct WavHeader {
|
||||
file_chunk: SubChunkHeader,
|
||||
file_container_id: ChunkId,
|
||||
file_chunk: SubChunkHeader,
|
||||
file_container_id: ChunkId,
|
||||
}
|
||||
|
||||
impl WavHeader {
|
||||
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, WavError> {
|
||||
let file_chunk = SubChunkHeader::read(reader)?;
|
||||
let file_container_id = ChunkId::read(reader)?;
|
||||
Ok(WavHeader {
|
||||
file_chunk,
|
||||
file_container_id,
|
||||
})
|
||||
}
|
||||
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, WavError> {
|
||||
let file_chunk = SubChunkHeader::read(reader)?;
|
||||
let file_container_id = ChunkId::read(reader)?;
|
||||
Ok(WavHeader {
|
||||
file_chunk,
|
||||
file_container_id,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), WavError> {
|
||||
self.file_chunk.write(writer)?;
|
||||
self.file_container_id.write(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), WavError> {
|
||||
self.file_chunk.write(writer)?;
|
||||
self.file_container_id.write(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
struct FormatChunk {
|
||||
compression_code: u16,
|
||||
channels: u16,
|
||||
frequency: u32,
|
||||
bytes_per_second: u32,
|
||||
block_alignment: u16,
|
||||
bits_per_sample: u16,
|
||||
additional_data_length: u16,
|
||||
additional_data: Option<Box<[u8]>>,
|
||||
compression_code: u16,
|
||||
channels: u16,
|
||||
frequency: u32,
|
||||
bytes_per_second: u32,
|
||||
block_alignment: u16,
|
||||
bits_per_sample: u16,
|
||||
additional_data_length: u16,
|
||||
additional_data: Option<Box<[u8]>>,
|
||||
}
|
||||
|
||||
impl FormatChunk {
|
||||
pub fn read<T: ReadBytesExt>(
|
||||
reader: &mut T,
|
||||
chunk_header: &SubChunkHeader,
|
||||
) -> Result<Self, WavError> {
|
||||
let compression_code = reader.read_u16::<LittleEndian>()?;
|
||||
let channels = reader.read_u16::<LittleEndian>()?;
|
||||
let frequency = reader.read_u32::<LittleEndian>()?;
|
||||
let bytes_per_second = reader.read_u32::<LittleEndian>()?;
|
||||
let block_alignment = reader.read_u16::<LittleEndian>()?;
|
||||
let bits_per_sample = reader.read_u16::<LittleEndian>()?;
|
||||
let additional_data_length;
|
||||
let additional_data;
|
||||
if chunk_header.size > 16 {
|
||||
additional_data_length = reader.read_u16::<LittleEndian>()?;
|
||||
let mut buffer = vec![0u8; additional_data_length as usize];
|
||||
reader.read(&mut buffer)?;
|
||||
additional_data = Some(buffer.into_boxed_slice());
|
||||
} else {
|
||||
additional_data_length = 0;
|
||||
additional_data = None;
|
||||
}
|
||||
pub fn read<T: ReadBytesExt>(
|
||||
reader: &mut T,
|
||||
chunk_header: &SubChunkHeader,
|
||||
) -> Result<Self, WavError> {
|
||||
let compression_code = reader.read_u16::<LittleEndian>()?;
|
||||
let channels = reader.read_u16::<LittleEndian>()?;
|
||||
let frequency = reader.read_u32::<LittleEndian>()?;
|
||||
let bytes_per_second = reader.read_u32::<LittleEndian>()?;
|
||||
let block_alignment = reader.read_u16::<LittleEndian>()?;
|
||||
let bits_per_sample = reader.read_u16::<LittleEndian>()?;
|
||||
let additional_data_length;
|
||||
let additional_data;
|
||||
if chunk_header.size > 16 {
|
||||
additional_data_length = reader.read_u16::<LittleEndian>()?;
|
||||
let mut buffer = vec![0u8; additional_data_length as usize];
|
||||
reader.read(&mut buffer)?;
|
||||
additional_data = Some(buffer.into_boxed_slice());
|
||||
} else {
|
||||
additional_data_length = 0;
|
||||
additional_data = None;
|
||||
}
|
||||
|
||||
Ok(FormatChunk {
|
||||
compression_code,
|
||||
channels,
|
||||
frequency,
|
||||
bytes_per_second,
|
||||
block_alignment,
|
||||
bits_per_sample,
|
||||
additional_data_length,
|
||||
additional_data,
|
||||
})
|
||||
}
|
||||
Ok(FormatChunk {
|
||||
compression_code,
|
||||
channels,
|
||||
frequency,
|
||||
bytes_per_second,
|
||||
block_alignment,
|
||||
bits_per_sample,
|
||||
additional_data_length,
|
||||
additional_data,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), WavError> {
|
||||
writer.write_u16::<LittleEndian>(self.compression_code)?;
|
||||
writer.write_u16::<LittleEndian>(self.channels)?;
|
||||
writer.write_u32::<LittleEndian>(self.frequency)?;
|
||||
writer.write_u32::<LittleEndian>(self.bytes_per_second)?;
|
||||
writer.write_u16::<LittleEndian>(self.block_alignment)?;
|
||||
writer.write_u16::<LittleEndian>(self.bits_per_sample)?;
|
||||
if self.additional_data_length > 0 {
|
||||
writer.write_u16::<LittleEndian>(self.additional_data_length)?;
|
||||
writer.write_all(&self.additional_data.as_ref().unwrap())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), WavError> {
|
||||
writer.write_u16::<LittleEndian>(self.compression_code)?;
|
||||
writer.write_u16::<LittleEndian>(self.channels)?;
|
||||
writer.write_u32::<LittleEndian>(self.frequency)?;
|
||||
writer.write_u32::<LittleEndian>(self.bytes_per_second)?;
|
||||
writer.write_u16::<LittleEndian>(self.block_alignment)?;
|
||||
writer.write_u16::<LittleEndian>(self.bits_per_sample)?;
|
||||
if self.additional_data_length > 0 {
|
||||
writer.write_u16::<LittleEndian>(self.additional_data_length)?;
|
||||
writer.write_all(&self.additional_data.as_ref().unwrap())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DataChunk {
|
||||
data: Box<[u8]>,
|
||||
data: Box<[u8]>,
|
||||
}
|
||||
|
||||
impl DataChunk {
|
||||
pub fn read<T: ReadBytesExt>(
|
||||
reader: &mut T,
|
||||
chunk_header: &SubChunkHeader,
|
||||
is_probably_naive_file: bool,
|
||||
) -> Result<Self, WavError> {
|
||||
let mut buffer;
|
||||
if is_probably_naive_file {
|
||||
// in this scenario, we have doubts about the chunk size being recorded correctly
|
||||
// (the tool that created this file may have used a buggy calculation). we assume that
|
||||
// in this case, this wav file is probably written in a "naive" manner and likely only
|
||||
// contains "fmt" and "data" chunks with the "data" chunk being at the end of the file.
|
||||
// if this assumption is correct, then we can just read everything until EOF here as
|
||||
// the "data" chunk contents and that should be ok (assuming this file isn't corrupt
|
||||
// anyway).
|
||||
buffer = Vec::new();
|
||||
reader.read_to_end(&mut buffer)?;
|
||||
} else {
|
||||
// alternatively, this scenario means we are assuming the file was written out more
|
||||
// properly and we will assume the chunk size is correct and read that many bytes.
|
||||
// this is best if there is the possibility that there are more chunks than just "fmt"
|
||||
// and "data" in this wav file and maybe they are in a different order, etc.
|
||||
// it is important to note that this seems to be an uncommon case for wav files. most
|
||||
// wav files seem to be written in a fairly "naive" manner.
|
||||
buffer = vec![0u8; chunk_header.size as usize];
|
||||
reader.read_exact(&mut buffer)?;
|
||||
}
|
||||
Ok(DataChunk {
|
||||
data: buffer.into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
pub fn read<T: ReadBytesExt>(
|
||||
reader: &mut T,
|
||||
chunk_header: &SubChunkHeader,
|
||||
is_probably_naive_file: bool,
|
||||
) -> Result<Self, WavError> {
|
||||
let mut buffer;
|
||||
if is_probably_naive_file {
|
||||
// in this scenario, we have doubts about the chunk size being recorded correctly
|
||||
// (the tool that created this file may have used a buggy calculation). we assume that
|
||||
// in this case, this wav file is probably written in a "naive" manner and likely only
|
||||
// contains "fmt" and "data" chunks with the "data" chunk being at the end of the file.
|
||||
// if this assumption is correct, then we can just read everything until EOF here as
|
||||
// the "data" chunk contents and that should be ok (assuming this file isn't corrupt
|
||||
// anyway).
|
||||
buffer = Vec::new();
|
||||
reader.read_to_end(&mut buffer)?;
|
||||
} else {
|
||||
// alternatively, this scenario means we are assuming the file was written out more
|
||||
// properly and we will assume the chunk size is correct and read that many bytes.
|
||||
// this is best if there is the possibility that there are more chunks than just "fmt"
|
||||
// and "data" in this wav file and maybe they are in a different order, etc.
|
||||
// it is important to note that this seems to be an uncommon case for wav files. most
|
||||
// wav files seem to be written in a fairly "naive" manner.
|
||||
buffer = vec![0u8; chunk_header.size as usize];
|
||||
reader.read_exact(&mut buffer)?;
|
||||
}
|
||||
Ok(DataChunk {
|
||||
data: buffer.into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), WavError> {
|
||||
writer.write_all(self.data.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), WavError> {
|
||||
writer.write_all(self.data.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioBuffer {
|
||||
/// Loads the bytes of a WAV file into an [`AudioBuffer`]. The returned buffer will be in its
|
||||
/// original format and may need to be converted before it can be played.
|
||||
pub fn load_wav_bytes<T: ReadBytesExt + Seek>(reader: &mut T) -> Result<AudioBuffer, WavError> {
|
||||
let file_size = reader.stream_size()?;
|
||||
/// Loads the bytes of a WAV file into an [`AudioBuffer`]. The returned buffer will be in its
|
||||
/// original format and may need to be converted before it can be played.
|
||||
pub fn load_wav_bytes<T: ReadBytesExt + Seek>(reader: &mut T) -> Result<AudioBuffer, WavError> {
|
||||
let file_size = reader.stream_size()?;
|
||||
|
||||
let header = WavHeader::read(reader)?;
|
||||
if header.file_chunk.chunk_id.id != *b"RIFF" {
|
||||
return Err(WavError::BadFile(String::from(
|
||||
"Unexpected RIFF chunk id, probably not a WAV file",
|
||||
)));
|
||||
}
|
||||
if header.file_container_id.id != *b"WAVE" {
|
||||
return Err(WavError::BadFile(String::from(
|
||||
"Unexpected RIFF container id, probably not a WAV file",
|
||||
)));
|
||||
}
|
||||
let header = WavHeader::read(reader)?;
|
||||
if header.file_chunk.chunk_id.id != *b"RIFF" {
|
||||
return Err(WavError::BadFile(String::from(
|
||||
"Unexpected RIFF chunk id, probably not a WAV file",
|
||||
)));
|
||||
}
|
||||
if header.file_container_id.id != *b"WAVE" {
|
||||
return Err(WavError::BadFile(String::from(
|
||||
"Unexpected RIFF container id, probably not a WAV file",
|
||||
)));
|
||||
}
|
||||
|
||||
// some tools like sfxr and jsfxr incorrectly calculate data sizes, seemingly using a
|
||||
// hardcoded 16-bit sample size in their calculations even when the file being created has
|
||||
// 8-bit sample sizes. this means the chunk size here and the "data" chunk size will be
|
||||
// larger than they should be for the _actual_ data present in the file.
|
||||
// of course, if the chunk size here is wrong, it could also mean a corrupt file. but we'll
|
||||
// proceed regardless, with the assumption that an incorrect size here probably means that
|
||||
// the file was created using these semi-broken tools and should act accordingly later on.
|
||||
// if the file is actually corrupt (maybe truncated accidentally or something), then we'll
|
||||
// hit an EOF earlier than expected somewhere too ...
|
||||
let is_probably_naive_file = file_size - 8 != header.file_chunk.size as u64;
|
||||
// some tools like sfxr and jsfxr incorrectly calculate data sizes, seemingly using a
|
||||
// hardcoded 16-bit sample size in their calculations even when the file being created has
|
||||
// 8-bit sample sizes. this means the chunk size here and the "data" chunk size will be
|
||||
// larger than they should be for the _actual_ data present in the file.
|
||||
// of course, if the chunk size here is wrong, it could also mean a corrupt file. but we'll
|
||||
// proceed regardless, with the assumption that an incorrect size here probably means that
|
||||
// the file was created using these semi-broken tools and should act accordingly later on.
|
||||
// if the file is actually corrupt (maybe truncated accidentally or something), then we'll
|
||||
// hit an EOF earlier than expected somewhere too ...
|
||||
let is_probably_naive_file = file_size - 8 != header.file_chunk.size as u64;
|
||||
|
||||
let mut format: Option<FormatChunk> = None;
|
||||
let mut data: Option<DataChunk> = None;
|
||||
let mut format: Option<FormatChunk> = None;
|
||||
let mut data: Option<DataChunk> = None;
|
||||
|
||||
loop {
|
||||
let chunk_header = match SubChunkHeader::read(reader) {
|
||||
Ok(header) => header,
|
||||
Err(WavError::IOError(io_error))
|
||||
if io_error.kind() == io::ErrorKind::UnexpectedEof =>
|
||||
{
|
||||
break;
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let chunk_data_position = reader.stream_position()?;
|
||||
loop {
|
||||
let chunk_header = match SubChunkHeader::read(reader) {
|
||||
Ok(header) => header,
|
||||
Err(WavError::IOError(io_error))
|
||||
if io_error.kind() == io::ErrorKind::UnexpectedEof =>
|
||||
{
|
||||
break;
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let chunk_data_position = reader.stream_position()?;
|
||||
|
||||
// read only the chunks we recognize / care about
|
||||
if chunk_header.chunk_id.id == *b"fmt " {
|
||||
format = Some(FormatChunk::read(reader, &chunk_header)?);
|
||||
if format.as_ref().unwrap().compression_code != 1 {
|
||||
return Err(WavError::BadFile(String::from(
|
||||
"Only PCM format WAV files are supported",
|
||||
)));
|
||||
}
|
||||
if format.as_ref().unwrap().bits_per_sample != 8 &&
|
||||
format.as_ref().unwrap().bits_per_sample != 16 {
|
||||
return Err(WavError::BadFile(String::from(
|
||||
"Only 8-bit and 16-bit sample WAV files are supported",
|
||||
)));
|
||||
}
|
||||
} else if chunk_header.chunk_id.id == *b"data" {
|
||||
data = Some(DataChunk::read(reader, &chunk_header, is_probably_naive_file)?);
|
||||
}
|
||||
// read only the chunks we recognize / care about
|
||||
if chunk_header.chunk_id.id == *b"fmt " {
|
||||
format = Some(FormatChunk::read(reader, &chunk_header)?);
|
||||
if format.as_ref().unwrap().compression_code != 1 {
|
||||
return Err(WavError::BadFile(String::from(
|
||||
"Only PCM format WAV files are supported",
|
||||
)));
|
||||
}
|
||||
if format.as_ref().unwrap().bits_per_sample != 8 &&
|
||||
format.as_ref().unwrap().bits_per_sample != 16 {
|
||||
return Err(WavError::BadFile(String::from(
|
||||
"Only 8-bit and 16-bit sample WAV files are supported",
|
||||
)));
|
||||
}
|
||||
} else if chunk_header.chunk_id.id == *b"data" {
|
||||
data = Some(DataChunk::read(reader, &chunk_header, is_probably_naive_file)?);
|
||||
}
|
||||
|
||||
// move to the start of the next chunk (possibly skipping over the current chunk if we
|
||||
// didn't recognize it above ...)
|
||||
reader.seek(SeekFrom::Start(
|
||||
chunk_data_position + chunk_header.size as u64,
|
||||
))?;
|
||||
}
|
||||
// move to the start of the next chunk (possibly skipping over the current chunk if we
|
||||
// didn't recognize it above ...)
|
||||
reader.seek(SeekFrom::Start(
|
||||
chunk_data_position + chunk_header.size as u64,
|
||||
))?;
|
||||
}
|
||||
|
||||
// all done reading the file, now convert the read data into an AudioBuffer ...
|
||||
// all done reading the file, now convert the read data into an AudioBuffer ...
|
||||
|
||||
let mut audio_buffer;
|
||||
let mut audio_buffer;
|
||||
|
||||
if let Some(format) = format {
|
||||
let sample_format = match format.bits_per_sample {
|
||||
8 => AudioFormat::U8,
|
||||
16 => AudioFormat::S16LSB,
|
||||
// this shouldn't be able to happen given the above checks when reading the
|
||||
// "fmt" chunk
|
||||
_ => return Err(WavError::BadFile(String::from("Unsupported sample bit size.")))
|
||||
};
|
||||
let spec = AudioSpec::new(format.frequency, format.channels as u8, sample_format);
|
||||
audio_buffer = AudioBuffer::new(spec);
|
||||
} else {
|
||||
return Err(WavError::BadFile(String::from("No 'fmt ' chunk was found")));
|
||||
}
|
||||
if let Some(format) = format {
|
||||
let sample_format = match format.bits_per_sample {
|
||||
8 => AudioFormat::U8,
|
||||
16 => AudioFormat::S16LSB,
|
||||
// this shouldn't be able to happen given the above checks when reading the
|
||||
// "fmt" chunk
|
||||
_ => return Err(WavError::BadFile(String::from("Unsupported sample bit size.")))
|
||||
};
|
||||
let spec = AudioSpec::new(format.frequency, format.channels as u8, sample_format);
|
||||
audio_buffer = AudioBuffer::new(spec);
|
||||
} else {
|
||||
return Err(WavError::BadFile(String::from("No 'fmt ' chunk was found")));
|
||||
}
|
||||
|
||||
if let Some(data) = data {
|
||||
audio_buffer.data = data.data.into_vec();
|
||||
} else {
|
||||
return Err(WavError::BadFile(String::from("No 'data' chunk was found")));
|
||||
}
|
||||
if let Some(data) = data {
|
||||
audio_buffer.data = data.data.into_vec();
|
||||
} else {
|
||||
return Err(WavError::BadFile(String::from("No 'data' chunk was found")));
|
||||
}
|
||||
|
||||
Ok(audio_buffer)
|
||||
}
|
||||
Ok(audio_buffer)
|
||||
}
|
||||
|
||||
/// Loads a WAV file into an [`AudioBuffer`]. The returned buffer will be in its original
|
||||
/// format and may need to be converted before it can be played.
|
||||
pub fn load_wav_file(path: &Path) -> Result<AudioBuffer, WavError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_wav_bytes(&mut reader)
|
||||
}
|
||||
/// Loads a WAV file into an [`AudioBuffer`]. The returned buffer will be in its original
|
||||
/// format and may need to be converted before it can be played.
|
||||
pub fn load_wav_file(path: &Path) -> Result<AudioBuffer, WavError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_wav_bytes(&mut reader)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::audio::*;
|
||||
use crate::audio::*;
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn load_wav_file() -> Result<(), WavError> {
|
||||
let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/22khz_8bit_1ch.wav"))?;
|
||||
assert_eq!(AUDIO_FREQUENCY_22KHZ, wav_buffer.spec().frequency());
|
||||
assert_eq!(1, wav_buffer.spec().channels());
|
||||
assert_eq!(AudioFormat::U8, wav_buffer.spec.format);
|
||||
assert_eq!(11248, wav_buffer.data.len());
|
||||
#[test]
|
||||
pub fn load_wav_file() -> Result<(), WavError> {
|
||||
let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/22khz_8bit_1ch.wav"))?;
|
||||
assert_eq!(AUDIO_FREQUENCY_22KHZ, wav_buffer.spec().frequency());
|
||||
assert_eq!(1, wav_buffer.spec().channels());
|
||||
assert_eq!(AudioFormat::U8, wav_buffer.spec.format);
|
||||
assert_eq!(11248, wav_buffer.data.len());
|
||||
|
||||
let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/44khz_8bit_1ch.wav"))?;
|
||||
assert_eq!(AUDIO_FREQUENCY_44KHZ, wav_buffer.spec().frequency());
|
||||
assert_eq!(1, wav_buffer.spec().channels());
|
||||
assert_eq!(AudioFormat::U8, wav_buffer.spec.format);
|
||||
assert_eq!(22496, wav_buffer.data.len());
|
||||
let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/44khz_8bit_1ch.wav"))?;
|
||||
assert_eq!(AUDIO_FREQUENCY_44KHZ, wav_buffer.spec().frequency());
|
||||
assert_eq!(1, wav_buffer.spec().channels());
|
||||
assert_eq!(AudioFormat::U8, wav_buffer.spec.format);
|
||||
assert_eq!(22496, wav_buffer.data.len());
|
||||
|
||||
let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/22khz_16bit_1ch.wav"))?;
|
||||
assert_eq!(AUDIO_FREQUENCY_22KHZ, wav_buffer.spec().frequency());
|
||||
assert_eq!(1, wav_buffer.spec().channels());
|
||||
assert_eq!(AudioFormat::S16LSB, wav_buffer.spec.format);
|
||||
assert_eq!(22496, wav_buffer.data.len());
|
||||
let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/22khz_16bit_1ch.wav"))?;
|
||||
assert_eq!(AUDIO_FREQUENCY_22KHZ, wav_buffer.spec().frequency());
|
||||
assert_eq!(1, wav_buffer.spec().channels());
|
||||
assert_eq!(AudioFormat::S16LSB, wav_buffer.spec.format);
|
||||
assert_eq!(22496, wav_buffer.data.len());
|
||||
|
||||
let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/44khz_16bit_1ch.wav"))?;
|
||||
assert_eq!(AUDIO_FREQUENCY_44KHZ, wav_buffer.spec().frequency());
|
||||
assert_eq!(1, wav_buffer.spec().channels());
|
||||
assert_eq!(AudioFormat::S16LSB, wav_buffer.spec.format);
|
||||
assert_eq!(44992, wav_buffer.data.len());
|
||||
let wav_buffer = AudioBuffer::load_wav_file(Path::new("./test-assets/44khz_16bit_1ch.wav"))?;
|
||||
assert_eq!(AUDIO_FREQUENCY_44KHZ, wav_buffer.spec().frequency());
|
||||
assert_eq!(1, wav_buffer.spec().channels());
|
||||
assert_eq!(AudioFormat::S16LSB, wav_buffer.spec.format);
|
||||
assert_eq!(44992, wav_buffer.data.len());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,180 +8,180 @@ use crate::audio::*;
|
|||
/// Represents a "channel" of audio playback that will be mixed together with all of the other
|
||||
/// actively playing audio channels to get the final audio playback.
|
||||
pub struct AudioChannel {
|
||||
/// Whether the channel is currently playing or not.
|
||||
pub playing: bool,
|
||||
/// Whether this channel is playing on a loop or not. If not, once the end of the [`data`]
|
||||
/// buffer is reached, or the [`AudioGenerator::gen_sample`] method returns `None`, playback
|
||||
/// on this channel will automatically stop and [`playing`] will be changed to `false`.
|
||||
pub loops: bool,
|
||||
/// The audio data buffer (samples) that this channel will play from, **only** if [`generator`]
|
||||
/// is `None`.
|
||||
pub data: Vec<u8>,
|
||||
/// An [`AudioGenerator`] instance that will be used to dynamically generate audio data to play
|
||||
/// on this channel _instead of_ playing from [`data`]. Set this to `None` to play from audio
|
||||
/// data in [`data`] instead.
|
||||
pub generator: Option<Box<dyn AudioGenerator>>,
|
||||
/// The volume level to play this channel at. 1.0 is "normal", 0.0 is completely silent.
|
||||
pub volume: f32,
|
||||
/// The current playback position (index). 0 is the start of playback. The end position is
|
||||
/// either the (current) size of the [`data`] buffer or dependant on the implementation of this
|
||||
/// channel's current [`generator`] if not `None`.
|
||||
pub position: usize,
|
||||
/// Whether the channel is currently playing or not.
|
||||
pub playing: bool,
|
||||
/// Whether this channel is playing on a loop or not. If not, once the end of the [`data`]
|
||||
/// buffer is reached, or the [`AudioGenerator::gen_sample`] method returns `None`, playback
|
||||
/// on this channel will automatically stop and [`playing`] will be changed to `false`.
|
||||
pub loops: bool,
|
||||
/// The audio data buffer (samples) that this channel will play from, **only** if [`generator`]
|
||||
/// is `None`.
|
||||
pub data: Vec<u8>,
|
||||
/// An [`AudioGenerator`] instance that will be used to dynamically generate audio data to play
|
||||
/// on this channel _instead of_ playing from [`data`]. Set this to `None` to play from audio
|
||||
/// data in [`data`] instead.
|
||||
pub generator: Option<Box<dyn AudioGenerator>>,
|
||||
/// The volume level to play this channel at. 1.0 is "normal", 0.0 is completely silent.
|
||||
pub volume: f32,
|
||||
/// The current playback position (index). 0 is the start of playback. The end position is
|
||||
/// either the (current) size of the [`data`] buffer or dependant on the implementation of this
|
||||
/// channel's current [`generator`] if not `None`.
|
||||
pub position: usize,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for AudioChannel {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AudioChannel")
|
||||
.field("playing", &self.playing)
|
||||
.field("loops", &self.loops)
|
||||
.field("data.len()", &self.data.len())
|
||||
.field("generator", match self.generator {
|
||||
Some(..) => &"Some(..)",
|
||||
None => &"None",
|
||||
})
|
||||
.field("volume", &self.volume)
|
||||
.field("position", &self.position)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("AudioChannel")
|
||||
.field("playing", &self.playing)
|
||||
.field("loops", &self.loops)
|
||||
.field("data.len()", &self.data.len())
|
||||
.field("generator", match self.generator {
|
||||
Some(..) => &"Some(..)",
|
||||
None => &"None",
|
||||
})
|
||||
.field("volume", &self.volume)
|
||||
.field("position", &self.position)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioChannel {
|
||||
pub fn new() -> Self {
|
||||
AudioChannel {
|
||||
playing: false,
|
||||
loops: false,
|
||||
volume: 1.0,
|
||||
position: 0,
|
||||
generator: None,
|
||||
data: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
AudioChannel {
|
||||
playing: false,
|
||||
loops: false,
|
||||
volume: 1.0,
|
||||
position: 0,
|
||||
generator: None,
|
||||
data: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the audio sample for the given position, or `None` if that position is invalid.
|
||||
#[inline]
|
||||
fn data_at(&mut self, position: usize) -> Option<u8> {
|
||||
if let Some(generator) = &mut self.generator {
|
||||
generator.gen_sample(position)
|
||||
} else {
|
||||
self.data.get(position).copied()
|
||||
}
|
||||
}
|
||||
/// Returns the audio sample for the given position, or `None` if that position is invalid.
|
||||
#[inline]
|
||||
fn data_at(&mut self, position: usize) -> Option<u8> {
|
||||
if let Some(generator) = &mut self.generator {
|
||||
generator.gen_sample(position)
|
||||
} else {
|
||||
self.data.get(position).copied()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the next sample from this channel's buffer. If this channel's buffer is done
|
||||
/// playing or there is no buffer data at all, `None` is returned. If the next sample was
|
||||
/// successfully loaded from the buffer, the channel's current position is advanced by 1.
|
||||
///
|
||||
/// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8`
|
||||
/// value centered around 0 (meaning the returned sample will be within the range -128 to 127
|
||||
/// instead of 0 to 255).
|
||||
#[inline]
|
||||
fn next_sample(&mut self) -> Option<i16> {
|
||||
if let Some(sample) = self.data_at(self.position) {
|
||||
self.position += 1;
|
||||
Some(sample as i16 - 128)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Returns the next sample from this channel's buffer. If this channel's buffer is done
|
||||
/// playing or there is no buffer data at all, `None` is returned. If the next sample was
|
||||
/// successfully loaded from the buffer, the channel's current position is advanced by 1.
|
||||
///
|
||||
/// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8`
|
||||
/// value centered around 0 (meaning the returned sample will be within the range -128 to 127
|
||||
/// instead of 0 to 255).
|
||||
#[inline]
|
||||
fn next_sample(&mut self) -> Option<i16> {
|
||||
if let Some(sample) = self.data_at(self.position) {
|
||||
self.position += 1;
|
||||
Some(sample as i16 - 128)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Samples the channel's current audio buffer, advancing the position within that buffer by 1.
|
||||
/// The channel will automatically stop playing when the end of the buffer is reached and if
|
||||
/// the channel is not set to loop. `None` is returned if no data can be read from the buffer
|
||||
/// for any reason, or if the channel is not currently playing.
|
||||
///
|
||||
/// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8`
|
||||
/// value centered around 0 (meaning the returned sample will be within the range -128 to 127
|
||||
/// instead of 0 to 255).
|
||||
#[inline]
|
||||
pub fn sample(&mut self) -> Option<i16> {
|
||||
if !self.playing {
|
||||
return None;
|
||||
}
|
||||
/// Samples the channel's current audio buffer, advancing the position within that buffer by 1.
|
||||
/// The channel will automatically stop playing when the end of the buffer is reached and if
|
||||
/// the channel is not set to loop. `None` is returned if no data can be read from the buffer
|
||||
/// for any reason, or if the channel is not currently playing.
|
||||
///
|
||||
/// The returned sample will be a byte value, but in an `i16` with the buffer's original `u8`
|
||||
/// value centered around 0 (meaning the returned sample will be within the range -128 to 127
|
||||
/// instead of 0 to 255).
|
||||
#[inline]
|
||||
pub fn sample(&mut self) -> Option<i16> {
|
||||
if !self.playing {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(sample) = self.next_sample() {
|
||||
Some((sample as f32 * self.volume) as i16)
|
||||
} else {
|
||||
if self.loops {
|
||||
self.position = 0;
|
||||
None
|
||||
} else {
|
||||
self.stop();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(sample) = self.next_sample() {
|
||||
Some((sample as f32 * self.volume) as i16)
|
||||
} else {
|
||||
if self.loops {
|
||||
self.position = 0;
|
||||
None
|
||||
} else {
|
||||
self.stop();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets the audio channel to a "blank slate", clearing the audio buffer, setting no current
|
||||
/// audio generator, and turning playback off.
|
||||
#[inline]
|
||||
pub fn reset(&mut self) {
|
||||
self.data.clear();
|
||||
self.generator = None;
|
||||
self.position = 0;
|
||||
self.playing = false;
|
||||
}
|
||||
/// Resets the audio channel to a "blank slate", clearing the audio buffer, setting no current
|
||||
/// audio generator, and turning playback off.
|
||||
#[inline]
|
||||
pub fn reset(&mut self) {
|
||||
self.data.clear();
|
||||
self.generator = None;
|
||||
self.position = 0;
|
||||
self.playing = false;
|
||||
}
|
||||
|
||||
/// Copies the data from the given audio buffer into this channel's buffer (clearing it first,
|
||||
/// and extending the size of the buffer if necessary) and then begins playback from position 0.
|
||||
/// This also sets the associated [`generator`] to `None`.
|
||||
#[inline]
|
||||
pub fn play_buffer(&mut self, buffer: &AudioBuffer, loops: bool) {
|
||||
self.data.clear();
|
||||
self.data.extend(&buffer.data);
|
||||
self.generator = None;
|
||||
self.position = 0;
|
||||
self.playing = true;
|
||||
self.loops = loops;
|
||||
}
|
||||
/// Copies the data from the given audio buffer into this channel's buffer (clearing it first,
|
||||
/// and extending the size of the buffer if necessary) and then begins playback from position 0.
|
||||
/// This also sets the associated [`generator`] to `None`.
|
||||
#[inline]
|
||||
pub fn play_buffer(&mut self, buffer: &AudioBuffer, loops: bool) {
|
||||
self.data.clear();
|
||||
self.data.extend(&buffer.data);
|
||||
self.generator = None;
|
||||
self.position = 0;
|
||||
self.playing = true;
|
||||
self.loops = loops;
|
||||
}
|
||||
|
||||
/// Begins playback on this channel from the given [`AudioGenerator`] instance from position 0.
|
||||
/// This also clears the existing audio buffer contents.
|
||||
#[inline]
|
||||
pub fn play_generator(&mut self, generator: Box<dyn AudioGenerator>, loops: bool) {
|
||||
self.data.clear();
|
||||
self.generator = Some(generator);
|
||||
self.position = 0;
|
||||
self.playing = true;
|
||||
self.loops = loops;
|
||||
}
|
||||
/// Begins playback on this channel from the given [`AudioGenerator`] instance from position 0.
|
||||
/// This also clears the existing audio buffer contents.
|
||||
#[inline]
|
||||
pub fn play_generator(&mut self, generator: Box<dyn AudioGenerator>, loops: bool) {
|
||||
self.data.clear();
|
||||
self.generator = Some(generator);
|
||||
self.position = 0;
|
||||
self.playing = true;
|
||||
self.loops = loops;
|
||||
}
|
||||
|
||||
/// Returns true if this channel has something that can be played back currently.
|
||||
#[inline]
|
||||
pub fn is_playable(&self) -> bool {
|
||||
!self.data.is_empty() || self.generator.is_some()
|
||||
}
|
||||
/// Returns true if this channel has something that can be played back currently.
|
||||
#[inline]
|
||||
pub fn is_playable(&self) -> bool {
|
||||
!self.data.is_empty() || self.generator.is_some()
|
||||
}
|
||||
|
||||
/// Begins playback on this channel, only if playback is currently possible with its current
|
||||
/// state (if it has some sample data in the buffer or if an [`AudioGenerator`] is set).
|
||||
/// Resets the position to 0 if playback is started and returns true, otherwise returns false.
|
||||
#[inline]
|
||||
pub fn play(&mut self, loops: bool) -> bool {
|
||||
if self.is_playable() {
|
||||
self.position = 0;
|
||||
self.playing = true;
|
||||
self.loops = loops;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
/// Begins playback on this channel, only if playback is currently possible with its current
|
||||
/// state (if it has some sample data in the buffer or if an [`AudioGenerator`] is set).
|
||||
/// Resets the position to 0 if playback is started and returns true, otherwise returns false.
|
||||
#[inline]
|
||||
pub fn play(&mut self, loops: bool) -> bool {
|
||||
if self.is_playable() {
|
||||
self.position = 0;
|
||||
self.playing = true;
|
||||
self.loops = loops;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Stops playback on this channel.
|
||||
#[inline]
|
||||
pub fn stop(&mut self) {
|
||||
self.playing = false;
|
||||
}
|
||||
/// Stops playback on this channel.
|
||||
#[inline]
|
||||
pub fn stop(&mut self) {
|
||||
self.playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum AudioDeviceError {
|
||||
#[error("That buffer's AudioSpec does not match the device's AudioSpec")]
|
||||
AudioSpecMismatch,
|
||||
#[error("That buffer's AudioSpec does not match the device's AudioSpec")]
|
||||
AudioSpecMismatch,
|
||||
|
||||
#[error("The channel index {0} is out of range")]
|
||||
ChannelIndexOutOfRange(usize),
|
||||
#[error("The channel index {0} is out of range")]
|
||||
ChannelIndexOutOfRange(usize),
|
||||
}
|
||||
|
||||
/// Represents the audio device and performs mixing of all of the [`AudioChannel`]s that are
|
||||
|
@ -189,214 +189,214 @@ pub enum AudioDeviceError {
|
|||
/// [`Audio::lock`].
|
||||
#[derive(Debug)]
|
||||
pub struct AudioDevice {
|
||||
spec: AudioSpec,
|
||||
channels: Vec<AudioChannel>,
|
||||
pub volume: f32,
|
||||
spec: AudioSpec,
|
||||
channels: Vec<AudioChannel>,
|
||||
pub volume: f32,
|
||||
}
|
||||
|
||||
/// SDL audio callback implementation which performs audio mixing, generating the final sample data
|
||||
/// that will be played by the system's audio device.
|
||||
impl AudioCallback for AudioDevice {
|
||||
type Channel = u8;
|
||||
type Channel = u8;
|
||||
|
||||
fn callback(&mut self, out: &mut [u8]) {
|
||||
for dest in out.iter_mut() {
|
||||
let mut sample: i16 = 0;
|
||||
for channel in self.channels.iter_mut() {
|
||||
if let Some(this_sample) = channel.sample() {
|
||||
sample += this_sample;
|
||||
}
|
||||
}
|
||||
sample = ((sample as f32) * self.volume) as i16;
|
||||
*dest = (sample.clamp(-128, 127) + 128) as u8;
|
||||
}
|
||||
}
|
||||
fn callback(&mut self, out: &mut [u8]) {
|
||||
for dest in out.iter_mut() {
|
||||
let mut sample: i16 = 0;
|
||||
for channel in self.channels.iter_mut() {
|
||||
if let Some(this_sample) = channel.sample() {
|
||||
sample += this_sample;
|
||||
}
|
||||
}
|
||||
sample = ((sample as f32) * self.volume) as i16;
|
||||
*dest = (sample.clamp(-128, 127) + 128) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioDevice {
|
||||
/// Creates a new [`AudioDevice`] instance, using the given spec as its playback format.
|
||||
pub fn new(spec: AudioSpec) -> Self {
|
||||
let mut channels = Vec::new();
|
||||
for _ in 0..NUM_CHANNELS {
|
||||
channels.push(AudioChannel::new());
|
||||
}
|
||||
AudioDevice {
|
||||
spec,
|
||||
channels,
|
||||
volume: 1.0,
|
||||
}
|
||||
}
|
||||
/// Creates a new [`AudioDevice`] instance, using the given spec as its playback format.
|
||||
pub fn new(spec: AudioSpec) -> Self {
|
||||
let mut channels = Vec::new();
|
||||
for _ in 0..NUM_CHANNELS {
|
||||
channels.push(AudioChannel::new());
|
||||
}
|
||||
AudioDevice {
|
||||
spec,
|
||||
channels,
|
||||
volume: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the spec that this device is currently set to play. All audio to be played via
|
||||
/// this device must be pre-converted to match this spec!
|
||||
#[inline]
|
||||
pub fn spec(&self) -> &AudioSpec {
|
||||
&self.spec
|
||||
}
|
||||
/// Returns the spec that this device is currently set to play. All audio to be played via
|
||||
/// this device must be pre-converted to match this spec!
|
||||
#[inline]
|
||||
pub fn spec(&self) -> &AudioSpec {
|
||||
&self.spec
|
||||
}
|
||||
|
||||
/// Returns true if any of the audio channels are currently playing, false otherwise.
|
||||
#[inline]
|
||||
pub fn is_playing(&self) -> bool {
|
||||
self.channels.iter().any(|channel| channel.playing)
|
||||
}
|
||||
/// Returns true if any of the audio channels are currently playing, false otherwise.
|
||||
#[inline]
|
||||
pub fn is_playing(&self) -> bool {
|
||||
self.channels.iter().any(|channel| channel.playing)
|
||||
}
|
||||
|
||||
/// Stops the specified channel's playback, or does nothing if that channel was not currently
|
||||
/// playing. This does not affect the channel's other state (data buffer, etc).
|
||||
pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> {
|
||||
if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.channels[channel_index].stop();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Stops the specified channel's playback, or does nothing if that channel was not currently
|
||||
/// playing. This does not affect the channel's other state (data buffer, etc).
|
||||
pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> {
|
||||
if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.channels[channel_index].stop();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Stops playback of all channels.
|
||||
pub fn stop_all(&mut self) {
|
||||
for channel in self.channels.iter_mut() {
|
||||
channel.stop();
|
||||
}
|
||||
}
|
||||
/// Stops playback of all channels.
|
||||
pub fn stop_all(&mut self) {
|
||||
for channel in self.channels.iter_mut() {
|
||||
channel.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to play the given [`AudioBuffer`] on the first channel found that is not already
|
||||
/// playing. If a free channel is found, playback will be started by copying the buffer's
|
||||
/// contents to the channel. The index of the channel is returned. If playback was not started
|
||||
/// because no channel is free currently, then `None` is returned.
|
||||
pub fn play_buffer(
|
||||
&mut self,
|
||||
buffer: &AudioBuffer,
|
||||
loops: bool,
|
||||
) -> Result<Option<usize>, AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else {
|
||||
if let Some((index, channel)) = self.stopped_channels_iter_mut().enumerate().next() {
|
||||
channel.play_buffer(buffer, loops);
|
||||
Ok(Some(index))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Tries to play the given [`AudioBuffer`] on the first channel found that is not already
|
||||
/// playing. If a free channel is found, playback will be started by copying the buffer's
|
||||
/// contents to the channel. The index of the channel is returned. If playback was not started
|
||||
/// because no channel is free currently, then `None` is returned.
|
||||
pub fn play_buffer(
|
||||
&mut self,
|
||||
buffer: &AudioBuffer,
|
||||
loops: bool,
|
||||
) -> Result<Option<usize>, AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else {
|
||||
if let Some((index, channel)) = self.stopped_channels_iter_mut().enumerate().next() {
|
||||
channel.play_buffer(buffer, loops);
|
||||
Ok(Some(index))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Plays the given [`AudioBuffer`] on the specified channel. Whatever that channel was playing
|
||||
/// will be interrupted and replaced with a copy of the given buffer's data.
|
||||
pub fn play_buffer_on_channel(
|
||||
&mut self,
|
||||
channel_index: usize,
|
||||
buffer: &AudioBuffer,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.channels[channel_index].play_buffer(buffer, loops);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Plays the given [`AudioBuffer`] on the specified channel. Whatever that channel was playing
|
||||
/// will be interrupted and replaced with a copy of the given buffer's data.
|
||||
pub fn play_buffer_on_channel(
|
||||
&mut self,
|
||||
channel_index: usize,
|
||||
buffer: &AudioBuffer,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.channels[channel_index].play_buffer(buffer, loops);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to play the given [`AudioGenerator`] on the first channel found that is not already
|
||||
/// playing. If a free channel is found, playback will be started and the index of the channel
|
||||
/// will be returned. If playback was not started because no channel is free currently, then
|
||||
/// `None` is returned.
|
||||
pub fn play_generator(
|
||||
&mut self,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
) -> Result<Option<usize>, AudioDeviceError> {
|
||||
if let Some((index, channel)) = self.stopped_channels_iter_mut().enumerate().next() {
|
||||
channel.play_generator(generator, loops);
|
||||
Ok(Some(index))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
/// Tries to play the given [`AudioGenerator`] on the first channel found that is not already
|
||||
/// playing. If a free channel is found, playback will be started and the index of the channel
|
||||
/// will be returned. If playback was not started because no channel is free currently, then
|
||||
/// `None` is returned.
|
||||
pub fn play_generator(
|
||||
&mut self,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
) -> Result<Option<usize>, AudioDeviceError> {
|
||||
if let Some((index, channel)) = self.stopped_channels_iter_mut().enumerate().next() {
|
||||
channel.play_generator(generator, loops);
|
||||
Ok(Some(index))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Plays the given [`AudioGenerator`] on the specified channel. Whatever that channel was
|
||||
/// playing will be interrupted and replaced.
|
||||
pub fn play_generator_on_channel(
|
||||
&mut self,
|
||||
channel_index: usize,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.channels[channel_index].play_generator(generator, loops);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Plays the given [`AudioGenerator`] on the specified channel. Whatever that channel was
|
||||
/// playing will be interrupted and replaced.
|
||||
pub fn play_generator_on_channel(
|
||||
&mut self,
|
||||
channel_index: usize,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.channels[channel_index].play_generator(generator, loops);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator of any [`AudioChannel`]s that are currently playing.
|
||||
#[inline]
|
||||
pub fn playing_channels_iter(&mut self) -> impl Iterator<Item = &AudioChannel> {
|
||||
self.channels.iter().filter(|channel| channel.playing)
|
||||
}
|
||||
/// Returns an iterator of any [`AudioChannel`]s that are currently playing.
|
||||
#[inline]
|
||||
pub fn playing_channels_iter(&mut self) -> impl Iterator<Item=&AudioChannel> {
|
||||
self.channels.iter().filter(|channel| channel.playing)
|
||||
}
|
||||
|
||||
/// Returns an iterator of mutable [`AudioChannel`]s that are currently playing.
|
||||
#[inline]
|
||||
pub fn playing_channels_iter_mut(&mut self) -> impl Iterator<Item = &mut AudioChannel> {
|
||||
self.channels.iter_mut().filter(|channel| channel.playing)
|
||||
}
|
||||
/// Returns an iterator of mutable [`AudioChannel`]s that are currently playing.
|
||||
#[inline]
|
||||
pub fn playing_channels_iter_mut(&mut self) -> impl Iterator<Item=&mut AudioChannel> {
|
||||
self.channels.iter_mut().filter(|channel| channel.playing)
|
||||
}
|
||||
|
||||
/// Returns an iterator of [`AudioChannel`]s that are not currently playing.
|
||||
#[inline]
|
||||
pub fn stopped_channels_iter(&mut self) -> impl Iterator<Item = &AudioChannel> {
|
||||
self.channels.iter().filter(|channel| !channel.playing)
|
||||
}
|
||||
/// Returns an iterator of [`AudioChannel`]s that are not currently playing.
|
||||
#[inline]
|
||||
pub fn stopped_channels_iter(&mut self) -> impl Iterator<Item=&AudioChannel> {
|
||||
self.channels.iter().filter(|channel| !channel.playing)
|
||||
}
|
||||
|
||||
/// Returns an iterator of mutable [`AudioChannel`]s that are not currently playing.
|
||||
#[inline]
|
||||
pub fn stopped_channels_iter_mut(&mut self) -> impl Iterator<Item = &mut AudioChannel> {
|
||||
self.channels.iter_mut().filter(|channel| !channel.playing)
|
||||
}
|
||||
/// Returns an iterator of mutable [`AudioChannel`]s that are not currently playing.
|
||||
#[inline]
|
||||
pub fn stopped_channels_iter_mut(&mut self) -> impl Iterator<Item=&mut AudioChannel> {
|
||||
self.channels.iter_mut().filter(|channel| !channel.playing)
|
||||
}
|
||||
|
||||
/// Returns an iterator of all [`AudioChannel`]s.
|
||||
#[inline]
|
||||
pub fn channels_iter(&mut self) -> impl Iterator<Item = &AudioChannel> {
|
||||
self.channels.iter()
|
||||
}
|
||||
/// Returns an iterator of all [`AudioChannel`]s.
|
||||
#[inline]
|
||||
pub fn channels_iter(&mut self) -> impl Iterator<Item=&AudioChannel> {
|
||||
self.channels.iter()
|
||||
}
|
||||
|
||||
/// Returns an iterator of all [`AudioChannel`]s as mutable references.
|
||||
#[inline]
|
||||
pub fn channels_iter_mut(&mut self) -> impl Iterator<Item = &mut AudioChannel> {
|
||||
self.channels.iter_mut()
|
||||
}
|
||||
/// Returns an iterator of all [`AudioChannel`]s as mutable references.
|
||||
#[inline]
|
||||
pub fn channels_iter_mut(&mut self) -> impl Iterator<Item=&mut AudioChannel> {
|
||||
self.channels.iter_mut()
|
||||
}
|
||||
|
||||
/// Returns a reference to the specified [`AudioChannel`] or `None` if the index specified
|
||||
/// is not valid.
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&AudioChannel> {
|
||||
self.channels.get(index)
|
||||
}
|
||||
/// Returns a reference to the specified [`AudioChannel`] or `None` if the index specified
|
||||
/// is not valid.
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&AudioChannel> {
|
||||
self.channels.get(index)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the specified [`AudioChannel`] or `None` if the index
|
||||
/// specified is not valid.
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut AudioChannel> {
|
||||
self.channels.get_mut(index)
|
||||
}
|
||||
/// Returns a mutable reference to the specified [`AudioChannel`] or `None` if the index
|
||||
/// specified is not valid.
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut AudioChannel> {
|
||||
self.channels.get_mut(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for AudioDevice {
|
||||
type Output = AudioChannel;
|
||||
type Output = AudioChannel;
|
||||
|
||||
/// Returns a reference to the specified [`AudioChannel`] or panics if the index specified is
|
||||
/// not valid.
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
/// Returns a reference to the specified [`AudioChannel`] or panics if the index specified is
|
||||
/// not valid.
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for AudioDevice {
|
||||
/// Returns a mutable reference to the specified [`AudioChannel`] or panics if the index
|
||||
/// specified is not valid.
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.get_mut(index).unwrap()
|
||||
}
|
||||
/// Returns a mutable reference to the specified [`AudioChannel`] or panics if the index
|
||||
/// specified is not valid.
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,43 +32,43 @@ pub const TARGET_AUDIO_CHANNELS: u8 = 1;
|
|||
/// to know what format an audio buffer is in and to specify conversion formats, etc.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct AudioSpec {
|
||||
frequency: u32,
|
||||
channels: u8,
|
||||
format: AudioFormat,
|
||||
frequency: u32,
|
||||
channels: u8,
|
||||
format: AudioFormat,
|
||||
}
|
||||
|
||||
impl AudioSpec {
|
||||
/// Creates a new `AudioSpec` with the properties specified.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `frequency`: the frequency of the audio
|
||||
/// * `channels`: the number of channels of the audio (e.g. 1 = mono, 2 = stereo, etc)
|
||||
/// * `format`: indicates the format of the bytes making up the audio buffer.
|
||||
pub fn new(frequency: u32, channels: u8, format: AudioFormat) -> Self {
|
||||
AudioSpec {
|
||||
frequency,
|
||||
channels,
|
||||
format,
|
||||
}
|
||||
}
|
||||
/// Creates a new `AudioSpec` with the properties specified.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `frequency`: the frequency of the audio
|
||||
/// * `channels`: the number of channels of the audio (e.g. 1 = mono, 2 = stereo, etc)
|
||||
/// * `format`: indicates the format of the bytes making up the audio buffer.
|
||||
pub fn new(frequency: u32, channels: u8, format: AudioFormat) -> Self {
|
||||
AudioSpec {
|
||||
frequency,
|
||||
channels,
|
||||
format,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn frequency(&self) -> u32 {
|
||||
self.frequency
|
||||
}
|
||||
#[inline]
|
||||
pub fn frequency(&self) -> u32 {
|
||||
self.frequency
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn channels(&self) -> u8 {
|
||||
self.channels
|
||||
}
|
||||
#[inline]
|
||||
pub fn channels(&self) -> u8 {
|
||||
self.channels
|
||||
}
|
||||
|
||||
/// An SDL2 [`sdl2::audio::AudioFormat`] value indicating the audio format of the bytes making
|
||||
/// up an audio buffer.
|
||||
#[inline]
|
||||
pub fn format(&self) -> AudioFormat {
|
||||
self.format
|
||||
}
|
||||
/// An SDL2 [`sdl2::audio::AudioFormat`] value indicating the audio format of the bytes making
|
||||
/// up an audio buffer.
|
||||
#[inline]
|
||||
pub fn format(&self) -> AudioFormat {
|
||||
self.format
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -77,104 +77,104 @@ impl AudioSpec {
|
|||
|
||||
/// Used to implement custom/dynamic audio generation.
|
||||
pub trait AudioGenerator: Send {
|
||||
/// Generates and returns the sample for the given playback position. `None` is returned if
|
||||
/// there is no sample for that position (e.g. it might be past the "end").
|
||||
fn gen_sample(&mut self, position: usize) -> Option<u8>;
|
||||
/// Generates and returns the sample for the given playback position. `None` is returned if
|
||||
/// there is no sample for that position (e.g. it might be past the "end").
|
||||
fn gen_sample(&mut self, position: usize) -> Option<u8>;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum AudioError {
|
||||
#[error("Failed to open audio device for playback: {0}")]
|
||||
OpenDeviceFailed(String),
|
||||
#[error("Failed to open audio device for playback: {0}")]
|
||||
OpenDeviceFailed(String),
|
||||
}
|
||||
|
||||
/// Top-level abstraction over the system's audio output device. To play audio or change other
|
||||
/// playback properties, you will need to lock the audio device via [`Audio::lock`] to obtain an
|
||||
/// [`AudioDevice`].
|
||||
pub struct Audio {
|
||||
spec: AudioSpec,
|
||||
sdl_audio_device: sdl2::audio::AudioDevice<AudioDevice>,
|
||||
spec: AudioSpec,
|
||||
sdl_audio_device: sdl2::audio::AudioDevice<AudioDevice>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Audio {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Audio")
|
||||
.field("spec", &self.spec)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Audio")
|
||||
.field("spec", &self.spec)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl Audio {
|
||||
/// Creates a new [`Audio`] instance, wrapping the given SDL [`sdl2::audio::AudioSubsystem`].
|
||||
/// The `desired_spec` given specifies the target audio playback format.
|
||||
///
|
||||
/// Ideally, you should not be creating an instance of this yourself and should just use the
|
||||
/// one provided by [`crate::system::System`].
|
||||
pub fn new(
|
||||
desired_spec: AudioSpecDesired,
|
||||
sdl_audio_subsystem: &AudioSubsystem,
|
||||
) -> Result<Self, AudioError> {
|
||||
let mut spec = None;
|
||||
let sdl_audio_device =
|
||||
match sdl_audio_subsystem.open_playback(None, &desired_spec, |opened_spec| {
|
||||
let our_spec = AudioSpec::new(
|
||||
opened_spec.freq as u32,
|
||||
opened_spec.channels,
|
||||
opened_spec.format,
|
||||
);
|
||||
spec = Some(our_spec);
|
||||
AudioDevice::new(our_spec)
|
||||
}) {
|
||||
Ok(audio_device) => audio_device,
|
||||
Err(error) => return Err(AudioError::OpenDeviceFailed(error)),
|
||||
};
|
||||
/// Creates a new [`Audio`] instance, wrapping the given SDL [`sdl2::audio::AudioSubsystem`].
|
||||
/// The `desired_spec` given specifies the target audio playback format.
|
||||
///
|
||||
/// Ideally, you should not be creating an instance of this yourself and should just use the
|
||||
/// one provided by [`crate::system::System`].
|
||||
pub fn new(
|
||||
desired_spec: AudioSpecDesired,
|
||||
sdl_audio_subsystem: &AudioSubsystem,
|
||||
) -> Result<Self, AudioError> {
|
||||
let mut spec = None;
|
||||
let sdl_audio_device =
|
||||
match sdl_audio_subsystem.open_playback(None, &desired_spec, |opened_spec| {
|
||||
let our_spec = AudioSpec::new(
|
||||
opened_spec.freq as u32,
|
||||
opened_spec.channels,
|
||||
opened_spec.format,
|
||||
);
|
||||
spec = Some(our_spec);
|
||||
AudioDevice::new(our_spec)
|
||||
}) {
|
||||
Ok(audio_device) => audio_device,
|
||||
Err(error) => return Err(AudioError::OpenDeviceFailed(error)),
|
||||
};
|
||||
|
||||
if let Some(spec) = spec {
|
||||
Ok(Audio {
|
||||
spec,
|
||||
sdl_audio_device,
|
||||
})
|
||||
} else {
|
||||
Err(AudioError::OpenDeviceFailed(String::from(
|
||||
"Device initialization failed to set AudioSpec",
|
||||
)))
|
||||
}
|
||||
}
|
||||
if let Some(spec) = spec {
|
||||
Ok(Audio {
|
||||
spec,
|
||||
sdl_audio_device,
|
||||
})
|
||||
} else {
|
||||
Err(AudioError::OpenDeviceFailed(String::from(
|
||||
"Device initialization failed to set AudioSpec",
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns current audio device's audio specification/format for playback. All [`AudioBuffer`]s
|
||||
/// that are to be used for playback must be converted to match this before they can be played.
|
||||
#[inline]
|
||||
pub fn spec(&self) -> &AudioSpec {
|
||||
&self.spec
|
||||
}
|
||||
/// Returns current audio device's audio specification/format for playback. All [`AudioBuffer`]s
|
||||
/// that are to be used for playback must be converted to match this before they can be played.
|
||||
#[inline]
|
||||
pub fn spec(&self) -> &AudioSpec {
|
||||
&self.spec
|
||||
}
|
||||
|
||||
/// Returns the current status of the audio device (e.g. whether it is paused, stopped, etc).
|
||||
#[inline]
|
||||
pub fn status(&self) -> sdl2::audio::AudioStatus {
|
||||
self.sdl_audio_device.status()
|
||||
}
|
||||
/// Returns the current status of the audio device (e.g. whether it is paused, stopped, etc).
|
||||
#[inline]
|
||||
pub fn status(&self) -> sdl2::audio::AudioStatus {
|
||||
self.sdl_audio_device.status()
|
||||
}
|
||||
|
||||
/// Pauses all audio playback.
|
||||
#[inline]
|
||||
pub fn pause(&mut self) {
|
||||
self.sdl_audio_device.pause()
|
||||
}
|
||||
/// Pauses all audio playback.
|
||||
#[inline]
|
||||
pub fn pause(&mut self) {
|
||||
self.sdl_audio_device.pause()
|
||||
}
|
||||
|
||||
/// Resumes all audio playback.
|
||||
#[inline]
|
||||
pub fn resume(&mut self) {
|
||||
self.sdl_audio_device.resume()
|
||||
}
|
||||
/// Resumes all audio playback.
|
||||
#[inline]
|
||||
pub fn resume(&mut self) {
|
||||
self.sdl_audio_device.resume()
|
||||
}
|
||||
|
||||
/// Locks the audio device so that new audio data can be provided or playback altered. A
|
||||
/// [`AudioDevice`] instance is returned on successful lock which can be used to interact with
|
||||
/// the actual system's audio playback. The audio device is unlocked once this instance is
|
||||
/// dropped. Ideally, you will want to keep the audio device for **as _short_ a time as
|
||||
/// possible!**
|
||||
#[inline]
|
||||
pub fn lock(&mut self) -> sdl2::audio::AudioDeviceLockGuard<AudioDevice> {
|
||||
self.sdl_audio_device.lock()
|
||||
}
|
||||
/// Locks the audio device so that new audio data can be provided or playback altered. A
|
||||
/// [`AudioDevice`] instance is returned on successful lock which can be used to interact with
|
||||
/// the actual system's audio playback. The audio device is unlocked once this instance is
|
||||
/// dropped. Ideally, you will want to keep the audio device for **as _short_ a time as
|
||||
/// possible!**
|
||||
#[inline]
|
||||
pub fn lock(&mut self) -> sdl2::audio::AudioDeviceLockGuard<AudioDevice> {
|
||||
self.sdl_audio_device.lock()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,82 +4,82 @@ use std::rc::Rc;
|
|||
use crate::audio::*;
|
||||
|
||||
pub enum AudioCommand {
|
||||
StopChannel(usize),
|
||||
StopAllChannels,
|
||||
PlayBuffer {
|
||||
buffer: AudioBuffer,
|
||||
loops: bool,
|
||||
},
|
||||
PlayRcBuffer {
|
||||
buffer: Rc<AudioBuffer>,
|
||||
loops: bool,
|
||||
},
|
||||
PlayBufferOnChannel {
|
||||
channel: usize,
|
||||
buffer: AudioBuffer,
|
||||
loops: bool,
|
||||
},
|
||||
PlayRcBufferOnChannel {
|
||||
channel: usize,
|
||||
buffer: Rc<AudioBuffer>,
|
||||
loops: bool,
|
||||
},
|
||||
PlayGenerator {
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
},
|
||||
PlayGeneratorOnChannel {
|
||||
channel: usize,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
},
|
||||
StopChannel(usize),
|
||||
StopAllChannels,
|
||||
PlayBuffer {
|
||||
buffer: AudioBuffer,
|
||||
loops: bool,
|
||||
},
|
||||
PlayRcBuffer {
|
||||
buffer: Rc<AudioBuffer>,
|
||||
loops: bool,
|
||||
},
|
||||
PlayBufferOnChannel {
|
||||
channel: usize,
|
||||
buffer: AudioBuffer,
|
||||
loops: bool,
|
||||
},
|
||||
PlayRcBufferOnChannel {
|
||||
channel: usize,
|
||||
buffer: Rc<AudioBuffer>,
|
||||
loops: bool,
|
||||
},
|
||||
PlayGenerator {
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
},
|
||||
PlayGeneratorOnChannel {
|
||||
channel: usize,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for AudioCommand {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
use AudioCommand::*;
|
||||
match self {
|
||||
StopChannel(n) => write!(f, "StopChannel({})", n),
|
||||
StopAllChannels => write!(f, "StopAllChannels"),
|
||||
PlayBuffer { buffer, loops } => {
|
||||
f.debug_struct("PlayBuffer")
|
||||
.field("buffer", buffer)
|
||||
.field("loops", loops)
|
||||
.finish()
|
||||
},
|
||||
PlayRcBuffer { buffer, loops } => {
|
||||
f.debug_struct("PlayRcBuffer")
|
||||
.field("buffer", buffer)
|
||||
.field("loops", loops)
|
||||
.finish()
|
||||
},
|
||||
PlayBufferOnChannel { channel, buffer, loops } => {
|
||||
f.debug_struct("PlayBufferOnChannel")
|
||||
.field("channel", channel)
|
||||
.field("buffer", buffer)
|
||||
.field("loops", loops)
|
||||
.finish()
|
||||
},
|
||||
PlayRcBufferOnChannel { channel, buffer, loops } => {
|
||||
f.debug_struct("PlayRcBufferOnChannel")
|
||||
.field("channel", channel)
|
||||
.field("buffer", buffer)
|
||||
.field("loops", loops)
|
||||
.finish()
|
||||
},
|
||||
PlayGenerator { loops, .. } => {
|
||||
f.debug_struct("PlayGenerator")
|
||||
.field("loops", loops)
|
||||
.finish_non_exhaustive()
|
||||
},
|
||||
PlayGeneratorOnChannel { channel, loops, .. } => {
|
||||
f.debug_struct("PlayGeneratorOnChannel")
|
||||
.field("channel", channel)
|
||||
.field("loops", loops)
|
||||
.finish_non_exhaustive()
|
||||
},
|
||||
}
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
use AudioCommand::*;
|
||||
match self {
|
||||
StopChannel(n) => write!(f, "StopChannel({})", n),
|
||||
StopAllChannels => write!(f, "StopAllChannels"),
|
||||
PlayBuffer { buffer, loops } => {
|
||||
f.debug_struct("PlayBuffer")
|
||||
.field("buffer", buffer)
|
||||
.field("loops", loops)
|
||||
.finish()
|
||||
}
|
||||
PlayRcBuffer { buffer, loops } => {
|
||||
f.debug_struct("PlayRcBuffer")
|
||||
.field("buffer", buffer)
|
||||
.field("loops", loops)
|
||||
.finish()
|
||||
}
|
||||
PlayBufferOnChannel { channel, buffer, loops } => {
|
||||
f.debug_struct("PlayBufferOnChannel")
|
||||
.field("channel", channel)
|
||||
.field("buffer", buffer)
|
||||
.field("loops", loops)
|
||||
.finish()
|
||||
}
|
||||
PlayRcBufferOnChannel { channel, buffer, loops } => {
|
||||
f.debug_struct("PlayRcBufferOnChannel")
|
||||
.field("channel", channel)
|
||||
.field("buffer", buffer)
|
||||
.field("loops", loops)
|
||||
.finish()
|
||||
}
|
||||
PlayGenerator { loops, .. } => {
|
||||
f.debug_struct("PlayGenerator")
|
||||
.field("loops", loops)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
PlayGeneratorOnChannel { channel, loops, .. } => {
|
||||
f.debug_struct("PlayGeneratorOnChannel")
|
||||
.field("channel", channel)
|
||||
.field("loops", loops)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience abstraction that can be used to queue up commands to be issued to an
|
||||
|
@ -89,199 +89,199 @@ impl std::fmt::Debug for AudioCommand {
|
|||
/// manner.
|
||||
#[derive(Debug)]
|
||||
pub struct AudioQueue {
|
||||
spec: AudioSpec,
|
||||
commands: VecDeque<AudioCommand>,
|
||||
spec: AudioSpec,
|
||||
commands: VecDeque<AudioCommand>,
|
||||
}
|
||||
|
||||
impl AudioQueue {
|
||||
/// Creates and returns a new [`AudioQueue`] instance.
|
||||
pub fn new(audio: &Audio) -> Self {
|
||||
AudioQueue {
|
||||
spec: audio.spec,
|
||||
commands: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
/// Creates and returns a new [`AudioQueue`] instance.
|
||||
pub fn new(audio: &Audio) -> Self {
|
||||
AudioQueue {
|
||||
spec: audio.spec,
|
||||
commands: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the spec that this queue is currently set to play. All audio to be played via
|
||||
/// this queue must be pre-converted to match this spec! This spec is a copy of the one that
|
||||
/// was obtained from the [`Audio`] instance used to create this [`AudioQueue`].
|
||||
#[inline]
|
||||
pub fn spec(&self) -> &AudioSpec {
|
||||
&self.spec
|
||||
}
|
||||
/// Returns the spec that this queue is currently set to play. All audio to be played via
|
||||
/// this queue must be pre-converted to match this spec! This spec is a copy of the one that
|
||||
/// was obtained from the [`Audio`] instance used to create this [`AudioQueue`].
|
||||
#[inline]
|
||||
pub fn spec(&self) -> &AudioSpec {
|
||||
&self.spec
|
||||
}
|
||||
|
||||
/// Queues a stop command for the given channel.
|
||||
pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> {
|
||||
if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.commands.push_back(AudioCommand::StopChannel(channel_index));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Queues a stop command for the given channel.
|
||||
pub fn stop_channel(&mut self, channel_index: usize) -> Result<(), AudioDeviceError> {
|
||||
if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.commands.push_back(AudioCommand::StopChannel(channel_index));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues a command that will stop playback on all channels.
|
||||
pub fn stop_all(&mut self) {
|
||||
self.commands.push_back(AudioCommand::StopAllChannels);
|
||||
}
|
||||
/// Queues a command that will stop playback on all channels.
|
||||
pub fn stop_all(&mut self) {
|
||||
self.commands.push_back(AudioCommand::StopAllChannels);
|
||||
}
|
||||
|
||||
/// Queues a command to play a copy of the given [`AudioBuffer`]'s data. The buffer will be
|
||||
/// played on the first channel found that is not already playing. If all channels are already
|
||||
/// playing, then nothing will be done.
|
||||
pub fn play_buffer(
|
||||
&mut self,
|
||||
buffer: &AudioBuffer,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else {
|
||||
self.commands.push_back(AudioCommand::PlayBuffer {
|
||||
buffer: buffer.clone(),
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Queues a command to play a copy of the given [`AudioBuffer`]'s data. The buffer will be
|
||||
/// played on the first channel found that is not already playing. If all channels are already
|
||||
/// playing, then nothing will be done.
|
||||
pub fn play_buffer(
|
||||
&mut self,
|
||||
buffer: &AudioBuffer,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else {
|
||||
self.commands.push_back(AudioCommand::PlayBuffer {
|
||||
buffer: buffer.clone(),
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues a command to play the given [`AudioBuffer`]'s data. The buffer will be played on
|
||||
/// the first channel found that is not already playing. If all channels are already playing,
|
||||
/// then nothing will be done. This method is more performant than [`AudioQueue::play_buffer`],
|
||||
/// as that method will always immediately copy the given buffer to create the queued command.
|
||||
pub fn play_buffer_rc(
|
||||
&mut self,
|
||||
buffer: Rc<AudioBuffer>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else {
|
||||
self.commands.push_back(AudioCommand::PlayRcBuffer {
|
||||
buffer,
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Queues a command to play the given [`AudioBuffer`]'s data. The buffer will be played on
|
||||
/// the first channel found that is not already playing. If all channels are already playing,
|
||||
/// then nothing will be done. This method is more performant than [`AudioQueue::play_buffer`],
|
||||
/// as that method will always immediately copy the given buffer to create the queued command.
|
||||
pub fn play_buffer_rc(
|
||||
&mut self,
|
||||
buffer: Rc<AudioBuffer>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else {
|
||||
self.commands.push_back(AudioCommand::PlayRcBuffer {
|
||||
buffer,
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues a command to play a copy of the given [`AudioBuffer`]'s data on the channel
|
||||
/// specified. Whatever that channel was playing will be interrupted to begin playing this
|
||||
/// buffer.
|
||||
pub fn play_buffer_on_channel(
|
||||
&mut self,
|
||||
channel_index: usize,
|
||||
buffer: &AudioBuffer,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.commands.push_back(AudioCommand::PlayBufferOnChannel {
|
||||
channel: channel_index,
|
||||
buffer: buffer.clone(),
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Queues a command to play a copy of the given [`AudioBuffer`]'s data on the channel
|
||||
/// specified. Whatever that channel was playing will be interrupted to begin playing this
|
||||
/// buffer.
|
||||
pub fn play_buffer_on_channel(
|
||||
&mut self,
|
||||
channel_index: usize,
|
||||
buffer: &AudioBuffer,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.commands.push_back(AudioCommand::PlayBufferOnChannel {
|
||||
channel: channel_index,
|
||||
buffer: buffer.clone(),
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues a command to play the given [`AudioBuffer`]'s data on the channel specified. Whatever
|
||||
/// that channel was playing will be interrupted to begin playing this buffer. This method is
|
||||
/// more performant than [`AudioQueue::play_buffer_on_channel`], as that method will always
|
||||
/// immediately copy the given buffer to create the queued command.
|
||||
pub fn play_buffer_rc_on_channel(
|
||||
&mut self,
|
||||
channel_index: usize,
|
||||
buffer: Rc<AudioBuffer>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.commands.push_back(AudioCommand::PlayRcBufferOnChannel {
|
||||
channel: channel_index,
|
||||
buffer,
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
/// Queues a command to play the given [`AudioBuffer`]'s data on the channel specified. Whatever
|
||||
/// that channel was playing will be interrupted to begin playing this buffer. This method is
|
||||
/// more performant than [`AudioQueue::play_buffer_on_channel`], as that method will always
|
||||
/// immediately copy the given buffer to create the queued command.
|
||||
pub fn play_buffer_rc_on_channel(
|
||||
&mut self,
|
||||
channel_index: usize,
|
||||
buffer: Rc<AudioBuffer>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
if *buffer.spec() != self.spec {
|
||||
Err(AudioDeviceError::AudioSpecMismatch)
|
||||
} else if channel_index >= NUM_CHANNELS {
|
||||
Err(AudioDeviceError::ChannelIndexOutOfRange(channel_index))
|
||||
} else {
|
||||
self.commands.push_back(AudioCommand::PlayRcBufferOnChannel {
|
||||
channel: channel_index,
|
||||
buffer,
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues a command to play the given [`AudioGenerator`] on the first channel found that is
|
||||
/// not already playing. If all channels are already playing, then nothing will be done.
|
||||
pub fn play_generator(
|
||||
&mut self,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
self.commands.push_back(AudioCommand::PlayGenerator { generator, loops });
|
||||
Ok(())
|
||||
}
|
||||
/// Queues a command to play the given [`AudioGenerator`] on the first channel found that is
|
||||
/// not already playing. If all channels are already playing, then nothing will be done.
|
||||
pub fn play_generator(
|
||||
&mut self,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
self.commands.push_back(AudioCommand::PlayGenerator { generator, loops });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Queues a command to play the given [`AudioGenerator`] on the channel specified. Whatever
|
||||
/// that channel was playing will be interrupted to begin playing this generator.
|
||||
pub fn play_generator_on_channel(
|
||||
&mut self,
|
||||
channel_index: usize,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
self.commands.push_back(AudioCommand::PlayGeneratorOnChannel {
|
||||
channel: channel_index,
|
||||
generator,
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
/// Queues a command to play the given [`AudioGenerator`] on the channel specified. Whatever
|
||||
/// that channel was playing will be interrupted to begin playing this generator.
|
||||
pub fn play_generator_on_channel(
|
||||
&mut self,
|
||||
channel_index: usize,
|
||||
generator: Box<dyn AudioGenerator>,
|
||||
loops: bool,
|
||||
) -> Result<(), AudioDeviceError> {
|
||||
self.commands.push_back(AudioCommand::PlayGeneratorOnChannel {
|
||||
channel: channel_index,
|
||||
generator,
|
||||
loops,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Flushes the queued commands, issuing them in the same order they were created, to the
|
||||
/// given [`AudioDevice`].
|
||||
pub fn apply_to_device(&mut self, device: &mut AudioDevice) -> Result<(), AudioDeviceError> {
|
||||
loop {
|
||||
if let Some(command) = self.commands.pop_front() {
|
||||
use AudioCommand::*;
|
||||
match command {
|
||||
StopChannel(channel_index) => {
|
||||
device.stop_channel(channel_index)?;
|
||||
},
|
||||
StopAllChannels => {
|
||||
device.stop_all();
|
||||
},
|
||||
PlayBuffer { buffer, loops } => {
|
||||
device.play_buffer(&buffer, loops)?;
|
||||
}
|
||||
PlayRcBuffer { buffer, loops } => {
|
||||
device.play_buffer(&buffer, loops)?;
|
||||
},
|
||||
PlayBufferOnChannel { channel, buffer, loops } => {
|
||||
device.play_buffer_on_channel(channel, &buffer, loops)?;
|
||||
}
|
||||
PlayRcBufferOnChannel { channel, buffer, loops } => {
|
||||
device.play_buffer_on_channel(channel, &buffer, loops)?;
|
||||
},
|
||||
PlayGenerator { generator, loops } => {
|
||||
device.play_generator(generator, loops)?;
|
||||
},
|
||||
PlayGeneratorOnChannel { channel, generator, loops } => {
|
||||
device.play_generator_on_channel(channel, generator, loops)?;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Flushes the queued commands, issuing them in the same order they were created, to the
|
||||
/// given [`AudioDevice`].
|
||||
pub fn apply_to_device(&mut self, device: &mut AudioDevice) -> Result<(), AudioDeviceError> {
|
||||
loop {
|
||||
if let Some(command) = self.commands.pop_front() {
|
||||
use AudioCommand::*;
|
||||
match command {
|
||||
StopChannel(channel_index) => {
|
||||
device.stop_channel(channel_index)?;
|
||||
}
|
||||
StopAllChannels => {
|
||||
device.stop_all();
|
||||
}
|
||||
PlayBuffer { buffer, loops } => {
|
||||
device.play_buffer(&buffer, loops)?;
|
||||
}
|
||||
PlayRcBuffer { buffer, loops } => {
|
||||
device.play_buffer(&buffer, loops)?;
|
||||
}
|
||||
PlayBufferOnChannel { channel, buffer, loops } => {
|
||||
device.play_buffer_on_channel(channel, &buffer, loops)?;
|
||||
}
|
||||
PlayRcBufferOnChannel { channel, buffer, loops } => {
|
||||
device.play_buffer_on_channel(channel, &buffer, loops)?;
|
||||
}
|
||||
PlayGenerator { generator, loops } => {
|
||||
device.play_generator(generator, loops)?;
|
||||
}
|
||||
PlayGeneratorOnChannel { channel, generator, loops } => {
|
||||
device.play_generator_on_channel(channel, generator, loops)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Flushes the queued commands, issuing them in the same order they were created, to the
|
||||
/// given [`Audio`] instance. This method automatically handles obtaining a locked
|
||||
/// [`AudioDevice`], and so is a bit more convenient to use if you don't actually need to
|
||||
/// interact with the [`AudioDevice`] itself in your code.
|
||||
pub fn apply(&mut self, audio: &mut Audio) -> Result<(), AudioDeviceError> {
|
||||
let mut device = audio.lock();
|
||||
self.apply_to_device(&mut device)
|
||||
}
|
||||
/// Flushes the queued commands, issuing them in the same order they were created, to the
|
||||
/// given [`Audio`] instance. This method automatically handles obtaining a locked
|
||||
/// [`AudioDevice`], and so is a bit more convenient to use if you don't actually need to
|
||||
/// interact with the [`AudioDevice`] itself in your code.
|
||||
pub fn apply(&mut self, audio: &mut Audio) -> Result<(), AudioDeviceError> {
|
||||
let mut device = audio.lock();
|
||||
self.apply_to_device(&mut device)
|
||||
}
|
||||
}
|
|
@ -186,9 +186,9 @@ pub fn main_loop<ContextType, State>(
|
|||
mut app: ContextType,
|
||||
initial_state: State,
|
||||
) -> Result<(), MainLoopError>
|
||||
where
|
||||
ContextType: AppContext,
|
||||
State: AppState<ContextType> + 'static,
|
||||
where
|
||||
ContextType: AppContext,
|
||||
State: AppState<ContextType> + 'static,
|
||||
{
|
||||
let mut states = States::new();
|
||||
states.push(initial_state)?;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,47 +10,47 @@ pub type ListenerFn<EventType, ContextType> = fn(event: &EventType, &mut Context
|
|||
/// instance. The `EventType` here should usually be an application-specific "events" enum.
|
||||
#[derive(Clone)]
|
||||
pub struct EventPublisher<EventType> {
|
||||
queue: VecDeque<EventType>,
|
||||
queue: VecDeque<EventType>,
|
||||
}
|
||||
|
||||
impl<EventType> std::fmt::Debug for EventPublisher<EventType> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("EventPublisher")
|
||||
.field("queue.len()", &self.queue.len())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("EventPublisher")
|
||||
.field("queue.len()", &self.queue.len())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<EventType> EventPublisher<EventType> {
|
||||
pub fn new() -> Self {
|
||||
EventPublisher {
|
||||
queue: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
EventPublisher {
|
||||
queue: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of events that have been queued.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.queue.len()
|
||||
}
|
||||
/// Returns the number of events that have been queued.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.queue.len()
|
||||
}
|
||||
|
||||
/// Clears the current event queue. The events will not be processed/handled.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.queue.clear();
|
||||
}
|
||||
/// Clears the current event queue. The events will not be processed/handled.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.queue.clear();
|
||||
}
|
||||
|
||||
/// Pushes the given event to the back of the queue.
|
||||
#[inline]
|
||||
pub fn queue(&mut self, event: EventType) {
|
||||
self.queue.push_back(event);
|
||||
}
|
||||
/// Pushes the given event to the back of the queue.
|
||||
#[inline]
|
||||
pub fn queue(&mut self, event: EventType) {
|
||||
self.queue.push_back(event);
|
||||
}
|
||||
|
||||
fn take_queue(&mut self, destination: &mut VecDeque<EventType>) {
|
||||
destination.clear();
|
||||
destination.append(&mut self.queue);
|
||||
self.clear();
|
||||
}
|
||||
fn take_queue(&mut self, destination: &mut VecDeque<EventType>) {
|
||||
destination.clear();
|
||||
destination.append(&mut self.queue);
|
||||
self.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// A manager for application event listeners/handlers that can dispatch events queued up by a
|
||||
|
@ -63,280 +63,278 @@ impl<EventType> EventPublisher<EventType> {
|
|||
/// want available in all of your event listener/handler functions.
|
||||
#[derive(Clone)]
|
||||
pub struct EventListeners<EventType, ContextType> {
|
||||
listeners: Vec<ListenerFn<EventType, ContextType>>,
|
||||
dispatch_queue: VecDeque<EventType>,
|
||||
listeners: Vec<ListenerFn<EventType, ContextType>>,
|
||||
dispatch_queue: VecDeque<EventType>,
|
||||
}
|
||||
|
||||
impl<EventType, ContextType> std::fmt::Debug for EventListeners<EventType, ContextType> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("EventListeners")
|
||||
.field("listeners.len()", &self.listeners.len())
|
||||
.field("dispatch_queue.len()", &self.dispatch_queue.len())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("EventListeners")
|
||||
.field("listeners.len()", &self.listeners.len())
|
||||
.field("dispatch_queue.len()", &self.dispatch_queue.len())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<EventType, ContextType> EventListeners<EventType, ContextType> {
|
||||
pub fn new() -> Self {
|
||||
EventListeners {
|
||||
listeners: Vec::new(),
|
||||
dispatch_queue: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
EventListeners {
|
||||
listeners: Vec::new(),
|
||||
dispatch_queue: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of event listeners/handlers registered with this manager.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.listeners.len()
|
||||
}
|
||||
/// Returns the number of event listeners/handlers registered with this manager.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.listeners.len()
|
||||
}
|
||||
|
||||
/// Unregisters all event listeners/managers previously registered with this manager.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.listeners.clear();
|
||||
}
|
||||
/// Unregisters all event listeners/managers previously registered with this manager.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.listeners.clear();
|
||||
}
|
||||
|
||||
/// Adds/Registers the given event listener/handler function with this manager so that
|
||||
/// it will be called during dispatching of events. Returns true if the function was added.
|
||||
pub fn add(&mut self, listener: ListenerFn<EventType, ContextType>) -> bool {
|
||||
// HACK?: most advice i've seen right now for comparing function pointers suggests doing
|
||||
// this, but i've also come across comments suggesting there are times where this
|
||||
// might not be foolproof? (e.g. where generics or lifetimes come into play ... )
|
||||
if self.listeners.iter().any(|&l| l as usize == listener as usize) {
|
||||
false // don't add a duplicate listener
|
||||
} else {
|
||||
self.listeners.push(listener);
|
||||
true
|
||||
}
|
||||
}
|
||||
/// Adds/Registers the given event listener/handler function with this manager so that
|
||||
/// it will be called during dispatching of events. Returns true if the function was added.
|
||||
pub fn add(&mut self, listener: ListenerFn<EventType, ContextType>) -> bool {
|
||||
// HACK?: most advice i've seen right now for comparing function pointers suggests doing
|
||||
// this, but i've also come across comments suggesting there are times where this
|
||||
// might not be foolproof? (e.g. where generics or lifetimes come into play ... )
|
||||
if self.listeners.iter().any(|&l| l as usize == listener as usize) {
|
||||
false // don't add a duplicate listener
|
||||
} else {
|
||||
self.listeners.push(listener);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes/Unregisters the specified event listener/handler function from this manager.
|
||||
pub fn remove(&mut self, listener: ListenerFn<EventType, ContextType>) -> bool {
|
||||
let before_size = self.listeners.len();
|
||||
// HACK?: comparing function pointers -- see above "HACK?" comment. same concern here.
|
||||
self.listeners.retain(|&l| l as usize != listener as usize);
|
||||
// return true if the listener was removed
|
||||
return before_size != self.listeners.len()
|
||||
}
|
||||
/// Removes/Unregisters the specified event listener/handler function from this manager.
|
||||
pub fn remove(&mut self, listener: ListenerFn<EventType, ContextType>) -> bool {
|
||||
let before_size = self.listeners.len();
|
||||
// HACK?: comparing function pointers -- see above "HACK?" comment. same concern here.
|
||||
self.listeners.retain(|&l| l as usize != listener as usize);
|
||||
// return true if the listener was removed
|
||||
return before_size != self.listeners.len();
|
||||
}
|
||||
|
||||
/// Moves the queue from the given [`EventPublisher`] to this manager in preparation for
|
||||
/// dispatching the queued events via [`EventListeners::dispatch_queue`]. After calling this,
|
||||
/// the [`EventPublisher`]'s queue will be empty.
|
||||
pub fn take_queue_from(&mut self, publisher: &mut EventPublisher<EventType>) -> usize {
|
||||
publisher.take_queue(&mut self.dispatch_queue);
|
||||
self.dispatch_queue.len()
|
||||
}
|
||||
|
||||
/// Dispatches the previous obtained event queue (via a call to
|
||||
/// [`EventListeners::take_queue_from`]) to all of the registered event listeners/handlers,
|
||||
/// passing each of them the given context argument. Not all of the event listeners/handlers
|
||||
/// will necessarily be called for each event being dispatched depending on which ones handled
|
||||
/// which events.
|
||||
pub fn dispatch_queue(&mut self, context: &mut ContextType) {
|
||||
while let Some(event) = self.dispatch_queue.pop_front() {
|
||||
for listener in &self.listeners {
|
||||
if listener(&event, context) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Moves the queue from the given [`EventPublisher`] to this manager in preparation for
|
||||
/// dispatching the queued events via [`EventListeners::dispatch_queue`]. After calling this,
|
||||
/// the [`EventPublisher`]'s queue will be empty.
|
||||
pub fn take_queue_from(&mut self, publisher: &mut EventPublisher<EventType>) -> usize {
|
||||
publisher.take_queue(&mut self.dispatch_queue);
|
||||
self.dispatch_queue.len()
|
||||
}
|
||||
|
||||
/// Dispatches the previous obtained event queue (via a call to
|
||||
/// [`EventListeners::take_queue_from`]) to all of the registered event listeners/handlers,
|
||||
/// passing each of them the given context argument. Not all of the event listeners/handlers
|
||||
/// will necessarily be called for each event being dispatched depending on which ones handled
|
||||
/// which events.
|
||||
pub fn dispatch_queue(&mut self, context: &mut ContextType) {
|
||||
while let Some(event) = self.dispatch_queue.pop_front() {
|
||||
for listener in &self.listeners {
|
||||
if listener(&event, context) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
enum TestEvent {
|
||||
Dummy,
|
||||
Foobar(i32),
|
||||
Message(&'static str),
|
||||
}
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
enum TestEvent {
|
||||
Dummy,
|
||||
Foobar(i32),
|
||||
Message(&'static str),
|
||||
}
|
||||
|
||||
struct DummyContext;
|
||||
struct DummyContext;
|
||||
|
||||
struct TestContext {
|
||||
pub count: i32,
|
||||
pub events: Vec<TestEvent>,
|
||||
}
|
||||
struct TestContext {
|
||||
pub count: i32,
|
||||
pub events: Vec<TestEvent>,
|
||||
}
|
||||
|
||||
impl TestContext {
|
||||
pub fn new() -> Self {
|
||||
TestContext { count: 0, events: Vec::new() }
|
||||
}
|
||||
}
|
||||
impl TestContext {
|
||||
pub fn new() -> Self {
|
||||
TestContext { count: 0, events: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
fn dummy_listener(_event: &TestEvent, _context: &mut DummyContext) -> bool {
|
||||
false
|
||||
}
|
||||
fn dummy_listener(_event: &TestEvent, _context: &mut DummyContext) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn other_dummy_listener(_event: &TestEvent, _context: &mut DummyContext) -> bool {
|
||||
false
|
||||
}
|
||||
fn other_dummy_listener(_event: &TestEvent, _context: &mut DummyContext) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn event_logger(event: &TestEvent, context: &mut TestContext) -> bool {
|
||||
context.events.push(*event);
|
||||
false
|
||||
}
|
||||
fn event_logger(event: &TestEvent, context: &mut TestContext) -> bool {
|
||||
context.events.push(*event);
|
||||
false
|
||||
}
|
||||
|
||||
fn event_counter(_event: &TestEvent, context: &mut TestContext) -> bool {
|
||||
context.count += 1;
|
||||
false
|
||||
}
|
||||
fn event_counter(_event: &TestEvent, context: &mut TestContext) -> bool {
|
||||
context.count += 1;
|
||||
false
|
||||
}
|
||||
|
||||
fn message_filter(event: &TestEvent, _context: &mut TestContext) -> bool {
|
||||
match event {
|
||||
TestEvent::Message(s) => {
|
||||
if *s == "filter" {
|
||||
true // means event was handled, and no subsequent listeners should be called
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
fn message_filter(event: &TestEvent, _context: &mut TestContext) -> bool {
|
||||
match event {
|
||||
TestEvent::Message(s) => {
|
||||
if *s == "filter" {
|
||||
true // means event was handled, and no subsequent listeners should be called
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn adding_and_removing_listeners() {
|
||||
let mut listeners = EventListeners::<TestEvent, DummyContext>::new();
|
||||
#[test]
|
||||
pub fn adding_and_removing_listeners() {
|
||||
let mut listeners = EventListeners::<TestEvent, DummyContext>::new();
|
||||
|
||||
// add and remove
|
||||
assert_eq!(0, listeners.len());
|
||||
assert!(listeners.add(dummy_listener));
|
||||
assert_eq!(1, listeners.len());
|
||||
assert!(!listeners.add(dummy_listener));
|
||||
assert_eq!(1, listeners.len());
|
||||
assert!(listeners.remove(dummy_listener));
|
||||
assert_eq!(0, listeners.len());
|
||||
assert!(!listeners.remove(dummy_listener));
|
||||
assert_eq!(0, listeners.len());
|
||||
// add and remove
|
||||
assert_eq!(0, listeners.len());
|
||||
assert!(listeners.add(dummy_listener));
|
||||
assert_eq!(1, listeners.len());
|
||||
assert!(!listeners.add(dummy_listener));
|
||||
assert_eq!(1, listeners.len());
|
||||
assert!(listeners.remove(dummy_listener));
|
||||
assert_eq!(0, listeners.len());
|
||||
assert!(!listeners.remove(dummy_listener));
|
||||
assert_eq!(0, listeners.len());
|
||||
|
||||
// add and remove multiple
|
||||
assert!(listeners.add(dummy_listener));
|
||||
assert_eq!(1, listeners.len());
|
||||
assert!(listeners.add(other_dummy_listener));
|
||||
assert_eq!(2, listeners.len());
|
||||
assert!(listeners.remove(dummy_listener));
|
||||
assert_eq!(1, listeners.len());
|
||||
assert!(!listeners.remove(dummy_listener));
|
||||
assert_eq!(1, listeners.len());
|
||||
assert!(listeners.remove(other_dummy_listener));
|
||||
assert_eq!(0, listeners.len());
|
||||
// add and remove multiple
|
||||
assert!(listeners.add(dummy_listener));
|
||||
assert_eq!(1, listeners.len());
|
||||
assert!(listeners.add(other_dummy_listener));
|
||||
assert_eq!(2, listeners.len());
|
||||
assert!(listeners.remove(dummy_listener));
|
||||
assert_eq!(1, listeners.len());
|
||||
assert!(!listeners.remove(dummy_listener));
|
||||
assert_eq!(1, listeners.len());
|
||||
assert!(listeners.remove(other_dummy_listener));
|
||||
assert_eq!(0, listeners.len());
|
||||
|
||||
// clear all
|
||||
assert!(listeners.add(dummy_listener));
|
||||
assert!(listeners.add(other_dummy_listener));
|
||||
assert_eq!(2, listeners.len());
|
||||
listeners.clear();
|
||||
assert_eq!(0, listeners.len());
|
||||
}
|
||||
// clear all
|
||||
assert!(listeners.add(dummy_listener));
|
||||
assert!(listeners.add(other_dummy_listener));
|
||||
assert_eq!(2, listeners.len());
|
||||
listeners.clear();
|
||||
assert_eq!(0, listeners.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn queueing_events() {
|
||||
use TestEvent::*;
|
||||
#[test]
|
||||
pub fn queueing_events() {
|
||||
use TestEvent::*;
|
||||
|
||||
let mut publisher = EventPublisher::<TestEvent>::new();
|
||||
assert_eq!(0, publisher.len());
|
||||
publisher.queue(Dummy);
|
||||
assert_eq!(1, publisher.len());
|
||||
publisher.queue(Foobar(1));
|
||||
assert_eq!(2, publisher.len());
|
||||
publisher.queue(Foobar(2));
|
||||
assert_eq!(3, publisher.len());
|
||||
let mut publisher = EventPublisher::<TestEvent>::new();
|
||||
assert_eq!(0, publisher.len());
|
||||
publisher.queue(Dummy);
|
||||
assert_eq!(1, publisher.len());
|
||||
publisher.queue(Foobar(1));
|
||||
assert_eq!(2, publisher.len());
|
||||
publisher.queue(Foobar(2));
|
||||
assert_eq!(3, publisher.len());
|
||||
|
||||
let mut queue = VecDeque::<TestEvent>::new();
|
||||
publisher.take_queue(&mut queue);
|
||||
assert_eq!(0, publisher.len());
|
||||
assert_eq!(Dummy, queue.pop_front().unwrap());
|
||||
assert_eq!(Foobar(1), queue.pop_front().unwrap());
|
||||
assert_eq!(Foobar(2), queue.pop_front().unwrap());
|
||||
assert!(queue.pop_front().is_none());
|
||||
let mut queue = VecDeque::<TestEvent>::new();
|
||||
publisher.take_queue(&mut queue);
|
||||
assert_eq!(0, publisher.len());
|
||||
assert_eq!(Dummy, queue.pop_front().unwrap());
|
||||
assert_eq!(Foobar(1), queue.pop_front().unwrap());
|
||||
assert_eq!(Foobar(2), queue.pop_front().unwrap());
|
||||
assert!(queue.pop_front().is_none());
|
||||
|
||||
publisher.queue(Dummy);
|
||||
assert_eq!(1, publisher.len());
|
||||
publisher.clear();
|
||||
assert_eq!(0, publisher.len());
|
||||
let mut queue = VecDeque::<TestEvent>::new();
|
||||
publisher.take_queue(&mut queue);
|
||||
assert_eq!(0, publisher.len());
|
||||
assert_eq!(0, queue.len());
|
||||
}
|
||||
publisher.queue(Dummy);
|
||||
assert_eq!(1, publisher.len());
|
||||
publisher.clear();
|
||||
assert_eq!(0, publisher.len());
|
||||
let mut queue = VecDeque::<TestEvent>::new();
|
||||
publisher.take_queue(&mut queue);
|
||||
assert_eq!(0, publisher.len());
|
||||
assert_eq!(0, queue.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn listeners_receive_events() {
|
||||
use TestEvent::*;
|
||||
#[test]
|
||||
pub fn listeners_receive_events() {
|
||||
use TestEvent::*;
|
||||
|
||||
let mut listeners = EventListeners::<TestEvent, TestContext>::new();
|
||||
assert!(listeners.add(event_logger));
|
||||
let mut listeners = EventListeners::<TestEvent, TestContext>::new();
|
||||
assert!(listeners.add(event_logger));
|
||||
|
||||
let mut publisher = EventPublisher::<TestEvent>::new();
|
||||
publisher.queue(Dummy);
|
||||
publisher.queue(Foobar(1));
|
||||
publisher.queue(Dummy);
|
||||
publisher.queue(Foobar(42));
|
||||
assert_eq!(4, listeners.take_queue_from(&mut publisher));
|
||||
let mut publisher = EventPublisher::<TestEvent>::new();
|
||||
publisher.queue(Dummy);
|
||||
publisher.queue(Foobar(1));
|
||||
publisher.queue(Dummy);
|
||||
publisher.queue(Foobar(42));
|
||||
assert_eq!(4, listeners.take_queue_from(&mut publisher));
|
||||
|
||||
let mut context = TestContext::new();
|
||||
assert!(context.events.is_empty());
|
||||
assert_eq!(0, context.count);
|
||||
listeners.dispatch_queue(&mut context);
|
||||
assert!(!context.events.is_empty());
|
||||
assert_eq!(0, context.count);
|
||||
assert_eq!(
|
||||
vec![Dummy, Foobar(1), Dummy, Foobar(42)],
|
||||
context.events
|
||||
);
|
||||
let mut context = TestContext::new();
|
||||
assert!(context.events.is_empty());
|
||||
assert_eq!(0, context.count);
|
||||
listeners.dispatch_queue(&mut context);
|
||||
assert!(!context.events.is_empty());
|
||||
assert_eq!(0, context.count);
|
||||
assert_eq!(
|
||||
vec![Dummy, Foobar(1), Dummy, Foobar(42)],
|
||||
context.events
|
||||
);
|
||||
|
||||
let mut context = TestContext::new();
|
||||
assert!(context.events.is_empty());
|
||||
assert_eq!(0, context.count);
|
||||
listeners.dispatch_queue(&mut context);
|
||||
assert!(context.events.is_empty());
|
||||
let mut context = TestContext::new();
|
||||
assert!(context.events.is_empty());
|
||||
assert_eq!(0, context.count);
|
||||
listeners.dispatch_queue(&mut context);
|
||||
assert!(context.events.is_empty());
|
||||
|
||||
assert!(listeners.add(event_counter));
|
||||
publisher.queue(Foobar(10));
|
||||
publisher.queue(Foobar(20));
|
||||
publisher.queue(Dummy);
|
||||
listeners.take_queue_from(&mut publisher);
|
||||
let mut context = TestContext::new();
|
||||
listeners.dispatch_queue(&mut context);
|
||||
assert!(!context.events.is_empty());
|
||||
assert_eq!(3, context.count);
|
||||
assert_eq!(
|
||||
vec![Foobar(10), Foobar(20), Dummy],
|
||||
context.events
|
||||
);
|
||||
}
|
||||
assert!(listeners.add(event_counter));
|
||||
publisher.queue(Foobar(10));
|
||||
publisher.queue(Foobar(20));
|
||||
publisher.queue(Dummy);
|
||||
listeners.take_queue_from(&mut publisher);
|
||||
let mut context = TestContext::new();
|
||||
listeners.dispatch_queue(&mut context);
|
||||
assert!(!context.events.is_empty());
|
||||
assert_eq!(3, context.count);
|
||||
assert_eq!(
|
||||
vec![Foobar(10), Foobar(20), Dummy],
|
||||
context.events
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn listener_filtering() {
|
||||
use TestEvent::*;
|
||||
#[test]
|
||||
pub fn listener_filtering() {
|
||||
use TestEvent::*;
|
||||
|
||||
let mut listeners = EventListeners::<TestEvent, TestContext>::new();
|
||||
assert!(listeners.add(message_filter));
|
||||
assert!(listeners.add(event_logger));
|
||||
assert!(listeners.add(event_counter));
|
||||
let mut listeners = EventListeners::<TestEvent, TestContext>::new();
|
||||
assert!(listeners.add(message_filter));
|
||||
assert!(listeners.add(event_logger));
|
||||
assert!(listeners.add(event_counter));
|
||||
|
||||
let mut publisher = EventPublisher::<TestEvent>::new();
|
||||
publisher.queue(Message("hello"));
|
||||
publisher.queue(Dummy);
|
||||
publisher.queue(Message("filter"));
|
||||
publisher.queue(Foobar(3));
|
||||
assert_eq!(4, listeners.take_queue_from(&mut publisher));
|
||||
let mut publisher = EventPublisher::<TestEvent>::new();
|
||||
publisher.queue(Message("hello"));
|
||||
publisher.queue(Dummy);
|
||||
publisher.queue(Message("filter"));
|
||||
publisher.queue(Foobar(3));
|
||||
assert_eq!(4, listeners.take_queue_from(&mut publisher));
|
||||
|
||||
let mut context = TestContext::new();
|
||||
assert!(context.events.is_empty());
|
||||
assert_eq!(0, context.count);
|
||||
listeners.dispatch_queue(&mut context);
|
||||
assert!(!context.events.is_empty());
|
||||
assert_eq!(3, context.count);
|
||||
assert_eq!(
|
||||
vec![Message("hello"), Dummy, Foobar(3)],
|
||||
context.events
|
||||
);
|
||||
|
||||
}
|
||||
let mut context = TestContext::new();
|
||||
assert!(context.events.is_empty());
|
||||
assert_eq!(0, context.count);
|
||||
listeners.dispatch_queue(&mut context);
|
||||
assert!(!context.events.is_empty());
|
||||
assert_eq!(3, context.count);
|
||||
assert_eq!(
|
||||
vec![Message("hello"), Dummy, Foobar(3)],
|
||||
context.events
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -10,340 +10,340 @@ use crate::utils::bytes::ReadFixedLengthByteArray;
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PcxError {
|
||||
#[error("Bad or unsupported PCX file: {0}")]
|
||||
BadFile(String),
|
||||
#[error("Bad or unsupported PCX file: {0}")]
|
||||
BadFile(String),
|
||||
|
||||
#[error("PCX palette data error")]
|
||||
BadPalette(#[from] PaletteError),
|
||||
#[error("PCX palette data error")]
|
||||
BadPalette(#[from] PaletteError),
|
||||
|
||||
#[error("PCX I/O error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
#[error("PCX I/O error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(packed)]
|
||||
struct PcxHeader {
|
||||
manufacturer: u8,
|
||||
version: u8,
|
||||
encoding: u8,
|
||||
bpp: u8,
|
||||
x1: u16,
|
||||
y1: u16,
|
||||
x2: u16,
|
||||
y2: u16,
|
||||
horizontal_dpi: u16,
|
||||
vertical_dpi: u16,
|
||||
ega_palette: [u8; 48],
|
||||
reserved: u8,
|
||||
num_color_planes: u8,
|
||||
bytes_per_line: u16,
|
||||
palette_type: u16,
|
||||
horizontal_size: u16,
|
||||
vertical_size: u16,
|
||||
padding: [u8; 54],
|
||||
manufacturer: u8,
|
||||
version: u8,
|
||||
encoding: u8,
|
||||
bpp: u8,
|
||||
x1: u16,
|
||||
y1: u16,
|
||||
x2: u16,
|
||||
y2: u16,
|
||||
horizontal_dpi: u16,
|
||||
vertical_dpi: u16,
|
||||
ega_palette: [u8; 48],
|
||||
reserved: u8,
|
||||
num_color_planes: u8,
|
||||
bytes_per_line: u16,
|
||||
palette_type: u16,
|
||||
horizontal_size: u16,
|
||||
vertical_size: u16,
|
||||
padding: [u8; 54],
|
||||
}
|
||||
|
||||
impl PcxHeader {
|
||||
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, PcxError> {
|
||||
Ok(PcxHeader {
|
||||
manufacturer: reader.read_u8()?,
|
||||
version: reader.read_u8()?,
|
||||
encoding: reader.read_u8()?,
|
||||
bpp: reader.read_u8()?,
|
||||
x1: reader.read_u16::<LittleEndian>()?,
|
||||
y1: reader.read_u16::<LittleEndian>()?,
|
||||
x2: reader.read_u16::<LittleEndian>()?,
|
||||
y2: reader.read_u16::<LittleEndian>()?,
|
||||
horizontal_dpi: reader.read_u16::<LittleEndian>()?,
|
||||
vertical_dpi: reader.read_u16::<LittleEndian>()?,
|
||||
ega_palette: reader.read_bytes()?,
|
||||
reserved: reader.read_u8()?,
|
||||
num_color_planes: reader.read_u8()?,
|
||||
bytes_per_line: reader.read_u16::<LittleEndian>()?,
|
||||
palette_type: reader.read_u16::<LittleEndian>()?,
|
||||
horizontal_size: reader.read_u16::<LittleEndian>()?,
|
||||
vertical_size: reader.read_u16::<LittleEndian>()?,
|
||||
padding: reader.read_bytes()?,
|
||||
})
|
||||
}
|
||||
pub fn read<T: ReadBytesExt>(reader: &mut T) -> Result<Self, PcxError> {
|
||||
Ok(PcxHeader {
|
||||
manufacturer: reader.read_u8()?,
|
||||
version: reader.read_u8()?,
|
||||
encoding: reader.read_u8()?,
|
||||
bpp: reader.read_u8()?,
|
||||
x1: reader.read_u16::<LittleEndian>()?,
|
||||
y1: reader.read_u16::<LittleEndian>()?,
|
||||
x2: reader.read_u16::<LittleEndian>()?,
|
||||
y2: reader.read_u16::<LittleEndian>()?,
|
||||
horizontal_dpi: reader.read_u16::<LittleEndian>()?,
|
||||
vertical_dpi: reader.read_u16::<LittleEndian>()?,
|
||||
ega_palette: reader.read_bytes()?,
|
||||
reserved: reader.read_u8()?,
|
||||
num_color_planes: reader.read_u8()?,
|
||||
bytes_per_line: reader.read_u16::<LittleEndian>()?,
|
||||
palette_type: reader.read_u16::<LittleEndian>()?,
|
||||
horizontal_size: reader.read_u16::<LittleEndian>()?,
|
||||
vertical_size: reader.read_u16::<LittleEndian>()?,
|
||||
padding: reader.read_bytes()?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), PcxError> {
|
||||
writer.write_u8(self.manufacturer)?;
|
||||
writer.write_u8(self.version)?;
|
||||
writer.write_u8(self.encoding)?;
|
||||
writer.write_u8(self.bpp)?;
|
||||
writer.write_u16::<LittleEndian>(self.x1)?;
|
||||
writer.write_u16::<LittleEndian>(self.y1)?;
|
||||
writer.write_u16::<LittleEndian>(self.x2)?;
|
||||
writer.write_u16::<LittleEndian>(self.y2)?;
|
||||
writer.write_u16::<LittleEndian>(self.horizontal_dpi)?;
|
||||
writer.write_u16::<LittleEndian>(self.vertical_dpi)?;
|
||||
writer.write_all(&self.ega_palette)?;
|
||||
writer.write_u8(self.reserved)?;
|
||||
writer.write_u8(self.num_color_planes)?;
|
||||
writer.write_u16::<LittleEndian>(self.bytes_per_line)?;
|
||||
writer.write_u16::<LittleEndian>(self.palette_type)?;
|
||||
writer.write_u16::<LittleEndian>(self.horizontal_size)?;
|
||||
writer.write_u16::<LittleEndian>(self.vertical_size)?;
|
||||
writer.write_all(&self.padding)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), PcxError> {
|
||||
writer.write_u8(self.manufacturer)?;
|
||||
writer.write_u8(self.version)?;
|
||||
writer.write_u8(self.encoding)?;
|
||||
writer.write_u8(self.bpp)?;
|
||||
writer.write_u16::<LittleEndian>(self.x1)?;
|
||||
writer.write_u16::<LittleEndian>(self.y1)?;
|
||||
writer.write_u16::<LittleEndian>(self.x2)?;
|
||||
writer.write_u16::<LittleEndian>(self.y2)?;
|
||||
writer.write_u16::<LittleEndian>(self.horizontal_dpi)?;
|
||||
writer.write_u16::<LittleEndian>(self.vertical_dpi)?;
|
||||
writer.write_all(&self.ega_palette)?;
|
||||
writer.write_u8(self.reserved)?;
|
||||
writer.write_u8(self.num_color_planes)?;
|
||||
writer.write_u16::<LittleEndian>(self.bytes_per_line)?;
|
||||
writer.write_u16::<LittleEndian>(self.palette_type)?;
|
||||
writer.write_u16::<LittleEndian>(self.horizontal_size)?;
|
||||
writer.write_u16::<LittleEndian>(self.vertical_size)?;
|
||||
writer.write_all(&self.padding)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_pcx_data<T: WriteBytesExt>(
|
||||
writer: &mut T,
|
||||
run_count: u8,
|
||||
pixel: u8,
|
||||
writer: &mut T,
|
||||
run_count: u8,
|
||||
pixel: u8,
|
||||
) -> Result<(), PcxError> {
|
||||
if (run_count > 1) || ((pixel & 0xc0) == 0xc0) {
|
||||
writer.write_u8(0xc0 | run_count)?;
|
||||
}
|
||||
writer.write_u8(pixel)?;
|
||||
Ok(())
|
||||
if (run_count > 1) || ((pixel & 0xc0) == 0xc0) {
|
||||
writer.write_u8(0xc0 | run_count)?;
|
||||
}
|
||||
writer.write_u8(pixel)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Bitmap {
|
||||
pub fn load_pcx_bytes<T: ReadBytesExt + Seek>(
|
||||
reader: &mut T,
|
||||
) -> Result<(Bitmap, Palette), PcxError> {
|
||||
let header = PcxHeader::read(reader)?;
|
||||
pub fn load_pcx_bytes<T: ReadBytesExt + Seek>(
|
||||
reader: &mut T,
|
||||
) -> Result<(Bitmap, Palette), PcxError> {
|
||||
let header = PcxHeader::read(reader)?;
|
||||
|
||||
if header.manufacturer != 10 {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Unexpected header.manufacturer value, probably not a PCX file",
|
||||
)));
|
||||
}
|
||||
if header.version != 5 {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Only version 5 PCX files are supported",
|
||||
)));
|
||||
}
|
||||
if header.encoding != 1 {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Only RLE-compressed PCX files are supported",
|
||||
)));
|
||||
}
|
||||
if header.bpp != 8 {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Only 8-bit indexed (256 color palette) PCX files are supported",
|
||||
)));
|
||||
}
|
||||
if header.x2 == 0 || header.y2 == 0 {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Invalid PCX image dimensions",
|
||||
)));
|
||||
}
|
||||
if header.manufacturer != 10 {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Unexpected header.manufacturer value, probably not a PCX file",
|
||||
)));
|
||||
}
|
||||
if header.version != 5 {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Only version 5 PCX files are supported",
|
||||
)));
|
||||
}
|
||||
if header.encoding != 1 {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Only RLE-compressed PCX files are supported",
|
||||
)));
|
||||
}
|
||||
if header.bpp != 8 {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Only 8-bit indexed (256 color palette) PCX files are supported",
|
||||
)));
|
||||
}
|
||||
if header.x2 == 0 || header.y2 == 0 {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Invalid PCX image dimensions",
|
||||
)));
|
||||
}
|
||||
|
||||
// read the PCX file's pixel data into a bitmap
|
||||
// read the PCX file's pixel data into a bitmap
|
||||
|
||||
let width = (header.x2 + 1) as u32;
|
||||
let height = (header.y2 + 1) as u32;
|
||||
let mut bmp = Bitmap::new(width, height).unwrap();
|
||||
let mut writer = Cursor::new(bmp.pixels_mut());
|
||||
let width = (header.x2 + 1) as u32;
|
||||
let height = (header.y2 + 1) as u32;
|
||||
let mut bmp = Bitmap::new(width, height).unwrap();
|
||||
let mut writer = Cursor::new(bmp.pixels_mut());
|
||||
|
||||
for _y in 0..height {
|
||||
// read the next scanline's worth of pixels from the PCX file
|
||||
let mut x: u32 = 0;
|
||||
while x < (header.bytes_per_line as u32) {
|
||||
let mut data: u8;
|
||||
let mut count: u32;
|
||||
for _y in 0..height {
|
||||
// read the next scanline's worth of pixels from the PCX file
|
||||
let mut x: u32 = 0;
|
||||
while x < (header.bytes_per_line as u32) {
|
||||
let mut data: u8;
|
||||
let mut count: u32;
|
||||
|
||||
// read pixel or RLE count
|
||||
data = reader.read_u8()?;
|
||||
// read pixel or RLE count
|
||||
data = reader.read_u8()?;
|
||||
|
||||
if (data & 0xc0) == 0xc0 {
|
||||
// it was an RLE count, actual pixel is the next byte ...
|
||||
count = (data & 0x3f) as u32;
|
||||
data = reader.read_u8()?;
|
||||
} else {
|
||||
// it was just a single pixel
|
||||
count = 1;
|
||||
}
|
||||
if (data & 0xc0) == 0xc0 {
|
||||
// it was an RLE count, actual pixel is the next byte ...
|
||||
count = (data & 0x3f) as u32;
|
||||
data = reader.read_u8()?;
|
||||
} else {
|
||||
// it was just a single pixel
|
||||
count = 1;
|
||||
}
|
||||
|
||||
// write the current pixel value 'data' to the bitmap 'count' number of times
|
||||
while count > 0 {
|
||||
if x <= width {
|
||||
writer.write_u8(data)?;
|
||||
} else {
|
||||
writer.seek(SeekFrom::Current(1))?;
|
||||
}
|
||||
// write the current pixel value 'data' to the bitmap 'count' number of times
|
||||
while count > 0 {
|
||||
if x <= width {
|
||||
writer.write_u8(data)?;
|
||||
} else {
|
||||
writer.seek(SeekFrom::Current(1))?;
|
||||
}
|
||||
|
||||
x += 1;
|
||||
count -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
count -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now read the palette data located at the end of the PCX file
|
||||
// palette data should be for 256 colors, 3 bytes per color = 768 bytes
|
||||
// the palette is preceded by a single byte, 0x0c, which we will also validate
|
||||
// now read the palette data located at the end of the PCX file
|
||||
// palette data should be for 256 colors, 3 bytes per color = 768 bytes
|
||||
// the palette is preceded by a single byte, 0x0c, which we will also validate
|
||||
|
||||
reader.seek(SeekFrom::End(-769))?;
|
||||
reader.seek(SeekFrom::End(-769))?;
|
||||
|
||||
let palette_marker = reader.read_u8()?;
|
||||
if palette_marker != 0x0c {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Palette not found at end of file",
|
||||
)));
|
||||
}
|
||||
let palette_marker = reader.read_u8()?;
|
||||
if palette_marker != 0x0c {
|
||||
return Err(PcxError::BadFile(String::from(
|
||||
"Palette not found at end of file",
|
||||
)));
|
||||
}
|
||||
|
||||
let palette = Palette::load_from_bytes(reader, PaletteFormat::Normal)?;
|
||||
let palette = Palette::load_from_bytes(reader, PaletteFormat::Normal)?;
|
||||
|
||||
Ok((bmp, palette))
|
||||
}
|
||||
Ok((bmp, palette))
|
||||
}
|
||||
|
||||
pub fn load_pcx_file(path: &Path) -> Result<(Bitmap, Palette), PcxError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_pcx_bytes(&mut reader)
|
||||
}
|
||||
pub fn load_pcx_file(path: &Path) -> Result<(Bitmap, Palette), PcxError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_pcx_bytes(&mut reader)
|
||||
}
|
||||
|
||||
pub fn to_pcx_bytes<T: WriteBytesExt>(
|
||||
&self,
|
||||
writer: &mut T,
|
||||
palette: &Palette,
|
||||
) -> Result<(), PcxError> {
|
||||
let header = PcxHeader {
|
||||
manufacturer: 10,
|
||||
version: 5,
|
||||
encoding: 1,
|
||||
bpp: 8,
|
||||
x1: 0,
|
||||
y1: 0,
|
||||
x2: self.right() as u16,
|
||||
y2: self.bottom() as u16,
|
||||
horizontal_dpi: 320,
|
||||
vertical_dpi: 200,
|
||||
ega_palette: [0u8; 48],
|
||||
reserved: 0,
|
||||
num_color_planes: 1,
|
||||
bytes_per_line: self.width() as u16,
|
||||
palette_type: 1,
|
||||
horizontal_size: self.width() as u16,
|
||||
vertical_size: self.height() as u16,
|
||||
padding: [0u8; 54],
|
||||
};
|
||||
header.write(writer)?;
|
||||
pub fn to_pcx_bytes<T: WriteBytesExt>(
|
||||
&self,
|
||||
writer: &mut T,
|
||||
palette: &Palette,
|
||||
) -> Result<(), PcxError> {
|
||||
let header = PcxHeader {
|
||||
manufacturer: 10,
|
||||
version: 5,
|
||||
encoding: 1,
|
||||
bpp: 8,
|
||||
x1: 0,
|
||||
y1: 0,
|
||||
x2: self.right() as u16,
|
||||
y2: self.bottom() as u16,
|
||||
horizontal_dpi: 320,
|
||||
vertical_dpi: 200,
|
||||
ega_palette: [0u8; 48],
|
||||
reserved: 0,
|
||||
num_color_planes: 1,
|
||||
bytes_per_line: self.width() as u16,
|
||||
palette_type: 1,
|
||||
horizontal_size: self.width() as u16,
|
||||
vertical_size: self.height() as u16,
|
||||
padding: [0u8; 54],
|
||||
};
|
||||
header.write(writer)?;
|
||||
|
||||
let pixels = self.pixels();
|
||||
let mut i = 0;
|
||||
let pixels = self.pixels();
|
||||
let mut i = 0;
|
||||
|
||||
for _y in 0..=self.bottom() {
|
||||
// write one scanline at a time. breaking runs that could have continued across
|
||||
// scanlines in the process, as per the pcx standard
|
||||
for _y in 0..=self.bottom() {
|
||||
// write one scanline at a time. breaking runs that could have continued across
|
||||
// scanlines in the process, as per the pcx standard
|
||||
|
||||
let mut run_count = 0;
|
||||
let mut run_pixel = 0;
|
||||
let mut run_count = 0;
|
||||
let mut run_pixel = 0;
|
||||
|
||||
for _x in 0..=self.right() {
|
||||
let pixel = pixels[i];
|
||||
i += 1;
|
||||
for _x in 0..=self.right() {
|
||||
let pixel = pixels[i];
|
||||
i += 1;
|
||||
|
||||
if run_count == 0 {
|
||||
run_count = 1;
|
||||
run_pixel = pixel;
|
||||
} else {
|
||||
if (pixel != run_pixel) || (run_count >= 63) {
|
||||
write_pcx_data(writer, run_count, run_pixel)?;
|
||||
run_count = 1;
|
||||
run_pixel = pixel;
|
||||
} else {
|
||||
run_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if run_count == 0 {
|
||||
run_count = 1;
|
||||
run_pixel = pixel;
|
||||
} else {
|
||||
if (pixel != run_pixel) || (run_count >= 63) {
|
||||
write_pcx_data(writer, run_count, run_pixel)?;
|
||||
run_count = 1;
|
||||
run_pixel = pixel;
|
||||
} else {
|
||||
run_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// end the scanline, writing out whatever run we might have had going
|
||||
write_pcx_data(writer, run_count, run_pixel)?;
|
||||
}
|
||||
// end the scanline, writing out whatever run we might have had going
|
||||
write_pcx_data(writer, run_count, run_pixel)?;
|
||||
}
|
||||
|
||||
// marker for beginning of palette data
|
||||
writer.write_u8(0xc)?;
|
||||
// marker for beginning of palette data
|
||||
writer.write_u8(0xc)?;
|
||||
|
||||
for i in 0..=255 {
|
||||
let argb = palette[i];
|
||||
let (r, g, b) = from_rgb32(argb);
|
||||
writer.write_u8(r)?;
|
||||
writer.write_u8(g)?;
|
||||
writer.write_u8(b)?;
|
||||
}
|
||||
for i in 0..=255 {
|
||||
let argb = palette[i];
|
||||
let (r, g, b) = from_rgb32(argb);
|
||||
writer.write_u8(r)?;
|
||||
writer.write_u8(g)?;
|
||||
writer.write_u8(b)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_pcx_file(&self, path: &Path, palette: &Palette) -> Result<(), PcxError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_pcx_bytes(&mut writer, palette)
|
||||
}
|
||||
pub fn to_pcx_file(&self, path: &Path, palette: &Palette) -> Result<(), PcxError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_pcx_bytes(&mut writer, palette)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use tempfile::TempDir;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin");
|
||||
pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin");
|
||||
pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin");
|
||||
pub static TEST_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_bmp_pixels_raw.bin");
|
||||
pub static TEST_LARGE_BMP_PIXELS_RAW: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw.bin");
|
||||
pub static TEST_LARGE_BMP_PIXELS_RAW_2: &[u8] = include_bytes!("../../../test-assets/test_large_bmp_pixels_raw2.bin");
|
||||
|
||||
#[test]
|
||||
pub fn load_and_save() -> Result<(), PcxError> {
|
||||
let dp2_palette =
|
||||
Palette::load_from_file(Path::new("./test-assets/dp2.pal"), PaletteFormat::Normal)
|
||||
.unwrap();
|
||||
let tmp_dir = TempDir::new()?;
|
||||
#[test]
|
||||
pub fn load_and_save() -> Result<(), PcxError> {
|
||||
let dp2_palette =
|
||||
Palette::load_from_file(Path::new("./test-assets/dp2.pal"), PaletteFormat::Normal)
|
||||
.unwrap();
|
||||
let tmp_dir = TempDir::new()?;
|
||||
|
||||
let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test.pcx"))?;
|
||||
assert_eq!(16, bmp.width());
|
||||
assert_eq!(16, bmp.height());
|
||||
assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||
assert_eq!(palette, dp2_palette);
|
||||
let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test.pcx"))?;
|
||||
assert_eq!(16, bmp.width());
|
||||
assert_eq!(16, bmp.height());
|
||||
assert_eq!(bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||
assert_eq!(palette, dp2_palette);
|
||||
|
||||
let save_path = tmp_dir.path().join("test_save.pcx");
|
||||
bmp.to_pcx_file(&save_path, &palette)?;
|
||||
let (reloaded_bmp, reloaded_palette) = Bitmap::load_pcx_file(&save_path)?;
|
||||
assert_eq!(16, reloaded_bmp.width());
|
||||
assert_eq!(16, reloaded_bmp.height());
|
||||
assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||
assert_eq!(reloaded_palette, dp2_palette);
|
||||
let save_path = tmp_dir.path().join("test_save.pcx");
|
||||
bmp.to_pcx_file(&save_path, &palette)?;
|
||||
let (reloaded_bmp, reloaded_palette) = Bitmap::load_pcx_file(&save_path)?;
|
||||
assert_eq!(16, reloaded_bmp.width());
|
||||
assert_eq!(16, reloaded_bmp.height());
|
||||
assert_eq!(reloaded_bmp.pixels(), TEST_BMP_PIXELS_RAW);
|
||||
assert_eq!(reloaded_palette, dp2_palette);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn load_and_save_larger_image() -> Result<(), PcxError> {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
#[test]
|
||||
pub fn load_and_save_larger_image() -> Result<(), PcxError> {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
|
||||
// first image
|
||||
// first image
|
||||
|
||||
let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test_image.pcx"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||
let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test_image.pcx"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||
|
||||
let save_path = tmp_dir.path().join("test_save.pcx");
|
||||
bmp.to_pcx_file(&save_path, &palette)?;
|
||||
let (reloaded_bmp, _) = Bitmap::load_pcx_file(&save_path)?;
|
||||
assert_eq!(320, reloaded_bmp.width());
|
||||
assert_eq!(200, reloaded_bmp.height());
|
||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||
let save_path = tmp_dir.path().join("test_save.pcx");
|
||||
bmp.to_pcx_file(&save_path, &palette)?;
|
||||
let (reloaded_bmp, _) = Bitmap::load_pcx_file(&save_path)?;
|
||||
assert_eq!(320, reloaded_bmp.width());
|
||||
assert_eq!(200, reloaded_bmp.height());
|
||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW);
|
||||
|
||||
// second image
|
||||
// second image
|
||||
|
||||
let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test_image2.pcx"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
||||
let (bmp, palette) = Bitmap::load_pcx_file(Path::new("./test-assets/test_image2.pcx"))?;
|
||||
assert_eq!(320, bmp.width());
|
||||
assert_eq!(200, bmp.height());
|
||||
assert_eq!(bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
||||
|
||||
let save_path = tmp_dir.path().join("test_save_2.pcx");
|
||||
bmp.to_pcx_file(&save_path, &palette)?;
|
||||
let (reloaded_bmp, _) = Bitmap::load_pcx_file(&save_path)?;
|
||||
assert_eq!(320, reloaded_bmp.width());
|
||||
assert_eq!(200, reloaded_bmp.height());
|
||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
||||
let save_path = tmp_dir.path().join("test_save_2.pcx");
|
||||
bmp.to_pcx_file(&save_path, &palette)?;
|
||||
let (reloaded_bmp, _) = Bitmap::load_pcx_file(&save_path)?;
|
||||
assert_eq!(320, reloaded_bmp.width());
|
||||
assert_eq!(200, reloaded_bmp.height());
|
||||
assert_eq!(reloaded_bmp.pixels(), TEST_LARGE_BMP_PIXELS_RAW_2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,181 +7,181 @@ use crate::math::*;
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BitmapAtlasError {
|
||||
#[error("Region is out of bounds for the Bitmap used by the BitmapAtlas")]
|
||||
OutOfBounds,
|
||||
#[error("Region is out of bounds for the Bitmap used by the BitmapAtlas")]
|
||||
OutOfBounds,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct BitmapAtlas {
|
||||
bitmap: Bitmap,
|
||||
bounds: Rect,
|
||||
tiles: Vec<Rect>,
|
||||
bitmap: Bitmap,
|
||||
bounds: Rect,
|
||||
tiles: Vec<Rect>,
|
||||
}
|
||||
|
||||
impl BitmapAtlas {
|
||||
pub fn new(bitmap: Bitmap) -> BitmapAtlas {
|
||||
let bounds = bitmap.full_bounds();
|
||||
BitmapAtlas {
|
||||
bitmap,
|
||||
bounds,
|
||||
tiles: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn new(bitmap: Bitmap) -> BitmapAtlas {
|
||||
let bounds = bitmap.full_bounds();
|
||||
BitmapAtlas {
|
||||
bitmap,
|
||||
bounds,
|
||||
tiles: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, rect: Rect) -> Result<usize, BitmapAtlasError> {
|
||||
if !self.bounds.contains_rect(&rect) {
|
||||
return Err(BitmapAtlasError::OutOfBounds);
|
||||
}
|
||||
pub fn add(&mut self, rect: Rect) -> Result<usize, BitmapAtlasError> {
|
||||
if !self.bounds.contains_rect(&rect) {
|
||||
return Err(BitmapAtlasError::OutOfBounds);
|
||||
}
|
||||
|
||||
self.tiles.push(rect);
|
||||
Ok(self.tiles.len() - 1)
|
||||
}
|
||||
self.tiles.push(rect);
|
||||
Ok(self.tiles.len() - 1)
|
||||
}
|
||||
|
||||
pub fn add_grid(
|
||||
&mut self,
|
||||
tile_width: u32,
|
||||
tile_height: u32,
|
||||
) -> Result<usize, BitmapAtlasError> {
|
||||
if self.bounds.width < tile_width || self.bounds.height < tile_height {
|
||||
return Err(BitmapAtlasError::OutOfBounds);
|
||||
}
|
||||
pub fn add_grid(
|
||||
&mut self,
|
||||
tile_width: u32,
|
||||
tile_height: u32,
|
||||
) -> Result<usize, BitmapAtlasError> {
|
||||
if self.bounds.width < tile_width || self.bounds.height < tile_height {
|
||||
return Err(BitmapAtlasError::OutOfBounds);
|
||||
}
|
||||
|
||||
for yt in 0..(self.bounds.height / tile_height) {
|
||||
for xt in 0..(self.bounds.width) / tile_width {
|
||||
let x = xt * tile_width;
|
||||
let y = yt * tile_height;
|
||||
let rect = Rect::new(x as i32, y as i32, tile_width, tile_height);
|
||||
self.tiles.push(rect);
|
||||
}
|
||||
}
|
||||
for yt in 0..(self.bounds.height / tile_height) {
|
||||
for xt in 0..(self.bounds.width) / tile_width {
|
||||
let x = xt * tile_width;
|
||||
let y = yt * tile_height;
|
||||
let rect = Rect::new(x as i32, y as i32, tile_width, tile_height);
|
||||
self.tiles.push(rect);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.tiles.len() - 1)
|
||||
}
|
||||
Ok(self.tiles.len() - 1)
|
||||
}
|
||||
|
||||
pub fn add_custom_grid(
|
||||
&mut self,
|
||||
start_x: u32,
|
||||
start_y: u32,
|
||||
tile_width: u32,
|
||||
tile_height: u32,
|
||||
x_tiles: u32,
|
||||
y_tiles: u32,
|
||||
border: u32,
|
||||
) -> Result<usize, BitmapAtlasError> {
|
||||
// figure out of the grid properties given would result in us creating any
|
||||
// rects that lie out of the bounds of this bitmap
|
||||
let grid_region = Rect::new(
|
||||
start_x as i32,
|
||||
start_y as i32,
|
||||
(tile_width + border) * x_tiles + border,
|
||||
(tile_height + border) * y_tiles + border,
|
||||
);
|
||||
if !self.bounds.contains_rect(&grid_region) {
|
||||
return Err(BitmapAtlasError::OutOfBounds);
|
||||
}
|
||||
pub fn add_custom_grid(
|
||||
&mut self,
|
||||
start_x: u32,
|
||||
start_y: u32,
|
||||
tile_width: u32,
|
||||
tile_height: u32,
|
||||
x_tiles: u32,
|
||||
y_tiles: u32,
|
||||
border: u32,
|
||||
) -> Result<usize, BitmapAtlasError> {
|
||||
// figure out of the grid properties given would result in us creating any
|
||||
// rects that lie out of the bounds of this bitmap
|
||||
let grid_region = Rect::new(
|
||||
start_x as i32,
|
||||
start_y as i32,
|
||||
(tile_width + border) * x_tiles + border,
|
||||
(tile_height + border) * y_tiles + border,
|
||||
);
|
||||
if !self.bounds.contains_rect(&grid_region) {
|
||||
return Err(BitmapAtlasError::OutOfBounds);
|
||||
}
|
||||
|
||||
// all good! now create all the tiles needed for the grid specified
|
||||
for yt in 0..y_tiles {
|
||||
for xt in 0..x_tiles {
|
||||
let x = start_x + (tile_width + border) * xt;
|
||||
let y = start_y + (tile_height + border) * yt;
|
||||
let rect = Rect::new(x as i32, y as i32, tile_width, tile_height);
|
||||
self.tiles.push(rect);
|
||||
}
|
||||
}
|
||||
// all good! now create all the tiles needed for the grid specified
|
||||
for yt in 0..y_tiles {
|
||||
for xt in 0..x_tiles {
|
||||
let x = start_x + (tile_width + border) * xt;
|
||||
let y = start_y + (tile_height + border) * yt;
|
||||
let rect = Rect::new(x as i32, y as i32, tile_width, tile_height);
|
||||
self.tiles.push(rect);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.tiles.len() - 1)
|
||||
}
|
||||
Ok(self.tiles.len() - 1)
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.tiles.clear()
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
self.tiles.clear()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.tiles.len()
|
||||
}
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.tiles.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&Rect> {
|
||||
self.tiles.get(index)
|
||||
}
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&Rect> {
|
||||
self.tiles.get(index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bitmap(&self) -> &Bitmap {
|
||||
&self.bitmap
|
||||
}
|
||||
#[inline]
|
||||
pub fn bitmap(&self) -> &Bitmap {
|
||||
&self.bitmap
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for BitmapAtlas {
|
||||
type Output = Rect;
|
||||
type Output = Rect;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.get(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use claim::*;
|
||||
use claim::*;
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn adding_rects() {
|
||||
let bmp = Bitmap::new(64, 64).unwrap();
|
||||
let mut atlas = BitmapAtlas::new(bmp);
|
||||
#[test]
|
||||
pub fn adding_rects() {
|
||||
let bmp = Bitmap::new(64, 64).unwrap();
|
||||
let mut atlas = BitmapAtlas::new(bmp);
|
||||
|
||||
let rect = Rect::new(0, 0, 16, 16);
|
||||
assert_eq!(0, atlas.add(rect.clone()).unwrap());
|
||||
assert_eq!(rect, atlas[0]);
|
||||
assert_eq!(1, atlas.len());
|
||||
let rect = Rect::new(0, 0, 16, 16);
|
||||
assert_eq!(0, atlas.add(rect.clone()).unwrap());
|
||||
assert_eq!(rect, atlas[0]);
|
||||
assert_eq!(1, atlas.len());
|
||||
|
||||
let rect = Rect::new(16, 0, 16, 16);
|
||||
assert_eq!(1, atlas.add(rect.clone()).unwrap());
|
||||
assert_eq!(rect, atlas[1]);
|
||||
assert_eq!(2, atlas.len());
|
||||
let rect = Rect::new(16, 0, 16, 16);
|
||||
assert_eq!(1, atlas.add(rect.clone()).unwrap());
|
||||
assert_eq!(rect, atlas[1]);
|
||||
assert_eq!(2, atlas.len());
|
||||
|
||||
assert_matches!(
|
||||
assert_matches!(
|
||||
atlas.add(Rect::new(56, 0, 16, 16)),
|
||||
Err(BitmapAtlasError::OutOfBounds)
|
||||
);
|
||||
assert_eq!(2, atlas.len());
|
||||
assert_eq!(2, atlas.len());
|
||||
|
||||
assert_matches!(
|
||||
assert_matches!(
|
||||
atlas.add(Rect::new(-8, 4, 16, 16)),
|
||||
Err(BitmapAtlasError::OutOfBounds)
|
||||
);
|
||||
assert_eq!(2, atlas.len());
|
||||
assert_eq!(2, atlas.len());
|
||||
|
||||
assert_matches!(
|
||||
assert_matches!(
|
||||
atlas.add(Rect::new(0, 0, 128, 128)),
|
||||
Err(BitmapAtlasError::OutOfBounds)
|
||||
);
|
||||
assert_eq!(2, atlas.len());
|
||||
}
|
||||
assert_eq!(2, atlas.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn adding_grid() {
|
||||
let bmp = Bitmap::new(64, 64).unwrap();
|
||||
let mut atlas = BitmapAtlas::new(bmp);
|
||||
#[test]
|
||||
pub fn adding_grid() {
|
||||
let bmp = Bitmap::new(64, 64).unwrap();
|
||||
let mut atlas = BitmapAtlas::new(bmp);
|
||||
|
||||
assert_eq!(3, atlas.add_custom_grid(0, 0, 8, 8, 2, 2, 0).unwrap());
|
||||
assert_eq!(4, atlas.len());
|
||||
assert_eq!(Rect::new(0, 0, 8, 8), atlas[0]);
|
||||
assert_eq!(Rect::new(8, 0, 8, 8), atlas[1]);
|
||||
assert_eq!(Rect::new(0, 8, 8, 8), atlas[2]);
|
||||
assert_eq!(Rect::new(8, 8, 8, 8), atlas[3]);
|
||||
assert_eq!(3, atlas.add_custom_grid(0, 0, 8, 8, 2, 2, 0).unwrap());
|
||||
assert_eq!(4, atlas.len());
|
||||
assert_eq!(Rect::new(0, 0, 8, 8), atlas[0]);
|
||||
assert_eq!(Rect::new(8, 0, 8, 8), atlas[1]);
|
||||
assert_eq!(Rect::new(0, 8, 8, 8), atlas[2]);
|
||||
assert_eq!(Rect::new(8, 8, 8, 8), atlas[3]);
|
||||
|
||||
atlas.clear();
|
||||
assert_eq!(0, atlas.len());
|
||||
atlas.clear();
|
||||
assert_eq!(0, atlas.len());
|
||||
|
||||
assert_eq!(3, atlas.add_custom_grid(0, 0, 4, 8, 2, 2, 1).unwrap());
|
||||
assert_eq!(4, atlas.len());
|
||||
assert_eq!(Rect::new(0, 0, 4, 8), atlas[0]);
|
||||
assert_eq!(Rect::new(5, 0, 4, 8), atlas[1]);
|
||||
assert_eq!(Rect::new(0, 9, 4, 8), atlas[2]);
|
||||
assert_eq!(Rect::new(5, 9, 4, 8), atlas[3]);
|
||||
}
|
||||
assert_eq!(3, atlas.add_custom_grid(0, 0, 4, 8, 2, 2, 1).unwrap());
|
||||
assert_eq!(4, atlas.len());
|
||||
assert_eq!(Rect::new(0, 0, 4, 8), atlas[0]);
|
||||
assert_eq!(Rect::new(5, 0, 4, 8), atlas[1]);
|
||||
assert_eq!(Rect::new(0, 9, 4, 8), atlas[2]);
|
||||
assert_eq!(Rect::new(5, 9, 4, 8), atlas[3]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,14 +12,14 @@ use crate::utils::bytes::ReadFixedLengthByteArray;
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BlendMapError {
|
||||
#[error("Source color {0} is out of range for this BlendMap")]
|
||||
InvalidSourceColor(u8),
|
||||
#[error("Source color {0} is out of range for this BlendMap")]
|
||||
InvalidSourceColor(u8),
|
||||
|
||||
#[error("Bad or unsupported BlendMap file: {0}")]
|
||||
BadFile(String),
|
||||
#[error("Bad or unsupported BlendMap file: {0}")]
|
||||
BadFile(String),
|
||||
|
||||
#[error("BlendMap I/O error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
#[error("BlendMap I/O error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
/// A lookup table used by [`BlendMap`]s. This table stores destination color to blend color
|
||||
|
@ -39,378 +39,378 @@ pub type BlendMapping = [u8; 256];
|
|||
/// source color, it will have 256 destination to blended color mappings.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct BlendMap {
|
||||
start_color: u8,
|
||||
end_color: u8,
|
||||
mapping: Box<[BlendMapping]>,
|
||||
start_color: u8,
|
||||
end_color: u8,
|
||||
mapping: Box<[BlendMapping]>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BlendMap {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("BlendMap")
|
||||
.field("start_color", &self.start_color)
|
||||
.field("end_color", &self.end_color)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("BlendMap")
|
||||
.field("start_color", &self.start_color)
|
||||
.field("end_color", &self.end_color)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl BlendMap {
|
||||
/// Creates and returns a new [`BlendMap`] with source color mappings for the given inclusive
|
||||
/// range only. The `start_color` and `end_color` may also be equal to create a blend map with
|
||||
/// only a single source color mapping.
|
||||
pub fn new(start_color: u8, end_color: u8) -> Self {
|
||||
let (start_color, end_color) = if start_color > end_color {
|
||||
(end_color, start_color)
|
||||
} else {
|
||||
(start_color, end_color)
|
||||
};
|
||||
let num_colors = (end_color - start_color) as usize + 1;
|
||||
BlendMap {
|
||||
start_color,
|
||||
end_color,
|
||||
mapping: vec![[0u8; 256]; num_colors].into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
/// Creates and returns a new [`BlendMap`] with source color mappings for the given inclusive
|
||||
/// range only. The `start_color` and `end_color` may also be equal to create a blend map with
|
||||
/// only a single source color mapping.
|
||||
pub fn new(start_color: u8, end_color: u8) -> Self {
|
||||
let (start_color, end_color) = if start_color > end_color {
|
||||
(end_color, start_color)
|
||||
} else {
|
||||
(start_color, end_color)
|
||||
};
|
||||
let num_colors = (end_color - start_color) as usize + 1;
|
||||
BlendMap {
|
||||
start_color,
|
||||
end_color,
|
||||
mapping: vec![[0u8; 256]; num_colors].into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates and returns a new [`BlendMap`] with a single source color mapping which maps to
|
||||
/// a table pre-calculated for the given palette based on the color gradient specified. The
|
||||
/// resulting blend map can be used to create simple "colorization" overlay effects, which look
|
||||
/// like a simple translucency effect. The starting color in the gradient is used as the source
|
||||
/// color mapping in the returned blend map.
|
||||
pub fn new_colorized_map(gradient_start: u8, gradient_end: u8, palette: &Palette) -> Self {
|
||||
let (gradient_start, gradient_end) = if gradient_start > gradient_end {
|
||||
(gradient_end, gradient_start)
|
||||
} else {
|
||||
(gradient_start, gradient_end)
|
||||
};
|
||||
let gradient_size = gradient_end - gradient_start + 1;
|
||||
let source_color = gradient_start;
|
||||
/// Creates and returns a new [`BlendMap`] with a single source color mapping which maps to
|
||||
/// a table pre-calculated for the given palette based on the color gradient specified. The
|
||||
/// resulting blend map can be used to create simple "colorization" overlay effects, which look
|
||||
/// like a simple translucency effect. The starting color in the gradient is used as the source
|
||||
/// color mapping in the returned blend map.
|
||||
pub fn new_colorized_map(gradient_start: u8, gradient_end: u8, palette: &Palette) -> Self {
|
||||
let (gradient_start, gradient_end) = if gradient_start > gradient_end {
|
||||
(gradient_end, gradient_start)
|
||||
} else {
|
||||
(gradient_start, gradient_end)
|
||||
};
|
||||
let gradient_size = gradient_end - gradient_start + 1;
|
||||
let source_color = gradient_start;
|
||||
|
||||
let mut blend_map = Self::new(source_color, source_color);
|
||||
for idx in 0..=255 {
|
||||
let (r, g, b) = from_rgb32(palette[idx]);
|
||||
let lit = (luminance(r, g, b) * 255.0) as u8;
|
||||
blend_map.set_mapping(
|
||||
source_color,
|
||||
idx as u8,
|
||||
(gradient_size - 1) - (lit / (256 / gradient_size as u32) as u8) + source_color
|
||||
).unwrap();
|
||||
}
|
||||
blend_map
|
||||
}
|
||||
let mut blend_map = Self::new(source_color, source_color);
|
||||
for idx in 0..=255 {
|
||||
let (r, g, b) = from_rgb32(palette[idx]);
|
||||
let lit = (luminance(r, g, b) * 255.0) as u8;
|
||||
blend_map.set_mapping(
|
||||
source_color,
|
||||
idx as u8,
|
||||
(gradient_size - 1) - (lit / (256 / gradient_size as u32) as u8) + source_color,
|
||||
).unwrap();
|
||||
}
|
||||
blend_map
|
||||
}
|
||||
|
||||
/// Creates and returns a new [`BlendMap`] which can be used to blend source colors together
|
||||
/// with the destination using a colorization effect based on a function providing a custom
|
||||
/// calculation combining the source and destination color luminance values to return a weight
|
||||
/// into the gradient range given.
|
||||
pub fn new_colored_luminance_map(
|
||||
gradient_start: u8,
|
||||
gradient_end: u8,
|
||||
palette: &Palette,
|
||||
f: impl Fn(f32, f32) -> f32
|
||||
) -> BlendMap {
|
||||
let (gradient_start, gradient_end) = if gradient_start > gradient_end {
|
||||
(gradient_end, gradient_start)
|
||||
} else {
|
||||
(gradient_start, gradient_end)
|
||||
};
|
||||
let gradient_size = gradient_end - gradient_start + 1;
|
||||
/// Creates and returns a new [`BlendMap`] which can be used to blend source colors together
|
||||
/// with the destination using a colorization effect based on a function providing a custom
|
||||
/// calculation combining the source and destination color luminance values to return a weight
|
||||
/// into the gradient range given.
|
||||
pub fn new_colored_luminance_map(
|
||||
gradient_start: u8,
|
||||
gradient_end: u8,
|
||||
palette: &Palette,
|
||||
f: impl Fn(f32, f32) -> f32,
|
||||
) -> BlendMap {
|
||||
let (gradient_start, gradient_end) = if gradient_start > gradient_end {
|
||||
(gradient_end, gradient_start)
|
||||
} else {
|
||||
(gradient_start, gradient_end)
|
||||
};
|
||||
let gradient_size = gradient_end - gradient_start + 1;
|
||||
|
||||
let mut blend_map = BlendMap::new(0, 255);
|
||||
for source_color in 0..=255 {
|
||||
let (r, g, b) = from_rgb32(palette[source_color]);
|
||||
let source_luminance = luminance(r, g, b);
|
||||
for dest_color in 0..=255 {
|
||||
let (r, g, b) = from_rgb32(palette[dest_color]);
|
||||
let destination_luminance = luminance(r, g, b);
|
||||
let weight = (f(source_luminance, destination_luminance) * 255.0) as u8;
|
||||
blend_map.set_mapping(
|
||||
source_color,
|
||||
dest_color,
|
||||
(gradient_size - 1).wrapping_sub(weight / (256 / gradient_size as u32) as u8) + gradient_start
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
blend_map
|
||||
}
|
||||
let mut blend_map = BlendMap::new(0, 255);
|
||||
for source_color in 0..=255 {
|
||||
let (r, g, b) = from_rgb32(palette[source_color]);
|
||||
let source_luminance = luminance(r, g, b);
|
||||
for dest_color in 0..=255 {
|
||||
let (r, g, b) = from_rgb32(palette[dest_color]);
|
||||
let destination_luminance = luminance(r, g, b);
|
||||
let weight = (f(source_luminance, destination_luminance) * 255.0) as u8;
|
||||
blend_map.set_mapping(
|
||||
source_color,
|
||||
dest_color,
|
||||
(gradient_size - 1).wrapping_sub(weight / (256 / gradient_size as u32) as u8) + gradient_start,
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
blend_map
|
||||
}
|
||||
|
||||
/// Creates and returns a new [`BlendMap`] which can be used to blend all 256 colors together
|
||||
/// with every other color, weighting the blending based on the ratios given where 0.0 will
|
||||
/// result in that component being totally transparent and 1.0, totally opaque.
|
||||
///
|
||||
/// This method is SLOW! It is computing 65536 different blend colors by searching the given
|
||||
/// palette for the closest RGB match between two colors.
|
||||
///
|
||||
/// Because simple palette searches are being used to build the blending table, results will
|
||||
/// vary palette to palette. There will not always be a perfect blend color available.
|
||||
pub fn new_translucency_map(blend_r: f32, blend_g: f32, blend_b: f32, palette: &Palette) -> Self {
|
||||
let mut blend_map = BlendMap::new(0, 255);
|
||||
for source in 0..=255 {
|
||||
let (source_r, source_g, source_b) = from_rgb32(palette[source]);
|
||||
let mapping = blend_map.get_mapping_mut(source).unwrap();
|
||||
for dest in 0..=255 {
|
||||
let (dest_r, dest_g, dest_b) = from_rgb32(palette[dest]);
|
||||
/// Creates and returns a new [`BlendMap`] which can be used to blend all 256 colors together
|
||||
/// with every other color, weighting the blending based on the ratios given where 0.0 will
|
||||
/// result in that component being totally transparent and 1.0, totally opaque.
|
||||
///
|
||||
/// This method is SLOW! It is computing 65536 different blend colors by searching the given
|
||||
/// palette for the closest RGB match between two colors.
|
||||
///
|
||||
/// Because simple palette searches are being used to build the blending table, results will
|
||||
/// vary palette to palette. There will not always be a perfect blend color available.
|
||||
pub fn new_translucency_map(blend_r: f32, blend_g: f32, blend_b: f32, palette: &Palette) -> Self {
|
||||
let mut blend_map = BlendMap::new(0, 255);
|
||||
for source in 0..=255 {
|
||||
let (source_r, source_g, source_b) = from_rgb32(palette[source]);
|
||||
let mapping = blend_map.get_mapping_mut(source).unwrap();
|
||||
for dest in 0..=255 {
|
||||
let (dest_r, dest_g, dest_b) = from_rgb32(palette[dest]);
|
||||
|
||||
let find_r = lerp(dest_r as f32, source_r as f32, blend_r) as u8;
|
||||
let find_g = lerp(dest_g as f32, source_g as f32, blend_g) as u8;
|
||||
let find_b = lerp(dest_b as f32, source_b as f32, blend_b) as u8;
|
||||
let find_r = lerp(dest_r as f32, source_r as f32, blend_r) as u8;
|
||||
let find_g = lerp(dest_g as f32, source_g as f32, blend_g) as u8;
|
||||
let find_b = lerp(dest_b as f32, source_b as f32, blend_b) as u8;
|
||||
|
||||
let result_c = palette.find_color(find_r, find_g, find_b);
|
||||
mapping[dest as usize] = result_c;
|
||||
}
|
||||
}
|
||||
blend_map
|
||||
}
|
||||
let result_c = palette.find_color(find_r, find_g, find_b);
|
||||
mapping[dest as usize] = result_c;
|
||||
}
|
||||
}
|
||||
blend_map
|
||||
}
|
||||
|
||||
/// The beginning source color that is mapped in this blend map.
|
||||
#[inline]
|
||||
pub fn start_color(&self) -> u8 {
|
||||
self.start_color
|
||||
}
|
||||
/// The beginning source color that is mapped in this blend map.
|
||||
#[inline]
|
||||
pub fn start_color(&self) -> u8 {
|
||||
self.start_color
|
||||
}
|
||||
|
||||
/// The ending source color that is mapped in this blend map.
|
||||
#[inline]
|
||||
pub fn end_color(&self) -> u8 {
|
||||
self.end_color
|
||||
}
|
||||
/// The ending source color that is mapped in this blend map.
|
||||
#[inline]
|
||||
pub fn end_color(&self) -> u8 {
|
||||
self.end_color
|
||||
}
|
||||
|
||||
/// Returns true if the given source color is mapped in this blend map.
|
||||
#[inline]
|
||||
pub fn is_mapped(&self, color: u8) -> bool {
|
||||
color >= self.start_color && color <= self.end_color
|
||||
}
|
||||
/// Returns true if the given source color is mapped in this blend map.
|
||||
#[inline]
|
||||
pub fn is_mapped(&self, color: u8) -> bool {
|
||||
color >= self.start_color && color <= self.end_color
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_mapping_index(&self, color: u8) -> Option<usize> {
|
||||
if color >= self.start_color && color <= self.end_color {
|
||||
let index = (color - self.start_color) as usize;
|
||||
Some(index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn get_mapping_index(&self, color: u8) -> Option<usize> {
|
||||
if color >= self.start_color && color <= self.end_color {
|
||||
let index = (color - self.start_color) as usize;
|
||||
Some(index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the destination-to-blend color mapping table for the given source
|
||||
/// color. Returns `None` if the specified source color is not in this blend map.
|
||||
#[inline]
|
||||
pub fn get_mapping(&self, color: u8) -> Option<&BlendMapping> {
|
||||
if let Some(index) = self.get_mapping_index(color) {
|
||||
// safety: index cannot be outside 0-255 since color and start_color are both u8
|
||||
unsafe { Some(self.mapping.get_unchecked(index)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Returns a reference to the destination-to-blend color mapping table for the given source
|
||||
/// color. Returns `None` if the specified source color is not in this blend map.
|
||||
#[inline]
|
||||
pub fn get_mapping(&self, color: u8) -> Option<&BlendMapping> {
|
||||
if let Some(index) = self.get_mapping_index(color) {
|
||||
// safety: index cannot be outside 0-255 since color and start_color are both u8
|
||||
unsafe { Some(self.mapping.get_unchecked(index)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the destination-to-blend color mapping table for the given
|
||||
/// source color. Returns `None` if the specified source color is not in this blend map.
|
||||
#[inline]
|
||||
pub fn get_mapping_mut(&mut self, color: u8) -> Option<&mut BlendMapping> {
|
||||
if let Some(index) = self.get_mapping_index(color) {
|
||||
// safety: index cannot be outside 0-255 since color and start_color are both u8
|
||||
unsafe { Some(self.mapping.get_unchecked_mut(index)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Returns a mutable reference to the destination-to-blend color mapping table for the given
|
||||
/// source color. Returns `None` if the specified source color is not in this blend map.
|
||||
#[inline]
|
||||
pub fn get_mapping_mut(&mut self, color: u8) -> Option<&mut BlendMapping> {
|
||||
if let Some(index) = self.get_mapping_index(color) {
|
||||
// safety: index cannot be outside 0-255 since color and start_color are both u8
|
||||
unsafe { Some(self.mapping.get_unchecked_mut(index)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the blend color mapping for the given source color and destination color combination.
|
||||
pub fn set_mapping(&mut self, source_color: u8, dest_color: u8, blended_color: u8) -> Result<(), BlendMapError> {
|
||||
if let Some(mapping) = self.get_mapping_mut(source_color) {
|
||||
mapping[dest_color as usize] = blended_color;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(BlendMapError::InvalidSourceColor(source_color))
|
||||
}
|
||||
}
|
||||
/// Sets the blend color mapping for the given source color and destination color combination.
|
||||
pub fn set_mapping(&mut self, source_color: u8, dest_color: u8, blended_color: u8) -> Result<(), BlendMapError> {
|
||||
if let Some(mapping) = self.get_mapping_mut(source_color) {
|
||||
mapping[dest_color as usize] = blended_color;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(BlendMapError::InvalidSourceColor(source_color))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Sets a series of blend color mappings for the given source color and starting from a base
|
||||
/// destination color.
|
||||
pub fn set_mappings<const N: usize>(&mut self, source_color: u8, base_dest_color: u8, mappings: [u8; N]) -> Result<(), BlendMapError> {
|
||||
if let Some(mapping) = self.get_mapping_mut(source_color) {
|
||||
assert!((base_dest_color as usize + N - 1) <= 255, "mappings array is too big for the remaining colors available");
|
||||
for index in 0..N {
|
||||
mapping[index + base_dest_color as usize] = mappings[index];
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(BlendMapError::InvalidSourceColor(source_color))
|
||||
}
|
||||
}
|
||||
/// Sets a series of blend color mappings for the given source color and starting from a base
|
||||
/// destination color.
|
||||
pub fn set_mappings<const N: usize>(&mut self, source_color: u8, base_dest_color: u8, mappings: [u8; N]) -> Result<(), BlendMapError> {
|
||||
if let Some(mapping) = self.get_mapping_mut(source_color) {
|
||||
assert!((base_dest_color as usize + N - 1) <= 255, "mappings array is too big for the remaining colors available");
|
||||
for index in 0..N {
|
||||
mapping[index + base_dest_color as usize] = mappings[index];
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(BlendMapError::InvalidSourceColor(source_color))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the blend color for the given source and destination colors. If the source color
|
||||
/// is not in this blend map, `None` is returned.
|
||||
#[inline]
|
||||
pub fn blend(&self, source_color: u8, dest_color: u8) -> Option<u8> {
|
||||
if let Some(mapping) = self.get_mapping(source_color) {
|
||||
Some(mapping[dest_color as usize])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Returns the blend color for the given source and destination colors. If the source color
|
||||
/// is not in this blend map, `None` is returned.
|
||||
#[inline]
|
||||
pub fn blend(&self, source_color: u8, dest_color: u8) -> Option<u8> {
|
||||
if let Some(mapping) = self.get_mapping(source_color) {
|
||||
Some(mapping[dest_color as usize])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_file(path: &Path) -> Result<Self, BlendMapError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_from_bytes(&mut reader)
|
||||
}
|
||||
pub fn load_from_file(path: &Path) -> Result<Self, BlendMapError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
Self::load_from_bytes(&mut reader)
|
||||
}
|
||||
|
||||
pub fn load_from_bytes<T: ReadBytesExt>(reader: &mut T) -> Result<Self, BlendMapError> {
|
||||
let ident: [u8; 4] = reader.read_bytes()?;
|
||||
if ident != *b"BMap" {
|
||||
return Err(BlendMapError::BadFile(String::from("Unrecognized header")));
|
||||
}
|
||||
pub fn load_from_bytes<T: ReadBytesExt>(reader: &mut T) -> Result<Self, BlendMapError> {
|
||||
let ident: [u8; 4] = reader.read_bytes()?;
|
||||
if ident != *b"BMap" {
|
||||
return Err(BlendMapError::BadFile(String::from("Unrecognized header")));
|
||||
}
|
||||
|
||||
let start_color = reader.read_u8()?;
|
||||
let end_color = reader.read_u8()?;
|
||||
let num_maps = end_color as usize - start_color as usize + 1;
|
||||
let start_color = reader.read_u8()?;
|
||||
let end_color = reader.read_u8()?;
|
||||
let num_maps = end_color as usize - start_color as usize + 1;
|
||||
|
||||
let mut maps = Vec::with_capacity(num_maps);
|
||||
for _ in 0..num_maps {
|
||||
let map: BlendMapping = reader.read_bytes()?;
|
||||
maps.push(map);
|
||||
}
|
||||
let mut maps = Vec::with_capacity(num_maps);
|
||||
for _ in 0..num_maps {
|
||||
let map: BlendMapping = reader.read_bytes()?;
|
||||
maps.push(map);
|
||||
}
|
||||
|
||||
Ok(BlendMap {
|
||||
start_color,
|
||||
end_color,
|
||||
mapping: maps.into_boxed_slice()
|
||||
})
|
||||
}
|
||||
Ok(BlendMap {
|
||||
start_color,
|
||||
end_color,
|
||||
mapping: maps.into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_file(&self, path: &Path) -> Result<(), BlendMapError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_bytes(&mut writer)
|
||||
}
|
||||
pub fn to_file(&self, path: &Path) -> Result<(), BlendMapError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_bytes(&mut writer)
|
||||
}
|
||||
|
||||
pub fn to_bytes<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), BlendMapError> {
|
||||
writer.write_all(b"BMap")?;
|
||||
writer.write_u8(self.start_color)?;
|
||||
writer.write_u8(self.end_color)?;
|
||||
for map in self.mapping.iter() {
|
||||
writer.write_all(map)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn to_bytes<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), BlendMapError> {
|
||||
writer.write_all(b"BMap")?;
|
||||
writer.write_u8(self.start_color)?;
|
||||
writer.write_u8(self.end_color)?;
|
||||
for map in self.mapping.iter() {
|
||||
writer.write_all(map)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use claim::*;
|
||||
use tempfile::TempDir;
|
||||
use claim::*;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn create() -> Result<(), BlendMapError> {
|
||||
let blend_map = BlendMap::new(10, 12);
|
||||
assert_eq!(10, blend_map.start_color());
|
||||
assert_eq!(12, blend_map.end_color());
|
||||
assert!(blend_map.is_mapped(10));
|
||||
assert!(blend_map.is_mapped(11));
|
||||
assert!(blend_map.is_mapped(12));
|
||||
assert!(!blend_map.is_mapped(9));
|
||||
assert!(!blend_map.is_mapped(13));
|
||||
assert_some!(blend_map.get_mapping(10));
|
||||
assert_some!(blend_map.get_mapping(11));
|
||||
assert_some!(blend_map.get_mapping(12));
|
||||
assert_none!(blend_map.get_mapping(9));
|
||||
assert_none!(blend_map.get_mapping(13));
|
||||
#[test]
|
||||
pub fn create() -> Result<(), BlendMapError> {
|
||||
let blend_map = BlendMap::new(10, 12);
|
||||
assert_eq!(10, blend_map.start_color());
|
||||
assert_eq!(12, blend_map.end_color());
|
||||
assert!(blend_map.is_mapped(10));
|
||||
assert!(blend_map.is_mapped(11));
|
||||
assert!(blend_map.is_mapped(12));
|
||||
assert!(!blend_map.is_mapped(9));
|
||||
assert!(!blend_map.is_mapped(13));
|
||||
assert_some!(blend_map.get_mapping(10));
|
||||
assert_some!(blend_map.get_mapping(11));
|
||||
assert_some!(blend_map.get_mapping(12));
|
||||
assert_none!(blend_map.get_mapping(9));
|
||||
assert_none!(blend_map.get_mapping(13));
|
||||
|
||||
let blend_map = BlendMap::new(12, 10);
|
||||
assert_eq!(10, blend_map.start_color());
|
||||
assert_eq!(12, blend_map.end_color());
|
||||
assert!(blend_map.is_mapped(10));
|
||||
assert!(blend_map.is_mapped(11));
|
||||
assert!(blend_map.is_mapped(12));
|
||||
assert!(!blend_map.is_mapped(9));
|
||||
assert!(!blend_map.is_mapped(13));
|
||||
assert_some!(blend_map.get_mapping(10));
|
||||
assert_some!(blend_map.get_mapping(11));
|
||||
assert_some!(blend_map.get_mapping(12));
|
||||
assert_none!(blend_map.get_mapping(9));
|
||||
assert_none!(blend_map.get_mapping(13));
|
||||
let blend_map = BlendMap::new(12, 10);
|
||||
assert_eq!(10, blend_map.start_color());
|
||||
assert_eq!(12, blend_map.end_color());
|
||||
assert!(blend_map.is_mapped(10));
|
||||
assert!(blend_map.is_mapped(11));
|
||||
assert!(blend_map.is_mapped(12));
|
||||
assert!(!blend_map.is_mapped(9));
|
||||
assert!(!blend_map.is_mapped(13));
|
||||
assert_some!(blend_map.get_mapping(10));
|
||||
assert_some!(blend_map.get_mapping(11));
|
||||
assert_some!(blend_map.get_mapping(12));
|
||||
assert_none!(blend_map.get_mapping(9));
|
||||
assert_none!(blend_map.get_mapping(13));
|
||||
|
||||
let blend_map = BlendMap::new(130, 130);
|
||||
assert_eq!(130, blend_map.start_color());
|
||||
assert_eq!(130, blend_map.end_color());
|
||||
assert!(blend_map.is_mapped(130));
|
||||
assert!(!blend_map.is_mapped(129));
|
||||
assert!(!blend_map.is_mapped(131));
|
||||
assert_some!(blend_map.get_mapping(130));
|
||||
assert_none!(blend_map.get_mapping(129));
|
||||
assert_none!(blend_map.get_mapping(131));
|
||||
let blend_map = BlendMap::new(130, 130);
|
||||
assert_eq!(130, blend_map.start_color());
|
||||
assert_eq!(130, blend_map.end_color());
|
||||
assert!(blend_map.is_mapped(130));
|
||||
assert!(!blend_map.is_mapped(129));
|
||||
assert!(!blend_map.is_mapped(131));
|
||||
assert_some!(blend_map.get_mapping(130));
|
||||
assert_none!(blend_map.get_mapping(129));
|
||||
assert_none!(blend_map.get_mapping(131));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn mapping() -> Result<(), BlendMapError> {
|
||||
let mut blend_map = BlendMap::new(16, 31);
|
||||
#[test]
|
||||
pub fn mapping() -> Result<(), BlendMapError> {
|
||||
let mut blend_map = BlendMap::new(16, 31);
|
||||
|
||||
assert_none!(blend_map.blend(15, 0));
|
||||
assert_eq!(Some(0), blend_map.blend(16, 0));
|
||||
assert_eq!(Some(0), blend_map.blend(16, 1));
|
||||
assert_ok!(blend_map.set_mapping(16, 0, 116));
|
||||
assert_eq!(Some(116), blend_map.blend(16, 0));
|
||||
assert_eq!(Some(0), blend_map.blend(16, 1));
|
||||
assert_none!(blend_map.blend(15, 0));
|
||||
assert_eq!(Some(0), blend_map.blend(16, 0));
|
||||
assert_eq!(Some(0), blend_map.blend(16, 1));
|
||||
assert_ok!(blend_map.set_mapping(16, 0, 116));
|
||||
assert_eq!(Some(116), blend_map.blend(16, 0));
|
||||
assert_eq!(Some(0), blend_map.blend(16, 1));
|
||||
|
||||
let mapping = blend_map.get_mapping(16).unwrap();
|
||||
assert_eq!(116, mapping[0]);
|
||||
assert_eq!(0, mapping[1]);
|
||||
let mapping = blend_map.get_mapping(16).unwrap();
|
||||
assert_eq!(116, mapping[0]);
|
||||
assert_eq!(0, mapping[1]);
|
||||
|
||||
assert_eq!(Some(0), blend_map.blend(17, 0));
|
||||
assert_ok!(blend_map.set_mapping(17, 0, 117));
|
||||
assert_eq!(Some(117), blend_map.blend(17, 0));
|
||||
let mapping = blend_map.get_mapping_mut(17).unwrap();
|
||||
assert_eq!(117, mapping[0]);
|
||||
mapping[0] = 217;
|
||||
assert_eq!(Some(217), blend_map.blend(17, 0));
|
||||
assert_eq!(Some(0), blend_map.blend(17, 0));
|
||||
assert_ok!(blend_map.set_mapping(17, 0, 117));
|
||||
assert_eq!(Some(117), blend_map.blend(17, 0));
|
||||
let mapping = blend_map.get_mapping_mut(17).unwrap();
|
||||
assert_eq!(117, mapping[0]);
|
||||
mapping[0] = 217;
|
||||
assert_eq!(Some(217), blend_map.blend(17, 0));
|
||||
|
||||
assert_matches!(
|
||||
assert_matches!(
|
||||
blend_map.set_mapping(64, 1, 2),
|
||||
Err(BlendMapError::InvalidSourceColor(64))
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn bulk_mappings() -> Result<(), BlendMapError> {
|
||||
let mut blend_map = BlendMap::new(0, 7);
|
||||
#[test]
|
||||
pub fn bulk_mappings() -> Result<(), BlendMapError> {
|
||||
let mut blend_map = BlendMap::new(0, 7);
|
||||
|
||||
let mapping = blend_map.get_mapping(2).unwrap();
|
||||
assert_eq!([0, 0, 0, 0, 0, 0, 0, 0], mapping[0..8]);
|
||||
let mapping = blend_map.get_mapping(2).unwrap();
|
||||
assert_eq!([0, 0, 0, 0, 0, 0, 0, 0], mapping[0..8]);
|
||||
|
||||
assert_ok!(blend_map.set_mappings(2, 4, [1, 2, 3, 4, 5, 6, 7, 8]));
|
||||
assert_ok!(blend_map.set_mappings(2, 4, [1, 2, 3, 4, 5, 6, 7, 8]));
|
||||
|
||||
let mapping = blend_map.get_mapping(2).unwrap();
|
||||
assert_eq!(
|
||||
[0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0],
|
||||
mapping[0..16]
|
||||
);
|
||||
let mapping = blend_map.get_mapping(2).unwrap();
|
||||
assert_eq!(
|
||||
[0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0],
|
||||
mapping[0..16]
|
||||
);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_and_save() -> Result<(), BlendMapError> {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
#[test]
|
||||
fn load_and_save() -> Result<(), BlendMapError> {
|
||||
let tmp_dir = TempDir::new()?;
|
||||
|
||||
let mut blend_map = BlendMap::new(2, 3);
|
||||
for i in 0..=255 {
|
||||
blend_map.set_mapping(2, i, i)?;
|
||||
blend_map.set_mapping(3, i, 255 - i)?;
|
||||
}
|
||||
let mut blend_map = BlendMap::new(2, 3);
|
||||
for i in 0..=255 {
|
||||
blend_map.set_mapping(2, i, i)?;
|
||||
blend_map.set_mapping(3, i, 255 - i)?;
|
||||
}
|
||||
|
||||
let save_path = tmp_dir.path().join("test_blend_map.blendmap");
|
||||
blend_map.to_file(&save_path)?;
|
||||
let save_path = tmp_dir.path().join("test_blend_map.blendmap");
|
||||
blend_map.to_file(&save_path)?;
|
||||
|
||||
let loaded_blend_map = BlendMap::load_from_file(&save_path)?;
|
||||
assert!(blend_map == loaded_blend_map, "loaded BlendMap is not the same as the original");
|
||||
let loaded_blend_map = BlendMap::load_from_file(&save_path)?;
|
||||
assert!(blend_map == loaded_blend_map, "loaded BlendMap is not the same as the original");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -17,263 +17,263 @@ pub const CHAR_FIXED_WIDTH: usize = 8;
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum FontError {
|
||||
#[error("Invalid font file: {0}")]
|
||||
InvalidFile(String),
|
||||
#[error("Invalid font file: {0}")]
|
||||
InvalidFile(String),
|
||||
|
||||
#[error("Font I/O error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
#[error("Font I/O error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum FontRenderOpts {
|
||||
Color(u8),
|
||||
None,
|
||||
Color(u8),
|
||||
None,
|
||||
}
|
||||
|
||||
pub trait Character {
|
||||
fn bounds(&self) -> &Rect;
|
||||
fn draw(&self, dest: &mut Bitmap, x: i32, y: i32, opts: FontRenderOpts);
|
||||
fn bounds(&self) -> &Rect;
|
||||
fn draw(&self, dest: &mut Bitmap, x: i32, y: i32, opts: FontRenderOpts);
|
||||
}
|
||||
|
||||
pub trait Font {
|
||||
type CharacterType: Character;
|
||||
type CharacterType: Character;
|
||||
|
||||
fn character(&self, ch: char) -> &Self::CharacterType;
|
||||
fn space_width(&self) -> u8;
|
||||
fn line_height(&self) -> u8;
|
||||
fn measure(&self, text: &str, opts: FontRenderOpts) -> (u32, u32);
|
||||
fn character(&self, ch: char) -> &Self::CharacterType;
|
||||
fn space_width(&self) -> u8;
|
||||
fn line_height(&self) -> u8;
|
||||
fn measure(&self, text: &str, opts: FontRenderOpts) -> (u32, u32);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct BitmaskCharacter {
|
||||
bytes: [u8; CHAR_HEIGHT],
|
||||
bounds: Rect,
|
||||
bytes: [u8; CHAR_HEIGHT],
|
||||
bounds: Rect,
|
||||
}
|
||||
|
||||
impl Character for BitmaskCharacter {
|
||||
#[inline]
|
||||
fn bounds(&self) -> &Rect {
|
||||
&self.bounds
|
||||
}
|
||||
#[inline]
|
||||
fn bounds(&self) -> &Rect {
|
||||
&self.bounds
|
||||
}
|
||||
|
||||
fn draw(&self, dest: &mut Bitmap, x: i32, y: i32, opts: FontRenderOpts) {
|
||||
// out of bounds check
|
||||
if ((x + self.bounds.width as i32) < dest.clip_region().x)
|
||||
|| ((y + self.bounds.height as i32) < dest.clip_region().y)
|
||||
|| (x >= dest.clip_region().right())
|
||||
|| (y >= dest.clip_region().bottom())
|
||||
{
|
||||
return;
|
||||
}
|
||||
fn draw(&self, dest: &mut Bitmap, x: i32, y: i32, opts: FontRenderOpts) {
|
||||
// out of bounds check
|
||||
if ((x + self.bounds.width as i32) < dest.clip_region().x)
|
||||
|| ((y + self.bounds.height as i32) < dest.clip_region().y)
|
||||
|| (x >= dest.clip_region().right())
|
||||
|| (y >= dest.clip_region().bottom())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let color = match opts {
|
||||
FontRenderOpts::Color(color) => color,
|
||||
_ => 0,
|
||||
};
|
||||
let color = match opts {
|
||||
FontRenderOpts::Color(color) => color,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
// TODO: i'm sure this can be optimized, lol
|
||||
for char_y in 0..self.bounds.height as usize {
|
||||
let mut bit_mask = 0x80;
|
||||
for char_x in 0..self.bounds.width as usize {
|
||||
if self.bytes[char_y] & bit_mask > 0 {
|
||||
dest.set_pixel(x + char_x as i32, y + char_y as i32, color);
|
||||
}
|
||||
bit_mask >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: i'm sure this can be optimized, lol
|
||||
for char_y in 0..self.bounds.height as usize {
|
||||
let mut bit_mask = 0x80;
|
||||
for char_x in 0..self.bounds.width as usize {
|
||||
if self.bytes[char_y] & bit_mask > 0 {
|
||||
dest.set_pixel(x + char_x as i32, y + char_y as i32, color);
|
||||
}
|
||||
bit_mask >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct BitmaskFont {
|
||||
characters: Box<[BitmaskCharacter]>,
|
||||
line_height: u8,
|
||||
space_width: u8,
|
||||
characters: Box<[BitmaskCharacter]>,
|
||||
line_height: u8,
|
||||
space_width: u8,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for BitmaskFont {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("BitmaskFont")
|
||||
.field("line_height", &self.line_height)
|
||||
.field("space_width", &self.space_width)
|
||||
.field("characters.len()", &self.characters.len())
|
||||
.finish()
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("BitmaskFont")
|
||||
.field("line_height", &self.line_height)
|
||||
.field("space_width", &self.space_width)
|
||||
.field("characters.len()", &self.characters.len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl BitmaskFont {
|
||||
pub fn new_vga_font() -> Result<BitmaskFont, FontError> {
|
||||
BitmaskFont::load_from_bytes(&mut Cursor::new(VGA_FONT_BYTES))
|
||||
}
|
||||
pub fn new_vga_font() -> Result<BitmaskFont, FontError> {
|
||||
BitmaskFont::load_from_bytes(&mut Cursor::new(VGA_FONT_BYTES))
|
||||
}
|
||||
|
||||
pub fn load_from_file(path: &Path) -> Result<BitmaskFont, FontError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
pub fn load_from_file(path: &Path) -> Result<BitmaskFont, FontError> {
|
||||
let f = File::open(path)?;
|
||||
let mut reader = BufReader::new(f);
|
||||
|
||||
BitmaskFont::load_from_bytes(&mut reader)
|
||||
}
|
||||
BitmaskFont::load_from_bytes(&mut reader)
|
||||
}
|
||||
|
||||
pub fn load_from_bytes<T: ReadBytesExt>(reader: &mut T) -> Result<BitmaskFont, FontError> {
|
||||
let mut characters: Vec<BitmaskCharacter> = Vec::with_capacity(NUM_CHARS);
|
||||
pub fn load_from_bytes<T: ReadBytesExt>(reader: &mut T) -> Result<BitmaskFont, FontError> {
|
||||
let mut characters: Vec<BitmaskCharacter> = Vec::with_capacity(NUM_CHARS);
|
||||
|
||||
// read character bitmap data
|
||||
for _ in 0..NUM_CHARS {
|
||||
let mut buffer = [0u8; CHAR_HEIGHT];
|
||||
reader.read_exact(&mut buffer)?;
|
||||
let character = BitmaskCharacter {
|
||||
bytes: buffer,
|
||||
// bounds are filled in below. ugh.
|
||||
bounds: Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
};
|
||||
characters.push(character);
|
||||
}
|
||||
// read character bitmap data
|
||||
for _ in 0..NUM_CHARS {
|
||||
let mut buffer = [0u8; CHAR_HEIGHT];
|
||||
reader.read_exact(&mut buffer)?;
|
||||
let character = BitmaskCharacter {
|
||||
bytes: buffer,
|
||||
// bounds are filled in below. ugh.
|
||||
bounds: Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
};
|
||||
characters.push(character);
|
||||
}
|
||||
|
||||
// read character widths (used for rendering)
|
||||
for i in 0..NUM_CHARS {
|
||||
characters[i].bounds.width = reader.read_u8()? as u32;
|
||||
}
|
||||
// read character widths (used for rendering)
|
||||
for i in 0..NUM_CHARS {
|
||||
characters[i].bounds.width = reader.read_u8()? as u32;
|
||||
}
|
||||
|
||||
// read global font height (used for rendering)
|
||||
let line_height = reader.read_u8()?;
|
||||
for i in 0..NUM_CHARS {
|
||||
characters[i].bounds.height = line_height as u32;
|
||||
}
|
||||
// read global font height (used for rendering)
|
||||
let line_height = reader.read_u8()?;
|
||||
for i in 0..NUM_CHARS {
|
||||
characters[i].bounds.height = line_height as u32;
|
||||
}
|
||||
|
||||
let space_width = characters[' ' as usize].bounds.width as u8;
|
||||
let space_width = characters[' ' as usize].bounds.width as u8;
|
||||
|
||||
Ok(BitmaskFont {
|
||||
characters: characters.into_boxed_slice(),
|
||||
line_height,
|
||||
space_width,
|
||||
})
|
||||
}
|
||||
Ok(BitmaskFont {
|
||||
characters: characters.into_boxed_slice(),
|
||||
line_height,
|
||||
space_width,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_file(&self, path: &Path) -> Result<(), FontError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_bytes(&mut writer)
|
||||
}
|
||||
pub fn to_file(&self, path: &Path) -> Result<(), FontError> {
|
||||
let f = File::create(path)?;
|
||||
let mut writer = BufWriter::new(f);
|
||||
self.to_bytes(&mut writer)
|
||||
}
|
||||
|
||||
pub fn to_bytes<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), FontError> {
|
||||
// write character bitmap data
|
||||
for i in 0..NUM_CHARS {
|
||||
writer.write_all(&self.characters[i].bytes)?;
|
||||
}
|
||||
pub fn to_bytes<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), FontError> {
|
||||
// write character bitmap data
|
||||
for i in 0..NUM_CHARS {
|
||||
writer.write_all(&self.characters[i].bytes)?;
|
||||
}
|
||||
|
||||
// write character widths
|
||||
for i in 0..NUM_CHARS {
|
||||
writer.write_u8(self.characters[i].bounds.width as u8)?;
|
||||
}
|
||||
// write character widths
|
||||
for i in 0..NUM_CHARS {
|
||||
writer.write_u8(self.characters[i].bounds.width as u8)?;
|
||||
}
|
||||
|
||||
// write global font height
|
||||
writer.write_u8(self.line_height)?;
|
||||
// write global font height
|
||||
writer.write_u8(self.line_height)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Font for BitmaskFont {
|
||||
type CharacterType = BitmaskCharacter;
|
||||
type CharacterType = BitmaskCharacter;
|
||||
|
||||
#[inline]
|
||||
fn character(&self, ch: char) -> &Self::CharacterType {
|
||||
&self.characters[ch as usize]
|
||||
}
|
||||
#[inline]
|
||||
fn character(&self, ch: char) -> &Self::CharacterType {
|
||||
&self.characters[ch as usize]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn space_width(&self) -> u8 {
|
||||
self.space_width
|
||||
}
|
||||
#[inline]
|
||||
fn space_width(&self) -> u8 {
|
||||
self.space_width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn line_height(&self) -> u8 {
|
||||
self.line_height
|
||||
}
|
||||
#[inline]
|
||||
fn line_height(&self) -> u8 {
|
||||
self.line_height
|
||||
}
|
||||
|
||||
fn measure(&self, text: &str, _opts: FontRenderOpts) -> (u32, u32) {
|
||||
if text.is_empty() {
|
||||
return (0, 0);
|
||||
}
|
||||
let mut height = 0;
|
||||
let mut width = 0;
|
||||
let mut x = 0;
|
||||
// trimming whitespace off the end because it won't be rendered (since it's whitespace)
|
||||
// and thus, won't contribute to visible rendered output (what we're measuring)
|
||||
for ch in text.trim_end().chars() {
|
||||
match ch {
|
||||
'\n' => {
|
||||
if x == 0 {
|
||||
height += self.line_height as u32;
|
||||
}
|
||||
width = std::cmp::max(width, x);
|
||||
x = 0;
|
||||
},
|
||||
'\r' => (),
|
||||
ch => {
|
||||
if x == 0 {
|
||||
height += self.line_height as u32;
|
||||
}
|
||||
x += self.character(ch).bounds().width;
|
||||
}
|
||||
}
|
||||
}
|
||||
width = std::cmp::max(width, x);
|
||||
(width, height)
|
||||
}
|
||||
fn measure(&self, text: &str, _opts: FontRenderOpts) -> (u32, u32) {
|
||||
if text.is_empty() {
|
||||
return (0, 0);
|
||||
}
|
||||
let mut height = 0;
|
||||
let mut width = 0;
|
||||
let mut x = 0;
|
||||
// trimming whitespace off the end because it won't be rendered (since it's whitespace)
|
||||
// and thus, won't contribute to visible rendered output (what we're measuring)
|
||||
for ch in text.trim_end().chars() {
|
||||
match ch {
|
||||
'\n' => {
|
||||
if x == 0 {
|
||||
height += self.line_height as u32;
|
||||
}
|
||||
width = std::cmp::max(width, x);
|
||||
x = 0;
|
||||
}
|
||||
'\r' => (),
|
||||
ch => {
|
||||
if x == 0 {
|
||||
height += self.line_height as u32;
|
||||
}
|
||||
x += self.character(ch).bounds().width;
|
||||
}
|
||||
}
|
||||
}
|
||||
width = std::cmp::max(width, x);
|
||||
(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn load_font() -> Result<(), FontError> {
|
||||
let font = BitmaskFont::load_from_file(Path::new("./assets/vga.fnt"))?;
|
||||
assert_eq!(256, font.characters.len());
|
||||
assert_eq!(CHAR_FIXED_WIDTH as u8, font.space_width);
|
||||
for character in font.characters.iter() {
|
||||
assert_eq!(CHAR_FIXED_WIDTH as u8, character.bounds.width as u8);
|
||||
assert_eq!(CHAR_HEIGHT, character.bytes.len());
|
||||
}
|
||||
#[test]
|
||||
pub fn load_font() -> Result<(), FontError> {
|
||||
let font = BitmaskFont::load_from_file(Path::new("./assets/vga.fnt"))?;
|
||||
assert_eq!(256, font.characters.len());
|
||||
assert_eq!(CHAR_FIXED_WIDTH as u8, font.space_width);
|
||||
for character in font.characters.iter() {
|
||||
assert_eq!(CHAR_FIXED_WIDTH as u8, character.bounds.width as u8);
|
||||
assert_eq!(CHAR_HEIGHT, character.bytes.len());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn measure_text() -> Result<(), FontError> {
|
||||
{
|
||||
let font = BitmaskFont::load_from_file(Path::new("./assets/vga.fnt"))?;
|
||||
#[test]
|
||||
pub fn measure_text() -> Result<(), FontError> {
|
||||
{
|
||||
let font = BitmaskFont::load_from_file(Path::new("./assets/vga.fnt"))?;
|
||||
|
||||
assert_eq!((40, 8), font.measure("Hello", FontRenderOpts::None));
|
||||
assert_eq!((40, 16), font.measure("Hello\nthere", FontRenderOpts::None));
|
||||
assert_eq!((88, 24), font.measure("longer line\nshort\nthe end", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure("", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure(" ", FontRenderOpts::None));
|
||||
assert_eq!((40, 16), font.measure("\nhello", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure("\n", FontRenderOpts::None));
|
||||
assert_eq!((40, 8), font.measure("hello\n", FontRenderOpts::None));
|
||||
assert_eq!((40, 24), font.measure("hello\n\nthere", FontRenderOpts::None));
|
||||
}
|
||||
assert_eq!((40, 8), font.measure("Hello", FontRenderOpts::None));
|
||||
assert_eq!((40, 16), font.measure("Hello\nthere", FontRenderOpts::None));
|
||||
assert_eq!((88, 24), font.measure("longer line\nshort\nthe end", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure("", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure(" ", FontRenderOpts::None));
|
||||
assert_eq!((40, 16), font.measure("\nhello", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure("\n", FontRenderOpts::None));
|
||||
assert_eq!((40, 8), font.measure("hello\n", FontRenderOpts::None));
|
||||
assert_eq!((40, 24), font.measure("hello\n\nthere", FontRenderOpts::None));
|
||||
}
|
||||
|
||||
{
|
||||
let font = BitmaskFont::load_from_file(Path::new("./test-assets/small.fnt"))?;
|
||||
{
|
||||
let font = BitmaskFont::load_from_file(Path::new("./test-assets/small.fnt"))?;
|
||||
|
||||
assert_eq!((22, 7), font.measure("Hello", FontRenderOpts::None));
|
||||
assert_eq!((24, 14), font.measure("Hello\nthere", FontRenderOpts::None));
|
||||
assert_eq!((50, 21), font.measure("longer line\nshort\nthe end", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure("", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure(" ", FontRenderOpts::None));
|
||||
assert_eq!((21, 14), font.measure("\nhello", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure("\n", FontRenderOpts::None));
|
||||
assert_eq!((21, 7), font.measure("hello\n", FontRenderOpts::None));
|
||||
assert_eq!((24, 21), font.measure("hello\n\nthere", FontRenderOpts::None));
|
||||
}
|
||||
assert_eq!((22, 7), font.measure("Hello", FontRenderOpts::None));
|
||||
assert_eq!((24, 14), font.measure("Hello\nthere", FontRenderOpts::None));
|
||||
assert_eq!((50, 21), font.measure("longer line\nshort\nthe end", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure("", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure(" ", FontRenderOpts::None));
|
||||
assert_eq!((21, 14), font.measure("\nhello", FontRenderOpts::None));
|
||||
assert_eq!((0, 0), font.measure("\n", FontRenderOpts::None));
|
||||
assert_eq!((21, 7), font.measure("hello\n", FontRenderOpts::None));
|
||||
assert_eq!((24, 21), font.measure("hello\n\nthere", FontRenderOpts::None));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,33 +12,33 @@ pub mod system;
|
|||
pub mod utils;
|
||||
|
||||
pub const LOW_RES: bool = if cfg!(feature = "low_res") {
|
||||
true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
false
|
||||
};
|
||||
pub const WIDE_SCREEN: bool = if cfg!(feature = "wide") {
|
||||
true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
false
|
||||
};
|
||||
|
||||
pub const SCREEN_WIDTH: u32 = if cfg!(feature = "low_res") {
|
||||
if cfg!(feature = "wide") {
|
||||
214
|
||||
} else {
|
||||
160
|
||||
}
|
||||
if cfg!(feature = "wide") {
|
||||
214
|
||||
} else {
|
||||
160
|
||||
}
|
||||
} else {
|
||||
if cfg!(feature = "wide") {
|
||||
428
|
||||
} else {
|
||||
320
|
||||
}
|
||||
if cfg!(feature = "wide") {
|
||||
428
|
||||
} else {
|
||||
320
|
||||
}
|
||||
};
|
||||
pub const SCREEN_HEIGHT: u32 = if cfg!(feature = "low_res") {
|
||||
120
|
||||
120
|
||||
} else {
|
||||
240
|
||||
240
|
||||
};
|
||||
|
||||
pub const SCREEN_TOP: u32 = 0;
|
||||
|
@ -47,9 +47,9 @@ pub const SCREEN_RIGHT: u32 = SCREEN_WIDTH - 1;
|
|||
pub const SCREEN_BOTTOM: u32 = SCREEN_HEIGHT - 1;
|
||||
|
||||
pub const DEFAULT_SCALE_FACTOR: u32 = if cfg!(feature = "low_res") {
|
||||
6
|
||||
6
|
||||
} else {
|
||||
3
|
||||
3
|
||||
};
|
||||
|
||||
pub const NUM_COLORS: usize = 256; // i mean ... the number of colors is really defined by the size of u8 ...
|
||||
|
|
|
@ -3,111 +3,111 @@ use crate::math::*;
|
|||
/// Represents a 2D circle, using integer center coordinates and radius.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Circle {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub radius: u32,
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub radius: u32,
|
||||
}
|
||||
|
||||
impl Circle {
|
||||
#[inline]
|
||||
pub fn new(x: i32, y: i32, radius: u32) -> Self {
|
||||
Circle { x, y, radius }
|
||||
}
|
||||
#[inline]
|
||||
pub fn new(x: i32, y: i32, radius: u32) -> Self {
|
||||
Circle { x, y, radius }
|
||||
}
|
||||
|
||||
pub fn new_encapsulating(points: &[Vector2]) -> Option<Circle> {
|
||||
if points.is_empty() {
|
||||
return None;
|
||||
}
|
||||
pub fn new_encapsulating(points: &[Vector2]) -> Option<Circle> {
|
||||
if points.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut min_x = points[0].x;
|
||||
let mut min_y = points[0].y;
|
||||
let mut max_x = min_x;
|
||||
let mut max_y = min_y;
|
||||
let mut min_x = points[0].x;
|
||||
let mut min_y = points[0].y;
|
||||
let mut max_x = min_x;
|
||||
let mut max_y = min_y;
|
||||
|
||||
for i in 0..points.len() {
|
||||
let point = &points[i];
|
||||
min_x = point.x.min(min_x);
|
||||
min_y = point.y.min(min_y);
|
||||
max_x = point.x.max(max_x);
|
||||
max_y = point.y.max(max_y);
|
||||
}
|
||||
for i in 0..points.len() {
|
||||
let point = &points[i];
|
||||
min_x = point.x.min(min_x);
|
||||
min_y = point.y.min(min_y);
|
||||
max_x = point.x.max(max_x);
|
||||
max_y = point.y.max(max_y);
|
||||
}
|
||||
|
||||
let radius = distance_between(min_x, min_y, max_x, max_y) * 0.5;
|
||||
let center_x = (max_x - min_x) / 2.0;
|
||||
let center_y = (max_y - min_y) / 2.0;
|
||||
let radius = distance_between(min_x, min_y, max_x, max_y) * 0.5;
|
||||
let center_x = (max_x - min_x) / 2.0;
|
||||
let center_y = (max_y - min_y) / 2.0;
|
||||
|
||||
Some(Circle {
|
||||
x: center_x as i32,
|
||||
y: center_y as i32,
|
||||
radius: radius as u32,
|
||||
})
|
||||
}
|
||||
Some(Circle {
|
||||
x: center_x as i32,
|
||||
y: center_y as i32,
|
||||
radius: radius as u32,
|
||||
})
|
||||
}
|
||||
|
||||
/// Calculates the diameter of the circle.
|
||||
#[inline]
|
||||
pub fn diameter(&self) -> u32 {
|
||||
self.radius * 2
|
||||
}
|
||||
/// Calculates the diameter of the circle.
|
||||
#[inline]
|
||||
pub fn diameter(&self) -> u32 {
|
||||
self.radius * 2
|
||||
}
|
||||
|
||||
/// Returns true if the given point is contained within the bounds of this circle.
|
||||
pub fn contains_point(&self, x: i32, y: i32) -> bool {
|
||||
let distance_squared =
|
||||
distance_squared_between(self.x as f32, self.y as f32, x as f32, y as f32);
|
||||
let radius_squared = (self.radius * self.radius) as f32;
|
||||
distance_squared <= radius_squared
|
||||
}
|
||||
/// Returns true if the given point is contained within the bounds of this circle.
|
||||
pub fn contains_point(&self, x: i32, y: i32) -> bool {
|
||||
let distance_squared =
|
||||
distance_squared_between(self.x as f32, self.y as f32, x as f32, y as f32);
|
||||
let radius_squared = (self.radius * self.radius) as f32;
|
||||
distance_squared <= radius_squared
|
||||
}
|
||||
|
||||
/// Returns true if the given circle at least partially overlaps the bounds of this circle.
|
||||
pub fn overlaps(&self, other: &Circle) -> bool {
|
||||
let distance_squared =
|
||||
distance_squared_between(self.x as f32, self.y as f32, other.x as f32, other.y as f32);
|
||||
let minimum_distance_squared =
|
||||
((self.radius + other.radius) * (self.radius + other.radius)) as f32;
|
||||
distance_squared <= minimum_distance_squared
|
||||
}
|
||||
/// Returns true if the given circle at least partially overlaps the bounds of this circle.
|
||||
pub fn overlaps(&self, other: &Circle) -> bool {
|
||||
let distance_squared =
|
||||
distance_squared_between(self.x as f32, self.y as f32, other.x as f32, other.y as f32);
|
||||
let minimum_distance_squared =
|
||||
((self.radius + other.radius) * (self.radius + other.radius)) as f32;
|
||||
distance_squared <= minimum_distance_squared
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn test_new() {
|
||||
let c = Circle::new(1, 2, 4);
|
||||
assert_eq!(1, c.x);
|
||||
assert_eq!(2, c.y);
|
||||
assert_eq!(4, c.radius);
|
||||
}
|
||||
#[test]
|
||||
pub fn test_new() {
|
||||
let c = Circle::new(1, 2, 4);
|
||||
assert_eq!(1, c.x);
|
||||
assert_eq!(2, c.y);
|
||||
assert_eq!(4, c.radius);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_diameter() {
|
||||
let c = Circle::new(4, 4, 3);
|
||||
assert_eq!(6, c.diameter());
|
||||
}
|
||||
#[test]
|
||||
pub fn test_diameter() {
|
||||
let c = Circle::new(4, 4, 3);
|
||||
assert_eq!(6, c.diameter());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_contains_point() {
|
||||
let c = Circle::new(1, 1, 6);
|
||||
assert!(c.contains_point(4, 4));
|
||||
assert!(!c.contains_point(8, 4));
|
||||
#[test]
|
||||
pub fn test_contains_point() {
|
||||
let c = Circle::new(1, 1, 6);
|
||||
assert!(c.contains_point(4, 4));
|
||||
assert!(!c.contains_point(8, 4));
|
||||
|
||||
let c = Circle::new(0, 1, 2);
|
||||
assert!(!c.contains_point(3, 3));
|
||||
assert!(c.contains_point(0, 0));
|
||||
}
|
||||
let c = Circle::new(0, 1, 2);
|
||||
assert!(!c.contains_point(3, 3));
|
||||
assert!(c.contains_point(0, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_overlaps() {
|
||||
let a = Circle::new(3, 4, 5);
|
||||
let b = Circle::new(14, 18, 8);
|
||||
assert!(!a.overlaps(&b));
|
||||
#[test]
|
||||
pub fn test_overlaps() {
|
||||
let a = Circle::new(3, 4, 5);
|
||||
let b = Circle::new(14, 18, 8);
|
||||
assert!(!a.overlaps(&b));
|
||||
|
||||
let a = Circle::new(2, 3, 12);
|
||||
let b = Circle::new(15, 28, 10);
|
||||
assert!(!a.overlaps(&b));
|
||||
let a = Circle::new(2, 3, 12);
|
||||
let b = Circle::new(15, 28, 10);
|
||||
assert!(!a.overlaps(&b));
|
||||
|
||||
let a = Circle::new(-10, 8, 30);
|
||||
let b = Circle::new(14, -24, 10);
|
||||
assert!(a.overlaps(&b));
|
||||
}
|
||||
let a = Circle::new(-10, 8, 30);
|
||||
let b = Circle::new(14, -24, 10);
|
||||
assert!(a.overlaps(&b));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,414 +5,414 @@ use crate::math::*;
|
|||
/// Represents a 3x3 column-major matrix and provides common methods for matrix math.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Matrix3x3 {
|
||||
pub m: [f32; 9],
|
||||
pub m: [f32; 9],
|
||||
}
|
||||
|
||||
impl Matrix3x3 {
|
||||
pub const M11: usize = 0;
|
||||
pub const M12: usize = 3;
|
||||
pub const M13: usize = 6;
|
||||
pub const M21: usize = 1;
|
||||
pub const M22: usize = 4;
|
||||
pub const M23: usize = 7;
|
||||
pub const M31: usize = 2;
|
||||
pub const M32: usize = 5;
|
||||
pub const M33: usize = 8;
|
||||
pub const M11: usize = 0;
|
||||
pub const M12: usize = 3;
|
||||
pub const M13: usize = 6;
|
||||
pub const M21: usize = 1;
|
||||
pub const M22: usize = 4;
|
||||
pub const M23: usize = 7;
|
||||
pub const M31: usize = 2;
|
||||
pub const M32: usize = 5;
|
||||
pub const M33: usize = 8;
|
||||
|
||||
pub const IDENTITY: Matrix3x3 = Matrix3x3 {
|
||||
m: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
|
||||
};
|
||||
pub const IDENTITY: Matrix3x3 = Matrix3x3 {
|
||||
m: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
|
||||
};
|
||||
|
||||
/// Returns a new identity matrix.
|
||||
#[inline]
|
||||
pub fn identity() -> Matrix3x3 {
|
||||
Matrix3x3::IDENTITY
|
||||
}
|
||||
/// Returns a new identity matrix.
|
||||
#[inline]
|
||||
pub fn identity() -> Matrix3x3 {
|
||||
Matrix3x3::IDENTITY
|
||||
}
|
||||
|
||||
/// Creates a new matrix with the specified elements.
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new(
|
||||
m11: f32, m12: f32, m13: f32,
|
||||
m21: f32, m22: f32, m23: f32,
|
||||
m31: f32, m32: f32, m33: f32,
|
||||
) -> Matrix3x3 {
|
||||
Matrix3x3 {
|
||||
m: [
|
||||
m11, m21, m31,
|
||||
m12, m22, m32,
|
||||
m13, m23, m33
|
||||
],
|
||||
}
|
||||
}
|
||||
/// Creates a new matrix with the specified elements.
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new(
|
||||
m11: f32, m12: f32, m13: f32,
|
||||
m21: f32, m22: f32, m23: f32,
|
||||
m31: f32, m32: f32, m33: f32,
|
||||
) -> Matrix3x3 {
|
||||
Matrix3x3 {
|
||||
m: [
|
||||
m11, m21, m31,
|
||||
m12, m22, m32,
|
||||
m13, m23, m33
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new rotation matrix from a set of euler angles.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x`: the x angle (in radians)
|
||||
/// * `y`: the y angle (in radians)
|
||||
/// * `z`: the z angle (in radians)
|
||||
pub fn from_euler_angles(x: f32, y: f32, z: f32) -> Matrix3x3 {
|
||||
let rotate_z = Matrix3x3::new_rotation_z(z);
|
||||
let rotate_y = Matrix3x3::new_rotation_y(y);
|
||||
let rotate_x = Matrix3x3::new_rotation_x(x);
|
||||
/// Creates a new rotation matrix from a set of euler angles.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x`: the x angle (in radians)
|
||||
/// * `y`: the y angle (in radians)
|
||||
/// * `z`: the z angle (in radians)
|
||||
pub fn from_euler_angles(x: f32, y: f32, z: f32) -> Matrix3x3 {
|
||||
let rotate_z = Matrix3x3::new_rotation_z(z);
|
||||
let rotate_y = Matrix3x3::new_rotation_y(y);
|
||||
let rotate_x = Matrix3x3::new_rotation_x(x);
|
||||
|
||||
// "right-to-left" column-major matrix concatenation
|
||||
rotate_z * rotate_y * rotate_x
|
||||
}
|
||||
// "right-to-left" column-major matrix concatenation
|
||||
rotate_z * rotate_y * rotate_x
|
||||
}
|
||||
|
||||
/// Creates a new rotation matrix for rotation around the x axis.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `radians`: angle to rotate the x axis around (in radians)
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new_rotation_x(radians: f32) -> Matrix3x3 {
|
||||
let (s, c) = radians.sin_cos();
|
||||
Matrix3x3::new(
|
||||
1.0, 0.0, 0.0,
|
||||
0.0, c, -s,
|
||||
0.0, s, c
|
||||
)
|
||||
}
|
||||
/// Creates a new rotation matrix for rotation around the x axis.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `radians`: angle to rotate the x axis around (in radians)
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new_rotation_x(radians: f32) -> Matrix3x3 {
|
||||
let (s, c) = radians.sin_cos();
|
||||
Matrix3x3::new(
|
||||
1.0, 0.0, 0.0,
|
||||
0.0, c, -s,
|
||||
0.0, s, c,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a new rotation matrix for rotation around the y axis.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `radians`: angle to rotate the y axis around (in radians)
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new_rotation_y(radians: f32) -> Matrix3x3 {
|
||||
let (s, c) = radians.sin_cos();
|
||||
Matrix3x3::new(
|
||||
c, 0.0, s,
|
||||
0.0, 1.0, 0.0,
|
||||
-s, 0.0, c
|
||||
)
|
||||
}
|
||||
/// Creates a new rotation matrix for rotation around the y axis.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `radians`: angle to rotate the y axis around (in radians)
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new_rotation_y(radians: f32) -> Matrix3x3 {
|
||||
let (s, c) = radians.sin_cos();
|
||||
Matrix3x3::new(
|
||||
c, 0.0, s,
|
||||
0.0, 1.0, 0.0,
|
||||
-s, 0.0, c,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a new rotation matrix for rotation around the z axis.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `radians`: angle to rotate the z axis around (in radians)
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new_rotation_z(radians: f32) -> Matrix3x3 {
|
||||
let (s, c) = radians.sin_cos();
|
||||
Matrix3x3::new(
|
||||
c, -s, 0.0,
|
||||
s, c, 0.0,
|
||||
0.0, 0.0, 1.0
|
||||
)
|
||||
}
|
||||
/// Creates a new rotation matrix for rotation around the z axis.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `radians`: angle to rotate the z axis around (in radians)
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new_rotation_z(radians: f32) -> Matrix3x3 {
|
||||
let (s, c) = radians.sin_cos();
|
||||
Matrix3x3::new(
|
||||
c, -s, 0.0,
|
||||
s, c, 0.0,
|
||||
0.0, 0.0, 1.0,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a translation matrix. For use with 2D coordinates only.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x`: the amount to translate on the x axis
|
||||
/// * `y`: the amount to translate on the y axis
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new_2d_translation(x: f32, y: f32) -> Matrix3x3 {
|
||||
Matrix3x3::new(
|
||||
1.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0,
|
||||
x, y, 1.0
|
||||
)
|
||||
}
|
||||
/// Creates a translation matrix. For use with 2D coordinates only.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x`: the amount to translate on the x axis
|
||||
/// * `y`: the amount to translate on the y axis
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new_2d_translation(x: f32, y: f32) -> Matrix3x3 {
|
||||
Matrix3x3::new(
|
||||
1.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0,
|
||||
x, y, 1.0,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a scaling matrix from scaling factors for each axis. For use with 2D coordinates
|
||||
/// only.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x`: the scale factor for the x axis
|
||||
/// * `y`: the scale factor for the y axis
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new_2d_scaling(x: f32, y: f32) -> Matrix3x3 {
|
||||
Matrix3x3::new(
|
||||
x, 0.0, 0.0,
|
||||
0.0, y, 0.0,
|
||||
0.0, 0.0, 1.0
|
||||
)
|
||||
}
|
||||
/// Creates a scaling matrix from scaling factors for each axis. For use with 2D coordinates
|
||||
/// only.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x`: the scale factor for the x axis
|
||||
/// * `y`: the scale factor for the y axis
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn new_2d_scaling(x: f32, y: f32) -> Matrix3x3 {
|
||||
Matrix3x3::new(
|
||||
x, 0.0, 0.0,
|
||||
0.0, y, 0.0,
|
||||
0.0, 0.0, 1.0,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a new rotation matrix. For use with 2D coordinates only.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `radians`: angle to rotate by (in radians)
|
||||
#[inline(always)]
|
||||
pub fn new_2d_rotation(radians: f32) -> Matrix3x3 {
|
||||
Matrix3x3::new_rotation_z(radians)
|
||||
}
|
||||
/// Creates a new rotation matrix. For use with 2D coordinates only.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `radians`: angle to rotate by (in radians)
|
||||
#[inline(always)]
|
||||
pub fn new_2d_rotation(radians: f32) -> Matrix3x3 {
|
||||
Matrix3x3::new_rotation_z(radians)
|
||||
}
|
||||
|
||||
/// Calculates the determinant of this matrix.
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn determinant(&self) -> f32 {
|
||||
self.m[Matrix3x3::M11] * self.m[Matrix3x3::M22] * self.m[Matrix3x3::M33] +
|
||||
self.m[Matrix3x3::M12] * self.m[Matrix3x3::M23] * self.m[Matrix3x3::M31] +
|
||||
self.m[Matrix3x3::M13] * self.m[Matrix3x3::M21] * self.m[Matrix3x3::M32] -
|
||||
self.m[Matrix3x3::M11] * self.m[Matrix3x3::M23] * self.m[Matrix3x3::M32] -
|
||||
self.m[Matrix3x3::M12] * self.m[Matrix3x3::M21] * self.m[Matrix3x3::M33] -
|
||||
self.m[Matrix3x3::M13] * self.m[Matrix3x3::M22] * self.m[Matrix3x3::M31]
|
||||
}
|
||||
/// Calculates the determinant of this matrix.
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub fn determinant(&self) -> f32 {
|
||||
self.m[Matrix3x3::M11] * self.m[Matrix3x3::M22] * self.m[Matrix3x3::M33] +
|
||||
self.m[Matrix3x3::M12] * self.m[Matrix3x3::M23] * self.m[Matrix3x3::M31] +
|
||||
self.m[Matrix3x3::M13] * self.m[Matrix3x3::M21] * self.m[Matrix3x3::M32] -
|
||||
self.m[Matrix3x3::M11] * self.m[Matrix3x3::M23] * self.m[Matrix3x3::M32] -
|
||||
self.m[Matrix3x3::M12] * self.m[Matrix3x3::M21] * self.m[Matrix3x3::M33] -
|
||||
self.m[Matrix3x3::M13] * self.m[Matrix3x3::M22] * self.m[Matrix3x3::M31]
|
||||
}
|
||||
|
||||
/// Calculates the inverse of this matrix.
|
||||
#[rustfmt::skip]
|
||||
pub fn invert(&self) -> Matrix3x3 {
|
||||
let d = self.determinant();
|
||||
if nearly_equal(d, 0.0, 0.000001) {
|
||||
Matrix3x3::IDENTITY
|
||||
} else {
|
||||
let d = 1.0 / d;
|
||||
Matrix3x3 {
|
||||
m: [
|
||||
d * (self.m[Matrix3x3::M22] * self.m[Matrix3x3::M33] - self.m[Matrix3x3::M32] * self.m[Matrix3x3::M23]),
|
||||
d * (self.m[Matrix3x3::M31] * self.m[Matrix3x3::M23] - self.m[Matrix3x3::M21] * self.m[Matrix3x3::M33]),
|
||||
d * (self.m[Matrix3x3::M21] * self.m[Matrix3x3::M32] - self.m[Matrix3x3::M31] * self.m[Matrix3x3::M22]),
|
||||
d * (self.m[Matrix3x3::M32] * self.m[Matrix3x3::M13] - self.m[Matrix3x3::M12] * self.m[Matrix3x3::M33]),
|
||||
d * (self.m[Matrix3x3::M11] * self.m[Matrix3x3::M33] - self.m[Matrix3x3::M31] * self.m[Matrix3x3::M13]),
|
||||
d * (self.m[Matrix3x3::M31] * self.m[Matrix3x3::M12] - self.m[Matrix3x3::M11] * self.m[Matrix3x3::M32]),
|
||||
d * (self.m[Matrix3x3::M12] * self.m[Matrix3x3::M23] - self.m[Matrix3x3::M22] * self.m[Matrix3x3::M13]),
|
||||
d * (self.m[Matrix3x3::M21] * self.m[Matrix3x3::M13] - self.m[Matrix3x3::M11] * self.m[Matrix3x3::M23]),
|
||||
d * (self.m[Matrix3x3::M11] * self.m[Matrix3x3::M22] - self.m[Matrix3x3::M21] * self.m[Matrix3x3::M12]),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Calculates the inverse of this matrix.
|
||||
#[rustfmt::skip]
|
||||
pub fn invert(&self) -> Matrix3x3 {
|
||||
let d = self.determinant();
|
||||
if nearly_equal(d, 0.0, 0.000001) {
|
||||
Matrix3x3::IDENTITY
|
||||
} else {
|
||||
let d = 1.0 / d;
|
||||
Matrix3x3 {
|
||||
m: [
|
||||
d * (self.m[Matrix3x3::M22] * self.m[Matrix3x3::M33] - self.m[Matrix3x3::M32] * self.m[Matrix3x3::M23]),
|
||||
d * (self.m[Matrix3x3::M31] * self.m[Matrix3x3::M23] - self.m[Matrix3x3::M21] * self.m[Matrix3x3::M33]),
|
||||
d * (self.m[Matrix3x3::M21] * self.m[Matrix3x3::M32] - self.m[Matrix3x3::M31] * self.m[Matrix3x3::M22]),
|
||||
d * (self.m[Matrix3x3::M32] * self.m[Matrix3x3::M13] - self.m[Matrix3x3::M12] * self.m[Matrix3x3::M33]),
|
||||
d * (self.m[Matrix3x3::M11] * self.m[Matrix3x3::M33] - self.m[Matrix3x3::M31] * self.m[Matrix3x3::M13]),
|
||||
d * (self.m[Matrix3x3::M31] * self.m[Matrix3x3::M12] - self.m[Matrix3x3::M11] * self.m[Matrix3x3::M32]),
|
||||
d * (self.m[Matrix3x3::M12] * self.m[Matrix3x3::M23] - self.m[Matrix3x3::M22] * self.m[Matrix3x3::M13]),
|
||||
d * (self.m[Matrix3x3::M21] * self.m[Matrix3x3::M13] - self.m[Matrix3x3::M11] * self.m[Matrix3x3::M23]),
|
||||
d * (self.m[Matrix3x3::M11] * self.m[Matrix3x3::M22] - self.m[Matrix3x3::M21] * self.m[Matrix3x3::M12]),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the transpose of this matrix.
|
||||
#[inline]
|
||||
pub fn transpose(&self) -> Matrix3x3 {
|
||||
Matrix3x3::new(
|
||||
self.m[Matrix3x3::M11],
|
||||
self.m[Matrix3x3::M21],
|
||||
self.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M12],
|
||||
self.m[Matrix3x3::M22],
|
||||
self.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M13],
|
||||
self.m[Matrix3x3::M23],
|
||||
self.m[Matrix3x3::M33],
|
||||
)
|
||||
}
|
||||
/// Calculates the transpose of this matrix.
|
||||
#[inline]
|
||||
pub fn transpose(&self) -> Matrix3x3 {
|
||||
Matrix3x3::new(
|
||||
self.m[Matrix3x3::M11],
|
||||
self.m[Matrix3x3::M21],
|
||||
self.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M12],
|
||||
self.m[Matrix3x3::M22],
|
||||
self.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M13],
|
||||
self.m[Matrix3x3::M23],
|
||||
self.m[Matrix3x3::M33],
|
||||
)
|
||||
}
|
||||
|
||||
/// Sets all of the elements of this matrix.
|
||||
#[inline]
|
||||
pub fn set(
|
||||
&mut self,
|
||||
m11: f32,
|
||||
m12: f32,
|
||||
m13: f32,
|
||||
m21: f32,
|
||||
m22: f32,
|
||||
m23: f32,
|
||||
m31: f32,
|
||||
m32: f32,
|
||||
m33: f32,
|
||||
) {
|
||||
self.m[Matrix3x3::M11] = m11;
|
||||
self.m[Matrix3x3::M12] = m12;
|
||||
self.m[Matrix3x3::M13] = m13;
|
||||
self.m[Matrix3x3::M21] = m21;
|
||||
self.m[Matrix3x3::M22] = m22;
|
||||
self.m[Matrix3x3::M23] = m23;
|
||||
self.m[Matrix3x3::M31] = m31;
|
||||
self.m[Matrix3x3::M32] = m32;
|
||||
self.m[Matrix3x3::M33] = m33;
|
||||
}
|
||||
/// Sets all of the elements of this matrix.
|
||||
#[inline]
|
||||
pub fn set(
|
||||
&mut self,
|
||||
m11: f32,
|
||||
m12: f32,
|
||||
m13: f32,
|
||||
m21: f32,
|
||||
m22: f32,
|
||||
m23: f32,
|
||||
m31: f32,
|
||||
m32: f32,
|
||||
m33: f32,
|
||||
) {
|
||||
self.m[Matrix3x3::M11] = m11;
|
||||
self.m[Matrix3x3::M12] = m12;
|
||||
self.m[Matrix3x3::M13] = m13;
|
||||
self.m[Matrix3x3::M21] = m21;
|
||||
self.m[Matrix3x3::M22] = m22;
|
||||
self.m[Matrix3x3::M23] = m23;
|
||||
self.m[Matrix3x3::M31] = m31;
|
||||
self.m[Matrix3x3::M32] = m32;
|
||||
self.m[Matrix3x3::M33] = m33;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Matrix3x3 {
|
||||
type Output = Self;
|
||||
type Output = Self;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
Matrix3x3::new(
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M33],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M33],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M33]
|
||||
)
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
Matrix3x3::new(
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M33],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M33],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M33],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for Matrix3x3 {
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
self.set(
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M33],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M33],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M33]
|
||||
)
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
self.set(
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M11] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M12] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M13] * rhs.m[Matrix3x3::M33],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M21] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M22] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M23] * rhs.m[Matrix3x3::M33],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M11] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M21] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M31],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M12] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M22] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M32],
|
||||
self.m[Matrix3x3::M31] * rhs.m[Matrix3x3::M13] + self.m[Matrix3x3::M32] * rhs.m[Matrix3x3::M23] + self.m[Matrix3x3::M33] * rhs.m[Matrix3x3::M33],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Vector2> for Matrix3x3 {
|
||||
type Output = Vector2;
|
||||
type Output = Vector2;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
fn mul(self, rhs: Vector2) -> Self::Output {
|
||||
Vector2 {
|
||||
x: rhs.x * self.m[Matrix3x3::M11] + rhs.y * self.m[Matrix3x3::M12] + self.m[Matrix3x3::M13] + self.m[Matrix3x3::M31],
|
||||
y: rhs.x * self.m[Matrix3x3::M21] + rhs.y * self.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] + self.m[Matrix3x3::M32]
|
||||
}
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
fn mul(self, rhs: Vector2) -> Self::Output {
|
||||
Vector2 {
|
||||
x: rhs.x * self.m[Matrix3x3::M11] + rhs.y * self.m[Matrix3x3::M12] + self.m[Matrix3x3::M13] + self.m[Matrix3x3::M31],
|
||||
y: rhs.x * self.m[Matrix3x3::M21] + rhs.y * self.m[Matrix3x3::M22] + self.m[Matrix3x3::M23] + self.m[Matrix3x3::M32],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn test_new() {
|
||||
let m = Matrix3x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
|
||||
assert_eq!(1.0, m.m[Matrix3x3::M11]);
|
||||
assert_eq!(2.0, m.m[Matrix3x3::M12]);
|
||||
assert_eq!(3.0, m.m[Matrix3x3::M13]);
|
||||
assert_eq!(4.0, m.m[Matrix3x3::M21]);
|
||||
assert_eq!(5.0, m.m[Matrix3x3::M22]);
|
||||
assert_eq!(6.0, m.m[Matrix3x3::M23]);
|
||||
assert_eq!(7.0, m.m[Matrix3x3::M31]);
|
||||
assert_eq!(8.0, m.m[Matrix3x3::M32]);
|
||||
assert_eq!(9.0, m.m[Matrix3x3::M33]);
|
||||
}
|
||||
#[test]
|
||||
pub fn test_new() {
|
||||
let m = Matrix3x3::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
|
||||
assert_eq!(1.0, m.m[Matrix3x3::M11]);
|
||||
assert_eq!(2.0, m.m[Matrix3x3::M12]);
|
||||
assert_eq!(3.0, m.m[Matrix3x3::M13]);
|
||||
assert_eq!(4.0, m.m[Matrix3x3::M21]);
|
||||
assert_eq!(5.0, m.m[Matrix3x3::M22]);
|
||||
assert_eq!(6.0, m.m[Matrix3x3::M23]);
|
||||
assert_eq!(7.0, m.m[Matrix3x3::M31]);
|
||||
assert_eq!(8.0, m.m[Matrix3x3::M32]);
|
||||
assert_eq!(9.0, m.m[Matrix3x3::M33]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_set() {
|
||||
let mut m = Matrix3x3 { m: [0.0; 9] };
|
||||
m.set(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
|
||||
assert_eq!(1.0, m.m[Matrix3x3::M11]);
|
||||
assert_eq!(2.0, m.m[Matrix3x3::M12]);
|
||||
assert_eq!(3.0, m.m[Matrix3x3::M13]);
|
||||
assert_eq!(4.0, m.m[Matrix3x3::M21]);
|
||||
assert_eq!(5.0, m.m[Matrix3x3::M22]);
|
||||
assert_eq!(6.0, m.m[Matrix3x3::M23]);
|
||||
assert_eq!(7.0, m.m[Matrix3x3::M31]);
|
||||
assert_eq!(8.0, m.m[Matrix3x3::M32]);
|
||||
assert_eq!(9.0, m.m[Matrix3x3::M33]);
|
||||
}
|
||||
#[test]
|
||||
pub fn test_set() {
|
||||
let mut m = Matrix3x3 { m: [0.0; 9] };
|
||||
m.set(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
|
||||
assert_eq!(1.0, m.m[Matrix3x3::M11]);
|
||||
assert_eq!(2.0, m.m[Matrix3x3::M12]);
|
||||
assert_eq!(3.0, m.m[Matrix3x3::M13]);
|
||||
assert_eq!(4.0, m.m[Matrix3x3::M21]);
|
||||
assert_eq!(5.0, m.m[Matrix3x3::M22]);
|
||||
assert_eq!(6.0, m.m[Matrix3x3::M23]);
|
||||
assert_eq!(7.0, m.m[Matrix3x3::M31]);
|
||||
assert_eq!(8.0, m.m[Matrix3x3::M32]);
|
||||
assert_eq!(9.0, m.m[Matrix3x3::M33]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_identity() {
|
||||
let m = Matrix3x3::identity();
|
||||
assert_eq!(1.0, m.m[Matrix3x3::M11]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M12]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M13]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M21]);
|
||||
assert_eq!(1.0, m.m[Matrix3x3::M22]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M23]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M31]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M32]);
|
||||
assert_eq!(1.0, m.m[Matrix3x3::M33]);
|
||||
}
|
||||
#[test]
|
||||
pub fn test_identity() {
|
||||
let m = Matrix3x3::identity();
|
||||
assert_eq!(1.0, m.m[Matrix3x3::M11]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M12]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M13]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M21]);
|
||||
assert_eq!(1.0, m.m[Matrix3x3::M22]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M23]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M31]);
|
||||
assert_eq!(0.0, m.m[Matrix3x3::M32]);
|
||||
assert_eq!(1.0, m.m[Matrix3x3::M33]);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
pub fn test_transpose() {
|
||||
let m = Matrix3x3::new(
|
||||
1.0, 2.0, 3.0,
|
||||
4.0, 5.0, 6.0,
|
||||
7.0, 8.0, 9.0
|
||||
);
|
||||
let t = m.transpose();
|
||||
assert_eq!(1.0, t.m[Matrix3x3::M11]);
|
||||
assert_eq!(4.0, t.m[Matrix3x3::M12]);
|
||||
assert_eq!(7.0, t.m[Matrix3x3::M13]);
|
||||
assert_eq!(2.0, t.m[Matrix3x3::M21]);
|
||||
assert_eq!(5.0, t.m[Matrix3x3::M22]);
|
||||
assert_eq!(8.0, t.m[Matrix3x3::M23]);
|
||||
assert_eq!(3.0, t.m[Matrix3x3::M31]);
|
||||
assert_eq!(6.0, t.m[Matrix3x3::M32]);
|
||||
assert_eq!(9.0, t.m[Matrix3x3::M33]);
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
pub fn test_transpose() {
|
||||
let m = Matrix3x3::new(
|
||||
1.0, 2.0, 3.0,
|
||||
4.0, 5.0, 6.0,
|
||||
7.0, 8.0, 9.0,
|
||||
);
|
||||
let t = m.transpose();
|
||||
assert_eq!(1.0, t.m[Matrix3x3::M11]);
|
||||
assert_eq!(4.0, t.m[Matrix3x3::M12]);
|
||||
assert_eq!(7.0, t.m[Matrix3x3::M13]);
|
||||
assert_eq!(2.0, t.m[Matrix3x3::M21]);
|
||||
assert_eq!(5.0, t.m[Matrix3x3::M22]);
|
||||
assert_eq!(8.0, t.m[Matrix3x3::M23]);
|
||||
assert_eq!(3.0, t.m[Matrix3x3::M31]);
|
||||
assert_eq!(6.0, t.m[Matrix3x3::M32]);
|
||||
assert_eq!(9.0, t.m[Matrix3x3::M33]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_mul() {
|
||||
let a = Matrix3x3::new(12.0, 8.0, 4.0, 3.0, 17.0, 14.0, 9.0, 8.0, 10.0);
|
||||
let b = Matrix3x3::new(5.0, 19.0, 3.0, 6.0, 15.0, 9.0, 7.0, 8.0, 16.0);
|
||||
let c = a * b;
|
||||
assert!(nearly_equal(136.0, c.m[Matrix3x3::M11], 0.001));
|
||||
assert!(nearly_equal(380.0, c.m[Matrix3x3::M12], 0.001));
|
||||
assert!(nearly_equal(172.0, c.m[Matrix3x3::M13], 0.001));
|
||||
assert!(nearly_equal(215.0, c.m[Matrix3x3::M21], 0.001));
|
||||
assert!(nearly_equal(424.0, c.m[Matrix3x3::M22], 0.001));
|
||||
assert!(nearly_equal(386.0, c.m[Matrix3x3::M23], 0.001));
|
||||
assert!(nearly_equal(163.0, c.m[Matrix3x3::M31], 0.001));
|
||||
assert!(nearly_equal(371.0, c.m[Matrix3x3::M32], 0.001));
|
||||
assert!(nearly_equal(259.0, c.m[Matrix3x3::M33], 0.001));
|
||||
#[test]
|
||||
pub fn test_mul() {
|
||||
let a = Matrix3x3::new(12.0, 8.0, 4.0, 3.0, 17.0, 14.0, 9.0, 8.0, 10.0);
|
||||
let b = Matrix3x3::new(5.0, 19.0, 3.0, 6.0, 15.0, 9.0, 7.0, 8.0, 16.0);
|
||||
let c = a * b;
|
||||
assert!(nearly_equal(136.0, c.m[Matrix3x3::M11], 0.001));
|
||||
assert!(nearly_equal(380.0, c.m[Matrix3x3::M12], 0.001));
|
||||
assert!(nearly_equal(172.0, c.m[Matrix3x3::M13], 0.001));
|
||||
assert!(nearly_equal(215.0, c.m[Matrix3x3::M21], 0.001));
|
||||
assert!(nearly_equal(424.0, c.m[Matrix3x3::M22], 0.001));
|
||||
assert!(nearly_equal(386.0, c.m[Matrix3x3::M23], 0.001));
|
||||
assert!(nearly_equal(163.0, c.m[Matrix3x3::M31], 0.001));
|
||||
assert!(nearly_equal(371.0, c.m[Matrix3x3::M32], 0.001));
|
||||
assert!(nearly_equal(259.0, c.m[Matrix3x3::M33], 0.001));
|
||||
|
||||
let mut a = Matrix3x3::new(12.0, 8.0, 4.0, 3.0, 17.0, 14.0, 9.0, 8.0, 10.0);
|
||||
let b = Matrix3x3::new(5.0, 19.0, 3.0, 6.0, 15.0, 9.0, 7.0, 8.0, 16.0);
|
||||
a *= b;
|
||||
assert!(nearly_equal(136.0, a.m[Matrix3x3::M11], 0.001));
|
||||
assert!(nearly_equal(380.0, a.m[Matrix3x3::M12], 0.001));
|
||||
assert!(nearly_equal(172.0, a.m[Matrix3x3::M13], 0.001));
|
||||
assert!(nearly_equal(215.0, a.m[Matrix3x3::M21], 0.001));
|
||||
assert!(nearly_equal(424.0, a.m[Matrix3x3::M22], 0.001));
|
||||
assert!(nearly_equal(386.0, a.m[Matrix3x3::M23], 0.001));
|
||||
assert!(nearly_equal(163.0, a.m[Matrix3x3::M31], 0.001));
|
||||
assert!(nearly_equal(371.0, a.m[Matrix3x3::M32], 0.001));
|
||||
assert!(nearly_equal(259.0, a.m[Matrix3x3::M33], 0.001));
|
||||
}
|
||||
let mut a = Matrix3x3::new(12.0, 8.0, 4.0, 3.0, 17.0, 14.0, 9.0, 8.0, 10.0);
|
||||
let b = Matrix3x3::new(5.0, 19.0, 3.0, 6.0, 15.0, 9.0, 7.0, 8.0, 16.0);
|
||||
a *= b;
|
||||
assert!(nearly_equal(136.0, a.m[Matrix3x3::M11], 0.001));
|
||||
assert!(nearly_equal(380.0, a.m[Matrix3x3::M12], 0.001));
|
||||
assert!(nearly_equal(172.0, a.m[Matrix3x3::M13], 0.001));
|
||||
assert!(nearly_equal(215.0, a.m[Matrix3x3::M21], 0.001));
|
||||
assert!(nearly_equal(424.0, a.m[Matrix3x3::M22], 0.001));
|
||||
assert!(nearly_equal(386.0, a.m[Matrix3x3::M23], 0.001));
|
||||
assert!(nearly_equal(163.0, a.m[Matrix3x3::M31], 0.001));
|
||||
assert!(nearly_equal(371.0, a.m[Matrix3x3::M32], 0.001));
|
||||
assert!(nearly_equal(259.0, a.m[Matrix3x3::M33], 0.001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_2d_translation() {
|
||||
let v = Vector2::new(10.2, 5.7);
|
||||
let m = Matrix3x3::new_2d_translation(2.0, 3.0);
|
||||
let t = m * v;
|
||||
assert!(nearly_equal(12.2, t.x, 0.001));
|
||||
assert!(nearly_equal(8.7, t.y, 0.001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_2d_translation() {
|
||||
let v = Vector2::new(10.2, 5.7);
|
||||
let m = Matrix3x3::new_2d_translation(2.0, 3.0);
|
||||
let t = m * v;
|
||||
assert!(nearly_equal(12.2, t.x, 0.001));
|
||||
assert!(nearly_equal(8.7, t.y, 0.001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_2d_scaling() {
|
||||
let v = Vector2::new(10.2, 5.7);
|
||||
let m = Matrix3x3::new_2d_scaling(3.0, 4.0);
|
||||
let t = m * v;
|
||||
assert!(nearly_equal(30.6, t.x, 0.001));
|
||||
assert!(nearly_equal(22.8, t.y, 0.001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_2d_scaling() {
|
||||
let v = Vector2::new(10.2, 5.7);
|
||||
let m = Matrix3x3::new_2d_scaling(3.0, 4.0);
|
||||
let t = m * v;
|
||||
assert!(nearly_equal(30.6, t.x, 0.001));
|
||||
assert!(nearly_equal(22.8, t.y, 0.001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_2d_rotation() {
|
||||
let v = Vector2::new(0.0, 5.0);
|
||||
let m = Matrix3x3::new_2d_rotation(RADIANS_90);
|
||||
let t = m * v;
|
||||
assert!(nearly_equal(-5.0, t.x, 0.001));
|
||||
assert!(nearly_equal(0.0, t.y, 0.001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_2d_rotation() {
|
||||
let v = Vector2::new(0.0, 5.0);
|
||||
let m = Matrix3x3::new_2d_rotation(RADIANS_90);
|
||||
let t = m * v;
|
||||
assert!(nearly_equal(-5.0, t.x, 0.001));
|
||||
assert!(nearly_equal(0.0, t.y, 0.001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_2d_combined_transform() {
|
||||
let a = Matrix3x3::new_2d_translation(-2.0, 0.0);
|
||||
let b = Matrix3x3::new_2d_rotation(RADIANS_180);
|
||||
let m = a * b;
|
||||
let v = Vector2::new(0.0, 5.0);
|
||||
let t = m * v;
|
||||
assert!(nearly_equal(2.0, t.x, 0.001));
|
||||
assert!(nearly_equal(-5.0, t.y, 0.001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_2d_combined_transform() {
|
||||
let a = Matrix3x3::new_2d_translation(-2.0, 0.0);
|
||||
let b = Matrix3x3::new_2d_rotation(RADIANS_180);
|
||||
let m = a * b;
|
||||
let v = Vector2::new(0.0, 5.0);
|
||||
let t = m * v;
|
||||
assert!(nearly_equal(2.0, t.x, 0.001));
|
||||
assert!(nearly_equal(-5.0, t.y, 0.001));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,12 @@ pub mod matrix3x3;
|
|||
pub mod rect;
|
||||
pub mod vector2;
|
||||
|
||||
pub const PI: f32 = std::f32::consts::PI; // 180 degrees
|
||||
pub const HALF_PI: f32 = PI / 2.0; // 90 degrees
|
||||
pub const QUARTER_PI: f32 = PI / 4.0; // 45 degrees
|
||||
pub const PI: f32 = std::f32::consts::PI;
|
||||
// 180 degrees
|
||||
pub const HALF_PI: f32 = PI / 2.0;
|
||||
// 90 degrees
|
||||
pub const QUARTER_PI: f32 = PI / 4.0;
|
||||
// 45 degrees
|
||||
pub const TWO_PI: f32 = PI * 2.0; // 360 degrees
|
||||
|
||||
pub const RADIANS_0: f32 = 0.0;
|
||||
|
@ -40,8 +43,8 @@ pub const RIGHT: f32 = RADIANS_0;
|
|||
/// precision of the provided epsilon value.
|
||||
#[inline]
|
||||
pub fn nearly_equal(a: f32, b: f32, epsilon: f32) -> bool {
|
||||
// HACK/TODO: this is a shitty way to do this. but it's "good enough" for me ...
|
||||
(a - b).abs() <= epsilon
|
||||
// HACK/TODO: this is a shitty way to do this. but it's "good enough" for me ...
|
||||
(a - b).abs() <= epsilon
|
||||
}
|
||||
|
||||
/// Linearly interpolates between two values.
|
||||
|
@ -53,10 +56,10 @@ pub fn nearly_equal(a: f32, b: f32, epsilon: f32) -> bool {
|
|||
/// * `t`: the amount to interpolate between the two values, specified as a fraction
|
||||
#[inline]
|
||||
pub fn lerp<N>(a: N, b: N, t: f32) -> N
|
||||
where
|
||||
N: Copy + Add<Output = N> + Sub<Output = N> + Mul<f32, Output = N>,
|
||||
where
|
||||
N: Copy + Add<Output=N> + Sub<Output=N> + Mul<f32, Output=N>,
|
||||
{
|
||||
a + (b - a) * t
|
||||
a + (b - a) * t
|
||||
}
|
||||
|
||||
/// Given a linearly interpolated value and the original range (high and low) of the linear
|
||||
|
@ -69,10 +72,10 @@ where
|
|||
/// * `lerp_result`: the interpolated value between the range given
|
||||
#[inline]
|
||||
pub fn inverse_lerp<N>(a: N, b: N, lerp_result: N) -> f32
|
||||
where
|
||||
N: Copy + Sub<Output = N> + Div<N, Output = f32>,
|
||||
where
|
||||
N: Copy + Sub<Output=N> + Div<N, Output=f32>,
|
||||
{
|
||||
(lerp_result - a) / (b - a)
|
||||
(lerp_result - a) / (b - a)
|
||||
}
|
||||
|
||||
/// Interpolates between two values using a cubic equation.
|
||||
|
@ -84,11 +87,11 @@ where
|
|||
/// * `t`: the amount to interpolate between the two values, specified as a fraction
|
||||
#[inline]
|
||||
pub fn smooth_lerp<N>(a: N, b: N, t: f32) -> N
|
||||
where
|
||||
N: Copy + Add<Output = N> + Sub<Output = N> + Mul<f32, Output = N>,
|
||||
where
|
||||
N: Copy + Add<Output=N> + Sub<Output=N> + Mul<f32, Output=N>,
|
||||
{
|
||||
let t = t.clamp(0.0, 1.0);
|
||||
lerp(a, b, (t * t) * (3.0 - (2.0 * t)))
|
||||
let t = t.clamp(0.0, 1.0);
|
||||
lerp(a, b, (t * t) * (3.0 - (2.0 * t)))
|
||||
}
|
||||
|
||||
/// Re-scales a given value from an old min/max range to a new and different min/max range such
|
||||
|
@ -104,174 +107,174 @@ where
|
|||
/// * `new_max`: new max value (high end of range)
|
||||
#[inline]
|
||||
pub fn scale_range<N>(value: N, old_min: N, old_max: N, new_min: N, new_max: N) -> N
|
||||
where
|
||||
N: Copy + Add<Output = N> + Sub<Output = N> + Mul<Output = N> + Div<Output = N>,
|
||||
where
|
||||
N: Copy + Add<Output=N> + Sub<Output=N> + Mul<Output=N> + Div<Output=N>,
|
||||
{
|
||||
(new_max - new_min) * (value - old_min) / (old_max - old_min) + new_min
|
||||
(new_max - new_min) * (value - old_min) / (old_max - old_min) + new_min
|
||||
}
|
||||
|
||||
/// Calculates the angle (in radians) between the two points.
|
||||
#[inline]
|
||||
pub fn angle_between(x1: f32, y1: f32, x2: f32, y2: f32) -> f32 {
|
||||
let delta_x = x2 - x1;
|
||||
let delta_y = y2 - y1;
|
||||
delta_y.atan2(delta_x)
|
||||
let delta_x = x2 - x1;
|
||||
let delta_y = y2 - y1;
|
||||
delta_y.atan2(delta_x)
|
||||
}
|
||||
|
||||
/// Returns the X and Y point of a normalized 2D vector that points in the same direction as
|
||||
/// the given angle.
|
||||
#[inline]
|
||||
pub fn angle_to_direction(radians: f32) -> (f32, f32) {
|
||||
let x = radians.cos();
|
||||
let y = radians.sin();
|
||||
(x, y)
|
||||
let x = radians.cos();
|
||||
let y = radians.sin();
|
||||
(x, y)
|
||||
}
|
||||
|
||||
/// Calculates the squared distance between two 2D points.
|
||||
#[inline]
|
||||
pub fn distance_squared_between(x1: f32, y1: f32, x2: f32, y2: f32) -> f32 {
|
||||
(x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)
|
||||
(x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)
|
||||
}
|
||||
|
||||
/// Calculates the distance between two 2D points.
|
||||
#[inline]
|
||||
pub fn distance_between(x1: f32, y1: f32, x2: f32, y2: f32) -> f32 {
|
||||
distance_squared_between(x1, y1, x2, y2).sqrt()
|
||||
distance_squared_between(x1, y1, x2, y2).sqrt()
|
||||
}
|
||||
|
||||
pub trait NearlyEqual {
|
||||
type Output;
|
||||
type Output;
|
||||
|
||||
/// Returns true if the two f32 values are "close enough" to be considered equal using the
|
||||
/// precision of the provided epsilon value.
|
||||
fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool;
|
||||
/// Returns true if the two f32 values are "close enough" to be considered equal using the
|
||||
/// precision of the provided epsilon value.
|
||||
fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool;
|
||||
}
|
||||
|
||||
impl NearlyEqual for f32 {
|
||||
type Output = f32;
|
||||
type Output = f32;
|
||||
|
||||
#[inline(always)]
|
||||
fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool {
|
||||
nearly_equal(self, other, epsilon)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool {
|
||||
nearly_equal(self, other, epsilon)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WrappingRadians {
|
||||
type Type;
|
||||
type Type;
|
||||
|
||||
/// Adds two angle values in radians together returning the result. The addition will wrap
|
||||
/// around so that the returned result always lies within 0 -> 2π radians (0 -> 360 degrees).
|
||||
fn wrapping_radians_add(self, other: Self::Type) -> Self::Type;
|
||||
/// Adds two angle values in radians together returning the result. The addition will wrap
|
||||
/// around so that the returned result always lies within 0 -> 2π radians (0 -> 360 degrees).
|
||||
fn wrapping_radians_add(self, other: Self::Type) -> Self::Type;
|
||||
|
||||
/// Subtracts two angle values in radians returning the result. The subtraction will wrap
|
||||
/// around so that the returned result always lies within 0 -> 2π radians (0 -> 360 degrees).
|
||||
fn wrapping_radians_sub(self, other: Self::Type) -> Self::Type;
|
||||
/// Subtracts two angle values in radians returning the result. The subtraction will wrap
|
||||
/// around so that the returned result always lies within 0 -> 2π radians (0 -> 360 degrees).
|
||||
fn wrapping_radians_sub(self, other: Self::Type) -> Self::Type;
|
||||
}
|
||||
|
||||
impl WrappingRadians for f32 {
|
||||
type Type = f32;
|
||||
type Type = f32;
|
||||
|
||||
fn wrapping_radians_add(self, other: Self::Type) -> Self::Type {
|
||||
let result = self + other;
|
||||
if result < RADIANS_0 {
|
||||
result + RADIANS_360
|
||||
} else if result >= RADIANS_360 {
|
||||
result - RADIANS_360
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
fn wrapping_radians_add(self, other: Self::Type) -> Self::Type {
|
||||
let result = self + other;
|
||||
if result < RADIANS_0 {
|
||||
result + RADIANS_360
|
||||
} else if result >= RADIANS_360 {
|
||||
result - RADIANS_360
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn wrapping_radians_sub(self, other: Self::Type) -> Self::Type {
|
||||
let result = self - other;
|
||||
if result < RADIANS_0 {
|
||||
result + RADIANS_360
|
||||
} else if result >= RADIANS_360 {
|
||||
result - RADIANS_360
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
fn wrapping_radians_sub(self, other: Self::Type) -> Self::Type {
|
||||
let result = self - other;
|
||||
if result < RADIANS_0 {
|
||||
result + RADIANS_360
|
||||
} else if result >= RADIANS_360 {
|
||||
result - RADIANS_360
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn test_nearly_equal() {
|
||||
assert!(nearly_equal(4.0, 4.0, 0.1));
|
||||
assert!(4.0f32.nearly_equal(4.0, 0.1));
|
||||
assert!(nearly_equal(0.1 + 0.2, 0.3, 0.01));
|
||||
assert!(!nearly_equal(1.0001, 1.0005, 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_nearly_equal() {
|
||||
assert!(nearly_equal(4.0, 4.0, 0.1));
|
||||
assert!(4.0f32.nearly_equal(4.0, 0.1));
|
||||
assert!(nearly_equal(0.1 + 0.2, 0.3, 0.01));
|
||||
assert!(!nearly_equal(1.0001, 1.0005, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_lerp() {
|
||||
assert!(nearly_equal(15.0, lerp(10.0, 20.0, 0.5), 0.0001));
|
||||
assert!(nearly_equal(10.0, lerp(10.0, 20.0, 0.0), 0.0001));
|
||||
assert!(nearly_equal(20.0, lerp(10.0, 20.0, 1.0), 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_lerp() {
|
||||
assert!(nearly_equal(15.0, lerp(10.0, 20.0, 0.5), 0.0001));
|
||||
assert!(nearly_equal(10.0, lerp(10.0, 20.0, 0.0), 0.0001));
|
||||
assert!(nearly_equal(20.0, lerp(10.0, 20.0, 1.0), 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_lerp() {
|
||||
assert_eq!(0.5, inverse_lerp(10.0, 20.0, 15.0f32))
|
||||
}
|
||||
#[test]
|
||||
pub fn test_inverse_lerp() {
|
||||
assert_eq!(0.5, inverse_lerp(10.0, 20.0, 15.0f32))
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_angle_between() {
|
||||
let angle = angle_between(20.0, 20.0, 10.0, 10.0);
|
||||
assert!(nearly_equal(-RADIANS_135, angle, 0.0001));
|
||||
let angle = angle_between(0.0, 0.0, 10.0, 10.0);
|
||||
assert!(nearly_equal(RADIANS_45, angle, 0.0001));
|
||||
let angle = angle_between(5.0, 5.0, 5.0, 5.0);
|
||||
assert!(nearly_equal(0.0, angle, 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_angle_between() {
|
||||
let angle = angle_between(20.0, 20.0, 10.0, 10.0);
|
||||
assert!(nearly_equal(-RADIANS_135, angle, 0.0001));
|
||||
let angle = angle_between(0.0, 0.0, 10.0, 10.0);
|
||||
assert!(nearly_equal(RADIANS_45, angle, 0.0001));
|
||||
let angle = angle_between(5.0, 5.0, 5.0, 5.0);
|
||||
assert!(nearly_equal(0.0, angle, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_angle_to_direction() {
|
||||
let (x, y) = angle_to_direction(RADIANS_0);
|
||||
assert!(nearly_equal(x, 1.0, 0.000001));
|
||||
assert!(nearly_equal(y, 0.0, 0.000001));
|
||||
let (x, y) = angle_to_direction(RADIANS_45);
|
||||
assert!(nearly_equal(x, 0.707106, 0.000001));
|
||||
assert!(nearly_equal(y, 0.707106, 0.000001));
|
||||
let (x, y) = angle_to_direction(RADIANS_225);
|
||||
assert!(nearly_equal(x, -0.707106, 0.000001));
|
||||
assert!(nearly_equal(y, -0.707106, 0.000001));
|
||||
#[test]
|
||||
pub fn test_angle_to_direction() {
|
||||
let (x, y) = angle_to_direction(RADIANS_0);
|
||||
assert!(nearly_equal(x, 1.0, 0.000001));
|
||||
assert!(nearly_equal(y, 0.0, 0.000001));
|
||||
let (x, y) = angle_to_direction(RADIANS_45);
|
||||
assert!(nearly_equal(x, 0.707106, 0.000001));
|
||||
assert!(nearly_equal(y, 0.707106, 0.000001));
|
||||
let (x, y) = angle_to_direction(RADIANS_225);
|
||||
assert!(nearly_equal(x, -0.707106, 0.000001));
|
||||
assert!(nearly_equal(y, -0.707106, 0.000001));
|
||||
|
||||
let (x, y) = angle_to_direction(UP);
|
||||
assert!(nearly_equal(x, 0.0, 0.000001));
|
||||
assert!(nearly_equal(y, -1.0, 0.000001));
|
||||
let (x, y) = angle_to_direction(DOWN);
|
||||
assert!(nearly_equal(x, 0.0, 0.000001));
|
||||
assert!(nearly_equal(y, 1.0, 0.000001));
|
||||
let (x, y) = angle_to_direction(LEFT);
|
||||
assert!(nearly_equal(x, -1.0, 0.000001));
|
||||
assert!(nearly_equal(y, 0.0, 0.000001));
|
||||
let (x, y) = angle_to_direction(RIGHT);
|
||||
assert!(nearly_equal(x, 1.0, 0.000001));
|
||||
assert!(nearly_equal(y, 0.0, 0.000001));
|
||||
}
|
||||
let (x, y) = angle_to_direction(UP);
|
||||
assert!(nearly_equal(x, 0.0, 0.000001));
|
||||
assert!(nearly_equal(y, -1.0, 0.000001));
|
||||
let (x, y) = angle_to_direction(DOWN);
|
||||
assert!(nearly_equal(x, 0.0, 0.000001));
|
||||
assert!(nearly_equal(y, 1.0, 0.000001));
|
||||
let (x, y) = angle_to_direction(LEFT);
|
||||
assert!(nearly_equal(x, -1.0, 0.000001));
|
||||
assert!(nearly_equal(y, 0.0, 0.000001));
|
||||
let (x, y) = angle_to_direction(RIGHT);
|
||||
assert!(nearly_equal(x, 1.0, 0.000001));
|
||||
assert!(nearly_equal(y, 0.0, 0.000001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_distance_between() {
|
||||
let x1 = -2.0;
|
||||
let y1 = 1.0;
|
||||
let x2 = 4.0;
|
||||
let y2 = 3.0;
|
||||
let distance_squared = distance_squared_between(x1, y1, x2, y2);
|
||||
let distance = distance_between(x1, y1, x2, y2);
|
||||
assert!(nearly_equal(distance_squared, 40.0000, 0.0001));
|
||||
assert!(nearly_equal(distance, 6.3245, 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_distance_between() {
|
||||
let x1 = -2.0;
|
||||
let y1 = 1.0;
|
||||
let x2 = 4.0;
|
||||
let y2 = 3.0;
|
||||
let distance_squared = distance_squared_between(x1, y1, x2, y2);
|
||||
let distance = distance_between(x1, y1, x2, y2);
|
||||
assert!(nearly_equal(distance_squared, 40.0000, 0.0001));
|
||||
assert!(nearly_equal(distance, 6.3245, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_wrapping_radians() {
|
||||
assert!(nearly_equal(RADIANS_90, RADIANS_45.wrapping_radians_add(RADIANS_45), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_90, RADIANS_180.wrapping_radians_sub(RADIANS_90), 0.0001));
|
||||
#[test]
|
||||
pub fn test_wrapping_radians() {
|
||||
assert!(nearly_equal(RADIANS_90, RADIANS_45.wrapping_radians_add(RADIANS_45), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_90, RADIANS_180.wrapping_radians_sub(RADIANS_90), 0.0001));
|
||||
|
||||
assert!(nearly_equal(RADIANS_45, RADIANS_315.wrapping_radians_add(RADIANS_90), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_315, RADIANS_90.wrapping_radians_sub(RADIANS_135), 0.0001));
|
||||
}
|
||||
assert!(nearly_equal(RADIANS_45, RADIANS_315.wrapping_radians_add(RADIANS_90), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_315, RADIANS_90.wrapping_radians_sub(RADIANS_135), 0.0001));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,284 +1,284 @@
|
|||
/// Represents a 2D rectangle, using integer coordinates and dimensions.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Rect {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
#[inline]
|
||||
pub fn new(x: i32, y: i32, width: u32, height: u32) -> Rect {
|
||||
Rect {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn new(x: i32, y: i32, width: u32, height: u32) -> Rect {
|
||||
Rect {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new rect from the specified coordinates. Automatically determines if the
|
||||
/// coordinates provided are swapped (where the right/bottom coordinate is provided before the
|
||||
/// left/top). All of the coordinates are inclusive.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `left`: the left x coordinate
|
||||
/// * `top`: the top y coordinate
|
||||
/// * `right`: the right x coordinate
|
||||
/// * `bottom`: the bottom y coordinate
|
||||
pub fn from_coords(left: i32, top: i32, right: i32, bottom: i32) -> Rect {
|
||||
let x;
|
||||
let y;
|
||||
let width;
|
||||
let height;
|
||||
/// Creates a new rect from the specified coordinates. Automatically determines if the
|
||||
/// coordinates provided are swapped (where the right/bottom coordinate is provided before the
|
||||
/// left/top). All of the coordinates are inclusive.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `left`: the left x coordinate
|
||||
/// * `top`: the top y coordinate
|
||||
/// * `right`: the right x coordinate
|
||||
/// * `bottom`: the bottom y coordinate
|
||||
pub fn from_coords(left: i32, top: i32, right: i32, bottom: i32) -> Rect {
|
||||
let x;
|
||||
let y;
|
||||
let width;
|
||||
let height;
|
||||
|
||||
if left <= right {
|
||||
x = left;
|
||||
width = (right - left).abs() + 1;
|
||||
} else {
|
||||
x = right;
|
||||
width = (left - right).abs() + 1;
|
||||
}
|
||||
if left <= right {
|
||||
x = left;
|
||||
width = (right - left).abs() + 1;
|
||||
} else {
|
||||
x = right;
|
||||
width = (left - right).abs() + 1;
|
||||
}
|
||||
|
||||
if top <= bottom {
|
||||
y = top;
|
||||
height = (bottom - top).abs() + 1;
|
||||
} else {
|
||||
y = bottom;
|
||||
height = (top - bottom).abs() + 1;
|
||||
}
|
||||
if top <= bottom {
|
||||
y = top;
|
||||
height = (bottom - top).abs() + 1;
|
||||
} else {
|
||||
y = bottom;
|
||||
height = (top - bottom).abs() + 1;
|
||||
}
|
||||
|
||||
Rect {
|
||||
x,
|
||||
y,
|
||||
width: width as u32,
|
||||
height: height as u32,
|
||||
}
|
||||
}
|
||||
Rect {
|
||||
x,
|
||||
y,
|
||||
width: width as u32,
|
||||
height: height as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_from_coords(&mut self, left: i32, top: i32, right: i32, bottom: i32) {
|
||||
if left <= right {
|
||||
self.x = left;
|
||||
self.width = ((right - left).abs() + 1) as u32;
|
||||
} else {
|
||||
self.x = right;
|
||||
self.width = ((left - right).abs() + 1) as u32;
|
||||
}
|
||||
pub fn set_from_coords(&mut self, left: i32, top: i32, right: i32, bottom: i32) {
|
||||
if left <= right {
|
||||
self.x = left;
|
||||
self.width = ((right - left).abs() + 1) as u32;
|
||||
} else {
|
||||
self.x = right;
|
||||
self.width = ((left - right).abs() + 1) as u32;
|
||||
}
|
||||
|
||||
if top <= bottom {
|
||||
self.y = top;
|
||||
self.height = ((bottom - top).abs() + 1) as u32;
|
||||
} else {
|
||||
self.y = bottom;
|
||||
self.height = ((top - bottom).abs() + 1) as u32;
|
||||
}
|
||||
}
|
||||
if top <= bottom {
|
||||
self.y = top;
|
||||
self.height = ((bottom - top).abs() + 1) as u32;
|
||||
} else {
|
||||
self.y = bottom;
|
||||
self.height = ((top - bottom).abs() + 1) as u32;
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the right-most x coordinate contained by this rect.
|
||||
#[inline]
|
||||
pub fn right(&self) -> i32 {
|
||||
if self.width > 0 {
|
||||
self.x + self.width as i32 - 1
|
||||
} else {
|
||||
self.x
|
||||
}
|
||||
}
|
||||
/// Calculates the right-most x coordinate contained by this rect.
|
||||
#[inline]
|
||||
pub fn right(&self) -> i32 {
|
||||
if self.width > 0 {
|
||||
self.x + self.width as i32 - 1
|
||||
} else {
|
||||
self.x
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the bottom-most y coordinate contained by this rect.
|
||||
#[inline]
|
||||
pub fn bottom(&self) -> i32 {
|
||||
if self.height > 0 {
|
||||
self.y + self.height as i32 - 1
|
||||
} else {
|
||||
self.y
|
||||
}
|
||||
}
|
||||
/// Calculates the bottom-most y coordinate contained by this rect.
|
||||
#[inline]
|
||||
pub fn bottom(&self) -> i32 {
|
||||
if self.height > 0 {
|
||||
self.y + self.height as i32 - 1
|
||||
} else {
|
||||
self.y
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given point is contained within the bounds of this rect.
|
||||
pub fn contains_point(&self, x: i32, y: i32) -> bool {
|
||||
(self.x <= x) && (self.right() >= x) && (self.y <= y) && (self.bottom() >= y)
|
||||
}
|
||||
/// Returns true if the given point is contained within the bounds of this rect.
|
||||
pub fn contains_point(&self, x: i32, y: i32) -> bool {
|
||||
(self.x <= x) && (self.right() >= x) && (self.y <= y) && (self.bottom() >= y)
|
||||
}
|
||||
|
||||
/// Returns true if the given rect is contained completely within the bounds of this rect.
|
||||
pub fn contains_rect(&self, other: &Rect) -> bool {
|
||||
(other.x >= self.x && other.x < self.right())
|
||||
&& (other.right() > self.x && other.right() <= self.right())
|
||||
&& (other.y >= self.y && other.y < self.bottom())
|
||||
&& (other.bottom() > self.y && other.bottom() <= self.bottom())
|
||||
}
|
||||
/// Returns true if the given rect is contained completely within the bounds of this rect.
|
||||
pub fn contains_rect(&self, other: &Rect) -> bool {
|
||||
(other.x >= self.x && other.x < self.right())
|
||||
&& (other.right() > self.x && other.right() <= self.right())
|
||||
&& (other.y >= self.y && other.y < self.bottom())
|
||||
&& (other.bottom() > self.y && other.bottom() <= self.bottom())
|
||||
}
|
||||
|
||||
/// Returns true if the given rect at least partially overlaps the bounds of this rect.
|
||||
pub fn overlaps(&self, other: &Rect) -> bool {
|
||||
(self.x <= other.right())
|
||||
&& (self.right() >= other.x)
|
||||
&& (self.y <= other.bottom())
|
||||
&& (self.bottom() >= other.y)
|
||||
}
|
||||
/// Returns true if the given rect at least partially overlaps the bounds of this rect.
|
||||
pub fn overlaps(&self, other: &Rect) -> bool {
|
||||
(self.x <= other.right())
|
||||
&& (self.right() >= other.x)
|
||||
&& (self.y <= other.bottom())
|
||||
&& (self.bottom() >= other.y)
|
||||
}
|
||||
|
||||
pub fn clamp_to(&mut self, other: &Rect) -> bool {
|
||||
if !self.overlaps(other) {
|
||||
// not possible to clamp this rect to the other rect as they do not overlap at all
|
||||
false
|
||||
} else {
|
||||
// the rects at least partially overlap, so we will clamp this rect to the overlapping
|
||||
// region of the other rect
|
||||
let mut x1 = self.x;
|
||||
let mut y1 = self.y;
|
||||
let mut x2 = self.right();
|
||||
let mut y2 = self.bottom();
|
||||
if y1 < other.y {
|
||||
y1 = other.y;
|
||||
}
|
||||
if y1 > other.bottom() {
|
||||
y1 = other.bottom();
|
||||
}
|
||||
if y2 < other.y {
|
||||
y2 = other.y;
|
||||
}
|
||||
if y2 > other.bottom() {
|
||||
y2 = other.bottom();
|
||||
}
|
||||
if x1 < other.x {
|
||||
x1 = other.x;
|
||||
}
|
||||
if x1 > other.right() {
|
||||
x1 = other.right();
|
||||
}
|
||||
if x2 < other.x {
|
||||
x2 = other.x;
|
||||
}
|
||||
if x2 > other.right() {
|
||||
x2 = other.right();
|
||||
}
|
||||
pub fn clamp_to(&mut self, other: &Rect) -> bool {
|
||||
if !self.overlaps(other) {
|
||||
// not possible to clamp this rect to the other rect as they do not overlap at all
|
||||
false
|
||||
} else {
|
||||
// the rects at least partially overlap, so we will clamp this rect to the overlapping
|
||||
// region of the other rect
|
||||
let mut x1 = self.x;
|
||||
let mut y1 = self.y;
|
||||
let mut x2 = self.right();
|
||||
let mut y2 = self.bottom();
|
||||
if y1 < other.y {
|
||||
y1 = other.y;
|
||||
}
|
||||
if y1 > other.bottom() {
|
||||
y1 = other.bottom();
|
||||
}
|
||||
if y2 < other.y {
|
||||
y2 = other.y;
|
||||
}
|
||||
if y2 > other.bottom() {
|
||||
y2 = other.bottom();
|
||||
}
|
||||
if x1 < other.x {
|
||||
x1 = other.x;
|
||||
}
|
||||
if x1 > other.right() {
|
||||
x1 = other.right();
|
||||
}
|
||||
if x2 < other.x {
|
||||
x2 = other.x;
|
||||
}
|
||||
if x2 > other.right() {
|
||||
x2 = other.right();
|
||||
}
|
||||
|
||||
self.set_from_coords(x1, y1, x2, y2);
|
||||
true
|
||||
}
|
||||
}
|
||||
self.set_from_coords(x1, y1, x2, y2);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn right_and_left() {
|
||||
let rect = Rect {
|
||||
x: 5,
|
||||
y: 6,
|
||||
width: 16,
|
||||
height: 12,
|
||||
};
|
||||
assert_eq!(20, rect.right());
|
||||
assert_eq!(17, rect.bottom());
|
||||
#[test]
|
||||
pub fn right_and_left() {
|
||||
let rect = Rect {
|
||||
x: 5,
|
||||
y: 6,
|
||||
width: 16,
|
||||
height: 12,
|
||||
};
|
||||
assert_eq!(20, rect.right());
|
||||
assert_eq!(17, rect.bottom());
|
||||
|
||||
let rect = Rect {
|
||||
x: -11,
|
||||
y: -25,
|
||||
width: 16,
|
||||
height: 12,
|
||||
};
|
||||
assert_eq!(4, rect.right());
|
||||
assert_eq!(-14, rect.bottom());
|
||||
}
|
||||
let rect = Rect {
|
||||
x: -11,
|
||||
y: -25,
|
||||
width: 16,
|
||||
height: 12,
|
||||
};
|
||||
assert_eq!(4, rect.right());
|
||||
assert_eq!(-14, rect.bottom());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn create_from_coords() {
|
||||
let rect = Rect::from_coords(10, 15, 20, 30);
|
||||
assert_eq!(10, rect.x);
|
||||
assert_eq!(15, rect.y);
|
||||
assert_eq!(11, rect.width);
|
||||
assert_eq!(16, rect.height);
|
||||
assert_eq!(20, rect.right());
|
||||
assert_eq!(30, rect.bottom());
|
||||
#[test]
|
||||
pub fn create_from_coords() {
|
||||
let rect = Rect::from_coords(10, 15, 20, 30);
|
||||
assert_eq!(10, rect.x);
|
||||
assert_eq!(15, rect.y);
|
||||
assert_eq!(11, rect.width);
|
||||
assert_eq!(16, rect.height);
|
||||
assert_eq!(20, rect.right());
|
||||
assert_eq!(30, rect.bottom());
|
||||
|
||||
let rect = Rect::from_coords(-5, -13, 6, -2);
|
||||
assert_eq!(-5, rect.x);
|
||||
assert_eq!(-13, rect.y);
|
||||
assert_eq!(12, rect.width);
|
||||
assert_eq!(12, rect.height);
|
||||
assert_eq!(6, rect.right());
|
||||
assert_eq!(-2, rect.bottom());
|
||||
}
|
||||
let rect = Rect::from_coords(-5, -13, 6, -2);
|
||||
assert_eq!(-5, rect.x);
|
||||
assert_eq!(-13, rect.y);
|
||||
assert_eq!(12, rect.width);
|
||||
assert_eq!(12, rect.height);
|
||||
assert_eq!(6, rect.right());
|
||||
assert_eq!(-2, rect.bottom());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn create_from_coords_with_swapped_order() {
|
||||
let rect = Rect::from_coords(20, 30, 10, 15);
|
||||
assert_eq!(10, rect.x);
|
||||
assert_eq!(15, rect.y);
|
||||
assert_eq!(11, rect.width);
|
||||
assert_eq!(16, rect.height);
|
||||
assert_eq!(20, rect.right());
|
||||
assert_eq!(30, rect.bottom());
|
||||
#[test]
|
||||
pub fn create_from_coords_with_swapped_order() {
|
||||
let rect = Rect::from_coords(20, 30, 10, 15);
|
||||
assert_eq!(10, rect.x);
|
||||
assert_eq!(15, rect.y);
|
||||
assert_eq!(11, rect.width);
|
||||
assert_eq!(16, rect.height);
|
||||
assert_eq!(20, rect.right());
|
||||
assert_eq!(30, rect.bottom());
|
||||
|
||||
let rect = Rect::from_coords(6, -2, -5, -13);
|
||||
assert_eq!(-5, rect.x);
|
||||
assert_eq!(-13, rect.y);
|
||||
assert_eq!(12, rect.width);
|
||||
assert_eq!(12, rect.height);
|
||||
assert_eq!(6, rect.right());
|
||||
assert_eq!(-2, rect.bottom());
|
||||
}
|
||||
let rect = Rect::from_coords(6, -2, -5, -13);
|
||||
assert_eq!(-5, rect.x);
|
||||
assert_eq!(-13, rect.y);
|
||||
assert_eq!(12, rect.width);
|
||||
assert_eq!(12, rect.height);
|
||||
assert_eq!(6, rect.right());
|
||||
assert_eq!(-2, rect.bottom());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_contains_point() {
|
||||
let r = Rect::from_coords(10, 10, 20, 20);
|
||||
#[test]
|
||||
pub fn test_contains_point() {
|
||||
let r = Rect::from_coords(10, 10, 20, 20);
|
||||
|
||||
assert!(r.contains_point(10, 10));
|
||||
assert!(r.contains_point(15, 15));
|
||||
assert!(r.contains_point(20, 20));
|
||||
assert!(r.contains_point(10, 10));
|
||||
assert!(r.contains_point(15, 15));
|
||||
assert!(r.contains_point(20, 20));
|
||||
|
||||
assert!(!r.contains_point(12, 30));
|
||||
assert!(!r.contains_point(8, 12));
|
||||
assert!(!r.contains_point(25, 16));
|
||||
assert!(!r.contains_point(17, 4));
|
||||
}
|
||||
assert!(!r.contains_point(12, 30));
|
||||
assert!(!r.contains_point(8, 12));
|
||||
assert!(!r.contains_point(25, 16));
|
||||
assert!(!r.contains_point(17, 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_contains_rect() {
|
||||
let r = Rect::from_coords(10, 10, 20, 20);
|
||||
#[test]
|
||||
pub fn test_contains_rect() {
|
||||
let r = Rect::from_coords(10, 10, 20, 20);
|
||||
|
||||
assert!(r.contains_rect(&Rect::from_coords(12, 12, 15, 15)));
|
||||
assert!(r.contains_rect(&Rect::from_coords(10, 10, 15, 15)));
|
||||
assert!(r.contains_rect(&Rect::from_coords(15, 15, 20, 20)));
|
||||
assert!(r.contains_rect(&Rect::from_coords(10, 12, 20, 15)));
|
||||
assert!(r.contains_rect(&Rect::from_coords(12, 10, 15, 20)));
|
||||
assert!(r.contains_rect(&Rect::from_coords(12, 12, 15, 15)));
|
||||
assert!(r.contains_rect(&Rect::from_coords(10, 10, 15, 15)));
|
||||
assert!(r.contains_rect(&Rect::from_coords(15, 15, 20, 20)));
|
||||
assert!(r.contains_rect(&Rect::from_coords(10, 12, 20, 15)));
|
||||
assert!(r.contains_rect(&Rect::from_coords(12, 10, 15, 20)));
|
||||
|
||||
assert!(!r.contains_rect(&Rect::from_coords(5, 5, 15, 15)));
|
||||
assert!(!r.contains_rect(&Rect::from_coords(15, 15, 25, 25)));
|
||||
assert!(!r.contains_rect(&Rect::from_coords(5, 5, 15, 15)));
|
||||
assert!(!r.contains_rect(&Rect::from_coords(15, 15, 25, 25)));
|
||||
|
||||
assert!(!r.contains_rect(&Rect::from_coords(2, 2, 8, 4)));
|
||||
assert!(!r.contains_rect(&Rect::from_coords(12, 21, 18, 25)));
|
||||
assert!(!r.contains_rect(&Rect::from_coords(22, 12, 32, 17)));
|
||||
}
|
||||
assert!(!r.contains_rect(&Rect::from_coords(2, 2, 8, 4)));
|
||||
assert!(!r.contains_rect(&Rect::from_coords(12, 21, 18, 25)));
|
||||
assert!(!r.contains_rect(&Rect::from_coords(22, 12, 32, 17)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_overlaps() {
|
||||
let r = Rect::from_coords(10, 10, 20, 20);
|
||||
#[test]
|
||||
pub fn test_overlaps() {
|
||||
let r = Rect::from_coords(10, 10, 20, 20);
|
||||
|
||||
assert!(r.overlaps(&Rect::from_coords(12, 12, 15, 15)));
|
||||
assert!(r.overlaps(&Rect::from_coords(10, 10, 15, 15)));
|
||||
assert!(r.overlaps(&Rect::from_coords(15, 15, 20, 20)));
|
||||
assert!(r.overlaps(&Rect::from_coords(10, 12, 20, 15)));
|
||||
assert!(r.overlaps(&Rect::from_coords(12, 10, 15, 20)));
|
||||
assert!(r.overlaps(&Rect::from_coords(12, 12, 15, 15)));
|
||||
assert!(r.overlaps(&Rect::from_coords(10, 10, 15, 15)));
|
||||
assert!(r.overlaps(&Rect::from_coords(15, 15, 20, 20)));
|
||||
assert!(r.overlaps(&Rect::from_coords(10, 12, 20, 15)));
|
||||
assert!(r.overlaps(&Rect::from_coords(12, 10, 15, 20)));
|
||||
|
||||
assert!(r.overlaps(&Rect::from_coords(12, 5, 18, 10)));
|
||||
assert!(r.overlaps(&Rect::from_coords(13, 20, 16, 25)));
|
||||
assert!(r.overlaps(&Rect::from_coords(5, 12, 10, 18)));
|
||||
assert!(r.overlaps(&Rect::from_coords(20, 13, 25, 16)));
|
||||
assert!(r.overlaps(&Rect::from_coords(12, 5, 18, 10)));
|
||||
assert!(r.overlaps(&Rect::from_coords(13, 20, 16, 25)));
|
||||
assert!(r.overlaps(&Rect::from_coords(5, 12, 10, 18)));
|
||||
assert!(r.overlaps(&Rect::from_coords(20, 13, 25, 16)));
|
||||
|
||||
assert!(r.overlaps(&Rect::from_coords(5, 5, 15, 15)));
|
||||
assert!(r.overlaps(&Rect::from_coords(15, 15, 25, 25)));
|
||||
assert!(r.overlaps(&Rect::from_coords(5, 5, 15, 15)));
|
||||
assert!(r.overlaps(&Rect::from_coords(15, 15, 25, 25)));
|
||||
|
||||
assert!(!r.overlaps(&Rect::from_coords(2, 2, 8, 4)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(12, 21, 18, 25)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(22, 12, 32, 17)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(2, 2, 8, 4)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(12, 21, 18, 25)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(22, 12, 32, 17)));
|
||||
|
||||
assert!(!r.overlaps(&Rect::from_coords(12, 5, 18, 9)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(13, 21, 16, 25)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(5, 12, 9, 18)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(21, 13, 25, 16)));
|
||||
}
|
||||
assert!(!r.overlaps(&Rect::from_coords(12, 5, 18, 9)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(13, 21, 16, 25)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(5, 12, 9, 18)));
|
||||
assert!(!r.overlaps(&Rect::from_coords(21, 13, 25, 16)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,478 +5,478 @@ use crate::math::*;
|
|||
/// Represents a 2D vector and provides common methods for vector math.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Vector2 {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl Vector2 {
|
||||
pub const ZERO: Vector2 = Vector2 { x: 0.0, y: 0.0 };
|
||||
pub const ZERO: Vector2 = Vector2 { x: 0.0, y: 0.0 };
|
||||
|
||||
pub const UP: Vector2 = Vector2 { x: 0.0, y: -1.0 };
|
||||
pub const DOWN: Vector2 = Vector2 { x: 0.0, y: 1.0 };
|
||||
pub const LEFT: Vector2 = Vector2 { x: -1.0, y: 0.0 };
|
||||
pub const RIGHT: Vector2 = Vector2 { x: 1.0, y: 0.0 };
|
||||
pub const UP: Vector2 = Vector2 { x: 0.0, y: -1.0 };
|
||||
pub const DOWN: Vector2 = Vector2 { x: 0.0, y: 1.0 };
|
||||
pub const LEFT: Vector2 = Vector2 { x: -1.0, y: 0.0 };
|
||||
pub const RIGHT: Vector2 = Vector2 { x: 1.0, y: 0.0 };
|
||||
|
||||
pub const X_AXIS: Vector2 = Vector2 { x: 1.0, y: 0.0 };
|
||||
pub const Y_AXIS: Vector2 = Vector2 { x: 0.0, y: 1.0 };
|
||||
pub const X_AXIS: Vector2 = Vector2 { x: 1.0, y: 0.0 };
|
||||
pub const Y_AXIS: Vector2 = Vector2 { x: 0.0, y: 1.0 };
|
||||
|
||||
/// Creates a vector with the specified X and Y components.
|
||||
#[inline]
|
||||
pub fn new(x: f32, y: f32) -> Vector2 {
|
||||
Vector2 { x, y }
|
||||
}
|
||||
/// Creates a vector with the specified X and Y components.
|
||||
#[inline]
|
||||
pub fn new(x: f32, y: f32) -> Vector2 {
|
||||
Vector2 { x, y }
|
||||
}
|
||||
|
||||
/// Creates a normalized vector that points in the same direction as the given angle.
|
||||
#[inline]
|
||||
pub fn from_angle(radians: f32) -> Vector2 {
|
||||
let (x, y) = angle_to_direction(radians);
|
||||
Vector2 { x, y }
|
||||
}
|
||||
/// Creates a normalized vector that points in the same direction as the given angle.
|
||||
#[inline]
|
||||
pub fn from_angle(radians: f32) -> Vector2 {
|
||||
let (x, y) = angle_to_direction(radians);
|
||||
Vector2 { x, y }
|
||||
}
|
||||
|
||||
/// Calculates the distance between this and another vector.
|
||||
#[inline]
|
||||
pub fn distance(&self, other: &Vector2) -> f32 {
|
||||
self.distance_squared(other).sqrt()
|
||||
}
|
||||
/// Calculates the distance between this and another vector.
|
||||
#[inline]
|
||||
pub fn distance(&self, other: &Vector2) -> f32 {
|
||||
self.distance_squared(other).sqrt()
|
||||
}
|
||||
|
||||
/// Calculates the squared distance between this and another vector.
|
||||
#[inline]
|
||||
pub fn distance_squared(&self, other: &Vector2) -> f32 {
|
||||
(other.x - self.x) * (other.x - self.x) + (other.y - self.y) * (other.y - self.y)
|
||||
}
|
||||
/// Calculates the squared distance between this and another vector.
|
||||
#[inline]
|
||||
pub fn distance_squared(&self, other: &Vector2) -> f32 {
|
||||
(other.x - self.x) * (other.x - self.x) + (other.y - self.y) * (other.y - self.y)
|
||||
}
|
||||
|
||||
/// Calculates the dot product of this and another vector.
|
||||
#[inline]
|
||||
pub fn dot(&self, other: &Vector2) -> f32 {
|
||||
(self.x * other.x) + (self.y * other.y)
|
||||
}
|
||||
/// Calculates the dot product of this and another vector.
|
||||
#[inline]
|
||||
pub fn dot(&self, other: &Vector2) -> f32 {
|
||||
(self.x * other.x) + (self.y * other.y)
|
||||
}
|
||||
|
||||
/// Calculates the length (a.k.a. magnitude) of this vector.
|
||||
#[inline]
|
||||
pub fn length(&self) -> f32 {
|
||||
self.length_squared().sqrt()
|
||||
}
|
||||
/// Calculates the length (a.k.a. magnitude) of this vector.
|
||||
#[inline]
|
||||
pub fn length(&self) -> f32 {
|
||||
self.length_squared().sqrt()
|
||||
}
|
||||
|
||||
/// Calculates the squared length of this vector.
|
||||
#[inline]
|
||||
pub fn length_squared(&self) -> f32 {
|
||||
(self.x * self.x) + (self.y * self.y)
|
||||
}
|
||||
/// Calculates the squared length of this vector.
|
||||
#[inline]
|
||||
pub fn length_squared(&self) -> f32 {
|
||||
(self.x * self.x) + (self.y * self.y)
|
||||
}
|
||||
|
||||
/// Returns a normalized vector from this vector.
|
||||
pub fn normalize(&self) -> Vector2 {
|
||||
let inverse_length = 1.0 / self.length();
|
||||
Vector2 {
|
||||
x: self.x * inverse_length,
|
||||
y: self.y * inverse_length,
|
||||
}
|
||||
}
|
||||
/// Returns a normalized vector from this vector.
|
||||
pub fn normalize(&self) -> Vector2 {
|
||||
let inverse_length = 1.0 / self.length();
|
||||
Vector2 {
|
||||
x: self.x * inverse_length,
|
||||
y: self.y * inverse_length,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an extended (or shrunk) vector from this vector, where the returned vector will
|
||||
/// have a length exactly matching the specified length, but will retain the same direction.
|
||||
pub fn extend(&self, length: f32) -> Vector2 {
|
||||
*self * (length / self.length())
|
||||
}
|
||||
/// Returns an extended (or shrunk) vector from this vector, where the returned vector will
|
||||
/// have a length exactly matching the specified length, but will retain the same direction.
|
||||
pub fn extend(&self, length: f32) -> Vector2 {
|
||||
*self * (length / self.length())
|
||||
}
|
||||
|
||||
/// Returns the angle (in radians) equivalent to the direction of this vector.
|
||||
#[inline]
|
||||
pub fn angle(&self) -> f32 {
|
||||
self.y.atan2(self.x)
|
||||
}
|
||||
/// Returns the angle (in radians) equivalent to the direction of this vector.
|
||||
#[inline]
|
||||
pub fn angle(&self) -> f32 {
|
||||
self.y.atan2(self.x)
|
||||
}
|
||||
|
||||
/// Calculates the angle (in radians) between the this and another vector.
|
||||
#[inline]
|
||||
pub fn angle_between(&self, other: &Vector2) -> f32 {
|
||||
angle_between(self.x, self.y, other.x, other.y)
|
||||
}
|
||||
/// Calculates the angle (in radians) between the this and another vector.
|
||||
#[inline]
|
||||
pub fn angle_between(&self, other: &Vector2) -> f32 {
|
||||
angle_between(self.x, self.y, other.x, other.y)
|
||||
}
|
||||
|
||||
/// Returns true if this vector is nearly equal to the zero vector (0.0, 0.0).
|
||||
#[inline]
|
||||
pub fn almost_zero(&self, epsilon: f32) -> bool {
|
||||
self.nearly_equal(Vector2::ZERO, epsilon)
|
||||
}
|
||||
/// Returns true if this vector is nearly equal to the zero vector (0.0, 0.0).
|
||||
#[inline]
|
||||
pub fn almost_zero(&self, epsilon: f32) -> bool {
|
||||
self.nearly_equal(Vector2::ZERO, epsilon)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Vector2 {
|
||||
type Output = Self;
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
Vector2 {
|
||||
x: -self.x,
|
||||
y: -self.y,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn neg(self) -> Self::Output {
|
||||
Vector2 {
|
||||
x: -self.x,
|
||||
y: -self.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Vector2 {
|
||||
type Output = Self;
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x + rhs.x,
|
||||
y: self.y + rhs.y,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x + rhs.x,
|
||||
y: self.y + rhs.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Vector2 {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.x += rhs.x;
|
||||
self.y += rhs.y;
|
||||
}
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.x += rhs.x;
|
||||
self.y += rhs.y;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Vector2 {
|
||||
type Output = Self;
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x - rhs.x,
|
||||
y: self.y - rhs.y,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x - rhs.x,
|
||||
y: self.y - rhs.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Vector2 {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
self.x -= rhs.x;
|
||||
self.y -= rhs.y;
|
||||
}
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
self.x -= rhs.x;
|
||||
self.y -= rhs.y;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Vector2 {
|
||||
type Output = Self;
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x * rhs.x,
|
||||
y: self.y * rhs.y,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x * rhs.x,
|
||||
y: self.y * rhs.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for Vector2 {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
self.x *= rhs.x;
|
||||
self.y *= rhs.y;
|
||||
}
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
self.x *= rhs.x;
|
||||
self.y *= rhs.y;
|
||||
}
|
||||
}
|
||||
|
||||
impl Div for Vector2 {
|
||||
type Output = Self;
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn div(self, rhs: Self) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x / rhs.x,
|
||||
y: self.y / rhs.y,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn div(self, rhs: Self) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x / rhs.x,
|
||||
y: self.y / rhs.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign for Vector2 {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, rhs: Self) {
|
||||
self.x /= rhs.x;
|
||||
self.y /= rhs.y;
|
||||
}
|
||||
#[inline]
|
||||
fn div_assign(&mut self, rhs: Self) {
|
||||
self.x /= rhs.x;
|
||||
self.y /= rhs.y;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Vector2 {
|
||||
type Output = Self;
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: f32) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x * rhs,
|
||||
y: self.y * rhs,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn mul(self, rhs: f32) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x * rhs,
|
||||
y: self.y * rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<f32> for Vector2 {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: f32) {
|
||||
self.x *= rhs;
|
||||
self.y *= rhs;
|
||||
}
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: f32) {
|
||||
self.x *= rhs;
|
||||
self.y *= rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f32> for Vector2 {
|
||||
type Output = Self;
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn div(self, rhs: f32) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x / rhs,
|
||||
y: self.y / rhs,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn div(self, rhs: f32) -> Self::Output {
|
||||
Vector2 {
|
||||
x: self.x / rhs,
|
||||
y: self.y / rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign<f32> for Vector2 {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, rhs: f32) {
|
||||
self.x /= rhs;
|
||||
self.y /= rhs;
|
||||
}
|
||||
#[inline]
|
||||
fn div_assign(&mut self, rhs: f32) {
|
||||
self.x /= rhs;
|
||||
self.y /= rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl NearlyEqual for Vector2 {
|
||||
type Output = Self;
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool {
|
||||
nearly_equal(self.x, other.x, epsilon) && nearly_equal(self.y, other.y, epsilon)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn nearly_equal(self, other: Self::Output, epsilon: f32) -> bool {
|
||||
nearly_equal(self.x, other.x, epsilon) && nearly_equal(self.y, other.y, epsilon)
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<Matrix3x3> for Vector2 {
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: Matrix3x3) {
|
||||
let x = self.x * rhs.m[Matrix3x3::M11] + self.y * rhs.m[Matrix3x3::M12] + rhs.m[Matrix3x3::M13] + rhs.m[Matrix3x3::M31];
|
||||
let y = self.x * rhs.m[Matrix3x3::M21] + self.y * rhs.m[Matrix3x3::M22] + rhs.m[Matrix3x3::M23] + rhs.m[Matrix3x3::M32];
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: Matrix3x3) {
|
||||
let x = self.x * rhs.m[Matrix3x3::M11] + self.y * rhs.m[Matrix3x3::M12] + rhs.m[Matrix3x3::M13] + rhs.m[Matrix3x3::M31];
|
||||
let y = self.x * rhs.m[Matrix3x3::M21] + self.y * rhs.m[Matrix3x3::M22] + rhs.m[Matrix3x3::M23] + rhs.m[Matrix3x3::M32];
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
pub fn test_new() {
|
||||
let v = Vector2::new(3.0, 7.0);
|
||||
assert!(nearly_equal(v.x, 3.0, 0.0001));
|
||||
assert!(nearly_equal(v.y, 7.0, 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_new() {
|
||||
let v = Vector2::new(3.0, 7.0);
|
||||
assert!(nearly_equal(v.x, 3.0, 0.0001));
|
||||
assert!(nearly_equal(v.y, 7.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_neg() {
|
||||
let v = Vector2 { x: 1.0, y: 2.0 };
|
||||
let neg = -v;
|
||||
assert!(nearly_equal(neg.x, -1.0, 0.0001));
|
||||
assert!(nearly_equal(neg.y, -2.0, 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_neg() {
|
||||
let v = Vector2 { x: 1.0, y: 2.0 };
|
||||
let neg = -v;
|
||||
assert!(nearly_equal(neg.x, -1.0, 0.0001));
|
||||
assert!(nearly_equal(neg.y, -2.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let a = Vector2 { x: 3.0, y: 4.0 };
|
||||
let b = Vector2 { x: 1.0, y: 2.0 };
|
||||
let c = a + b;
|
||||
assert!(nearly_equal(c.x, 4.0, 0.0001));
|
||||
assert!(nearly_equal(c.y, 6.0, 0.0001));
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let a = Vector2 { x: 3.0, y: 4.0 };
|
||||
let b = Vector2 { x: 1.0, y: 2.0 };
|
||||
let c = a + b;
|
||||
assert!(nearly_equal(c.x, 4.0, 0.0001));
|
||||
assert!(nearly_equal(c.y, 6.0, 0.0001));
|
||||
|
||||
let mut a = Vector2 { x: 3.0, y: 4.0 };
|
||||
let b = Vector2 { x: 1.0, y: 2.0 };
|
||||
a += b;
|
||||
assert!(nearly_equal(a.x, 4.0, 0.0001));
|
||||
assert!(nearly_equal(a.y, 6.0, 0.0001));
|
||||
}
|
||||
let mut a = Vector2 { x: 3.0, y: 4.0 };
|
||||
let b = Vector2 { x: 1.0, y: 2.0 };
|
||||
a += b;
|
||||
assert!(nearly_equal(a.x, 4.0, 0.0001));
|
||||
assert!(nearly_equal(a.y, 6.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub() {
|
||||
let a = Vector2 { x: 3.0, y: 4.0 };
|
||||
let b = Vector2 { x: 1.0, y: 2.0 };
|
||||
let c = a - b;
|
||||
assert!(nearly_equal(c.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(c.y, 2.0, 0.0001));
|
||||
#[test]
|
||||
pub fn test_sub() {
|
||||
let a = Vector2 { x: 3.0, y: 4.0 };
|
||||
let b = Vector2 { x: 1.0, y: 2.0 };
|
||||
let c = a - b;
|
||||
assert!(nearly_equal(c.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(c.y, 2.0, 0.0001));
|
||||
|
||||
let mut a = Vector2 { x: 3.0, y: 4.0 };
|
||||
let b = Vector2 { x: 1.0, y: 2.0 };
|
||||
a -= b;
|
||||
assert!(nearly_equal(a.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(a.y, 2.0, 0.0001));
|
||||
}
|
||||
let mut a = Vector2 { x: 3.0, y: 4.0 };
|
||||
let b = Vector2 { x: 1.0, y: 2.0 };
|
||||
a -= b;
|
||||
assert!(nearly_equal(a.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(a.y, 2.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_mul() {
|
||||
let a = Vector2 { x: 2.5, y: 6.0 };
|
||||
let b = Vector2 { x: 1.25, y: 2.0 };
|
||||
let c = a * b;
|
||||
assert!(nearly_equal(c.x, 3.125, 0.0001));
|
||||
assert!(nearly_equal(c.y, 12.0, 0.0001));
|
||||
#[test]
|
||||
pub fn test_mul() {
|
||||
let a = Vector2 { x: 2.5, y: 6.0 };
|
||||
let b = Vector2 { x: 1.25, y: 2.0 };
|
||||
let c = a * b;
|
||||
assert!(nearly_equal(c.x, 3.125, 0.0001));
|
||||
assert!(nearly_equal(c.y, 12.0, 0.0001));
|
||||
|
||||
let mut a = Vector2 { x: 2.5, y: 6.0 };
|
||||
let b = Vector2 { x: 1.25, y: 2.0 };
|
||||
a *= b;
|
||||
assert!(nearly_equal(a.x, 3.125, 0.0001));
|
||||
assert!(nearly_equal(a.y, 12.0, 0.0001));
|
||||
}
|
||||
let mut a = Vector2 { x: 2.5, y: 6.0 };
|
||||
let b = Vector2 { x: 1.25, y: 2.0 };
|
||||
a *= b;
|
||||
assert!(nearly_equal(a.x, 3.125, 0.0001));
|
||||
assert!(nearly_equal(a.y, 12.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_div() {
|
||||
let a = Vector2 { x: 2.5, y: 6.0 };
|
||||
let b = Vector2 { x: 1.25, y: 2.0 };
|
||||
let c = a / b;
|
||||
assert!(nearly_equal(c.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(c.y, 3.0, 0.0001));
|
||||
#[test]
|
||||
pub fn test_div() {
|
||||
let a = Vector2 { x: 2.5, y: 6.0 };
|
||||
let b = Vector2 { x: 1.25, y: 2.0 };
|
||||
let c = a / b;
|
||||
assert!(nearly_equal(c.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(c.y, 3.0, 0.0001));
|
||||
|
||||
let mut a = Vector2 { x: 2.5, y: 6.0 };
|
||||
let b = Vector2 { x: 1.25, y: 2.0 };
|
||||
a /= b;
|
||||
assert!(nearly_equal(a.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(a.y, 3.0, 0.0001));
|
||||
}
|
||||
let mut a = Vector2 { x: 2.5, y: 6.0 };
|
||||
let b = Vector2 { x: 1.25, y: 2.0 };
|
||||
a /= b;
|
||||
assert!(nearly_equal(a.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(a.y, 3.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let a = Vector2 { x: 1.0, y: 2.0 };
|
||||
let b = a * 2.0;
|
||||
assert!(nearly_equal(b.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(b.y, 4.0, 0.0001));
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let a = Vector2 { x: 1.0, y: 2.0 };
|
||||
let b = a * 2.0;
|
||||
assert!(nearly_equal(b.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(b.y, 4.0, 0.0001));
|
||||
|
||||
let mut a = Vector2 { x: 1.0, y: 2.0 };
|
||||
a *= 2.0;
|
||||
assert!(nearly_equal(b.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(b.y, 4.0, 0.0001));
|
||||
}
|
||||
let mut a = Vector2 { x: 1.0, y: 2.0 };
|
||||
a *= 2.0;
|
||||
assert!(nearly_equal(b.x, 2.0, 0.0001));
|
||||
assert!(nearly_equal(b.y, 4.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_div() {
|
||||
let a = Vector2 { x: 1.0, y: 2.0 };
|
||||
let b = a / 2.0;
|
||||
assert!(nearly_equal(b.x, 0.5, 0.0001));
|
||||
assert!(nearly_equal(b.y, 1.0, 0.0001));
|
||||
#[test]
|
||||
pub fn test_scalar_div() {
|
||||
let a = Vector2 { x: 1.0, y: 2.0 };
|
||||
let b = a / 2.0;
|
||||
assert!(nearly_equal(b.x, 0.5, 0.0001));
|
||||
assert!(nearly_equal(b.y, 1.0, 0.0001));
|
||||
|
||||
let mut a = Vector2 { x: 1.0, y: 2.0 };
|
||||
a /= 2.0;
|
||||
assert!(nearly_equal(b.x, 0.5, 0.0001));
|
||||
assert!(nearly_equal(b.y, 1.0, 0.0001));
|
||||
}
|
||||
let mut a = Vector2 { x: 1.0, y: 2.0 };
|
||||
a /= 2.0;
|
||||
assert!(nearly_equal(b.x, 0.5, 0.0001));
|
||||
assert!(nearly_equal(b.y, 1.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_nearly_equal() {
|
||||
let a = Vector2 { x: 3.4, y: -7.1 };
|
||||
let b = Vector2 { x: 3.5, y: -7.1 };
|
||||
assert!(!a.nearly_equal(b, 0.0001));
|
||||
#[test]
|
||||
pub fn test_nearly_equal() {
|
||||
let a = Vector2 { x: 3.4, y: -7.1 };
|
||||
let b = Vector2 { x: 3.5, y: -7.1 };
|
||||
assert!(!a.nearly_equal(b, 0.0001));
|
||||
|
||||
let a = Vector2 { x: 2.0, y: 4.0 };
|
||||
let b = Vector2 { x: 2.0, y: 4.0 };
|
||||
assert!(a.nearly_equal(b, 0.0001));
|
||||
}
|
||||
let a = Vector2 { x: 2.0, y: 4.0 };
|
||||
let b = Vector2 { x: 2.0, y: 4.0 };
|
||||
assert!(a.nearly_equal(b, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_length() {
|
||||
let v = Vector2 { x: 6.0, y: 8.0 };
|
||||
let length_squared = v.length_squared();
|
||||
let length = v.length();
|
||||
assert!(nearly_equal(length_squared, 100.0, 0.0001));
|
||||
assert!(nearly_equal(length, 10.0, 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_length() {
|
||||
let v = Vector2 { x: 6.0, y: 8.0 };
|
||||
let length_squared = v.length_squared();
|
||||
let length = v.length();
|
||||
assert!(nearly_equal(length_squared, 100.0, 0.0001));
|
||||
assert!(nearly_equal(length, 10.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_dot() {
|
||||
let a = Vector2 { x: -6.0, y: 8.0 };
|
||||
let b = Vector2 { x: 5.0, y: 12.0 };
|
||||
let dot = a.dot(&b);
|
||||
assert!(nearly_equal(dot, 66.0, 0.0001));
|
||||
#[test]
|
||||
pub fn test_dot() {
|
||||
let a = Vector2 { x: -6.0, y: 8.0 };
|
||||
let b = Vector2 { x: 5.0, y: 12.0 };
|
||||
let dot = a.dot(&b);
|
||||
assert!(nearly_equal(dot, 66.0, 0.0001));
|
||||
|
||||
let a = Vector2 { x: -12.0, y: 16.0 };
|
||||
let b = Vector2 { x: 12.0, y: 9.0 };
|
||||
let dot = a.dot(&b);
|
||||
assert!(nearly_equal(dot, 0.0, 0.0001));
|
||||
}
|
||||
let a = Vector2 { x: -12.0, y: 16.0 };
|
||||
let b = Vector2 { x: 12.0, y: 9.0 };
|
||||
let dot = a.dot(&b);
|
||||
assert!(nearly_equal(dot, 0.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_distance() {
|
||||
let a = Vector2 { x: 1.0, y: 1.0 };
|
||||
let b = Vector2 { x: 1.0, y: 3.0 };
|
||||
let distance_squared = a.distance_squared(&b);
|
||||
let distance = a.distance(&b);
|
||||
assert!(nearly_equal(distance_squared, 4.0, 0.0001));
|
||||
assert!(nearly_equal(distance, 2.0, 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_distance() {
|
||||
let a = Vector2 { x: 1.0, y: 1.0 };
|
||||
let b = Vector2 { x: 1.0, y: 3.0 };
|
||||
let distance_squared = a.distance_squared(&b);
|
||||
let distance = a.distance(&b);
|
||||
assert!(nearly_equal(distance_squared, 4.0, 0.0001));
|
||||
assert!(nearly_equal(distance, 2.0, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_normalize() {
|
||||
let v = Vector2 { x: 3.0, y: 4.0 };
|
||||
let normalized = v.normalize();
|
||||
assert!(nearly_equal(normalized.x, 0.6, 0.0001));
|
||||
assert!(nearly_equal(normalized.y, 0.8, 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_normalize() {
|
||||
let v = Vector2 { x: 3.0, y: 4.0 };
|
||||
let normalized = v.normalize();
|
||||
assert!(nearly_equal(normalized.x, 0.6, 0.0001));
|
||||
assert!(nearly_equal(normalized.y, 0.8, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_extend() {
|
||||
let v = Vector2 { x: 10.0, y: 1.0 };
|
||||
let extended = v.extend(2.0);
|
||||
assert!(nearly_equal(extended.x, 1.990, 0.0001));
|
||||
assert!(nearly_equal(extended.y, 0.199, 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_extend() {
|
||||
let v = Vector2 { x: 10.0, y: 1.0 };
|
||||
let extended = v.extend(2.0);
|
||||
assert!(nearly_equal(extended.x, 1.990, 0.0001));
|
||||
assert!(nearly_equal(extended.y, 0.199, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
pub fn test_angle() {
|
||||
assert!(nearly_equal(RADIANS_0, Vector2::new(5.0, 0.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_45, Vector2::new(5.0, 5.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_90, Vector2::new(0.0, 5.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_135, Vector2::new(-5.0, 5.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_180, Vector2::new(-5.0, 0.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(-RADIANS_135, Vector2::new(-5.0, -5.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(-RADIANS_90, Vector2::new(0.0, -5.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(-RADIANS_45, Vector2::new(5.0, -5.0).angle(), 0.0001));
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
pub fn test_angle() {
|
||||
assert!(nearly_equal(RADIANS_0, Vector2::new(5.0, 0.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_45, Vector2::new(5.0, 5.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_90, Vector2::new(0.0, 5.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_135, Vector2::new(-5.0, 5.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(RADIANS_180, Vector2::new(-5.0, 0.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(-RADIANS_135, Vector2::new(-5.0, -5.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(-RADIANS_90, Vector2::new(0.0, -5.0).angle(), 0.0001));
|
||||
assert!(nearly_equal(-RADIANS_45, Vector2::new(5.0, -5.0).angle(), 0.0001));
|
||||
|
||||
assert!(nearly_equal(crate::math::UP, Vector2::UP.angle(), 0.0001));
|
||||
assert!(nearly_equal(crate::math::DOWN, Vector2::DOWN.angle(), 0.0001));
|
||||
assert!(nearly_equal(crate::math::LEFT, Vector2::LEFT.angle(), 0.0001));
|
||||
assert!(nearly_equal(crate::math::RIGHT, Vector2::RIGHT.angle(), 0.0001));
|
||||
}
|
||||
assert!(nearly_equal(crate::math::UP, Vector2::UP.angle(), 0.0001));
|
||||
assert!(nearly_equal(crate::math::DOWN, Vector2::DOWN.angle(), 0.0001));
|
||||
assert!(nearly_equal(crate::math::LEFT, Vector2::LEFT.angle(), 0.0001));
|
||||
assert!(nearly_equal(crate::math::RIGHT, Vector2::RIGHT.angle(), 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_from_angle() {
|
||||
let v = Vector2::from_angle(RADIANS_0);
|
||||
assert!(nearly_equal(v.x, 1.0, 0.000001));
|
||||
assert!(nearly_equal(v.y, 0.0, 0.000001));
|
||||
let v = Vector2::from_angle(RADIANS_45);
|
||||
assert!(nearly_equal(v.x, 0.707106, 0.000001));
|
||||
assert!(nearly_equal(v.y, 0.707106, 0.000001));
|
||||
let v = Vector2::from_angle(RADIANS_225);
|
||||
assert!(nearly_equal(v.x, -0.707106, 0.000001));
|
||||
assert!(nearly_equal(v.y, -0.707106, 0.000001));
|
||||
#[test]
|
||||
pub fn test_from_angle() {
|
||||
let v = Vector2::from_angle(RADIANS_0);
|
||||
assert!(nearly_equal(v.x, 1.0, 0.000001));
|
||||
assert!(nearly_equal(v.y, 0.0, 0.000001));
|
||||
let v = Vector2::from_angle(RADIANS_45);
|
||||
assert!(nearly_equal(v.x, 0.707106, 0.000001));
|
||||
assert!(nearly_equal(v.y, 0.707106, 0.000001));
|
||||
let v = Vector2::from_angle(RADIANS_225);
|
||||
assert!(nearly_equal(v.x, -0.707106, 0.000001));
|
||||
assert!(nearly_equal(v.y, -0.707106, 0.000001));
|
||||
|
||||
let v = Vector2::from_angle(UP);
|
||||
assert!(v.nearly_equal(Vector2::UP, 0.000001));
|
||||
let v = Vector2::from_angle(DOWN);
|
||||
assert!(v.nearly_equal(Vector2::DOWN, 0.000001));
|
||||
let v = Vector2::from_angle(LEFT);
|
||||
assert!(v.nearly_equal(Vector2::LEFT, 0.000001));
|
||||
let v = Vector2::from_angle(RIGHT);
|
||||
assert!(v.nearly_equal(Vector2::RIGHT, 0.000001));
|
||||
}
|
||||
let v = Vector2::from_angle(UP);
|
||||
assert!(v.nearly_equal(Vector2::UP, 0.000001));
|
||||
let v = Vector2::from_angle(DOWN);
|
||||
assert!(v.nearly_equal(Vector2::DOWN, 0.000001));
|
||||
let v = Vector2::from_angle(LEFT);
|
||||
assert!(v.nearly_equal(Vector2::LEFT, 0.000001));
|
||||
let v = Vector2::from_angle(RIGHT);
|
||||
assert!(v.nearly_equal(Vector2::RIGHT, 0.000001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_angle_between() {
|
||||
let a = Vector2::new(20.0, 20.0);
|
||||
let b = Vector2::new(10.0, 10.0);
|
||||
let angle = a.angle_between(&b);
|
||||
assert!(nearly_equal(-RADIANS_135, angle, 0.0001));
|
||||
#[test]
|
||||
pub fn test_angle_between() {
|
||||
let a = Vector2::new(20.0, 20.0);
|
||||
let b = Vector2::new(10.0, 10.0);
|
||||
let angle = a.angle_between(&b);
|
||||
assert!(nearly_equal(-RADIANS_135, angle, 0.0001));
|
||||
|
||||
let a = Vector2::new(0.0, 0.0);
|
||||
let b = Vector2::new(10.0, 10.0);
|
||||
let angle = a.angle_between(&b);
|
||||
assert!(nearly_equal(RADIANS_45, angle, 0.0001));
|
||||
let a = Vector2::new(0.0, 0.0);
|
||||
let b = Vector2::new(10.0, 10.0);
|
||||
let angle = a.angle_between(&b);
|
||||
assert!(nearly_equal(RADIANS_45, angle, 0.0001));
|
||||
|
||||
let a = Vector2::new(5.0, 5.0);
|
||||
let b = Vector2::new(5.0, 5.0);
|
||||
let angle = a.angle_between(&b);
|
||||
assert!(nearly_equal(0.0, angle, 0.0001));
|
||||
}
|
||||
let a = Vector2::new(5.0, 5.0);
|
||||
let b = Vector2::new(5.0, 5.0);
|
||||
let angle = a.angle_between(&b);
|
||||
assert!(nearly_equal(0.0, angle, 0.0001));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_lerp() {
|
||||
let a = Vector2 { x: 5.0, y: 1.0 };
|
||||
let b = Vector2 { x: 10.0, y: 2.0 };
|
||||
let c = lerp(a, b, 0.5);
|
||||
assert!(nearly_equal(c.x, 7.5, 0.0001));
|
||||
assert!(nearly_equal(c.y, 1.5, 0.0001));
|
||||
}
|
||||
#[test]
|
||||
pub fn test_lerp() {
|
||||
let a = Vector2 { x: 5.0, y: 1.0 };
|
||||
let b = Vector2 { x: 10.0, y: 2.0 };
|
||||
let c = lerp(a, b, 0.5);
|
||||
assert!(nearly_equal(c.x, 7.5, 0.0001));
|
||||
assert!(nearly_equal(c.y, 1.5, 0.0001));
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,44 +16,44 @@ use crate::system::MouseEvent::MouseButtonUp;
|
|||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum WindowEvent {
|
||||
Shown,
|
||||
Hidden,
|
||||
Exposed,
|
||||
Moved(i32, i32),
|
||||
Resized(i32, i32),
|
||||
SizeChanged(i32, i32),
|
||||
Minimized,
|
||||
Maximized,
|
||||
Restored,
|
||||
Enter,
|
||||
Leave,
|
||||
FocusGained,
|
||||
FocusLost,
|
||||
Close,
|
||||
// for sdl2::event::WindowEvent enum values we haven't mapped / don't care about (yet?)
|
||||
Unimplemented,
|
||||
Shown,
|
||||
Hidden,
|
||||
Exposed,
|
||||
Moved(i32, i32),
|
||||
Resized(i32, i32),
|
||||
SizeChanged(i32, i32),
|
||||
Minimized,
|
||||
Maximized,
|
||||
Restored,
|
||||
Enter,
|
||||
Leave,
|
||||
FocusGained,
|
||||
FocusLost,
|
||||
Close,
|
||||
// for sdl2::event::WindowEvent enum values we haven't mapped / don't care about (yet?)
|
||||
Unimplemented,
|
||||
}
|
||||
|
||||
impl From<sdl2::event::WindowEvent> for WindowEvent {
|
||||
fn from(value: sdl2::event::WindowEvent) -> Self {
|
||||
match value {
|
||||
sdl2::event::WindowEvent::Shown => WindowEvent::Shown,
|
||||
sdl2::event::WindowEvent::Hidden => WindowEvent::Hidden,
|
||||
sdl2::event::WindowEvent::Exposed => WindowEvent::Exposed,
|
||||
sdl2::event::WindowEvent::Moved(x, y) => WindowEvent::Moved(x, y),
|
||||
sdl2::event::WindowEvent::Resized(width, height) => WindowEvent::Resized(width, height),
|
||||
sdl2::event::WindowEvent::SizeChanged(width, height) => WindowEvent::SizeChanged(width, height),
|
||||
sdl2::event::WindowEvent::Minimized => WindowEvent::Minimized,
|
||||
sdl2::event::WindowEvent::Maximized => WindowEvent::Maximized,
|
||||
sdl2::event::WindowEvent::Restored => WindowEvent::Restored,
|
||||
sdl2::event::WindowEvent::Enter => WindowEvent::Enter,
|
||||
sdl2::event::WindowEvent::Leave => WindowEvent::Leave,
|
||||
sdl2::event::WindowEvent::FocusGained => WindowEvent::FocusGained,
|
||||
sdl2::event::WindowEvent::FocusLost => WindowEvent::FocusLost,
|
||||
sdl2::event::WindowEvent::Close => WindowEvent::Close,
|
||||
_ => WindowEvent::Unimplemented,
|
||||
}
|
||||
}
|
||||
fn from(value: sdl2::event::WindowEvent) -> Self {
|
||||
match value {
|
||||
sdl2::event::WindowEvent::Shown => WindowEvent::Shown,
|
||||
sdl2::event::WindowEvent::Hidden => WindowEvent::Hidden,
|
||||
sdl2::event::WindowEvent::Exposed => WindowEvent::Exposed,
|
||||
sdl2::event::WindowEvent::Moved(x, y) => WindowEvent::Moved(x, y),
|
||||
sdl2::event::WindowEvent::Resized(width, height) => WindowEvent::Resized(width, height),
|
||||
sdl2::event::WindowEvent::SizeChanged(width, height) => WindowEvent::SizeChanged(width, height),
|
||||
sdl2::event::WindowEvent::Minimized => WindowEvent::Minimized,
|
||||
sdl2::event::WindowEvent::Maximized => WindowEvent::Maximized,
|
||||
sdl2::event::WindowEvent::Restored => WindowEvent::Restored,
|
||||
sdl2::event::WindowEvent::Enter => WindowEvent::Enter,
|
||||
sdl2::event::WindowEvent::Leave => WindowEvent::Leave,
|
||||
sdl2::event::WindowEvent::FocusGained => WindowEvent::FocusGained,
|
||||
sdl2::event::WindowEvent::FocusLost => WindowEvent::FocusLost,
|
||||
sdl2::event::WindowEvent::Close => WindowEvent::Close,
|
||||
_ => WindowEvent::Unimplemented,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
|
@ -76,156 +76,156 @@ bitflags! {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum KeyboardEvent {
|
||||
KeyUp {
|
||||
keycode: Option<Keycode>,
|
||||
scancode: Option<Scancode>,
|
||||
keymod: KeyModifiers,
|
||||
repeat: bool,
|
||||
},
|
||||
KeyDown {
|
||||
keycode: Option<Keycode>,
|
||||
scancode: Option<Scancode>,
|
||||
keymod: KeyModifiers,
|
||||
repeat: bool,
|
||||
},
|
||||
KeyUp {
|
||||
keycode: Option<Keycode>,
|
||||
scancode: Option<Scancode>,
|
||||
keymod: KeyModifiers,
|
||||
repeat: bool,
|
||||
},
|
||||
KeyDown {
|
||||
keycode: Option<Keycode>,
|
||||
scancode: Option<Scancode>,
|
||||
keymod: KeyModifiers,
|
||||
repeat: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum MouseEvent {
|
||||
MouseMotion {
|
||||
x: i32,
|
||||
y: i32,
|
||||
x_delta: i32,
|
||||
y_delta: i32,
|
||||
buttons: MouseButtons,
|
||||
},
|
||||
MouseButtonDown {
|
||||
x: i32,
|
||||
y: i32,
|
||||
button: MouseButton,
|
||||
clicks: u8,
|
||||
},
|
||||
MouseButtonUp {
|
||||
x: i32,
|
||||
y: i32,
|
||||
button: MouseButton,
|
||||
clicks: u8,
|
||||
},
|
||||
MouseMotion {
|
||||
x: i32,
|
||||
y: i32,
|
||||
x_delta: i32,
|
||||
y_delta: i32,
|
||||
buttons: MouseButtons,
|
||||
},
|
||||
MouseButtonDown {
|
||||
x: i32,
|
||||
y: i32,
|
||||
button: MouseButton,
|
||||
clicks: u8,
|
||||
},
|
||||
MouseButtonUp {
|
||||
x: i32,
|
||||
y: i32,
|
||||
button: MouseButton,
|
||||
clicks: u8,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum SystemEvent {
|
||||
Quit,
|
||||
AppTerminating,
|
||||
AppLowMemory,
|
||||
AppWillEnterBackground,
|
||||
AppDidEnterBackground,
|
||||
AppWillEnterForeground,
|
||||
AppDidEnterForeground,
|
||||
Window(WindowEvent),
|
||||
Keyboard(KeyboardEvent),
|
||||
Mouse(MouseEvent),
|
||||
// for the many sdl2::event::Event enum values that we don't are about quite yet ...
|
||||
Unimplemented,
|
||||
Quit,
|
||||
AppTerminating,
|
||||
AppLowMemory,
|
||||
AppWillEnterBackground,
|
||||
AppDidEnterBackground,
|
||||
AppWillEnterForeground,
|
||||
AppDidEnterForeground,
|
||||
Window(WindowEvent),
|
||||
Keyboard(KeyboardEvent),
|
||||
Mouse(MouseEvent),
|
||||
// for the many sdl2::event::Event enum values that we don't are about quite yet ...
|
||||
Unimplemented,
|
||||
}
|
||||
|
||||
impl From<sdl2::event::Event> for SystemEvent {
|
||||
fn from(value: sdl2::event::Event) -> Self {
|
||||
match value {
|
||||
sdl2::event::Event::Quit { .. } => SystemEvent::Quit,
|
||||
sdl2::event::Event::AppTerminating { .. } => SystemEvent::AppTerminating,
|
||||
sdl2::event::Event::AppLowMemory { .. } => SystemEvent::AppLowMemory,
|
||||
sdl2::event::Event::AppWillEnterBackground { .. } => SystemEvent::AppWillEnterBackground,
|
||||
sdl2::event::Event::AppDidEnterBackground { .. } => SystemEvent::AppDidEnterBackground,
|
||||
sdl2::event::Event::AppWillEnterForeground { .. } => SystemEvent::AppWillEnterForeground,
|
||||
sdl2::event::Event::AppDidEnterForeground { .. } => SystemEvent::AppDidEnterForeground,
|
||||
sdl2::event::Event::Window { win_event, .. } => SystemEvent::Window(win_event.into()),
|
||||
fn from(value: sdl2::event::Event) -> Self {
|
||||
match value {
|
||||
sdl2::event::Event::Quit { .. } => SystemEvent::Quit,
|
||||
sdl2::event::Event::AppTerminating { .. } => SystemEvent::AppTerminating,
|
||||
sdl2::event::Event::AppLowMemory { .. } => SystemEvent::AppLowMemory,
|
||||
sdl2::event::Event::AppWillEnterBackground { .. } => SystemEvent::AppWillEnterBackground,
|
||||
sdl2::event::Event::AppDidEnterBackground { .. } => SystemEvent::AppDidEnterBackground,
|
||||
sdl2::event::Event::AppWillEnterForeground { .. } => SystemEvent::AppWillEnterForeground,
|
||||
sdl2::event::Event::AppDidEnterForeground { .. } => SystemEvent::AppDidEnterForeground,
|
||||
sdl2::event::Event::Window { win_event, .. } => SystemEvent::Window(win_event.into()),
|
||||
|
||||
sdl2::event::Event::KeyDown { keycode, scancode, keymod, repeat, .. } => {
|
||||
SystemEvent::Keyboard(KeyboardEvent::KeyDown {
|
||||
keycode: keycode.map(|keycode| keycode.into()),
|
||||
scancode: scancode.map(|scancode| scancode.into()),
|
||||
keymod: KeyModifiers::from_bits_truncate(keymod.bits()),
|
||||
repeat
|
||||
})
|
||||
},
|
||||
sdl2::event::Event::KeyUp { keycode, scancode, keymod, repeat, .. } => {
|
||||
SystemEvent::Keyboard(KeyboardEvent::KeyUp {
|
||||
keycode: keycode.map(|keycode| keycode.into()),
|
||||
scancode: scancode.map(|scancode| scancode.into()),
|
||||
keymod: KeyModifiers::from_bits_truncate(keymod.bits()),
|
||||
repeat
|
||||
})
|
||||
}
|
||||
sdl2::event::Event::MouseMotion { mousestate, x, y, xrel, yrel, .. } => {
|
||||
SystemEvent::Mouse(MouseEvent::MouseMotion {
|
||||
x,
|
||||
y,
|
||||
x_delta: xrel,
|
||||
y_delta: yrel,
|
||||
buttons: MouseButtons::from_bits_truncate(mousestate.to_sdl_state()),
|
||||
})
|
||||
},
|
||||
sdl2::event::Event::MouseButtonDown { mouse_btn, clicks, x, y, .. } => {
|
||||
SystemEvent::Mouse(MouseEvent::MouseButtonDown {
|
||||
x,
|
||||
y,
|
||||
clicks,
|
||||
button: mouse_btn.into(),
|
||||
})
|
||||
},
|
||||
sdl2::event::Event::MouseButtonUp { mouse_btn, clicks, x, y, .. } => {
|
||||
SystemEvent::Mouse(MouseButtonUp {
|
||||
x,
|
||||
y,
|
||||
clicks,
|
||||
button: mouse_btn.into(),
|
||||
})
|
||||
},
|
||||
sdl2::event::Event::KeyDown { keycode, scancode, keymod, repeat, .. } => {
|
||||
SystemEvent::Keyboard(KeyboardEvent::KeyDown {
|
||||
keycode: keycode.map(|keycode| keycode.into()),
|
||||
scancode: scancode.map(|scancode| scancode.into()),
|
||||
keymod: KeyModifiers::from_bits_truncate(keymod.bits()),
|
||||
repeat,
|
||||
})
|
||||
}
|
||||
sdl2::event::Event::KeyUp { keycode, scancode, keymod, repeat, .. } => {
|
||||
SystemEvent::Keyboard(KeyboardEvent::KeyUp {
|
||||
keycode: keycode.map(|keycode| keycode.into()),
|
||||
scancode: scancode.map(|scancode| scancode.into()),
|
||||
keymod: KeyModifiers::from_bits_truncate(keymod.bits()),
|
||||
repeat,
|
||||
})
|
||||
}
|
||||
sdl2::event::Event::MouseMotion { mousestate, x, y, xrel, yrel, .. } => {
|
||||
SystemEvent::Mouse(MouseEvent::MouseMotion {
|
||||
x,
|
||||
y,
|
||||
x_delta: xrel,
|
||||
y_delta: yrel,
|
||||
buttons: MouseButtons::from_bits_truncate(mousestate.to_sdl_state()),
|
||||
})
|
||||
}
|
||||
sdl2::event::Event::MouseButtonDown { mouse_btn, clicks, x, y, .. } => {
|
||||
SystemEvent::Mouse(MouseEvent::MouseButtonDown {
|
||||
x,
|
||||
y,
|
||||
clicks,
|
||||
button: mouse_btn.into(),
|
||||
})
|
||||
}
|
||||
sdl2::event::Event::MouseButtonUp { mouse_btn, clicks, x, y, .. } => {
|
||||
SystemEvent::Mouse(MouseButtonUp {
|
||||
x,
|
||||
y,
|
||||
clicks,
|
||||
button: mouse_btn.into(),
|
||||
})
|
||||
}
|
||||
|
||||
_ => SystemEvent::Unimplemented,
|
||||
}
|
||||
}
|
||||
_ => SystemEvent::Unimplemented,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Common trait for implementing a handler of [`SystemEvent`]s that are polled during the
|
||||
/// application's main loop.
|
||||
pub trait SystemEventHandler {
|
||||
/// Processes the data from the given [`SystemEvent`]. Returns true if the processing actually
|
||||
/// recognized the passed event and handled it, or false if the event was ignored.
|
||||
fn handle_event(&mut self, event: &SystemEvent) -> bool;
|
||||
/// Processes the data from the given [`SystemEvent`]. Returns true if the processing actually
|
||||
/// recognized the passed event and handled it, or false if the event was ignored.
|
||||
fn handle_event(&mut self, event: &SystemEvent) -> bool;
|
||||
}
|
||||
|
||||
/// An interator for SDL2 system events, polled via [`SystemEventPump`].
|
||||
pub struct SystemEventIterator<'a> {
|
||||
iter: sdl2::event::EventPollIterator<'a>,
|
||||
iter: sdl2::event::EventPollIterator<'a>,
|
||||
}
|
||||
|
||||
impl Iterator for SystemEventIterator<'_> {
|
||||
type Item = SystemEvent;
|
||||
type Item = SystemEvent;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|e| e.into())
|
||||
}
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides an event pump iterator that wraps over SDL2 events, allowing applications to respond
|
||||
/// to all events each frame as [`SystemEvent`] instances.
|
||||
pub struct SystemEventPump {
|
||||
sdl_event_pump: sdl2::EventPump,
|
||||
sdl_event_pump: sdl2::EventPump,
|
||||
}
|
||||
|
||||
impl SystemEventPump {
|
||||
pub fn from(pump: sdl2::EventPump) -> Self {
|
||||
SystemEventPump {
|
||||
sdl_event_pump: pump,
|
||||
}
|
||||
}
|
||||
pub fn from(pump: sdl2::EventPump) -> Self {
|
||||
SystemEventPump {
|
||||
sdl_event_pump: pump,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over [`SystemEvent`]s that have been generated since the last time
|
||||
/// events were polled (usually, in the previous frame).
|
||||
pub fn poll_iter(&mut self) -> SystemEventIterator {
|
||||
self.sdl_event_pump.pump_events();
|
||||
SystemEventIterator { iter: self.sdl_event_pump.poll_iter() }
|
||||
}
|
||||
/// Returns an iterator over [`SystemEvent`]s that have been generated since the last time
|
||||
/// events were polled (usually, in the previous frame).
|
||||
pub fn poll_iter(&mut self) -> SystemEventIterator {
|
||||
self.sdl_event_pump.pump_events();
|
||||
SystemEventIterator { iter: self.sdl_event_pump.poll_iter() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,481 +3,481 @@
|
|||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[repr(i32)]
|
||||
pub enum Keycode {
|
||||
Backspace = sdl2::keyboard::Keycode::Backspace as i32,
|
||||
Tab = sdl2::keyboard::Keycode::Tab as i32,
|
||||
Return = sdl2::keyboard::Keycode::Return as i32,
|
||||
Escape = sdl2::keyboard::Keycode::Escape as i32,
|
||||
Space = sdl2::keyboard::Keycode::Space as i32,
|
||||
Exclaim = sdl2::keyboard::Keycode::Exclaim as i32,
|
||||
Quotedbl = sdl2::keyboard::Keycode::Quotedbl as i32,
|
||||
Hash = sdl2::keyboard::Keycode::Hash as i32,
|
||||
Dollar = sdl2::keyboard::Keycode::Dollar as i32,
|
||||
Percent = sdl2::keyboard::Keycode::Percent as i32,
|
||||
Ampersand = sdl2::keyboard::Keycode::Ampersand as i32,
|
||||
Quote = sdl2::keyboard::Keycode::Quote as i32,
|
||||
LeftParen = sdl2::keyboard::Keycode::LeftParen as i32,
|
||||
RightParen = sdl2::keyboard::Keycode::RightParen as i32,
|
||||
Asterisk = sdl2::keyboard::Keycode::Asterisk as i32,
|
||||
Plus = sdl2::keyboard::Keycode::Plus as i32,
|
||||
Comma = sdl2::keyboard::Keycode::Comma as i32,
|
||||
Minus = sdl2::keyboard::Keycode::Minus as i32,
|
||||
Period = sdl2::keyboard::Keycode::Period as i32,
|
||||
Slash = sdl2::keyboard::Keycode::Slash as i32,
|
||||
Num0 = sdl2::keyboard::Keycode::Num0 as i32,
|
||||
Num1 = sdl2::keyboard::Keycode::Num1 as i32,
|
||||
Num2 = sdl2::keyboard::Keycode::Num2 as i32,
|
||||
Num3 = sdl2::keyboard::Keycode::Num3 as i32,
|
||||
Num4 = sdl2::keyboard::Keycode::Num4 as i32,
|
||||
Num5 = sdl2::keyboard::Keycode::Num5 as i32,
|
||||
Num6 = sdl2::keyboard::Keycode::Num6 as i32,
|
||||
Num7 = sdl2::keyboard::Keycode::Num7 as i32,
|
||||
Num8 = sdl2::keyboard::Keycode::Num8 as i32,
|
||||
Num9 = sdl2::keyboard::Keycode::Num9 as i32,
|
||||
Colon = sdl2::keyboard::Keycode::Colon as i32,
|
||||
Semicolon = sdl2::keyboard::Keycode::Semicolon as i32,
|
||||
Less = sdl2::keyboard::Keycode::Less as i32,
|
||||
Equals = sdl2::keyboard::Keycode::Equals as i32,
|
||||
Greater = sdl2::keyboard::Keycode::Greater as i32,
|
||||
Question = sdl2::keyboard::Keycode::Question as i32,
|
||||
At = sdl2::keyboard::Keycode::At as i32,
|
||||
LeftBracket = sdl2::keyboard::Keycode::LeftBracket as i32,
|
||||
Backslash = sdl2::keyboard::Keycode::Backslash as i32,
|
||||
RightBracket = sdl2::keyboard::Keycode::RightBracket as i32,
|
||||
Caret = sdl2::keyboard::Keycode::Caret as i32,
|
||||
Underscore = sdl2::keyboard::Keycode::Underscore as i32,
|
||||
Backquote = sdl2::keyboard::Keycode::Backquote as i32,
|
||||
A = sdl2::keyboard::Keycode::A as i32,
|
||||
B = sdl2::keyboard::Keycode::B as i32,
|
||||
C = sdl2::keyboard::Keycode::C as i32,
|
||||
D = sdl2::keyboard::Keycode::D as i32,
|
||||
E = sdl2::keyboard::Keycode::E as i32,
|
||||
F = sdl2::keyboard::Keycode::F as i32,
|
||||
G = sdl2::keyboard::Keycode::G as i32,
|
||||
H = sdl2::keyboard::Keycode::H as i32,
|
||||
I = sdl2::keyboard::Keycode::I as i32,
|
||||
J = sdl2::keyboard::Keycode::J as i32,
|
||||
K = sdl2::keyboard::Keycode::K as i32,
|
||||
L = sdl2::keyboard::Keycode::L as i32,
|
||||
M = sdl2::keyboard::Keycode::M as i32,
|
||||
N = sdl2::keyboard::Keycode::N as i32,
|
||||
O = sdl2::keyboard::Keycode::O as i32,
|
||||
P = sdl2::keyboard::Keycode::P as i32,
|
||||
Q = sdl2::keyboard::Keycode::Q as i32,
|
||||
R = sdl2::keyboard::Keycode::R as i32,
|
||||
S = sdl2::keyboard::Keycode::S as i32,
|
||||
T = sdl2::keyboard::Keycode::T as i32,
|
||||
U = sdl2::keyboard::Keycode::U as i32,
|
||||
V = sdl2::keyboard::Keycode::V as i32,
|
||||
W = sdl2::keyboard::Keycode::W as i32,
|
||||
X = sdl2::keyboard::Keycode::X as i32,
|
||||
Y = sdl2::keyboard::Keycode::Y as i32,
|
||||
Z = sdl2::keyboard::Keycode::Z as i32,
|
||||
Delete = sdl2::keyboard::Keycode::Delete as i32,
|
||||
CapsLock = sdl2::keyboard::Keycode::CapsLock as i32,
|
||||
F1 = sdl2::keyboard::Keycode::F1 as i32,
|
||||
F2 = sdl2::keyboard::Keycode::F2 as i32,
|
||||
F3 = sdl2::keyboard::Keycode::F3 as i32,
|
||||
F4 = sdl2::keyboard::Keycode::F4 as i32,
|
||||
F5 = sdl2::keyboard::Keycode::F5 as i32,
|
||||
F6 = sdl2::keyboard::Keycode::F6 as i32,
|
||||
F7 = sdl2::keyboard::Keycode::F7 as i32,
|
||||
F8 = sdl2::keyboard::Keycode::F8 as i32,
|
||||
F9 = sdl2::keyboard::Keycode::F9 as i32,
|
||||
F10 = sdl2::keyboard::Keycode::F10 as i32,
|
||||
F11 = sdl2::keyboard::Keycode::F11 as i32,
|
||||
F12 = sdl2::keyboard::Keycode::F12 as i32,
|
||||
PrintScreen = sdl2::keyboard::Keycode::PrintScreen as i32,
|
||||
ScrollLock = sdl2::keyboard::Keycode::ScrollLock as i32,
|
||||
Pause = sdl2::keyboard::Keycode::Pause as i32,
|
||||
Insert = sdl2::keyboard::Keycode::Insert as i32,
|
||||
Home = sdl2::keyboard::Keycode::Home as i32,
|
||||
PageUp = sdl2::keyboard::Keycode::PageUp as i32,
|
||||
End = sdl2::keyboard::Keycode::End as i32,
|
||||
PageDown = sdl2::keyboard::Keycode::PageDown as i32,
|
||||
Right = sdl2::keyboard::Keycode::Right as i32,
|
||||
Left = sdl2::keyboard::Keycode::Left as i32,
|
||||
Down = sdl2::keyboard::Keycode::Down as i32,
|
||||
Up = sdl2::keyboard::Keycode::Up as i32,
|
||||
NumLockClear = sdl2::keyboard::Keycode::NumLockClear as i32,
|
||||
KpDivide = sdl2::keyboard::Keycode::KpDivide as i32,
|
||||
KpMultiply = sdl2::keyboard::Keycode::KpMultiply as i32,
|
||||
KpMinus = sdl2::keyboard::Keycode::KpMinus as i32,
|
||||
KpPlus = sdl2::keyboard::Keycode::KpPlus as i32,
|
||||
KpEnter = sdl2::keyboard::Keycode::KpEnter as i32,
|
||||
Kp1 = sdl2::keyboard::Keycode::Kp1 as i32,
|
||||
Kp2 = sdl2::keyboard::Keycode::Kp2 as i32,
|
||||
Kp3 = sdl2::keyboard::Keycode::Kp3 as i32,
|
||||
Kp4 = sdl2::keyboard::Keycode::Kp4 as i32,
|
||||
Kp5 = sdl2::keyboard::Keycode::Kp5 as i32,
|
||||
Kp6 = sdl2::keyboard::Keycode::Kp6 as i32,
|
||||
Kp7 = sdl2::keyboard::Keycode::Kp7 as i32,
|
||||
Kp8 = sdl2::keyboard::Keycode::Kp8 as i32,
|
||||
Kp9 = sdl2::keyboard::Keycode::Kp9 as i32,
|
||||
Kp0 = sdl2::keyboard::Keycode::Kp0 as i32,
|
||||
KpPeriod = sdl2::keyboard::Keycode::KpPeriod as i32,
|
||||
Application = sdl2::keyboard::Keycode::Application as i32,
|
||||
Power = sdl2::keyboard::Keycode::Power as i32,
|
||||
KpEquals = sdl2::keyboard::Keycode::KpEquals as i32,
|
||||
F13 = sdl2::keyboard::Keycode::F13 as i32,
|
||||
F14 = sdl2::keyboard::Keycode::F14 as i32,
|
||||
F15 = sdl2::keyboard::Keycode::F15 as i32,
|
||||
F16 = sdl2::keyboard::Keycode::F16 as i32,
|
||||
F17 = sdl2::keyboard::Keycode::F17 as i32,
|
||||
F18 = sdl2::keyboard::Keycode::F18 as i32,
|
||||
F19 = sdl2::keyboard::Keycode::F19 as i32,
|
||||
F20 = sdl2::keyboard::Keycode::F20 as i32,
|
||||
F21 = sdl2::keyboard::Keycode::F21 as i32,
|
||||
F22 = sdl2::keyboard::Keycode::F22 as i32,
|
||||
F23 = sdl2::keyboard::Keycode::F23 as i32,
|
||||
F24 = sdl2::keyboard::Keycode::F24 as i32,
|
||||
Execute = sdl2::keyboard::Keycode::Execute as i32,
|
||||
Help = sdl2::keyboard::Keycode::Help as i32,
|
||||
Menu = sdl2::keyboard::Keycode::Menu as i32,
|
||||
Select = sdl2::keyboard::Keycode::Select as i32,
|
||||
Stop = sdl2::keyboard::Keycode::Stop as i32,
|
||||
Again = sdl2::keyboard::Keycode::Again as i32,
|
||||
Undo = sdl2::keyboard::Keycode::Undo as i32,
|
||||
Cut = sdl2::keyboard::Keycode::Cut as i32,
|
||||
Copy = sdl2::keyboard::Keycode::Copy as i32,
|
||||
Paste = sdl2::keyboard::Keycode::Paste as i32,
|
||||
Find = sdl2::keyboard::Keycode::Find as i32,
|
||||
Mute = sdl2::keyboard::Keycode::Mute as i32,
|
||||
VolumeUp = sdl2::keyboard::Keycode::VolumeUp as i32,
|
||||
VolumeDown = sdl2::keyboard::Keycode::VolumeDown as i32,
|
||||
KpComma = sdl2::keyboard::Keycode::KpComma as i32,
|
||||
KpEqualsAS400 = sdl2::keyboard::Keycode::KpEqualsAS400 as i32,
|
||||
AltErase = sdl2::keyboard::Keycode::AltErase as i32,
|
||||
Sysreq = sdl2::keyboard::Keycode::Sysreq as i32,
|
||||
Cancel = sdl2::keyboard::Keycode::Cancel as i32,
|
||||
Clear = sdl2::keyboard::Keycode::Clear as i32,
|
||||
Prior = sdl2::keyboard::Keycode::Prior as i32,
|
||||
Return2 = sdl2::keyboard::Keycode::Return2 as i32,
|
||||
Separator = sdl2::keyboard::Keycode::Separator as i32,
|
||||
Out = sdl2::keyboard::Keycode::Out as i32,
|
||||
Oper = sdl2::keyboard::Keycode::Oper as i32,
|
||||
ClearAgain = sdl2::keyboard::Keycode::ClearAgain as i32,
|
||||
CrSel = sdl2::keyboard::Keycode::CrSel as i32,
|
||||
ExSel = sdl2::keyboard::Keycode::ExSel as i32,
|
||||
Kp00 = sdl2::keyboard::Keycode::Kp00 as i32,
|
||||
Kp000 = sdl2::keyboard::Keycode::Kp000 as i32,
|
||||
ThousandsSeparator = sdl2::keyboard::Keycode::ThousandsSeparator as i32,
|
||||
DecimalSeparator = sdl2::keyboard::Keycode::DecimalSeparator as i32,
|
||||
CurrencyUnit = sdl2::keyboard::Keycode::CurrencyUnit as i32,
|
||||
CurrencySubUnit = sdl2::keyboard::Keycode::CurrencySubUnit as i32,
|
||||
KpLeftParen = sdl2::keyboard::Keycode::KpLeftParen as i32,
|
||||
KpRightParen = sdl2::keyboard::Keycode::KpRightParen as i32,
|
||||
KpLeftBrace = sdl2::keyboard::Keycode::KpLeftBrace as i32,
|
||||
KpRightBrace = sdl2::keyboard::Keycode::KpRightBrace as i32,
|
||||
KpTab = sdl2::keyboard::Keycode::KpTab as i32,
|
||||
KpBackspace = sdl2::keyboard::Keycode::KpBackspace as i32,
|
||||
KpA = sdl2::keyboard::Keycode::KpA as i32,
|
||||
KpB = sdl2::keyboard::Keycode::KpB as i32,
|
||||
KpC = sdl2::keyboard::Keycode::KpC as i32,
|
||||
KpD = sdl2::keyboard::Keycode::KpD as i32,
|
||||
KpE = sdl2::keyboard::Keycode::KpE as i32,
|
||||
KpF = sdl2::keyboard::Keycode::KpF as i32,
|
||||
KpXor = sdl2::keyboard::Keycode::KpXor as i32,
|
||||
KpPower = sdl2::keyboard::Keycode::KpPower as i32,
|
||||
KpPercent = sdl2::keyboard::Keycode::KpPercent as i32,
|
||||
KpLess = sdl2::keyboard::Keycode::KpLess as i32,
|
||||
KpGreater = sdl2::keyboard::Keycode::KpGreater as i32,
|
||||
KpAmpersand = sdl2::keyboard::Keycode::KpAmpersand as i32,
|
||||
KpDblAmpersand = sdl2::keyboard::Keycode::KpDblAmpersand as i32,
|
||||
KpVerticalBar = sdl2::keyboard::Keycode::KpVerticalBar as i32,
|
||||
KpDblVerticalBar = sdl2::keyboard::Keycode::KpDblVerticalBar as i32,
|
||||
KpColon = sdl2::keyboard::Keycode::KpColon as i32,
|
||||
KpHash = sdl2::keyboard::Keycode::KpHash as i32,
|
||||
KpSpace = sdl2::keyboard::Keycode::KpSpace as i32,
|
||||
KpAt = sdl2::keyboard::Keycode::KpAt as i32,
|
||||
KpExclam = sdl2::keyboard::Keycode::KpExclam as i32,
|
||||
KpMemStore = sdl2::keyboard::Keycode::KpMemStore as i32,
|
||||
KpMemRecall = sdl2::keyboard::Keycode::KpMemRecall as i32,
|
||||
KpMemClear = sdl2::keyboard::Keycode::KpMemClear as i32,
|
||||
KpMemAdd = sdl2::keyboard::Keycode::KpMemAdd as i32,
|
||||
KpMemSubtract = sdl2::keyboard::Keycode::KpMemSubtract as i32,
|
||||
KpMemMultiply = sdl2::keyboard::Keycode::KpMemMultiply as i32,
|
||||
KpMemDivide = sdl2::keyboard::Keycode::KpMemDivide as i32,
|
||||
KpPlusMinus = sdl2::keyboard::Keycode::KpPlusMinus as i32,
|
||||
KpClear = sdl2::keyboard::Keycode::KpClear as i32,
|
||||
KpClearEntry = sdl2::keyboard::Keycode::KpClearEntry as i32,
|
||||
KpBinary = sdl2::keyboard::Keycode::KpBinary as i32,
|
||||
KpOctal = sdl2::keyboard::Keycode::KpOctal as i32,
|
||||
KpDecimal = sdl2::keyboard::Keycode::KpDecimal as i32,
|
||||
KpHexadecimal = sdl2::keyboard::Keycode::KpHexadecimal as i32,
|
||||
LCtrl = sdl2::keyboard::Keycode::LCtrl as i32,
|
||||
LShift = sdl2::keyboard::Keycode::LShift as i32,
|
||||
LAlt = sdl2::keyboard::Keycode::LAlt as i32,
|
||||
LGui = sdl2::keyboard::Keycode::LGui as i32,
|
||||
RCtrl = sdl2::keyboard::Keycode::RCtrl as i32,
|
||||
RShift = sdl2::keyboard::Keycode::RShift as i32,
|
||||
RAlt = sdl2::keyboard::Keycode::RAlt as i32,
|
||||
RGui = sdl2::keyboard::Keycode::RGui as i32,
|
||||
Mode = sdl2::keyboard::Keycode::Mode as i32,
|
||||
AudioNext = sdl2::keyboard::Keycode::AudioNext as i32,
|
||||
AudioPrev = sdl2::keyboard::Keycode::AudioPrev as i32,
|
||||
AudioStop = sdl2::keyboard::Keycode::AudioStop as i32,
|
||||
AudioPlay = sdl2::keyboard::Keycode::AudioPlay as i32,
|
||||
AudioMute = sdl2::keyboard::Keycode::AudioMute as i32,
|
||||
MediaSelect = sdl2::keyboard::Keycode::MediaSelect as i32,
|
||||
Www = sdl2::keyboard::Keycode::Www as i32,
|
||||
Mail = sdl2::keyboard::Keycode::Mail as i32,
|
||||
Calculator = sdl2::keyboard::Keycode::Calculator as i32,
|
||||
Computer = sdl2::keyboard::Keycode::Computer as i32,
|
||||
AcSearch = sdl2::keyboard::Keycode::AcSearch as i32,
|
||||
AcHome = sdl2::keyboard::Keycode::AcHome as i32,
|
||||
AcBack = sdl2::keyboard::Keycode::AcBack as i32,
|
||||
AcForward = sdl2::keyboard::Keycode::AcForward as i32,
|
||||
AcStop = sdl2::keyboard::Keycode::AcStop as i32,
|
||||
AcRefresh = sdl2::keyboard::Keycode::AcRefresh as i32,
|
||||
AcBookmarks = sdl2::keyboard::Keycode::AcBookmarks as i32,
|
||||
BrightnessDown = sdl2::keyboard::Keycode::BrightnessDown as i32,
|
||||
BrightnessUp = sdl2::keyboard::Keycode::BrightnessUp as i32,
|
||||
DisplaySwitch = sdl2::keyboard::Keycode::DisplaySwitch as i32,
|
||||
KbdIllumToggle = sdl2::keyboard::Keycode::KbdIllumToggle as i32,
|
||||
KbdIllumDown = sdl2::keyboard::Keycode::KbdIllumDown as i32,
|
||||
KbdIllumUp = sdl2::keyboard::Keycode::KbdIllumUp as i32,
|
||||
Eject = sdl2::keyboard::Keycode::Eject as i32,
|
||||
Sleep = sdl2::keyboard::Keycode::Sleep as i32,
|
||||
Backspace = sdl2::keyboard::Keycode::Backspace as i32,
|
||||
Tab = sdl2::keyboard::Keycode::Tab as i32,
|
||||
Return = sdl2::keyboard::Keycode::Return as i32,
|
||||
Escape = sdl2::keyboard::Keycode::Escape as i32,
|
||||
Space = sdl2::keyboard::Keycode::Space as i32,
|
||||
Exclaim = sdl2::keyboard::Keycode::Exclaim as i32,
|
||||
Quotedbl = sdl2::keyboard::Keycode::Quotedbl as i32,
|
||||
Hash = sdl2::keyboard::Keycode::Hash as i32,
|
||||
Dollar = sdl2::keyboard::Keycode::Dollar as i32,
|
||||
Percent = sdl2::keyboard::Keycode::Percent as i32,
|
||||
Ampersand = sdl2::keyboard::Keycode::Ampersand as i32,
|
||||
Quote = sdl2::keyboard::Keycode::Quote as i32,
|
||||
LeftParen = sdl2::keyboard::Keycode::LeftParen as i32,
|
||||
RightParen = sdl2::keyboard::Keycode::RightParen as i32,
|
||||
Asterisk = sdl2::keyboard::Keycode::Asterisk as i32,
|
||||
Plus = sdl2::keyboard::Keycode::Plus as i32,
|
||||
Comma = sdl2::keyboard::Keycode::Comma as i32,
|
||||
Minus = sdl2::keyboard::Keycode::Minus as i32,
|
||||
Period = sdl2::keyboard::Keycode::Period as i32,
|
||||
Slash = sdl2::keyboard::Keycode::Slash as i32,
|
||||
Num0 = sdl2::keyboard::Keycode::Num0 as i32,
|
||||
Num1 = sdl2::keyboard::Keycode::Num1 as i32,
|
||||
Num2 = sdl2::keyboard::Keycode::Num2 as i32,
|
||||
Num3 = sdl2::keyboard::Keycode::Num3 as i32,
|
||||
Num4 = sdl2::keyboard::Keycode::Num4 as i32,
|
||||
Num5 = sdl2::keyboard::Keycode::Num5 as i32,
|
||||
Num6 = sdl2::keyboard::Keycode::Num6 as i32,
|
||||
Num7 = sdl2::keyboard::Keycode::Num7 as i32,
|
||||
Num8 = sdl2::keyboard::Keycode::Num8 as i32,
|
||||
Num9 = sdl2::keyboard::Keycode::Num9 as i32,
|
||||
Colon = sdl2::keyboard::Keycode::Colon as i32,
|
||||
Semicolon = sdl2::keyboard::Keycode::Semicolon as i32,
|
||||
Less = sdl2::keyboard::Keycode::Less as i32,
|
||||
Equals = sdl2::keyboard::Keycode::Equals as i32,
|
||||
Greater = sdl2::keyboard::Keycode::Greater as i32,
|
||||
Question = sdl2::keyboard::Keycode::Question as i32,
|
||||
At = sdl2::keyboard::Keycode::At as i32,
|
||||
LeftBracket = sdl2::keyboard::Keycode::LeftBracket as i32,
|
||||
Backslash = sdl2::keyboard::Keycode::Backslash as i32,
|
||||
RightBracket = sdl2::keyboard::Keycode::RightBracket as i32,
|
||||
Caret = sdl2::keyboard::Keycode::Caret as i32,
|
||||
Underscore = sdl2::keyboard::Keycode::Underscore as i32,
|
||||
Backquote = sdl2::keyboard::Keycode::Backquote as i32,
|
||||
A = sdl2::keyboard::Keycode::A as i32,
|
||||
B = sdl2::keyboard::Keycode::B as i32,
|
||||
C = sdl2::keyboard::Keycode::C as i32,
|
||||
D = sdl2::keyboard::Keycode::D as i32,
|
||||
E = sdl2::keyboard::Keycode::E as i32,
|
||||
F = sdl2::keyboard::Keycode::F as i32,
|
||||
G = sdl2::keyboard::Keycode::G as i32,
|
||||
H = sdl2::keyboard::Keycode::H as i32,
|
||||
I = sdl2::keyboard::Keycode::I as i32,
|
||||
J = sdl2::keyboard::Keycode::J as i32,
|
||||
K = sdl2::keyboard::Keycode::K as i32,
|
||||
L = sdl2::keyboard::Keycode::L as i32,
|
||||
M = sdl2::keyboard::Keycode::M as i32,
|
||||
N = sdl2::keyboard::Keycode::N as i32,
|
||||
O = sdl2::keyboard::Keycode::O as i32,
|
||||
P = sdl2::keyboard::Keycode::P as i32,
|
||||
Q = sdl2::keyboard::Keycode::Q as i32,
|
||||
R = sdl2::keyboard::Keycode::R as i32,
|
||||
S = sdl2::keyboard::Keycode::S as i32,
|
||||
T = sdl2::keyboard::Keycode::T as i32,
|
||||
U = sdl2::keyboard::Keycode::U as i32,
|
||||
V = sdl2::keyboard::Keycode::V as i32,
|
||||
W = sdl2::keyboard::Keycode::W as i32,
|
||||
X = sdl2::keyboard::Keycode::X as i32,
|
||||
Y = sdl2::keyboard::Keycode::Y as i32,
|
||||
Z = sdl2::keyboard::Keycode::Z as i32,
|
||||
Delete = sdl2::keyboard::Keycode::Delete as i32,
|
||||
CapsLock = sdl2::keyboard::Keycode::CapsLock as i32,
|
||||
F1 = sdl2::keyboard::Keycode::F1 as i32,
|
||||
F2 = sdl2::keyboard::Keycode::F2 as i32,
|
||||
F3 = sdl2::keyboard::Keycode::F3 as i32,
|
||||
F4 = sdl2::keyboard::Keycode::F4 as i32,
|
||||
F5 = sdl2::keyboard::Keycode::F5 as i32,
|
||||
F6 = sdl2::keyboard::Keycode::F6 as i32,
|
||||
F7 = sdl2::keyboard::Keycode::F7 as i32,
|
||||
F8 = sdl2::keyboard::Keycode::F8 as i32,
|
||||
F9 = sdl2::keyboard::Keycode::F9 as i32,
|
||||
F10 = sdl2::keyboard::Keycode::F10 as i32,
|
||||
F11 = sdl2::keyboard::Keycode::F11 as i32,
|
||||
F12 = sdl2::keyboard::Keycode::F12 as i32,
|
||||
PrintScreen = sdl2::keyboard::Keycode::PrintScreen as i32,
|
||||
ScrollLock = sdl2::keyboard::Keycode::ScrollLock as i32,
|
||||
Pause = sdl2::keyboard::Keycode::Pause as i32,
|
||||
Insert = sdl2::keyboard::Keycode::Insert as i32,
|
||||
Home = sdl2::keyboard::Keycode::Home as i32,
|
||||
PageUp = sdl2::keyboard::Keycode::PageUp as i32,
|
||||
End = sdl2::keyboard::Keycode::End as i32,
|
||||
PageDown = sdl2::keyboard::Keycode::PageDown as i32,
|
||||
Right = sdl2::keyboard::Keycode::Right as i32,
|
||||
Left = sdl2::keyboard::Keycode::Left as i32,
|
||||
Down = sdl2::keyboard::Keycode::Down as i32,
|
||||
Up = sdl2::keyboard::Keycode::Up as i32,
|
||||
NumLockClear = sdl2::keyboard::Keycode::NumLockClear as i32,
|
||||
KpDivide = sdl2::keyboard::Keycode::KpDivide as i32,
|
||||
KpMultiply = sdl2::keyboard::Keycode::KpMultiply as i32,
|
||||
KpMinus = sdl2::keyboard::Keycode::KpMinus as i32,
|
||||
KpPlus = sdl2::keyboard::Keycode::KpPlus as i32,
|
||||
KpEnter = sdl2::keyboard::Keycode::KpEnter as i32,
|
||||
Kp1 = sdl2::keyboard::Keycode::Kp1 as i32,
|
||||
Kp2 = sdl2::keyboard::Keycode::Kp2 as i32,
|
||||
Kp3 = sdl2::keyboard::Keycode::Kp3 as i32,
|
||||
Kp4 = sdl2::keyboard::Keycode::Kp4 as i32,
|
||||
Kp5 = sdl2::keyboard::Keycode::Kp5 as i32,
|
||||
Kp6 = sdl2::keyboard::Keycode::Kp6 as i32,
|
||||
Kp7 = sdl2::keyboard::Keycode::Kp7 as i32,
|
||||
Kp8 = sdl2::keyboard::Keycode::Kp8 as i32,
|
||||
Kp9 = sdl2::keyboard::Keycode::Kp9 as i32,
|
||||
Kp0 = sdl2::keyboard::Keycode::Kp0 as i32,
|
||||
KpPeriod = sdl2::keyboard::Keycode::KpPeriod as i32,
|
||||
Application = sdl2::keyboard::Keycode::Application as i32,
|
||||
Power = sdl2::keyboard::Keycode::Power as i32,
|
||||
KpEquals = sdl2::keyboard::Keycode::KpEquals as i32,
|
||||
F13 = sdl2::keyboard::Keycode::F13 as i32,
|
||||
F14 = sdl2::keyboard::Keycode::F14 as i32,
|
||||
F15 = sdl2::keyboard::Keycode::F15 as i32,
|
||||
F16 = sdl2::keyboard::Keycode::F16 as i32,
|
||||
F17 = sdl2::keyboard::Keycode::F17 as i32,
|
||||
F18 = sdl2::keyboard::Keycode::F18 as i32,
|
||||
F19 = sdl2::keyboard::Keycode::F19 as i32,
|
||||
F20 = sdl2::keyboard::Keycode::F20 as i32,
|
||||
F21 = sdl2::keyboard::Keycode::F21 as i32,
|
||||
F22 = sdl2::keyboard::Keycode::F22 as i32,
|
||||
F23 = sdl2::keyboard::Keycode::F23 as i32,
|
||||
F24 = sdl2::keyboard::Keycode::F24 as i32,
|
||||
Execute = sdl2::keyboard::Keycode::Execute as i32,
|
||||
Help = sdl2::keyboard::Keycode::Help as i32,
|
||||
Menu = sdl2::keyboard::Keycode::Menu as i32,
|
||||
Select = sdl2::keyboard::Keycode::Select as i32,
|
||||
Stop = sdl2::keyboard::Keycode::Stop as i32,
|
||||
Again = sdl2::keyboard::Keycode::Again as i32,
|
||||
Undo = sdl2::keyboard::Keycode::Undo as i32,
|
||||
Cut = sdl2::keyboard::Keycode::Cut as i32,
|
||||
Copy = sdl2::keyboard::Keycode::Copy as i32,
|
||||
Paste = sdl2::keyboard::Keycode::Paste as i32,
|
||||
Find = sdl2::keyboard::Keycode::Find as i32,
|
||||
Mute = sdl2::keyboard::Keycode::Mute as i32,
|
||||
VolumeUp = sdl2::keyboard::Keycode::VolumeUp as i32,
|
||||
VolumeDown = sdl2::keyboard::Keycode::VolumeDown as i32,
|
||||
KpComma = sdl2::keyboard::Keycode::KpComma as i32,
|
||||
KpEqualsAS400 = sdl2::keyboard::Keycode::KpEqualsAS400 as i32,
|
||||
AltErase = sdl2::keyboard::Keycode::AltErase as i32,
|
||||
Sysreq = sdl2::keyboard::Keycode::Sysreq as i32,
|
||||
Cancel = sdl2::keyboard::Keycode::Cancel as i32,
|
||||
Clear = sdl2::keyboard::Keycode::Clear as i32,
|
||||
Prior = sdl2::keyboard::Keycode::Prior as i32,
|
||||
Return2 = sdl2::keyboard::Keycode::Return2 as i32,
|
||||
Separator = sdl2::keyboard::Keycode::Separator as i32,
|
||||
Out = sdl2::keyboard::Keycode::Out as i32,
|
||||
Oper = sdl2::keyboard::Keycode::Oper as i32,
|
||||
ClearAgain = sdl2::keyboard::Keycode::ClearAgain as i32,
|
||||
CrSel = sdl2::keyboard::Keycode::CrSel as i32,
|
||||
ExSel = sdl2::keyboard::Keycode::ExSel as i32,
|
||||
Kp00 = sdl2::keyboard::Keycode::Kp00 as i32,
|
||||
Kp000 = sdl2::keyboard::Keycode::Kp000 as i32,
|
||||
ThousandsSeparator = sdl2::keyboard::Keycode::ThousandsSeparator as i32,
|
||||
DecimalSeparator = sdl2::keyboard::Keycode::DecimalSeparator as i32,
|
||||
CurrencyUnit = sdl2::keyboard::Keycode::CurrencyUnit as i32,
|
||||
CurrencySubUnit = sdl2::keyboard::Keycode::CurrencySubUnit as i32,
|
||||
KpLeftParen = sdl2::keyboard::Keycode::KpLeftParen as i32,
|
||||
KpRightParen = sdl2::keyboard::Keycode::KpRightParen as i32,
|
||||
KpLeftBrace = sdl2::keyboard::Keycode::KpLeftBrace as i32,
|
||||
KpRightBrace = sdl2::keyboard::Keycode::KpRightBrace as i32,
|
||||
KpTab = sdl2::keyboard::Keycode::KpTab as i32,
|
||||
KpBackspace = sdl2::keyboard::Keycode::KpBackspace as i32,
|
||||
KpA = sdl2::keyboard::Keycode::KpA as i32,
|
||||
KpB = sdl2::keyboard::Keycode::KpB as i32,
|
||||
KpC = sdl2::keyboard::Keycode::KpC as i32,
|
||||
KpD = sdl2::keyboard::Keycode::KpD as i32,
|
||||
KpE = sdl2::keyboard::Keycode::KpE as i32,
|
||||
KpF = sdl2::keyboard::Keycode::KpF as i32,
|
||||
KpXor = sdl2::keyboard::Keycode::KpXor as i32,
|
||||
KpPower = sdl2::keyboard::Keycode::KpPower as i32,
|
||||
KpPercent = sdl2::keyboard::Keycode::KpPercent as i32,
|
||||
KpLess = sdl2::keyboard::Keycode::KpLess as i32,
|
||||
KpGreater = sdl2::keyboard::Keycode::KpGreater as i32,
|
||||
KpAmpersand = sdl2::keyboard::Keycode::KpAmpersand as i32,
|
||||
KpDblAmpersand = sdl2::keyboard::Keycode::KpDblAmpersand as i32,
|
||||
KpVerticalBar = sdl2::keyboard::Keycode::KpVerticalBar as i32,
|
||||
KpDblVerticalBar = sdl2::keyboard::Keycode::KpDblVerticalBar as i32,
|
||||
KpColon = sdl2::keyboard::Keycode::KpColon as i32,
|
||||
KpHash = sdl2::keyboard::Keycode::KpHash as i32,
|
||||
KpSpace = sdl2::keyboard::Keycode::KpSpace as i32,
|
||||
KpAt = sdl2::keyboard::Keycode::KpAt as i32,
|
||||
KpExclam = sdl2::keyboard::Keycode::KpExclam as i32,
|
||||
KpMemStore = sdl2::keyboard::Keycode::KpMemStore as i32,
|
||||
KpMemRecall = sdl2::keyboard::Keycode::KpMemRecall as i32,
|
||||
KpMemClear = sdl2::keyboard::Keycode::KpMemClear as i32,
|
||||
KpMemAdd = sdl2::keyboard::Keycode::KpMemAdd as i32,
|
||||
KpMemSubtract = sdl2::keyboard::Keycode::KpMemSubtract as i32,
|
||||
KpMemMultiply = sdl2::keyboard::Keycode::KpMemMultiply as i32,
|
||||
KpMemDivide = sdl2::keyboard::Keycode::KpMemDivide as i32,
|
||||
KpPlusMinus = sdl2::keyboard::Keycode::KpPlusMinus as i32,
|
||||
KpClear = sdl2::keyboard::Keycode::KpClear as i32,
|
||||
KpClearEntry = sdl2::keyboard::Keycode::KpClearEntry as i32,
|
||||
KpBinary = sdl2::keyboard::Keycode::KpBinary as i32,
|
||||
KpOctal = sdl2::keyboard::Keycode::KpOctal as i32,
|
||||
KpDecimal = sdl2::keyboard::Keycode::KpDecimal as i32,
|
||||
KpHexadecimal = sdl2::keyboard::Keycode::KpHexadecimal as i32,
|
||||
LCtrl = sdl2::keyboard::Keycode::LCtrl as i32,
|
||||
LShift = sdl2::keyboard::Keycode::LShift as i32,
|
||||
LAlt = sdl2::keyboard::Keycode::LAlt as i32,
|
||||
LGui = sdl2::keyboard::Keycode::LGui as i32,
|
||||
RCtrl = sdl2::keyboard::Keycode::RCtrl as i32,
|
||||
RShift = sdl2::keyboard::Keycode::RShift as i32,
|
||||
RAlt = sdl2::keyboard::Keycode::RAlt as i32,
|
||||
RGui = sdl2::keyboard::Keycode::RGui as i32,
|
||||
Mode = sdl2::keyboard::Keycode::Mode as i32,
|
||||
AudioNext = sdl2::keyboard::Keycode::AudioNext as i32,
|
||||
AudioPrev = sdl2::keyboard::Keycode::AudioPrev as i32,
|
||||
AudioStop = sdl2::keyboard::Keycode::AudioStop as i32,
|
||||
AudioPlay = sdl2::keyboard::Keycode::AudioPlay as i32,
|
||||
AudioMute = sdl2::keyboard::Keycode::AudioMute as i32,
|
||||
MediaSelect = sdl2::keyboard::Keycode::MediaSelect as i32,
|
||||
Www = sdl2::keyboard::Keycode::Www as i32,
|
||||
Mail = sdl2::keyboard::Keycode::Mail as i32,
|
||||
Calculator = sdl2::keyboard::Keycode::Calculator as i32,
|
||||
Computer = sdl2::keyboard::Keycode::Computer as i32,
|
||||
AcSearch = sdl2::keyboard::Keycode::AcSearch as i32,
|
||||
AcHome = sdl2::keyboard::Keycode::AcHome as i32,
|
||||
AcBack = sdl2::keyboard::Keycode::AcBack as i32,
|
||||
AcForward = sdl2::keyboard::Keycode::AcForward as i32,
|
||||
AcStop = sdl2::keyboard::Keycode::AcStop as i32,
|
||||
AcRefresh = sdl2::keyboard::Keycode::AcRefresh as i32,
|
||||
AcBookmarks = sdl2::keyboard::Keycode::AcBookmarks as i32,
|
||||
BrightnessDown = sdl2::keyboard::Keycode::BrightnessDown as i32,
|
||||
BrightnessUp = sdl2::keyboard::Keycode::BrightnessUp as i32,
|
||||
DisplaySwitch = sdl2::keyboard::Keycode::DisplaySwitch as i32,
|
||||
KbdIllumToggle = sdl2::keyboard::Keycode::KbdIllumToggle as i32,
|
||||
KbdIllumDown = sdl2::keyboard::Keycode::KbdIllumDown as i32,
|
||||
KbdIllumUp = sdl2::keyboard::Keycode::KbdIllumUp as i32,
|
||||
Eject = sdl2::keyboard::Keycode::Eject as i32,
|
||||
Sleep = sdl2::keyboard::Keycode::Sleep as i32,
|
||||
}
|
||||
|
||||
impl From<sdl2::keyboard::Keycode> for Keycode {
|
||||
fn from(value: sdl2::keyboard::Keycode) -> Self {
|
||||
match value {
|
||||
sdl2::keyboard::Keycode::Backspace => Keycode::Backspace,
|
||||
sdl2::keyboard::Keycode::Tab => Keycode::Tab,
|
||||
sdl2::keyboard::Keycode::Return => Keycode::Return,
|
||||
sdl2::keyboard::Keycode::Escape => Keycode::Escape,
|
||||
sdl2::keyboard::Keycode::Space => Keycode::Space,
|
||||
sdl2::keyboard::Keycode::Exclaim => Keycode::Exclaim,
|
||||
sdl2::keyboard::Keycode::Quotedbl => Keycode::Quotedbl,
|
||||
sdl2::keyboard::Keycode::Hash => Keycode::Hash,
|
||||
sdl2::keyboard::Keycode::Dollar => Keycode::Dollar,
|
||||
sdl2::keyboard::Keycode::Percent => Keycode::Percent,
|
||||
sdl2::keyboard::Keycode::Ampersand => Keycode::Ampersand,
|
||||
sdl2::keyboard::Keycode::Quote => Keycode::Quote,
|
||||
sdl2::keyboard::Keycode::LeftParen => Keycode::LeftParen,
|
||||
sdl2::keyboard::Keycode::RightParen => Keycode::RightParen,
|
||||
sdl2::keyboard::Keycode::Asterisk => Keycode::Asterisk,
|
||||
sdl2::keyboard::Keycode::Plus => Keycode::Plus,
|
||||
sdl2::keyboard::Keycode::Comma => Keycode::Comma,
|
||||
sdl2::keyboard::Keycode::Minus => Keycode::Minus,
|
||||
sdl2::keyboard::Keycode::Period => Keycode::Period,
|
||||
sdl2::keyboard::Keycode::Slash => Keycode::Slash,
|
||||
sdl2::keyboard::Keycode::Num0 => Keycode::Num0,
|
||||
sdl2::keyboard::Keycode::Num1 => Keycode::Num1,
|
||||
sdl2::keyboard::Keycode::Num2 => Keycode::Num2,
|
||||
sdl2::keyboard::Keycode::Num3 => Keycode::Num3,
|
||||
sdl2::keyboard::Keycode::Num4 => Keycode::Num4,
|
||||
sdl2::keyboard::Keycode::Num5 => Keycode::Num5,
|
||||
sdl2::keyboard::Keycode::Num6 => Keycode::Num6,
|
||||
sdl2::keyboard::Keycode::Num7 => Keycode::Num7,
|
||||
sdl2::keyboard::Keycode::Num8 => Keycode::Num8,
|
||||
sdl2::keyboard::Keycode::Num9 => Keycode::Num9,
|
||||
sdl2::keyboard::Keycode::Colon => Keycode::Colon,
|
||||
sdl2::keyboard::Keycode::Semicolon => Keycode::Semicolon,
|
||||
sdl2::keyboard::Keycode::Less => Keycode::Less,
|
||||
sdl2::keyboard::Keycode::Equals => Keycode::Equals,
|
||||
sdl2::keyboard::Keycode::Greater => Keycode::Greater,
|
||||
sdl2::keyboard::Keycode::Question => Keycode::Question,
|
||||
sdl2::keyboard::Keycode::At => Keycode::At,
|
||||
sdl2::keyboard::Keycode::LeftBracket => Keycode::LeftBracket,
|
||||
sdl2::keyboard::Keycode::Backslash => Keycode::Backslash,
|
||||
sdl2::keyboard::Keycode::RightBracket => Keycode::RightBracket,
|
||||
sdl2::keyboard::Keycode::Caret => Keycode::Caret,
|
||||
sdl2::keyboard::Keycode::Underscore => Keycode::Underscore,
|
||||
sdl2::keyboard::Keycode::Backquote => Keycode::Backquote,
|
||||
sdl2::keyboard::Keycode::A => Keycode::A,
|
||||
sdl2::keyboard::Keycode::B => Keycode::B,
|
||||
sdl2::keyboard::Keycode::C => Keycode::C,
|
||||
sdl2::keyboard::Keycode::D => Keycode::D,
|
||||
sdl2::keyboard::Keycode::E => Keycode::E,
|
||||
sdl2::keyboard::Keycode::F => Keycode::F,
|
||||
sdl2::keyboard::Keycode::G => Keycode::G,
|
||||
sdl2::keyboard::Keycode::H => Keycode::H,
|
||||
sdl2::keyboard::Keycode::I => Keycode::I,
|
||||
sdl2::keyboard::Keycode::J => Keycode::J,
|
||||
sdl2::keyboard::Keycode::K => Keycode::K,
|
||||
sdl2::keyboard::Keycode::L => Keycode::L,
|
||||
sdl2::keyboard::Keycode::M => Keycode::M,
|
||||
sdl2::keyboard::Keycode::N => Keycode::N,
|
||||
sdl2::keyboard::Keycode::O => Keycode::O,
|
||||
sdl2::keyboard::Keycode::P => Keycode::P,
|
||||
sdl2::keyboard::Keycode::Q => Keycode::Q,
|
||||
sdl2::keyboard::Keycode::R => Keycode::R,
|
||||
sdl2::keyboard::Keycode::S => Keycode::S,
|
||||
sdl2::keyboard::Keycode::T => Keycode::T,
|
||||
sdl2::keyboard::Keycode::U => Keycode::U,
|
||||
sdl2::keyboard::Keycode::V => Keycode::V,
|
||||
sdl2::keyboard::Keycode::W => Keycode::W,
|
||||
sdl2::keyboard::Keycode::X => Keycode::X,
|
||||
sdl2::keyboard::Keycode::Y => Keycode::Y,
|
||||
sdl2::keyboard::Keycode::Z => Keycode::Z,
|
||||
sdl2::keyboard::Keycode::Delete => Keycode::Delete,
|
||||
sdl2::keyboard::Keycode::CapsLock => Keycode::CapsLock,
|
||||
sdl2::keyboard::Keycode::F1 => Keycode::F1,
|
||||
sdl2::keyboard::Keycode::F2 => Keycode::F2,
|
||||
sdl2::keyboard::Keycode::F3 => Keycode::F3,
|
||||
sdl2::keyboard::Keycode::F4 => Keycode::F4,
|
||||
sdl2::keyboard::Keycode::F5 => Keycode::F5,
|
||||
sdl2::keyboard::Keycode::F6 => Keycode::F6,
|
||||
sdl2::keyboard::Keycode::F7 => Keycode::F7,
|
||||
sdl2::keyboard::Keycode::F8 => Keycode::F8,
|
||||
sdl2::keyboard::Keycode::F9 => Keycode::F9,
|
||||
sdl2::keyboard::Keycode::F10 => Keycode::F10,
|
||||
sdl2::keyboard::Keycode::F11 => Keycode::F11,
|
||||
sdl2::keyboard::Keycode::F12 => Keycode::F12,
|
||||
sdl2::keyboard::Keycode::PrintScreen => Keycode::PrintScreen,
|
||||
sdl2::keyboard::Keycode::ScrollLock => Keycode::ScrollLock,
|
||||
sdl2::keyboard::Keycode::Pause => Keycode::Pause,
|
||||
sdl2::keyboard::Keycode::Insert => Keycode::Insert,
|
||||
sdl2::keyboard::Keycode::Home => Keycode::Home,
|
||||
sdl2::keyboard::Keycode::PageUp => Keycode::PageUp,
|
||||
sdl2::keyboard::Keycode::End => Keycode::End,
|
||||
sdl2::keyboard::Keycode::PageDown => Keycode::PageDown,
|
||||
sdl2::keyboard::Keycode::Right => Keycode::Right,
|
||||
sdl2::keyboard::Keycode::Left => Keycode::Left,
|
||||
sdl2::keyboard::Keycode::Down => Keycode::Down,
|
||||
sdl2::keyboard::Keycode::Up => Keycode::Up,
|
||||
sdl2::keyboard::Keycode::NumLockClear => Keycode::NumLockClear,
|
||||
sdl2::keyboard::Keycode::KpDivide => Keycode::KpDivide,
|
||||
sdl2::keyboard::Keycode::KpMultiply => Keycode::KpMultiply,
|
||||
sdl2::keyboard::Keycode::KpMinus => Keycode::KpMinus,
|
||||
sdl2::keyboard::Keycode::KpPlus => Keycode::KpPlus,
|
||||
sdl2::keyboard::Keycode::KpEnter => Keycode::KpEnter,
|
||||
sdl2::keyboard::Keycode::Kp1 => Keycode::Kp1,
|
||||
sdl2::keyboard::Keycode::Kp2 => Keycode::Kp2,
|
||||
sdl2::keyboard::Keycode::Kp3 => Keycode::Kp3,
|
||||
sdl2::keyboard::Keycode::Kp4 => Keycode::Kp4,
|
||||
sdl2::keyboard::Keycode::Kp5 => Keycode::Kp5,
|
||||
sdl2::keyboard::Keycode::Kp6 => Keycode::Kp6,
|
||||
sdl2::keyboard::Keycode::Kp7 => Keycode::Kp7,
|
||||
sdl2::keyboard::Keycode::Kp8 => Keycode::Kp8,
|
||||
sdl2::keyboard::Keycode::Kp9 => Keycode::Kp9,
|
||||
sdl2::keyboard::Keycode::Kp0 => Keycode::Kp0,
|
||||
sdl2::keyboard::Keycode::KpPeriod => Keycode::KpPeriod,
|
||||
sdl2::keyboard::Keycode::Application => Keycode::Application,
|
||||
sdl2::keyboard::Keycode::Power => Keycode::Power,
|
||||
sdl2::keyboard::Keycode::KpEquals => Keycode::KpEquals,
|
||||
sdl2::keyboard::Keycode::F13 => Keycode::F13,
|
||||
sdl2::keyboard::Keycode::F14 => Keycode::F14,
|
||||
sdl2::keyboard::Keycode::F15 => Keycode::F15,
|
||||
sdl2::keyboard::Keycode::F16 => Keycode::F16,
|
||||
sdl2::keyboard::Keycode::F17 => Keycode::F17,
|
||||
sdl2::keyboard::Keycode::F18 => Keycode::F18,
|
||||
sdl2::keyboard::Keycode::F19 => Keycode::F19,
|
||||
sdl2::keyboard::Keycode::F20 => Keycode::F20,
|
||||
sdl2::keyboard::Keycode::F21 => Keycode::F21,
|
||||
sdl2::keyboard::Keycode::F22 => Keycode::F22,
|
||||
sdl2::keyboard::Keycode::F23 => Keycode::F23,
|
||||
sdl2::keyboard::Keycode::F24 => Keycode::F24,
|
||||
sdl2::keyboard::Keycode::Execute => Keycode::Execute,
|
||||
sdl2::keyboard::Keycode::Help => Keycode::Help,
|
||||
sdl2::keyboard::Keycode::Menu => Keycode::Menu,
|
||||
sdl2::keyboard::Keycode::Select => Keycode::Select,
|
||||
sdl2::keyboard::Keycode::Stop => Keycode::Stop,
|
||||
sdl2::keyboard::Keycode::Again => Keycode::Again,
|
||||
sdl2::keyboard::Keycode::Undo => Keycode::Undo,
|
||||
sdl2::keyboard::Keycode::Cut => Keycode::Cut,
|
||||
sdl2::keyboard::Keycode::Copy => Keycode::Copy,
|
||||
sdl2::keyboard::Keycode::Paste => Keycode::Paste,
|
||||
sdl2::keyboard::Keycode::Find => Keycode::Find,
|
||||
sdl2::keyboard::Keycode::Mute => Keycode::Mute,
|
||||
sdl2::keyboard::Keycode::VolumeUp => Keycode::VolumeUp,
|
||||
sdl2::keyboard::Keycode::VolumeDown => Keycode::VolumeDown,
|
||||
sdl2::keyboard::Keycode::KpComma => Keycode::KpComma,
|
||||
sdl2::keyboard::Keycode::KpEqualsAS400 => Keycode::KpEqualsAS400,
|
||||
sdl2::keyboard::Keycode::AltErase => Keycode::AltErase,
|
||||
sdl2::keyboard::Keycode::Sysreq => Keycode::Sysreq,
|
||||
sdl2::keyboard::Keycode::Cancel => Keycode::Cancel,
|
||||
sdl2::keyboard::Keycode::Clear => Keycode::Clear,
|
||||
sdl2::keyboard::Keycode::Prior => Keycode::Prior,
|
||||
sdl2::keyboard::Keycode::Return2 => Keycode::Return2,
|
||||
sdl2::keyboard::Keycode::Separator => Keycode::Separator,
|
||||
sdl2::keyboard::Keycode::Out => Keycode::Out,
|
||||
sdl2::keyboard::Keycode::Oper => Keycode::Oper,
|
||||
sdl2::keyboard::Keycode::ClearAgain => Keycode::ClearAgain,
|
||||
sdl2::keyboard::Keycode::CrSel => Keycode::CrSel,
|
||||
sdl2::keyboard::Keycode::ExSel => Keycode::ExSel,
|
||||
sdl2::keyboard::Keycode::Kp00 => Keycode::Kp00,
|
||||
sdl2::keyboard::Keycode::Kp000 => Keycode::Kp000,
|
||||
sdl2::keyboard::Keycode::ThousandsSeparator => Keycode::ThousandsSeparator,
|
||||
sdl2::keyboard::Keycode::DecimalSeparator => Keycode::DecimalSeparator,
|
||||
sdl2::keyboard::Keycode::CurrencyUnit => Keycode::CurrencyUnit,
|
||||
sdl2::keyboard::Keycode::CurrencySubUnit => Keycode::CurrencySubUnit,
|
||||
sdl2::keyboard::Keycode::KpLeftParen => Keycode::KpLeftParen,
|
||||
sdl2::keyboard::Keycode::KpRightParen => Keycode::KpRightParen,
|
||||
sdl2::keyboard::Keycode::KpLeftBrace => Keycode::KpLeftBrace,
|
||||
sdl2::keyboard::Keycode::KpRightBrace => Keycode::KpRightBrace,
|
||||
sdl2::keyboard::Keycode::KpTab => Keycode::KpTab,
|
||||
sdl2::keyboard::Keycode::KpBackspace => Keycode::KpBackspace,
|
||||
sdl2::keyboard::Keycode::KpA => Keycode::KpA,
|
||||
sdl2::keyboard::Keycode::KpB => Keycode::KpB,
|
||||
sdl2::keyboard::Keycode::KpC => Keycode::KpC,
|
||||
sdl2::keyboard::Keycode::KpD => Keycode::KpD,
|
||||
sdl2::keyboard::Keycode::KpE => Keycode::KpE,
|
||||
sdl2::keyboard::Keycode::KpF => Keycode::KpF,
|
||||
sdl2::keyboard::Keycode::KpXor => Keycode::KpXor,
|
||||
sdl2::keyboard::Keycode::KpPower => Keycode::KpPower,
|
||||
sdl2::keyboard::Keycode::KpPercent => Keycode::KpPercent,
|
||||
sdl2::keyboard::Keycode::KpLess => Keycode::KpLess,
|
||||
sdl2::keyboard::Keycode::KpGreater => Keycode::KpGreater,
|
||||
sdl2::keyboard::Keycode::KpAmpersand => Keycode::KpAmpersand,
|
||||
sdl2::keyboard::Keycode::KpDblAmpersand => Keycode::KpDblAmpersand,
|
||||
sdl2::keyboard::Keycode::KpVerticalBar => Keycode::KpVerticalBar,
|
||||
sdl2::keyboard::Keycode::KpDblVerticalBar => Keycode::KpDblVerticalBar,
|
||||
sdl2::keyboard::Keycode::KpColon => Keycode::KpColon,
|
||||
sdl2::keyboard::Keycode::KpHash => Keycode::KpHash,
|
||||
sdl2::keyboard::Keycode::KpSpace => Keycode::KpSpace,
|
||||
sdl2::keyboard::Keycode::KpAt => Keycode::KpAt,
|
||||
sdl2::keyboard::Keycode::KpExclam => Keycode::KpExclam,
|
||||
sdl2::keyboard::Keycode::KpMemStore => Keycode::KpMemStore,
|
||||
sdl2::keyboard::Keycode::KpMemRecall => Keycode::KpMemRecall,
|
||||
sdl2::keyboard::Keycode::KpMemClear => Keycode::KpMemClear,
|
||||
sdl2::keyboard::Keycode::KpMemAdd => Keycode::KpMemAdd,
|
||||
sdl2::keyboard::Keycode::KpMemSubtract => Keycode::KpMemSubtract,
|
||||
sdl2::keyboard::Keycode::KpMemMultiply => Keycode::KpMemMultiply,
|
||||
sdl2::keyboard::Keycode::KpMemDivide => Keycode::KpMemDivide,
|
||||
sdl2::keyboard::Keycode::KpPlusMinus => Keycode::KpPlusMinus,
|
||||
sdl2::keyboard::Keycode::KpClear => Keycode::KpClear,
|
||||
sdl2::keyboard::Keycode::KpClearEntry => Keycode::KpClearEntry,
|
||||
sdl2::keyboard::Keycode::KpBinary => Keycode::KpBinary,
|
||||
sdl2::keyboard::Keycode::KpOctal => Keycode::KpOctal,
|
||||
sdl2::keyboard::Keycode::KpDecimal => Keycode::KpDecimal,
|
||||
sdl2::keyboard::Keycode::KpHexadecimal => Keycode::KpHexadecimal,
|
||||
sdl2::keyboard::Keycode::LCtrl => Keycode::LCtrl,
|
||||
sdl2::keyboard::Keycode::LShift => Keycode::LShift,
|
||||
sdl2::keyboard::Keycode::LAlt => Keycode::LAlt,
|
||||
sdl2::keyboard::Keycode::LGui => Keycode::LGui,
|
||||
sdl2::keyboard::Keycode::RCtrl => Keycode::RCtrl,
|
||||
sdl2::keyboard::Keycode::RShift => Keycode::RShift,
|
||||
sdl2::keyboard::Keycode::RAlt => Keycode::RAlt,
|
||||
sdl2::keyboard::Keycode::RGui => Keycode::RGui,
|
||||
sdl2::keyboard::Keycode::Mode => Keycode::Mode,
|
||||
sdl2::keyboard::Keycode::AudioNext => Keycode::AudioNext,
|
||||
sdl2::keyboard::Keycode::AudioPrev => Keycode::AudioPrev,
|
||||
sdl2::keyboard::Keycode::AudioStop => Keycode::AudioStop,
|
||||
sdl2::keyboard::Keycode::AudioPlay => Keycode::AudioPlay,
|
||||
sdl2::keyboard::Keycode::AudioMute => Keycode::AudioMute,
|
||||
sdl2::keyboard::Keycode::MediaSelect => Keycode::MediaSelect,
|
||||
sdl2::keyboard::Keycode::Www => Keycode::Www,
|
||||
sdl2::keyboard::Keycode::Mail => Keycode::Mail,
|
||||
sdl2::keyboard::Keycode::Calculator => Keycode::Calculator,
|
||||
sdl2::keyboard::Keycode::Computer => Keycode::Computer,
|
||||
sdl2::keyboard::Keycode::AcSearch => Keycode::AcSearch,
|
||||
sdl2::keyboard::Keycode::AcHome => Keycode::AcHome,
|
||||
sdl2::keyboard::Keycode::AcBack => Keycode::AcBack,
|
||||
sdl2::keyboard::Keycode::AcForward => Keycode::AcForward,
|
||||
sdl2::keyboard::Keycode::AcStop => Keycode::AcStop,
|
||||
sdl2::keyboard::Keycode::AcRefresh => Keycode::AcRefresh,
|
||||
sdl2::keyboard::Keycode::AcBookmarks => Keycode::AcBookmarks,
|
||||
sdl2::keyboard::Keycode::BrightnessDown => Keycode::BrightnessDown,
|
||||
sdl2::keyboard::Keycode::BrightnessUp => Keycode::BrightnessUp,
|
||||
sdl2::keyboard::Keycode::DisplaySwitch => Keycode::DisplaySwitch,
|
||||
sdl2::keyboard::Keycode::KbdIllumToggle => Keycode::KbdIllumToggle,
|
||||
sdl2::keyboard::Keycode::KbdIllumDown => Keycode::KbdIllumDown,
|
||||
sdl2::keyboard::Keycode::KbdIllumUp => Keycode::KbdIllumUp,
|
||||
sdl2::keyboard::Keycode::Eject => Keycode::Eject,
|
||||
sdl2::keyboard::Keycode::Sleep => Keycode::Sleep,
|
||||
}
|
||||
}
|
||||
fn from(value: sdl2::keyboard::Keycode) -> Self {
|
||||
match value {
|
||||
sdl2::keyboard::Keycode::Backspace => Keycode::Backspace,
|
||||
sdl2::keyboard::Keycode::Tab => Keycode::Tab,
|
||||
sdl2::keyboard::Keycode::Return => Keycode::Return,
|
||||
sdl2::keyboard::Keycode::Escape => Keycode::Escape,
|
||||
sdl2::keyboard::Keycode::Space => Keycode::Space,
|
||||
sdl2::keyboard::Keycode::Exclaim => Keycode::Exclaim,
|
||||
sdl2::keyboard::Keycode::Quotedbl => Keycode::Quotedbl,
|
||||
sdl2::keyboard::Keycode::Hash => Keycode::Hash,
|
||||
sdl2::keyboard::Keycode::Dollar => Keycode::Dollar,
|
||||
sdl2::keyboard::Keycode::Percent => Keycode::Percent,
|
||||
sdl2::keyboard::Keycode::Ampersand => Keycode::Ampersand,
|
||||
sdl2::keyboard::Keycode::Quote => Keycode::Quote,
|
||||
sdl2::keyboard::Keycode::LeftParen => Keycode::LeftParen,
|
||||
sdl2::keyboard::Keycode::RightParen => Keycode::RightParen,
|
||||
sdl2::keyboard::Keycode::Asterisk => Keycode::Asterisk,
|
||||
sdl2::keyboard::Keycode::Plus => Keycode::Plus,
|
||||
sdl2::keyboard::Keycode::Comma => Keycode::Comma,
|
||||
sdl2::keyboard::Keycode::Minus => Keycode::Minus,
|
||||
sdl2::keyboard::Keycode::Period => Keycode::Period,
|
||||
sdl2::keyboard::Keycode::Slash => Keycode::Slash,
|
||||
sdl2::keyboard::Keycode::Num0 => Keycode::Num0,
|
||||
sdl2::keyboard::Keycode::Num1 => Keycode::Num1,
|
||||
sdl2::keyboard::Keycode::Num2 => Keycode::Num2,
|
||||
sdl2::keyboard::Keycode::Num3 => Keycode::Num3,
|
||||
sdl2::keyboard::Keycode::Num4 => Keycode::Num4,
|
||||
sdl2::keyboard::Keycode::Num5 => Keycode::Num5,
|
||||
sdl2::keyboard::Keycode::Num6 => Keycode::Num6,
|
||||
sdl2::keyboard::Keycode::Num7 => Keycode::Num7,
|
||||
sdl2::keyboard::Keycode::Num8 => Keycode::Num8,
|
||||
sdl2::keyboard::Keycode::Num9 => Keycode::Num9,
|
||||
sdl2::keyboard::Keycode::Colon => Keycode::Colon,
|
||||
sdl2::keyboard::Keycode::Semicolon => Keycode::Semicolon,
|
||||
sdl2::keyboard::Keycode::Less => Keycode::Less,
|
||||
sdl2::keyboard::Keycode::Equals => Keycode::Equals,
|
||||
sdl2::keyboard::Keycode::Greater => Keycode::Greater,
|
||||
sdl2::keyboard::Keycode::Question => Keycode::Question,
|
||||
sdl2::keyboard::Keycode::At => Keycode::At,
|
||||
sdl2::keyboard::Keycode::LeftBracket => Keycode::LeftBracket,
|
||||
sdl2::keyboard::Keycode::Backslash => Keycode::Backslash,
|
||||
sdl2::keyboard::Keycode::RightBracket => Keycode::RightBracket,
|
||||
sdl2::keyboard::Keycode::Caret => Keycode::Caret,
|
||||
sdl2::keyboard::Keycode::Underscore => Keycode::Underscore,
|
||||
sdl2::keyboard::Keycode::Backquote => Keycode::Backquote,
|
||||
sdl2::keyboard::Keycode::A => Keycode::A,
|
||||
sdl2::keyboard::Keycode::B => Keycode::B,
|
||||
sdl2::keyboard::Keycode::C => Keycode::C,
|
||||
sdl2::keyboard::Keycode::D => Keycode::D,
|
||||
sdl2::keyboard::Keycode::E => Keycode::E,
|
||||
sdl2::keyboard::Keycode::F => Keycode::F,
|
||||
sdl2::keyboard::Keycode::G => Keycode::G,
|
||||
sdl2::keyboard::Keycode::H => Keycode::H,
|
||||
sdl2::keyboard::Keycode::I => Keycode::I,
|
||||
sdl2::keyboard::Keycode::J => Keycode::J,
|
||||
sdl2::keyboard::Keycode::K => Keycode::K,
|
||||
sdl2::keyboard::Keycode::L => Keycode::L,
|
||||
sdl2::keyboard::Keycode::M => Keycode::M,
|
||||
sdl2::keyboard::Keycode::N => Keycode::N,
|
||||
sdl2::keyboard::Keycode::O => Keycode::O,
|
||||
sdl2::keyboard::Keycode::P => Keycode::P,
|
||||
sdl2::keyboard::Keycode::Q => Keycode::Q,
|
||||
sdl2::keyboard::Keycode::R => Keycode::R,
|
||||
sdl2::keyboard::Keycode::S => Keycode::S,
|
||||
sdl2::keyboard::Keycode::T => Keycode::T,
|
||||
sdl2::keyboard::Keycode::U => Keycode::U,
|
||||
sdl2::keyboard::Keycode::V => Keycode::V,
|
||||
sdl2::keyboard::Keycode::W => Keycode::W,
|
||||
sdl2::keyboard::Keycode::X => Keycode::X,
|
||||
sdl2::keyboard::Keycode::Y => Keycode::Y,
|
||||
sdl2::keyboard::Keycode::Z => Keycode::Z,
|
||||
sdl2::keyboard::Keycode::Delete => Keycode::Delete,
|
||||
sdl2::keyboard::Keycode::CapsLock => Keycode::CapsLock,
|
||||
sdl2::keyboard::Keycode::F1 => Keycode::F1,
|
||||
sdl2::keyboard::Keycode::F2 => Keycode::F2,
|
||||
sdl2::keyboard::Keycode::F3 => Keycode::F3,
|
||||
sdl2::keyboard::Keycode::F4 => Keycode::F4,
|
||||
sdl2::keyboard::Keycode::F5 => Keycode::F5,
|
||||
sdl2::keyboard::Keycode::F6 => Keycode::F6,
|
||||
sdl2::keyboard::Keycode::F7 => Keycode::F7,
|
||||
sdl2::keyboard::Keycode::F8 => Keycode::F8,
|
||||
sdl2::keyboard::Keycode::F9 => Keycode::F9,
|
||||
sdl2::keyboard::Keycode::F10 => Keycode::F10,
|
||||
sdl2::keyboard::Keycode::F11 => Keycode::F11,
|
||||
sdl2::keyboard::Keycode::F12 => Keycode::F12,
|
||||
sdl2::keyboard::Keycode::PrintScreen => Keycode::PrintScreen,
|
||||
sdl2::keyboard::Keycode::ScrollLock => Keycode::ScrollLock,
|
||||
sdl2::keyboard::Keycode::Pause => Keycode::Pause,
|
||||
sdl2::keyboard::Keycode::Insert => Keycode::Insert,
|
||||
sdl2::keyboard::Keycode::Home => Keycode::Home,
|
||||
sdl2::keyboard::Keycode::PageUp => Keycode::PageUp,
|
||||
sdl2::keyboard::Keycode::End => Keycode::End,
|
||||
sdl2::keyboard::Keycode::PageDown => Keycode::PageDown,
|
||||
sdl2::keyboard::Keycode::Right => Keycode::Right,
|
||||
sdl2::keyboard::Keycode::Left => Keycode::Left,
|
||||
sdl2::keyboard::Keycode::Down => Keycode::Down,
|
||||
sdl2::keyboard::Keycode::Up => Keycode::Up,
|
||||
sdl2::keyboard::Keycode::NumLockClear => Keycode::NumLockClear,
|
||||
sdl2::keyboard::Keycode::KpDivide => Keycode::KpDivide,
|
||||
sdl2::keyboard::Keycode::KpMultiply => Keycode::KpMultiply,
|
||||
sdl2::keyboard::Keycode::KpMinus => Keycode::KpMinus,
|
||||
sdl2::keyboard::Keycode::KpPlus => Keycode::KpPlus,
|
||||
sdl2::keyboard::Keycode::KpEnter => Keycode::KpEnter,
|
||||
sdl2::keyboard::Keycode::Kp1 => Keycode::Kp1,
|
||||
sdl2::keyboard::Keycode::Kp2 => Keycode::Kp2,
|
||||
sdl2::keyboard::Keycode::Kp3 => Keycode::Kp3,
|
||||
sdl2::keyboard::Keycode::Kp4 => Keycode::Kp4,
|
||||
sdl2::keyboard::Keycode::Kp5 => Keycode::Kp5,
|
||||
sdl2::keyboard::Keycode::Kp6 => Keycode::Kp6,
|
||||
sdl2::keyboard::Keycode::Kp7 => Keycode::Kp7,
|
||||
sdl2::keyboard::Keycode::Kp8 => Keycode::Kp8,
|
||||
sdl2::keyboard::Keycode::Kp9 => Keycode::Kp9,
|
||||
sdl2::keyboard::Keycode::Kp0 => Keycode::Kp0,
|
||||
sdl2::keyboard::Keycode::KpPeriod => Keycode::KpPeriod,
|
||||
sdl2::keyboard::Keycode::Application => Keycode::Application,
|
||||
sdl2::keyboard::Keycode::Power => Keycode::Power,
|
||||
sdl2::keyboard::Keycode::KpEquals => Keycode::KpEquals,
|
||||
sdl2::keyboard::Keycode::F13 => Keycode::F13,
|
||||
sdl2::keyboard::Keycode::F14 => Keycode::F14,
|
||||
sdl2::keyboard::Keycode::F15 => Keycode::F15,
|
||||
sdl2::keyboard::Keycode::F16 => Keycode::F16,
|
||||
sdl2::keyboard::Keycode::F17 => Keycode::F17,
|
||||
sdl2::keyboard::Keycode::F18 => Keycode::F18,
|
||||
sdl2::keyboard::Keycode::F19 => Keycode::F19,
|
||||
sdl2::keyboard::Keycode::F20 => Keycode::F20,
|
||||
sdl2::keyboard::Keycode::F21 => Keycode::F21,
|
||||
sdl2::keyboard::Keycode::F22 => Keycode::F22,
|
||||
sdl2::keyboard::Keycode::F23 => Keycode::F23,
|
||||
sdl2::keyboard::Keycode::F24 => Keycode::F24,
|
||||
sdl2::keyboard::Keycode::Execute => Keycode::Execute,
|
||||
sdl2::keyboard::Keycode::Help => Keycode::Help,
|
||||
sdl2::keyboard::Keycode::Menu => Keycode::Menu,
|
||||
sdl2::keyboard::Keycode::Select => Keycode::Select,
|
||||
sdl2::keyboard::Keycode::Stop => Keycode::Stop,
|
||||
sdl2::keyboard::Keycode::Again => Keycode::Again,
|
||||
sdl2::keyboard::Keycode::Undo => Keycode::Undo,
|
||||
sdl2::keyboard::Keycode::Cut => Keycode::Cut,
|
||||
sdl2::keyboard::Keycode::Copy => Keycode::Copy,
|
||||
sdl2::keyboard::Keycode::Paste => Keycode::Paste,
|
||||
sdl2::keyboard::Keycode::Find => Keycode::Find,
|
||||
sdl2::keyboard::Keycode::Mute => Keycode::Mute,
|
||||
sdl2::keyboard::Keycode::VolumeUp => Keycode::VolumeUp,
|
||||
sdl2::keyboard::Keycode::VolumeDown => Keycode::VolumeDown,
|
||||
sdl2::keyboard::Keycode::KpComma => Keycode::KpComma,
|
||||
sdl2::keyboard::Keycode::KpEqualsAS400 => Keycode::KpEqualsAS400,
|
||||
sdl2::keyboard::Keycode::AltErase => Keycode::AltErase,
|
||||
sdl2::keyboard::Keycode::Sysreq => Keycode::Sysreq,
|
||||
sdl2::keyboard::Keycode::Cancel => Keycode::Cancel,
|
||||
sdl2::keyboard::Keycode::Clear => Keycode::Clear,
|
||||
sdl2::keyboard::Keycode::Prior => Keycode::Prior,
|
||||
sdl2::keyboard::Keycode::Return2 => Keycode::Return2,
|
||||
sdl2::keyboard::Keycode::Separator => Keycode::Separator,
|
||||
sdl2::keyboard::Keycode::Out => Keycode::Out,
|
||||
sdl2::keyboard::Keycode::Oper => Keycode::Oper,
|
||||
sdl2::keyboard::Keycode::ClearAgain => Keycode::ClearAgain,
|
||||
sdl2::keyboard::Keycode::CrSel => Keycode::CrSel,
|
||||
sdl2::keyboard::Keycode::ExSel => Keycode::ExSel,
|
||||
sdl2::keyboard::Keycode::Kp00 => Keycode::Kp00,
|
||||
sdl2::keyboard::Keycode::Kp000 => Keycode::Kp000,
|
||||
sdl2::keyboard::Keycode::ThousandsSeparator => Keycode::ThousandsSeparator,
|
||||
sdl2::keyboard::Keycode::DecimalSeparator => Keycode::DecimalSeparator,
|
||||
sdl2::keyboard::Keycode::CurrencyUnit => Keycode::CurrencyUnit,
|
||||
sdl2::keyboard::Keycode::CurrencySubUnit => Keycode::CurrencySubUnit,
|
||||
sdl2::keyboard::Keycode::KpLeftParen => Keycode::KpLeftParen,
|
||||
sdl2::keyboard::Keycode::KpRightParen => Keycode::KpRightParen,
|
||||
sdl2::keyboard::Keycode::KpLeftBrace => Keycode::KpLeftBrace,
|
||||
sdl2::keyboard::Keycode::KpRightBrace => Keycode::KpRightBrace,
|
||||
sdl2::keyboard::Keycode::KpTab => Keycode::KpTab,
|
||||
sdl2::keyboard::Keycode::KpBackspace => Keycode::KpBackspace,
|
||||
sdl2::keyboard::Keycode::KpA => Keycode::KpA,
|
||||
sdl2::keyboard::Keycode::KpB => Keycode::KpB,
|
||||
sdl2::keyboard::Keycode::KpC => Keycode::KpC,
|
||||
sdl2::keyboard::Keycode::KpD => Keycode::KpD,
|
||||
sdl2::keyboard::Keycode::KpE => Keycode::KpE,
|
||||
sdl2::keyboard::Keycode::KpF => Keycode::KpF,
|
||||
sdl2::keyboard::Keycode::KpXor => Keycode::KpXor,
|
||||
sdl2::keyboard::Keycode::KpPower => Keycode::KpPower,
|
||||
sdl2::keyboard::Keycode::KpPercent => Keycode::KpPercent,
|
||||
sdl2::keyboard::Keycode::KpLess => Keycode::KpLess,
|
||||
sdl2::keyboard::Keycode::KpGreater => Keycode::KpGreater,
|
||||
sdl2::keyboard::Keycode::KpAmpersand => Keycode::KpAmpersand,
|
||||
sdl2::keyboard::Keycode::KpDblAmpersand => Keycode::KpDblAmpersand,
|
||||
sdl2::keyboard::Keycode::KpVerticalBar => Keycode::KpVerticalBar,
|
||||
sdl2::keyboard::Keycode::KpDblVerticalBar => Keycode::KpDblVerticalBar,
|
||||
sdl2::keyboard::Keycode::KpColon => Keycode::KpColon,
|
||||
sdl2::keyboard::Keycode::KpHash => Keycode::KpHash,
|
||||
sdl2::keyboard::Keycode::KpSpace => Keycode::KpSpace,
|
||||
sdl2::keyboard::Keycode::KpAt => Keycode::KpAt,
|
||||
sdl2::keyboard::Keycode::KpExclam => Keycode::KpExclam,
|
||||
sdl2::keyboard::Keycode::KpMemStore => Keycode::KpMemStore,
|
||||
sdl2::keyboard::Keycode::KpMemRecall => Keycode::KpMemRecall,
|
||||
sdl2::keyboard::Keycode::KpMemClear => Keycode::KpMemClear,
|
||||
sdl2::keyboard::Keycode::KpMemAdd => Keycode::KpMemAdd,
|
||||
sdl2::keyboard::Keycode::KpMemSubtract => Keycode::KpMemSubtract,
|
||||
sdl2::keyboard::Keycode::KpMemMultiply => Keycode::KpMemMultiply,
|
||||
sdl2::keyboard::Keycode::KpMemDivide => Keycode::KpMemDivide,
|
||||
sdl2::keyboard::Keycode::KpPlusMinus => Keycode::KpPlusMinus,
|
||||
sdl2::keyboard::Keycode::KpClear => Keycode::KpClear,
|
||||
sdl2::keyboard::Keycode::KpClearEntry => Keycode::KpClearEntry,
|
||||
sdl2::keyboard::Keycode::KpBinary => Keycode::KpBinary,
|
||||
sdl2::keyboard::Keycode::KpOctal => Keycode::KpOctal,
|
||||
sdl2::keyboard::Keycode::KpDecimal => Keycode::KpDecimal,
|
||||
sdl2::keyboard::Keycode::KpHexadecimal => Keycode::KpHexadecimal,
|
||||
sdl2::keyboard::Keycode::LCtrl => Keycode::LCtrl,
|
||||
sdl2::keyboard::Keycode::LShift => Keycode::LShift,
|
||||
sdl2::keyboard::Keycode::LAlt => Keycode::LAlt,
|
||||
sdl2::keyboard::Keycode::LGui => Keycode::LGui,
|
||||
sdl2::keyboard::Keycode::RCtrl => Keycode::RCtrl,
|
||||
sdl2::keyboard::Keycode::RShift => Keycode::RShift,
|
||||
sdl2::keyboard::Keycode::RAlt => Keycode::RAlt,
|
||||
sdl2::keyboard::Keycode::RGui => Keycode::RGui,
|
||||
sdl2::keyboard::Keycode::Mode => Keycode::Mode,
|
||||
sdl2::keyboard::Keycode::AudioNext => Keycode::AudioNext,
|
||||
sdl2::keyboard::Keycode::AudioPrev => Keycode::AudioPrev,
|
||||
sdl2::keyboard::Keycode::AudioStop => Keycode::AudioStop,
|
||||
sdl2::keyboard::Keycode::AudioPlay => Keycode::AudioPlay,
|
||||
sdl2::keyboard::Keycode::AudioMute => Keycode::AudioMute,
|
||||
sdl2::keyboard::Keycode::MediaSelect => Keycode::MediaSelect,
|
||||
sdl2::keyboard::Keycode::Www => Keycode::Www,
|
||||
sdl2::keyboard::Keycode::Mail => Keycode::Mail,
|
||||
sdl2::keyboard::Keycode::Calculator => Keycode::Calculator,
|
||||
sdl2::keyboard::Keycode::Computer => Keycode::Computer,
|
||||
sdl2::keyboard::Keycode::AcSearch => Keycode::AcSearch,
|
||||
sdl2::keyboard::Keycode::AcHome => Keycode::AcHome,
|
||||
sdl2::keyboard::Keycode::AcBack => Keycode::AcBack,
|
||||
sdl2::keyboard::Keycode::AcForward => Keycode::AcForward,
|
||||
sdl2::keyboard::Keycode::AcStop => Keycode::AcStop,
|
||||
sdl2::keyboard::Keycode::AcRefresh => Keycode::AcRefresh,
|
||||
sdl2::keyboard::Keycode::AcBookmarks => Keycode::AcBookmarks,
|
||||
sdl2::keyboard::Keycode::BrightnessDown => Keycode::BrightnessDown,
|
||||
sdl2::keyboard::Keycode::BrightnessUp => Keycode::BrightnessUp,
|
||||
sdl2::keyboard::Keycode::DisplaySwitch => Keycode::DisplaySwitch,
|
||||
sdl2::keyboard::Keycode::KbdIllumToggle => Keycode::KbdIllumToggle,
|
||||
sdl2::keyboard::Keycode::KbdIllumDown => Keycode::KbdIllumDown,
|
||||
sdl2::keyboard::Keycode::KbdIllumUp => Keycode::KbdIllumUp,
|
||||
sdl2::keyboard::Keycode::Eject => Keycode::Eject,
|
||||
sdl2::keyboard::Keycode::Sleep => Keycode::Sleep,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,85 +19,85 @@ const MAX_KEYS: usize = 256;
|
|||
/// [`System`]: crate::System
|
||||
#[derive(Debug)]
|
||||
pub struct Keyboard {
|
||||
keyboard: [ButtonState; MAX_KEYS], // Box<[ButtonState]>,
|
||||
keyboard: [ButtonState; MAX_KEYS], // Box<[ButtonState]>,
|
||||
}
|
||||
|
||||
impl Keyboard {
|
||||
pub fn new() -> Keyboard {
|
||||
Keyboard {
|
||||
keyboard: [ButtonState::Idle; MAX_KEYS],
|
||||
}
|
||||
/*
|
||||
Keyboard {
|
||||
keyboard: vec![ButtonState::Idle; 256].into_boxed_slice(),
|
||||
}
|
||||
*/
|
||||
}
|
||||
pub fn new() -> Keyboard {
|
||||
Keyboard {
|
||||
keyboard: [ButtonState::Idle; MAX_KEYS],
|
||||
}
|
||||
/*
|
||||
Keyboard {
|
||||
keyboard: vec![ButtonState::Idle; 256].into_boxed_slice(),
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/// Returns true if the given key was just pressed or is being held down.
|
||||
#[inline]
|
||||
pub fn is_key_down(&self, scancode: Scancode) -> bool {
|
||||
matches!(
|
||||
/// Returns true if the given key was just pressed or is being held down.
|
||||
#[inline]
|
||||
pub fn is_key_down(&self, scancode: Scancode) -> bool {
|
||||
matches!(
|
||||
self.keyboard[scancode as usize],
|
||||
ButtonState::Pressed | ButtonState::Held
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given key was not just pressed and is not being held down.
|
||||
#[inline]
|
||||
pub fn is_key_up(&self, scancode: Scancode) -> bool {
|
||||
matches!(
|
||||
/// Returns true if the given key was not just pressed and is not being held down.
|
||||
#[inline]
|
||||
pub fn is_key_up(&self, scancode: Scancode) -> bool {
|
||||
matches!(
|
||||
self.keyboard[scancode as usize],
|
||||
ButtonState::Released | ButtonState::Idle
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given key was just pressed (not being held down, yet).
|
||||
#[inline]
|
||||
pub fn is_key_pressed(&self, scancode: Scancode) -> bool {
|
||||
self.keyboard[scancode as usize] == ButtonState::Pressed
|
||||
}
|
||||
/// Returns true if the given key was just pressed (not being held down, yet).
|
||||
#[inline]
|
||||
pub fn is_key_pressed(&self, scancode: Scancode) -> bool {
|
||||
self.keyboard[scancode as usize] == ButtonState::Pressed
|
||||
}
|
||||
|
||||
/// Returns true if the given key was just released.
|
||||
#[inline]
|
||||
pub fn is_key_released(&self, scancode: Scancode) -> bool {
|
||||
self.keyboard[scancode as usize] == ButtonState::Released
|
||||
}
|
||||
/// Returns true if the given key was just released.
|
||||
#[inline]
|
||||
pub fn is_key_released(&self, scancode: Scancode) -> bool {
|
||||
self.keyboard[scancode as usize] == ButtonState::Released
|
||||
}
|
||||
}
|
||||
|
||||
impl InputDevice for Keyboard {
|
||||
fn update(&mut self) {
|
||||
for state in self.keyboard.iter_mut() {
|
||||
*state = match *state {
|
||||
ButtonState::Pressed => ButtonState::Held,
|
||||
ButtonState::Released => ButtonState::Idle,
|
||||
otherwise => otherwise,
|
||||
};
|
||||
}
|
||||
}
|
||||
fn update(&mut self) {
|
||||
for state in self.keyboard.iter_mut() {
|
||||
*state = match *state {
|
||||
ButtonState::Pressed => ButtonState::Held,
|
||||
ButtonState::Released => ButtonState::Idle,
|
||||
otherwise => otherwise,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemEventHandler for Keyboard {
|
||||
fn handle_event(&mut self, event: &SystemEvent) -> bool {
|
||||
match event {
|
||||
SystemEvent::Keyboard(KeyboardEvent::KeyDown { scancode, .. }) => {
|
||||
if let Some(scancode) = scancode {
|
||||
let state = &mut self.keyboard[*scancode as usize];
|
||||
*state = match *state {
|
||||
ButtonState::Pressed => ButtonState::Held,
|
||||
ButtonState::Held => ButtonState::Held,
|
||||
_ => ButtonState::Pressed,
|
||||
};
|
||||
}
|
||||
true
|
||||
}
|
||||
SystemEvent::Keyboard(KeyboardEvent::KeyUp { scancode, .. }) => {
|
||||
if let Some(scancode) = scancode {
|
||||
self.keyboard[*scancode as usize] = ButtonState::Released;
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
fn handle_event(&mut self, event: &SystemEvent) -> bool {
|
||||
match event {
|
||||
SystemEvent::Keyboard(KeyboardEvent::KeyDown { scancode, .. }) => {
|
||||
if let Some(scancode) = scancode {
|
||||
let state = &mut self.keyboard[*scancode as usize];
|
||||
*state = match *state {
|
||||
ButtonState::Pressed => ButtonState::Held,
|
||||
ButtonState::Held => ButtonState::Held,
|
||||
_ => ButtonState::Pressed,
|
||||
};
|
||||
}
|
||||
true
|
||||
}
|
||||
SystemEvent::Keyboard(KeyboardEvent::KeyUp { scancode, .. }) => {
|
||||
if let Some(scancode) = scancode {
|
||||
self.keyboard[*scancode as usize] = ButtonState::Released;
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,493 +3,493 @@
|
|||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[repr(i32)]
|
||||
pub enum Scancode {
|
||||
A = sdl2::keyboard::Scancode::A as i32,
|
||||
B = sdl2::keyboard::Scancode::B as i32,
|
||||
C = sdl2::keyboard::Scancode::C as i32,
|
||||
D = sdl2::keyboard::Scancode::D as i32,
|
||||
E = sdl2::keyboard::Scancode::E as i32,
|
||||
F = sdl2::keyboard::Scancode::F as i32,
|
||||
G = sdl2::keyboard::Scancode::G as i32,
|
||||
H = sdl2::keyboard::Scancode::H as i32,
|
||||
I = sdl2::keyboard::Scancode::I as i32,
|
||||
J = sdl2::keyboard::Scancode::J as i32,
|
||||
K = sdl2::keyboard::Scancode::K as i32,
|
||||
L = sdl2::keyboard::Scancode::L as i32,
|
||||
M = sdl2::keyboard::Scancode::M as i32,
|
||||
N = sdl2::keyboard::Scancode::N as i32,
|
||||
O = sdl2::keyboard::Scancode::O as i32,
|
||||
P = sdl2::keyboard::Scancode::P as i32,
|
||||
Q = sdl2::keyboard::Scancode::Q as i32,
|
||||
R = sdl2::keyboard::Scancode::R as i32,
|
||||
S = sdl2::keyboard::Scancode::S as i32,
|
||||
T = sdl2::keyboard::Scancode::T as i32,
|
||||
U = sdl2::keyboard::Scancode::U as i32,
|
||||
V = sdl2::keyboard::Scancode::V as i32,
|
||||
W = sdl2::keyboard::Scancode::W as i32,
|
||||
X = sdl2::keyboard::Scancode::X as i32,
|
||||
Y = sdl2::keyboard::Scancode::Y as i32,
|
||||
Z = sdl2::keyboard::Scancode::Z as i32,
|
||||
Num1 = sdl2::keyboard::Scancode::Num1 as i32,
|
||||
Num2 = sdl2::keyboard::Scancode::Num2 as i32,
|
||||
Num3 = sdl2::keyboard::Scancode::Num3 as i32,
|
||||
Num4 = sdl2::keyboard::Scancode::Num4 as i32,
|
||||
Num5 = sdl2::keyboard::Scancode::Num5 as i32,
|
||||
Num6 = sdl2::keyboard::Scancode::Num6 as i32,
|
||||
Num7 = sdl2::keyboard::Scancode::Num7 as i32,
|
||||
Num8 = sdl2::keyboard::Scancode::Num8 as i32,
|
||||
Num9 = sdl2::keyboard::Scancode::Num9 as i32,
|
||||
Num0 = sdl2::keyboard::Scancode::Num0 as i32,
|
||||
Return = sdl2::keyboard::Scancode::Return as i32,
|
||||
Escape = sdl2::keyboard::Scancode::Escape as i32,
|
||||
Backspace = sdl2::keyboard::Scancode::Backspace as i32,
|
||||
Tab = sdl2::keyboard::Scancode::Tab as i32,
|
||||
Space = sdl2::keyboard::Scancode::Space as i32,
|
||||
Minus = sdl2::keyboard::Scancode::Minus as i32,
|
||||
Equals = sdl2::keyboard::Scancode::Equals as i32,
|
||||
LeftBracket = sdl2::keyboard::Scancode::LeftBracket as i32,
|
||||
RightBracket = sdl2::keyboard::Scancode::RightBracket as i32,
|
||||
Backslash = sdl2::keyboard::Scancode::Backslash as i32,
|
||||
NonUsHash = sdl2::keyboard::Scancode::NonUsHash as i32,
|
||||
Semicolon = sdl2::keyboard::Scancode::Semicolon as i32,
|
||||
Apostrophe = sdl2::keyboard::Scancode::Apostrophe as i32,
|
||||
Grave = sdl2::keyboard::Scancode::Grave as i32,
|
||||
Comma = sdl2::keyboard::Scancode::Comma as i32,
|
||||
Period = sdl2::keyboard::Scancode::Period as i32,
|
||||
Slash = sdl2::keyboard::Scancode::Slash as i32,
|
||||
CapsLock = sdl2::keyboard::Scancode::CapsLock as i32,
|
||||
F1 = sdl2::keyboard::Scancode::F1 as i32,
|
||||
F2 = sdl2::keyboard::Scancode::F2 as i32,
|
||||
F3 = sdl2::keyboard::Scancode::F3 as i32,
|
||||
F4 = sdl2::keyboard::Scancode::F4 as i32,
|
||||
F5 = sdl2::keyboard::Scancode::F5 as i32,
|
||||
F6 = sdl2::keyboard::Scancode::F6 as i32,
|
||||
F7 = sdl2::keyboard::Scancode::F7 as i32,
|
||||
F8 = sdl2::keyboard::Scancode::F8 as i32,
|
||||
F9 = sdl2::keyboard::Scancode::F9 as i32,
|
||||
F10 = sdl2::keyboard::Scancode::F10 as i32,
|
||||
F11 = sdl2::keyboard::Scancode::F11 as i32,
|
||||
F12 = sdl2::keyboard::Scancode::F12 as i32,
|
||||
PrintScreen = sdl2::keyboard::Scancode::PrintScreen as i32,
|
||||
ScrollLock = sdl2::keyboard::Scancode::ScrollLock as i32,
|
||||
Pause = sdl2::keyboard::Scancode::Pause as i32,
|
||||
Insert = sdl2::keyboard::Scancode::Insert as i32,
|
||||
Home = sdl2::keyboard::Scancode::Home as i32,
|
||||
PageUp = sdl2::keyboard::Scancode::PageUp as i32,
|
||||
Delete = sdl2::keyboard::Scancode::Delete as i32,
|
||||
End = sdl2::keyboard::Scancode::End as i32,
|
||||
PageDown = sdl2::keyboard::Scancode::PageDown as i32,
|
||||
Right = sdl2::keyboard::Scancode::Right as i32,
|
||||
Left = sdl2::keyboard::Scancode::Left as i32,
|
||||
Down = sdl2::keyboard::Scancode::Down as i32,
|
||||
Up = sdl2::keyboard::Scancode::Up as i32,
|
||||
NumLockClear = sdl2::keyboard::Scancode::NumLockClear as i32,
|
||||
KpDivide = sdl2::keyboard::Scancode::KpDivide as i32,
|
||||
KpMultiply = sdl2::keyboard::Scancode::KpMultiply as i32,
|
||||
KpMinus = sdl2::keyboard::Scancode::KpMinus as i32,
|
||||
KpPlus = sdl2::keyboard::Scancode::KpPlus as i32,
|
||||
KpEnter = sdl2::keyboard::Scancode::KpEnter as i32,
|
||||
Kp1 = sdl2::keyboard::Scancode::Kp1 as i32,
|
||||
Kp2 = sdl2::keyboard::Scancode::Kp2 as i32,
|
||||
Kp3 = sdl2::keyboard::Scancode::Kp3 as i32,
|
||||
Kp4 = sdl2::keyboard::Scancode::Kp4 as i32,
|
||||
Kp5 = sdl2::keyboard::Scancode::Kp5 as i32,
|
||||
Kp6 = sdl2::keyboard::Scancode::Kp6 as i32,
|
||||
Kp7 = sdl2::keyboard::Scancode::Kp7 as i32,
|
||||
Kp8 = sdl2::keyboard::Scancode::Kp8 as i32,
|
||||
Kp9 = sdl2::keyboard::Scancode::Kp9 as i32,
|
||||
Kp0 = sdl2::keyboard::Scancode::Kp0 as i32,
|
||||
KpPeriod = sdl2::keyboard::Scancode::KpPeriod as i32,
|
||||
NonUsBackslash = sdl2::keyboard::Scancode::NonUsBackslash as i32,
|
||||
Application = sdl2::keyboard::Scancode::Application as i32,
|
||||
Power = sdl2::keyboard::Scancode::Power as i32,
|
||||
KpEquals = sdl2::keyboard::Scancode::KpEquals as i32,
|
||||
F13 = sdl2::keyboard::Scancode::F13 as i32,
|
||||
F14 = sdl2::keyboard::Scancode::F14 as i32,
|
||||
F15 = sdl2::keyboard::Scancode::F15 as i32,
|
||||
F16 = sdl2::keyboard::Scancode::F16 as i32,
|
||||
F17 = sdl2::keyboard::Scancode::F17 as i32,
|
||||
F18 = sdl2::keyboard::Scancode::F18 as i32,
|
||||
F19 = sdl2::keyboard::Scancode::F19 as i32,
|
||||
F20 = sdl2::keyboard::Scancode::F20 as i32,
|
||||
F21 = sdl2::keyboard::Scancode::F21 as i32,
|
||||
F22 = sdl2::keyboard::Scancode::F22 as i32,
|
||||
F23 = sdl2::keyboard::Scancode::F23 as i32,
|
||||
F24 = sdl2::keyboard::Scancode::F24 as i32,
|
||||
Execute = sdl2::keyboard::Scancode::Execute as i32,
|
||||
Help = sdl2::keyboard::Scancode::Help as i32,
|
||||
Menu = sdl2::keyboard::Scancode::Menu as i32,
|
||||
Select = sdl2::keyboard::Scancode::Select as i32,
|
||||
Stop = sdl2::keyboard::Scancode::Stop as i32,
|
||||
Again = sdl2::keyboard::Scancode::Again as i32,
|
||||
Undo = sdl2::keyboard::Scancode::Undo as i32,
|
||||
Cut = sdl2::keyboard::Scancode::Cut as i32,
|
||||
Copy = sdl2::keyboard::Scancode::Copy as i32,
|
||||
Paste = sdl2::keyboard::Scancode::Paste as i32,
|
||||
Find = sdl2::keyboard::Scancode::Find as i32,
|
||||
Mute = sdl2::keyboard::Scancode::Mute as i32,
|
||||
VolumeUp = sdl2::keyboard::Scancode::VolumeUp as i32,
|
||||
VolumeDown = sdl2::keyboard::Scancode::VolumeDown as i32,
|
||||
KpComma = sdl2::keyboard::Scancode::KpComma as i32,
|
||||
KpEqualsAS400 = sdl2::keyboard::Scancode::KpEqualsAS400 as i32,
|
||||
International1 = sdl2::keyboard::Scancode::International1 as i32,
|
||||
International2 = sdl2::keyboard::Scancode::International2 as i32,
|
||||
International3 = sdl2::keyboard::Scancode::International3 as i32,
|
||||
International4 = sdl2::keyboard::Scancode::International4 as i32,
|
||||
International5 = sdl2::keyboard::Scancode::International5 as i32,
|
||||
International6 = sdl2::keyboard::Scancode::International6 as i32,
|
||||
International7 = sdl2::keyboard::Scancode::International7 as i32,
|
||||
International8 = sdl2::keyboard::Scancode::International8 as i32,
|
||||
International9 = sdl2::keyboard::Scancode::International9 as i32,
|
||||
Lang1 = sdl2::keyboard::Scancode::Lang1 as i32,
|
||||
Lang2 = sdl2::keyboard::Scancode::Lang2 as i32,
|
||||
Lang3 = sdl2::keyboard::Scancode::Lang3 as i32,
|
||||
Lang4 = sdl2::keyboard::Scancode::Lang4 as i32,
|
||||
Lang5 = sdl2::keyboard::Scancode::Lang5 as i32,
|
||||
Lang6 = sdl2::keyboard::Scancode::Lang6 as i32,
|
||||
Lang7 = sdl2::keyboard::Scancode::Lang7 as i32,
|
||||
Lang8 = sdl2::keyboard::Scancode::Lang8 as i32,
|
||||
Lang9 = sdl2::keyboard::Scancode::Lang9 as i32,
|
||||
AltErase = sdl2::keyboard::Scancode::AltErase as i32,
|
||||
SysReq = sdl2::keyboard::Scancode::SysReq as i32,
|
||||
Cancel = sdl2::keyboard::Scancode::Cancel as i32,
|
||||
Clear = sdl2::keyboard::Scancode::Clear as i32,
|
||||
Prior = sdl2::keyboard::Scancode::Prior as i32,
|
||||
Return2 = sdl2::keyboard::Scancode::Return2 as i32,
|
||||
Separator = sdl2::keyboard::Scancode::Separator as i32,
|
||||
Out = sdl2::keyboard::Scancode::Out as i32,
|
||||
Oper = sdl2::keyboard::Scancode::Oper as i32,
|
||||
ClearAgain = sdl2::keyboard::Scancode::ClearAgain as i32,
|
||||
CrSel = sdl2::keyboard::Scancode::CrSel as i32,
|
||||
ExSel = sdl2::keyboard::Scancode::ExSel as i32,
|
||||
Kp00 = sdl2::keyboard::Scancode::Kp00 as i32,
|
||||
Kp000 = sdl2::keyboard::Scancode::Kp000 as i32,
|
||||
ThousandsSeparator = sdl2::keyboard::Scancode::ThousandsSeparator as i32,
|
||||
DecimalSeparator = sdl2::keyboard::Scancode::DecimalSeparator as i32,
|
||||
CurrencyUnit = sdl2::keyboard::Scancode::CurrencyUnit as i32,
|
||||
CurrencySubUnit = sdl2::keyboard::Scancode::CurrencySubUnit as i32,
|
||||
KpLeftParen = sdl2::keyboard::Scancode::KpLeftParen as i32,
|
||||
KpRightParen = sdl2::keyboard::Scancode::KpRightParen as i32,
|
||||
KpLeftBrace = sdl2::keyboard::Scancode::KpLeftBrace as i32,
|
||||
KpRightBrace = sdl2::keyboard::Scancode::KpRightBrace as i32,
|
||||
KpTab = sdl2::keyboard::Scancode::KpTab as i32,
|
||||
KpBackspace = sdl2::keyboard::Scancode::KpBackspace as i32,
|
||||
KpA = sdl2::keyboard::Scancode::KpA as i32,
|
||||
KpB = sdl2::keyboard::Scancode::KpB as i32,
|
||||
KpC = sdl2::keyboard::Scancode::KpC as i32,
|
||||
KpD = sdl2::keyboard::Scancode::KpD as i32,
|
||||
KpE = sdl2::keyboard::Scancode::KpE as i32,
|
||||
KpF = sdl2::keyboard::Scancode::KpF as i32,
|
||||
KpXor = sdl2::keyboard::Scancode::KpXor as i32,
|
||||
KpPower = sdl2::keyboard::Scancode::KpPower as i32,
|
||||
KpPercent = sdl2::keyboard::Scancode::KpPercent as i32,
|
||||
KpLess = sdl2::keyboard::Scancode::KpLess as i32,
|
||||
KpGreater = sdl2::keyboard::Scancode::KpGreater as i32,
|
||||
KpAmpersand = sdl2::keyboard::Scancode::KpAmpersand as i32,
|
||||
KpDblAmpersand = sdl2::keyboard::Scancode::KpDblAmpersand as i32,
|
||||
KpVerticalBar = sdl2::keyboard::Scancode::KpVerticalBar as i32,
|
||||
KpDblVerticalBar = sdl2::keyboard::Scancode::KpDblVerticalBar as i32,
|
||||
KpColon = sdl2::keyboard::Scancode::KpColon as i32,
|
||||
KpHash = sdl2::keyboard::Scancode::KpHash as i32,
|
||||
KpSpace = sdl2::keyboard::Scancode::KpSpace as i32,
|
||||
KpAt = sdl2::keyboard::Scancode::KpAt as i32,
|
||||
KpExclam = sdl2::keyboard::Scancode::KpExclam as i32,
|
||||
KpMemStore = sdl2::keyboard::Scancode::KpMemStore as i32,
|
||||
KpMemRecall = sdl2::keyboard::Scancode::KpMemRecall as i32,
|
||||
KpMemClear = sdl2::keyboard::Scancode::KpMemClear as i32,
|
||||
KpMemAdd = sdl2::keyboard::Scancode::KpMemAdd as i32,
|
||||
KpMemSubtract = sdl2::keyboard::Scancode::KpMemSubtract as i32,
|
||||
KpMemMultiply = sdl2::keyboard::Scancode::KpMemMultiply as i32,
|
||||
KpMemDivide = sdl2::keyboard::Scancode::KpMemDivide as i32,
|
||||
KpPlusMinus = sdl2::keyboard::Scancode::KpPlusMinus as i32,
|
||||
KpClear = sdl2::keyboard::Scancode::KpClear as i32,
|
||||
KpClearEntry = sdl2::keyboard::Scancode::KpClearEntry as i32,
|
||||
KpBinary = sdl2::keyboard::Scancode::KpBinary as i32,
|
||||
KpOctal = sdl2::keyboard::Scancode::KpOctal as i32,
|
||||
KpDecimal = sdl2::keyboard::Scancode::KpDecimal as i32,
|
||||
KpHexadecimal = sdl2::keyboard::Scancode::KpHexadecimal as i32,
|
||||
LCtrl = sdl2::keyboard::Scancode::LCtrl as i32,
|
||||
LShift = sdl2::keyboard::Scancode::LShift as i32,
|
||||
LAlt = sdl2::keyboard::Scancode::LAlt as i32,
|
||||
LGui = sdl2::keyboard::Scancode::LGui as i32,
|
||||
RCtrl = sdl2::keyboard::Scancode::RCtrl as i32,
|
||||
RShift = sdl2::keyboard::Scancode::RShift as i32,
|
||||
RAlt = sdl2::keyboard::Scancode::RAlt as i32,
|
||||
RGui = sdl2::keyboard::Scancode::RGui as i32,
|
||||
Mode = sdl2::keyboard::Scancode::Mode as i32,
|
||||
AudioNext = sdl2::keyboard::Scancode::AudioNext as i32,
|
||||
AudioPrev = sdl2::keyboard::Scancode::AudioPrev as i32,
|
||||
AudioStop = sdl2::keyboard::Scancode::AudioStop as i32,
|
||||
AudioPlay = sdl2::keyboard::Scancode::AudioPlay as i32,
|
||||
AudioMute = sdl2::keyboard::Scancode::AudioMute as i32,
|
||||
MediaSelect = sdl2::keyboard::Scancode::MediaSelect as i32,
|
||||
Www = sdl2::keyboard::Scancode::Www as i32,
|
||||
Mail = sdl2::keyboard::Scancode::Mail as i32,
|
||||
Calculator = sdl2::keyboard::Scancode::Calculator as i32,
|
||||
Computer = sdl2::keyboard::Scancode::Computer as i32,
|
||||
AcSearch = sdl2::keyboard::Scancode::AcSearch as i32,
|
||||
AcHome = sdl2::keyboard::Scancode::AcHome as i32,
|
||||
AcBack = sdl2::keyboard::Scancode::AcBack as i32,
|
||||
AcForward = sdl2::keyboard::Scancode::AcForward as i32,
|
||||
AcStop = sdl2::keyboard::Scancode::AcStop as i32,
|
||||
AcRefresh = sdl2::keyboard::Scancode::AcRefresh as i32,
|
||||
AcBookmarks = sdl2::keyboard::Scancode::AcBookmarks as i32,
|
||||
BrightnessDown = sdl2::keyboard::Scancode::BrightnessDown as i32,
|
||||
BrightnessUp = sdl2::keyboard::Scancode::BrightnessUp as i32,
|
||||
DisplaySwitch = sdl2::keyboard::Scancode::DisplaySwitch as i32,
|
||||
KbdIllumToggle = sdl2::keyboard::Scancode::KbdIllumToggle as i32,
|
||||
KbdIllumDown = sdl2::keyboard::Scancode::KbdIllumDown as i32,
|
||||
KbdIllumUp = sdl2::keyboard::Scancode::KbdIllumUp as i32,
|
||||
Eject = sdl2::keyboard::Scancode::Eject as i32,
|
||||
Sleep = sdl2::keyboard::Scancode::Sleep as i32,
|
||||
App1 = sdl2::keyboard::Scancode::App1 as i32,
|
||||
App2 = sdl2::keyboard::Scancode::App2 as i32,
|
||||
Num = sdl2::keyboard::Scancode::Num as i32,
|
||||
A = sdl2::keyboard::Scancode::A as i32,
|
||||
B = sdl2::keyboard::Scancode::B as i32,
|
||||
C = sdl2::keyboard::Scancode::C as i32,
|
||||
D = sdl2::keyboard::Scancode::D as i32,
|
||||
E = sdl2::keyboard::Scancode::E as i32,
|
||||
F = sdl2::keyboard::Scancode::F as i32,
|
||||
G = sdl2::keyboard::Scancode::G as i32,
|
||||
H = sdl2::keyboard::Scancode::H as i32,
|
||||
I = sdl2::keyboard::Scancode::I as i32,
|
||||
J = sdl2::keyboard::Scancode::J as i32,
|
||||
K = sdl2::keyboard::Scancode::K as i32,
|
||||
L = sdl2::keyboard::Scancode::L as i32,
|
||||
M = sdl2::keyboard::Scancode::M as i32,
|
||||
N = sdl2::keyboard::Scancode::N as i32,
|
||||
O = sdl2::keyboard::Scancode::O as i32,
|
||||
P = sdl2::keyboard::Scancode::P as i32,
|
||||
Q = sdl2::keyboard::Scancode::Q as i32,
|
||||
R = sdl2::keyboard::Scancode::R as i32,
|
||||
S = sdl2::keyboard::Scancode::S as i32,
|
||||
T = sdl2::keyboard::Scancode::T as i32,
|
||||
U = sdl2::keyboard::Scancode::U as i32,
|
||||
V = sdl2::keyboard::Scancode::V as i32,
|
||||
W = sdl2::keyboard::Scancode::W as i32,
|
||||
X = sdl2::keyboard::Scancode::X as i32,
|
||||
Y = sdl2::keyboard::Scancode::Y as i32,
|
||||
Z = sdl2::keyboard::Scancode::Z as i32,
|
||||
Num1 = sdl2::keyboard::Scancode::Num1 as i32,
|
||||
Num2 = sdl2::keyboard::Scancode::Num2 as i32,
|
||||
Num3 = sdl2::keyboard::Scancode::Num3 as i32,
|
||||
Num4 = sdl2::keyboard::Scancode::Num4 as i32,
|
||||
Num5 = sdl2::keyboard::Scancode::Num5 as i32,
|
||||
Num6 = sdl2::keyboard::Scancode::Num6 as i32,
|
||||
Num7 = sdl2::keyboard::Scancode::Num7 as i32,
|
||||
Num8 = sdl2::keyboard::Scancode::Num8 as i32,
|
||||
Num9 = sdl2::keyboard::Scancode::Num9 as i32,
|
||||
Num0 = sdl2::keyboard::Scancode::Num0 as i32,
|
||||
Return = sdl2::keyboard::Scancode::Return as i32,
|
||||
Escape = sdl2::keyboard::Scancode::Escape as i32,
|
||||
Backspace = sdl2::keyboard::Scancode::Backspace as i32,
|
||||
Tab = sdl2::keyboard::Scancode::Tab as i32,
|
||||
Space = sdl2::keyboard::Scancode::Space as i32,
|
||||
Minus = sdl2::keyboard::Scancode::Minus as i32,
|
||||
Equals = sdl2::keyboard::Scancode::Equals as i32,
|
||||
LeftBracket = sdl2::keyboard::Scancode::LeftBracket as i32,
|
||||
RightBracket = sdl2::keyboard::Scancode::RightBracket as i32,
|
||||
Backslash = sdl2::keyboard::Scancode::Backslash as i32,
|
||||
NonUsHash = sdl2::keyboard::Scancode::NonUsHash as i32,
|
||||
Semicolon = sdl2::keyboard::Scancode::Semicolon as i32,
|
||||
Apostrophe = sdl2::keyboard::Scancode::Apostrophe as i32,
|
||||
Grave = sdl2::keyboard::Scancode::Grave as i32,
|
||||
Comma = sdl2::keyboard::Scancode::Comma as i32,
|
||||
Period = sdl2::keyboard::Scancode::Period as i32,
|
||||
Slash = sdl2::keyboard::Scancode::Slash as i32,
|
||||
CapsLock = sdl2::keyboard::Scancode::CapsLock as i32,
|
||||
F1 = sdl2::keyboard::Scancode::F1 as i32,
|
||||
F2 = sdl2::keyboard::Scancode::F2 as i32,
|
||||
F3 = sdl2::keyboard::Scancode::F3 as i32,
|
||||
F4 = sdl2::keyboard::Scancode::F4 as i32,
|
||||
F5 = sdl2::keyboard::Scancode::F5 as i32,
|
||||
F6 = sdl2::keyboard::Scancode::F6 as i32,
|
||||
F7 = sdl2::keyboard::Scancode::F7 as i32,
|
||||
F8 = sdl2::keyboard::Scancode::F8 as i32,
|
||||
F9 = sdl2::keyboard::Scancode::F9 as i32,
|
||||
F10 = sdl2::keyboard::Scancode::F10 as i32,
|
||||
F11 = sdl2::keyboard::Scancode::F11 as i32,
|
||||
F12 = sdl2::keyboard::Scancode::F12 as i32,
|
||||
PrintScreen = sdl2::keyboard::Scancode::PrintScreen as i32,
|
||||
ScrollLock = sdl2::keyboard::Scancode::ScrollLock as i32,
|
||||
Pause = sdl2::keyboard::Scancode::Pause as i32,
|
||||
Insert = sdl2::keyboard::Scancode::Insert as i32,
|
||||
Home = sdl2::keyboard::Scancode::Home as i32,
|
||||
PageUp = sdl2::keyboard::Scancode::PageUp as i32,
|
||||
Delete = sdl2::keyboard::Scancode::Delete as i32,
|
||||
End = sdl2::keyboard::Scancode::End as i32,
|
||||
PageDown = sdl2::keyboard::Scancode::PageDown as i32,
|
||||
Right = sdl2::keyboard::Scancode::Right as i32,
|
||||
Left = sdl2::keyboard::Scancode::Left as i32,
|
||||
Down = sdl2::keyboard::Scancode::Down as i32,
|
||||
Up = sdl2::keyboard::Scancode::Up as i32,
|
||||
NumLockClear = sdl2::keyboard::Scancode::NumLockClear as i32,
|
||||
KpDivide = sdl2::keyboard::Scancode::KpDivide as i32,
|
||||
KpMultiply = sdl2::keyboard::Scancode::KpMultiply as i32,
|
||||
KpMinus = sdl2::keyboard::Scancode::KpMinus as i32,
|
||||
KpPlus = sdl2::keyboard::Scancode::KpPlus as i32,
|
||||
KpEnter = sdl2::keyboard::Scancode::KpEnter as i32,
|
||||
Kp1 = sdl2::keyboard::Scancode::Kp1 as i32,
|
||||
Kp2 = sdl2::keyboard::Scancode::Kp2 as i32,
|
||||
Kp3 = sdl2::keyboard::Scancode::Kp3 as i32,
|
||||
Kp4 = sdl2::keyboard::Scancode::Kp4 as i32,
|
||||
Kp5 = sdl2::keyboard::Scancode::Kp5 as i32,
|
||||
Kp6 = sdl2::keyboard::Scancode::Kp6 as i32,
|
||||
Kp7 = sdl2::keyboard::Scancode::Kp7 as i32,
|
||||
Kp8 = sdl2::keyboard::Scancode::Kp8 as i32,
|
||||
Kp9 = sdl2::keyboard::Scancode::Kp9 as i32,
|
||||
Kp0 = sdl2::keyboard::Scancode::Kp0 as i32,
|
||||
KpPeriod = sdl2::keyboard::Scancode::KpPeriod as i32,
|
||||
NonUsBackslash = sdl2::keyboard::Scancode::NonUsBackslash as i32,
|
||||
Application = sdl2::keyboard::Scancode::Application as i32,
|
||||
Power = sdl2::keyboard::Scancode::Power as i32,
|
||||
KpEquals = sdl2::keyboard::Scancode::KpEquals as i32,
|
||||
F13 = sdl2::keyboard::Scancode::F13 as i32,
|
||||
F14 = sdl2::keyboard::Scancode::F14 as i32,
|
||||
F15 = sdl2::keyboard::Scancode::F15 as i32,
|
||||
F16 = sdl2::keyboard::Scancode::F16 as i32,
|
||||
F17 = sdl2::keyboard::Scancode::F17 as i32,
|
||||
F18 = sdl2::keyboard::Scancode::F18 as i32,
|
||||
F19 = sdl2::keyboard::Scancode::F19 as i32,
|
||||
F20 = sdl2::keyboard::Scancode::F20 as i32,
|
||||
F21 = sdl2::keyboard::Scancode::F21 as i32,
|
||||
F22 = sdl2::keyboard::Scancode::F22 as i32,
|
||||
F23 = sdl2::keyboard::Scancode::F23 as i32,
|
||||
F24 = sdl2::keyboard::Scancode::F24 as i32,
|
||||
Execute = sdl2::keyboard::Scancode::Execute as i32,
|
||||
Help = sdl2::keyboard::Scancode::Help as i32,
|
||||
Menu = sdl2::keyboard::Scancode::Menu as i32,
|
||||
Select = sdl2::keyboard::Scancode::Select as i32,
|
||||
Stop = sdl2::keyboard::Scancode::Stop as i32,
|
||||
Again = sdl2::keyboard::Scancode::Again as i32,
|
||||
Undo = sdl2::keyboard::Scancode::Undo as i32,
|
||||
Cut = sdl2::keyboard::Scancode::Cut as i32,
|
||||
Copy = sdl2::keyboard::Scancode::Copy as i32,
|
||||
Paste = sdl2::keyboard::Scancode::Paste as i32,
|
||||
Find = sdl2::keyboard::Scancode::Find as i32,
|
||||
Mute = sdl2::keyboard::Scancode::Mute as i32,
|
||||
VolumeUp = sdl2::keyboard::Scancode::VolumeUp as i32,
|
||||
VolumeDown = sdl2::keyboard::Scancode::VolumeDown as i32,
|
||||
KpComma = sdl2::keyboard::Scancode::KpComma as i32,
|
||||
KpEqualsAS400 = sdl2::keyboard::Scancode::KpEqualsAS400 as i32,
|
||||
International1 = sdl2::keyboard::Scancode::International1 as i32,
|
||||
International2 = sdl2::keyboard::Scancode::International2 as i32,
|
||||
International3 = sdl2::keyboard::Scancode::International3 as i32,
|
||||
International4 = sdl2::keyboard::Scancode::International4 as i32,
|
||||
International5 = sdl2::keyboard::Scancode::International5 as i32,
|
||||
International6 = sdl2::keyboard::Scancode::International6 as i32,
|
||||
International7 = sdl2::keyboard::Scancode::International7 as i32,
|
||||
International8 = sdl2::keyboard::Scancode::International8 as i32,
|
||||
International9 = sdl2::keyboard::Scancode::International9 as i32,
|
||||
Lang1 = sdl2::keyboard::Scancode::Lang1 as i32,
|
||||
Lang2 = sdl2::keyboard::Scancode::Lang2 as i32,
|
||||
Lang3 = sdl2::keyboard::Scancode::Lang3 as i32,
|
||||
Lang4 = sdl2::keyboard::Scancode::Lang4 as i32,
|
||||
Lang5 = sdl2::keyboard::Scancode::Lang5 as i32,
|
||||
Lang6 = sdl2::keyboard::Scancode::Lang6 as i32,
|
||||
Lang7 = sdl2::keyboard::Scancode::Lang7 as i32,
|
||||
Lang8 = sdl2::keyboard::Scancode::Lang8 as i32,
|
||||
Lang9 = sdl2::keyboard::Scancode::Lang9 as i32,
|
||||
AltErase = sdl2::keyboard::Scancode::AltErase as i32,
|
||||
SysReq = sdl2::keyboard::Scancode::SysReq as i32,
|
||||
Cancel = sdl2::keyboard::Scancode::Cancel as i32,
|
||||
Clear = sdl2::keyboard::Scancode::Clear as i32,
|
||||
Prior = sdl2::keyboard::Scancode::Prior as i32,
|
||||
Return2 = sdl2::keyboard::Scancode::Return2 as i32,
|
||||
Separator = sdl2::keyboard::Scancode::Separator as i32,
|
||||
Out = sdl2::keyboard::Scancode::Out as i32,
|
||||
Oper = sdl2::keyboard::Scancode::Oper as i32,
|
||||
ClearAgain = sdl2::keyboard::Scancode::ClearAgain as i32,
|
||||
CrSel = sdl2::keyboard::Scancode::CrSel as i32,
|
||||
ExSel = sdl2::keyboard::Scancode::ExSel as i32,
|
||||
Kp00 = sdl2::keyboard::Scancode::Kp00 as i32,
|
||||
Kp000 = sdl2::keyboard::Scancode::Kp000 as i32,
|
||||
ThousandsSeparator = sdl2::keyboard::Scancode::ThousandsSeparator as i32,
|
||||
DecimalSeparator = sdl2::keyboard::Scancode::DecimalSeparator as i32,
|
||||
CurrencyUnit = sdl2::keyboard::Scancode::CurrencyUnit as i32,
|
||||
CurrencySubUnit = sdl2::keyboard::Scancode::CurrencySubUnit as i32,
|
||||
KpLeftParen = sdl2::keyboard::Scancode::KpLeftParen as i32,
|
||||
KpRightParen = sdl2::keyboard::Scancode::KpRightParen as i32,
|
||||
KpLeftBrace = sdl2::keyboard::Scancode::KpLeftBrace as i32,
|
||||
KpRightBrace = sdl2::keyboard::Scancode::KpRightBrace as i32,
|
||||
KpTab = sdl2::keyboard::Scancode::KpTab as i32,
|
||||
KpBackspace = sdl2::keyboard::Scancode::KpBackspace as i32,
|
||||
KpA = sdl2::keyboard::Scancode::KpA as i32,
|
||||
KpB = sdl2::keyboard::Scancode::KpB as i32,
|
||||
KpC = sdl2::keyboard::Scancode::KpC as i32,
|
||||
KpD = sdl2::keyboard::Scancode::KpD as i32,
|
||||
KpE = sdl2::keyboard::Scancode::KpE as i32,
|
||||
KpF = sdl2::keyboard::Scancode::KpF as i32,
|
||||
KpXor = sdl2::keyboard::Scancode::KpXor as i32,
|
||||
KpPower = sdl2::keyboard::Scancode::KpPower as i32,
|
||||
KpPercent = sdl2::keyboard::Scancode::KpPercent as i32,
|
||||
KpLess = sdl2::keyboard::Scancode::KpLess as i32,
|
||||
KpGreater = sdl2::keyboard::Scancode::KpGreater as i32,
|
||||
KpAmpersand = sdl2::keyboard::Scancode::KpAmpersand as i32,
|
||||
KpDblAmpersand = sdl2::keyboard::Scancode::KpDblAmpersand as i32,
|
||||
KpVerticalBar = sdl2::keyboard::Scancode::KpVerticalBar as i32,
|
||||
KpDblVerticalBar = sdl2::keyboard::Scancode::KpDblVerticalBar as i32,
|
||||
KpColon = sdl2::keyboard::Scancode::KpColon as i32,
|
||||
KpHash = sdl2::keyboard::Scancode::KpHash as i32,
|
||||
KpSpace = sdl2::keyboard::Scancode::KpSpace as i32,
|
||||
KpAt = sdl2::keyboard::Scancode::KpAt as i32,
|
||||
KpExclam = sdl2::keyboard::Scancode::KpExclam as i32,
|
||||
KpMemStore = sdl2::keyboard::Scancode::KpMemStore as i32,
|
||||
KpMemRecall = sdl2::keyboard::Scancode::KpMemRecall as i32,
|
||||
KpMemClear = sdl2::keyboard::Scancode::KpMemClear as i32,
|
||||
KpMemAdd = sdl2::keyboard::Scancode::KpMemAdd as i32,
|
||||
KpMemSubtract = sdl2::keyboard::Scancode::KpMemSubtract as i32,
|
||||
KpMemMultiply = sdl2::keyboard::Scancode::KpMemMultiply as i32,
|
||||
KpMemDivide = sdl2::keyboard::Scancode::KpMemDivide as i32,
|
||||
KpPlusMinus = sdl2::keyboard::Scancode::KpPlusMinus as i32,
|
||||
KpClear = sdl2::keyboard::Scancode::KpClear as i32,
|
||||
KpClearEntry = sdl2::keyboard::Scancode::KpClearEntry as i32,
|
||||
KpBinary = sdl2::keyboard::Scancode::KpBinary as i32,
|
||||
KpOctal = sdl2::keyboard::Scancode::KpOctal as i32,
|
||||
KpDecimal = sdl2::keyboard::Scancode::KpDecimal as i32,
|
||||
KpHexadecimal = sdl2::keyboard::Scancode::KpHexadecimal as i32,
|
||||
LCtrl = sdl2::keyboard::Scancode::LCtrl as i32,
|
||||
LShift = sdl2::keyboard::Scancode::LShift as i32,
|
||||
LAlt = sdl2::keyboard::Scancode::LAlt as i32,
|
||||
LGui = sdl2::keyboard::Scancode::LGui as i32,
|
||||
RCtrl = sdl2::keyboard::Scancode::RCtrl as i32,
|
||||
RShift = sdl2::keyboard::Scancode::RShift as i32,
|
||||
RAlt = sdl2::keyboard::Scancode::RAlt as i32,
|
||||
RGui = sdl2::keyboard::Scancode::RGui as i32,
|
||||
Mode = sdl2::keyboard::Scancode::Mode as i32,
|
||||
AudioNext = sdl2::keyboard::Scancode::AudioNext as i32,
|
||||
AudioPrev = sdl2::keyboard::Scancode::AudioPrev as i32,
|
||||
AudioStop = sdl2::keyboard::Scancode::AudioStop as i32,
|
||||
AudioPlay = sdl2::keyboard::Scancode::AudioPlay as i32,
|
||||
AudioMute = sdl2::keyboard::Scancode::AudioMute as i32,
|
||||
MediaSelect = sdl2::keyboard::Scancode::MediaSelect as i32,
|
||||
Www = sdl2::keyboard::Scancode::Www as i32,
|
||||
Mail = sdl2::keyboard::Scancode::Mail as i32,
|
||||
Calculator = sdl2::keyboard::Scancode::Calculator as i32,
|
||||
Computer = sdl2::keyboard::Scancode::Computer as i32,
|
||||
AcSearch = sdl2::keyboard::Scancode::AcSearch as i32,
|
||||
AcHome = sdl2::keyboard::Scancode::AcHome as i32,
|
||||
AcBack = sdl2::keyboard::Scancode::AcBack as i32,
|
||||
AcForward = sdl2::keyboard::Scancode::AcForward as i32,
|
||||
AcStop = sdl2::keyboard::Scancode::AcStop as i32,
|
||||
AcRefresh = sdl2::keyboard::Scancode::AcRefresh as i32,
|
||||
AcBookmarks = sdl2::keyboard::Scancode::AcBookmarks as i32,
|
||||
BrightnessDown = sdl2::keyboard::Scancode::BrightnessDown as i32,
|
||||
BrightnessUp = sdl2::keyboard::Scancode::BrightnessUp as i32,
|
||||
DisplaySwitch = sdl2::keyboard::Scancode::DisplaySwitch as i32,
|
||||
KbdIllumToggle = sdl2::keyboard::Scancode::KbdIllumToggle as i32,
|
||||
KbdIllumDown = sdl2::keyboard::Scancode::KbdIllumDown as i32,
|
||||
KbdIllumUp = sdl2::keyboard::Scancode::KbdIllumUp as i32,
|
||||
Eject = sdl2::keyboard::Scancode::Eject as i32,
|
||||
Sleep = sdl2::keyboard::Scancode::Sleep as i32,
|
||||
App1 = sdl2::keyboard::Scancode::App1 as i32,
|
||||
App2 = sdl2::keyboard::Scancode::App2 as i32,
|
||||
Num = sdl2::keyboard::Scancode::Num as i32,
|
||||
}
|
||||
|
||||
impl From<sdl2::keyboard::Scancode> for Scancode {
|
||||
fn from(value: sdl2::keyboard::Scancode) -> Self {
|
||||
match value {
|
||||
sdl2::keyboard::Scancode::A => Scancode::A,
|
||||
sdl2::keyboard::Scancode::B => Scancode::B,
|
||||
sdl2::keyboard::Scancode::C => Scancode::C,
|
||||
sdl2::keyboard::Scancode::D => Scancode::D,
|
||||
sdl2::keyboard::Scancode::E => Scancode::E,
|
||||
sdl2::keyboard::Scancode::F => Scancode::F,
|
||||
sdl2::keyboard::Scancode::G => Scancode::G,
|
||||
sdl2::keyboard::Scancode::H => Scancode::H,
|
||||
sdl2::keyboard::Scancode::I => Scancode::I,
|
||||
sdl2::keyboard::Scancode::J => Scancode::J,
|
||||
sdl2::keyboard::Scancode::K => Scancode::K,
|
||||
sdl2::keyboard::Scancode::L => Scancode::L,
|
||||
sdl2::keyboard::Scancode::M => Scancode::M,
|
||||
sdl2::keyboard::Scancode::N => Scancode::N,
|
||||
sdl2::keyboard::Scancode::O => Scancode::O,
|
||||
sdl2::keyboard::Scancode::P => Scancode::P,
|
||||
sdl2::keyboard::Scancode::Q => Scancode::Q,
|
||||
sdl2::keyboard::Scancode::R => Scancode::R,
|
||||
sdl2::keyboard::Scancode::S => Scancode::S,
|
||||
sdl2::keyboard::Scancode::T => Scancode::T,
|
||||
sdl2::keyboard::Scancode::U => Scancode::U,
|
||||
sdl2::keyboard::Scancode::V => Scancode::V,
|
||||
sdl2::keyboard::Scancode::W => Scancode::W,
|
||||
sdl2::keyboard::Scancode::X => Scancode::X,
|
||||
sdl2::keyboard::Scancode::Y => Scancode::Y,
|
||||
sdl2::keyboard::Scancode::Z => Scancode::Z,
|
||||
sdl2::keyboard::Scancode::Num1 => Scancode::Num1,
|
||||
sdl2::keyboard::Scancode::Num2 => Scancode::Num2,
|
||||
sdl2::keyboard::Scancode::Num3 => Scancode::Num3,
|
||||
sdl2::keyboard::Scancode::Num4 => Scancode::Num4,
|
||||
sdl2::keyboard::Scancode::Num5 => Scancode::Num5,
|
||||
sdl2::keyboard::Scancode::Num6 => Scancode::Num6,
|
||||
sdl2::keyboard::Scancode::Num7 => Scancode::Num7,
|
||||
sdl2::keyboard::Scancode::Num8 => Scancode::Num8,
|
||||
sdl2::keyboard::Scancode::Num9 => Scancode::Num9,
|
||||
sdl2::keyboard::Scancode::Num0 => Scancode::Num0,
|
||||
sdl2::keyboard::Scancode::Return => Scancode::Return,
|
||||
sdl2::keyboard::Scancode::Escape => Scancode::Escape,
|
||||
sdl2::keyboard::Scancode::Backspace => Scancode::Backspace,
|
||||
sdl2::keyboard::Scancode::Tab => Scancode::Tab,
|
||||
sdl2::keyboard::Scancode::Space => Scancode::Space,
|
||||
sdl2::keyboard::Scancode::Minus => Scancode::Minus,
|
||||
sdl2::keyboard::Scancode::Equals => Scancode::Equals,
|
||||
sdl2::keyboard::Scancode::LeftBracket => Scancode::LeftBracket,
|
||||
sdl2::keyboard::Scancode::RightBracket => Scancode::RightBracket,
|
||||
sdl2::keyboard::Scancode::Backslash => Scancode::Backslash,
|
||||
sdl2::keyboard::Scancode::NonUsHash => Scancode::NonUsHash,
|
||||
sdl2::keyboard::Scancode::Semicolon => Scancode::Semicolon,
|
||||
sdl2::keyboard::Scancode::Apostrophe => Scancode::Apostrophe,
|
||||
sdl2::keyboard::Scancode::Grave => Scancode::Grave,
|
||||
sdl2::keyboard::Scancode::Comma => Scancode::Comma,
|
||||
sdl2::keyboard::Scancode::Period => Scancode::Period,
|
||||
sdl2::keyboard::Scancode::Slash => Scancode::Slash,
|
||||
sdl2::keyboard::Scancode::CapsLock => Scancode::CapsLock,
|
||||
sdl2::keyboard::Scancode::F1 => Scancode::F1,
|
||||
sdl2::keyboard::Scancode::F2 => Scancode::F2,
|
||||
sdl2::keyboard::Scancode::F3 => Scancode::F3,
|
||||
sdl2::keyboard::Scancode::F4 => Scancode::F4,
|
||||
sdl2::keyboard::Scancode::F5 => Scancode::F5,
|
||||
sdl2::keyboard::Scancode::F6 => Scancode::F6,
|
||||
sdl2::keyboard::Scancode::F7 => Scancode::F7,
|
||||
sdl2::keyboard::Scancode::F8 => Scancode::F8,
|
||||
sdl2::keyboard::Scancode::F9 => Scancode::F9,
|
||||
sdl2::keyboard::Scancode::F10 => Scancode::F10,
|
||||
sdl2::keyboard::Scancode::F11 => Scancode::F11,
|
||||
sdl2::keyboard::Scancode::F12 => Scancode::F12,
|
||||
sdl2::keyboard::Scancode::PrintScreen => Scancode::PrintScreen,
|
||||
sdl2::keyboard::Scancode::ScrollLock => Scancode::ScrollLock,
|
||||
sdl2::keyboard::Scancode::Pause => Scancode::Pause,
|
||||
sdl2::keyboard::Scancode::Insert => Scancode::Insert,
|
||||
sdl2::keyboard::Scancode::Home => Scancode::Home,
|
||||
sdl2::keyboard::Scancode::PageUp => Scancode::PageUp,
|
||||
sdl2::keyboard::Scancode::Delete => Scancode::Delete,
|
||||
sdl2::keyboard::Scancode::End => Scancode::End,
|
||||
sdl2::keyboard::Scancode::PageDown => Scancode::PageDown,
|
||||
sdl2::keyboard::Scancode::Right => Scancode::Right,
|
||||
sdl2::keyboard::Scancode::Left => Scancode::Left,
|
||||
sdl2::keyboard::Scancode::Down => Scancode::Down,
|
||||
sdl2::keyboard::Scancode::Up => Scancode::Up,
|
||||
sdl2::keyboard::Scancode::NumLockClear => Scancode::NumLockClear,
|
||||
sdl2::keyboard::Scancode::KpDivide => Scancode::KpDivide,
|
||||
sdl2::keyboard::Scancode::KpMultiply => Scancode::KpMultiply,
|
||||
sdl2::keyboard::Scancode::KpMinus => Scancode::KpMinus,
|
||||
sdl2::keyboard::Scancode::KpPlus => Scancode::KpPlus,
|
||||
sdl2::keyboard::Scancode::KpEnter => Scancode::KpEnter,
|
||||
sdl2::keyboard::Scancode::Kp1 => Scancode::Kp1,
|
||||
sdl2::keyboard::Scancode::Kp2 => Scancode::Kp2,
|
||||
sdl2::keyboard::Scancode::Kp3 => Scancode::Kp3,
|
||||
sdl2::keyboard::Scancode::Kp4 => Scancode::Kp4,
|
||||
sdl2::keyboard::Scancode::Kp5 => Scancode::Kp5,
|
||||
sdl2::keyboard::Scancode::Kp6 => Scancode::Kp6,
|
||||
sdl2::keyboard::Scancode::Kp7 => Scancode::Kp7,
|
||||
sdl2::keyboard::Scancode::Kp8 => Scancode::Kp8,
|
||||
sdl2::keyboard::Scancode::Kp9 => Scancode::Kp9,
|
||||
sdl2::keyboard::Scancode::Kp0 => Scancode::Kp0,
|
||||
sdl2::keyboard::Scancode::KpPeriod => Scancode::KpPeriod,
|
||||
sdl2::keyboard::Scancode::NonUsBackslash => Scancode::NonUsBackslash,
|
||||
sdl2::keyboard::Scancode::Application => Scancode::Application,
|
||||
sdl2::keyboard::Scancode::Power => Scancode::Power,
|
||||
sdl2::keyboard::Scancode::KpEquals => Scancode::KpEquals,
|
||||
sdl2::keyboard::Scancode::F13 => Scancode::F13,
|
||||
sdl2::keyboard::Scancode::F14 => Scancode::F14,
|
||||
sdl2::keyboard::Scancode::F15 => Scancode::F15,
|
||||
sdl2::keyboard::Scancode::F16 => Scancode::F16,
|
||||
sdl2::keyboard::Scancode::F17 => Scancode::F17,
|
||||
sdl2::keyboard::Scancode::F18 => Scancode::F18,
|
||||
sdl2::keyboard::Scancode::F19 => Scancode::F19,
|
||||
sdl2::keyboard::Scancode::F20 => Scancode::F20,
|
||||
sdl2::keyboard::Scancode::F21 => Scancode::F21,
|
||||
sdl2::keyboard::Scancode::F22 => Scancode::F22,
|
||||
sdl2::keyboard::Scancode::F23 => Scancode::F23,
|
||||
sdl2::keyboard::Scancode::F24 => Scancode::F24,
|
||||
sdl2::keyboard::Scancode::Execute => Scancode::Execute,
|
||||
sdl2::keyboard::Scancode::Help => Scancode::Help,
|
||||
sdl2::keyboard::Scancode::Menu => Scancode::Menu,
|
||||
sdl2::keyboard::Scancode::Select => Scancode::Select,
|
||||
sdl2::keyboard::Scancode::Stop => Scancode::Stop,
|
||||
sdl2::keyboard::Scancode::Again => Scancode::Again,
|
||||
sdl2::keyboard::Scancode::Undo => Scancode::Undo,
|
||||
sdl2::keyboard::Scancode::Cut => Scancode::Cut,
|
||||
sdl2::keyboard::Scancode::Copy => Scancode::Copy,
|
||||
sdl2::keyboard::Scancode::Paste => Scancode::Paste,
|
||||
sdl2::keyboard::Scancode::Find => Scancode::Find,
|
||||
sdl2::keyboard::Scancode::Mute => Scancode::Mute,
|
||||
sdl2::keyboard::Scancode::VolumeUp => Scancode::VolumeUp,
|
||||
sdl2::keyboard::Scancode::VolumeDown => Scancode::VolumeDown,
|
||||
sdl2::keyboard::Scancode::KpComma => Scancode::KpComma,
|
||||
sdl2::keyboard::Scancode::KpEqualsAS400 => Scancode::KpEqualsAS400,
|
||||
sdl2::keyboard::Scancode::International1 => Scancode::International1,
|
||||
sdl2::keyboard::Scancode::International2 => Scancode::International2,
|
||||
sdl2::keyboard::Scancode::International3 => Scancode::International3,
|
||||
sdl2::keyboard::Scancode::International4 => Scancode::International4,
|
||||
sdl2::keyboard::Scancode::International5 => Scancode::International5,
|
||||
sdl2::keyboard::Scancode::International6 => Scancode::International6,
|
||||
sdl2::keyboard::Scancode::International7 => Scancode::International7,
|
||||
sdl2::keyboard::Scancode::International8 => Scancode::International8,
|
||||
sdl2::keyboard::Scancode::International9 => Scancode::International9,
|
||||
sdl2::keyboard::Scancode::Lang1 => Scancode::Lang1,
|
||||
sdl2::keyboard::Scancode::Lang2 => Scancode::Lang2,
|
||||
sdl2::keyboard::Scancode::Lang3 => Scancode::Lang3,
|
||||
sdl2::keyboard::Scancode::Lang4 => Scancode::Lang4,
|
||||
sdl2::keyboard::Scancode::Lang5 => Scancode::Lang5,
|
||||
sdl2::keyboard::Scancode::Lang6 => Scancode::Lang6,
|
||||
sdl2::keyboard::Scancode::Lang7 => Scancode::Lang7,
|
||||
sdl2::keyboard::Scancode::Lang8 => Scancode::Lang8,
|
||||
sdl2::keyboard::Scancode::Lang9 => Scancode::Lang9,
|
||||
sdl2::keyboard::Scancode::AltErase => Scancode::AltErase,
|
||||
sdl2::keyboard::Scancode::SysReq => Scancode::SysReq,
|
||||
sdl2::keyboard::Scancode::Cancel => Scancode::Cancel,
|
||||
sdl2::keyboard::Scancode::Clear => Scancode::Clear,
|
||||
sdl2::keyboard::Scancode::Prior => Scancode::Prior,
|
||||
sdl2::keyboard::Scancode::Return2 => Scancode::Return2,
|
||||
sdl2::keyboard::Scancode::Separator => Scancode::Separator,
|
||||
sdl2::keyboard::Scancode::Out => Scancode::Out,
|
||||
sdl2::keyboard::Scancode::Oper => Scancode::Oper,
|
||||
sdl2::keyboard::Scancode::ClearAgain => Scancode::ClearAgain,
|
||||
sdl2::keyboard::Scancode::CrSel => Scancode::CrSel,
|
||||
sdl2::keyboard::Scancode::ExSel => Scancode::ExSel,
|
||||
sdl2::keyboard::Scancode::Kp00 => Scancode::Kp00,
|
||||
sdl2::keyboard::Scancode::Kp000 => Scancode::Kp000,
|
||||
sdl2::keyboard::Scancode::ThousandsSeparator => Scancode::ThousandsSeparator,
|
||||
sdl2::keyboard::Scancode::DecimalSeparator => Scancode::DecimalSeparator,
|
||||
sdl2::keyboard::Scancode::CurrencyUnit => Scancode::CurrencyUnit,
|
||||
sdl2::keyboard::Scancode::CurrencySubUnit => Scancode::CurrencySubUnit,
|
||||
sdl2::keyboard::Scancode::KpLeftParen => Scancode::KpLeftParen,
|
||||
sdl2::keyboard::Scancode::KpRightParen => Scancode::KpRightParen,
|
||||
sdl2::keyboard::Scancode::KpLeftBrace => Scancode::KpLeftBrace,
|
||||
sdl2::keyboard::Scancode::KpRightBrace => Scancode::KpRightBrace,
|
||||
sdl2::keyboard::Scancode::KpTab => Scancode::KpTab,
|
||||
sdl2::keyboard::Scancode::KpBackspace => Scancode::KpBackspace,
|
||||
sdl2::keyboard::Scancode::KpA => Scancode::KpA,
|
||||
sdl2::keyboard::Scancode::KpB => Scancode::KpB,
|
||||
sdl2::keyboard::Scancode::KpC => Scancode::KpC,
|
||||
sdl2::keyboard::Scancode::KpD => Scancode::KpD,
|
||||
sdl2::keyboard::Scancode::KpE => Scancode::KpE,
|
||||
sdl2::keyboard::Scancode::KpF => Scancode::KpF,
|
||||
sdl2::keyboard::Scancode::KpXor => Scancode::KpXor,
|
||||
sdl2::keyboard::Scancode::KpPower => Scancode::KpPower,
|
||||
sdl2::keyboard::Scancode::KpPercent => Scancode::KpPercent,
|
||||
sdl2::keyboard::Scancode::KpLess => Scancode::KpLess,
|
||||
sdl2::keyboard::Scancode::KpGreater => Scancode::KpGreater,
|
||||
sdl2::keyboard::Scancode::KpAmpersand => Scancode::KpAmpersand,
|
||||
sdl2::keyboard::Scancode::KpDblAmpersand => Scancode::KpDblAmpersand,
|
||||
sdl2::keyboard::Scancode::KpVerticalBar => Scancode::KpVerticalBar,
|
||||
sdl2::keyboard::Scancode::KpDblVerticalBar => Scancode::KpDblVerticalBar,
|
||||
sdl2::keyboard::Scancode::KpColon => Scancode::KpColon,
|
||||
sdl2::keyboard::Scancode::KpHash => Scancode::KpHash,
|
||||
sdl2::keyboard::Scancode::KpSpace => Scancode::KpSpace,
|
||||
sdl2::keyboard::Scancode::KpAt => Scancode::KpAt,
|
||||
sdl2::keyboard::Scancode::KpExclam => Scancode::KpExclam,
|
||||
sdl2::keyboard::Scancode::KpMemStore => Scancode::KpMemStore,
|
||||
sdl2::keyboard::Scancode::KpMemRecall => Scancode::KpMemRecall,
|
||||
sdl2::keyboard::Scancode::KpMemClear => Scancode::KpMemClear,
|
||||
sdl2::keyboard::Scancode::KpMemAdd => Scancode::KpMemAdd,
|
||||
sdl2::keyboard::Scancode::KpMemSubtract => Scancode::KpMemSubtract,
|
||||
sdl2::keyboard::Scancode::KpMemMultiply => Scancode::KpMemMultiply,
|
||||
sdl2::keyboard::Scancode::KpMemDivide => Scancode::KpMemDivide,
|
||||
sdl2::keyboard::Scancode::KpPlusMinus => Scancode::KpPlusMinus,
|
||||
sdl2::keyboard::Scancode::KpClear => Scancode::KpClear,
|
||||
sdl2::keyboard::Scancode::KpClearEntry => Scancode::KpClearEntry,
|
||||
sdl2::keyboard::Scancode::KpBinary => Scancode::KpBinary,
|
||||
sdl2::keyboard::Scancode::KpOctal => Scancode::KpOctal,
|
||||
sdl2::keyboard::Scancode::KpDecimal => Scancode::KpDecimal,
|
||||
sdl2::keyboard::Scancode::KpHexadecimal => Scancode::KpHexadecimal,
|
||||
sdl2::keyboard::Scancode::LCtrl => Scancode::LCtrl,
|
||||
sdl2::keyboard::Scancode::LShift => Scancode::LShift,
|
||||
sdl2::keyboard::Scancode::LAlt => Scancode::LAlt,
|
||||
sdl2::keyboard::Scancode::LGui => Scancode::LGui,
|
||||
sdl2::keyboard::Scancode::RCtrl => Scancode::RCtrl,
|
||||
sdl2::keyboard::Scancode::RShift => Scancode::RShift,
|
||||
sdl2::keyboard::Scancode::RAlt => Scancode::RAlt,
|
||||
sdl2::keyboard::Scancode::RGui => Scancode::RGui,
|
||||
sdl2::keyboard::Scancode::Mode => Scancode::Mode,
|
||||
sdl2::keyboard::Scancode::AudioNext => Scancode::AudioNext,
|
||||
sdl2::keyboard::Scancode::AudioPrev => Scancode::AudioPrev,
|
||||
sdl2::keyboard::Scancode::AudioStop => Scancode::AudioStop,
|
||||
sdl2::keyboard::Scancode::AudioPlay => Scancode::AudioPlay,
|
||||
sdl2::keyboard::Scancode::AudioMute => Scancode::AudioMute,
|
||||
sdl2::keyboard::Scancode::MediaSelect => Scancode::MediaSelect,
|
||||
sdl2::keyboard::Scancode::Www => Scancode::Www,
|
||||
sdl2::keyboard::Scancode::Mail => Scancode::Mail,
|
||||
sdl2::keyboard::Scancode::Calculator => Scancode::Calculator,
|
||||
sdl2::keyboard::Scancode::Computer => Scancode::Computer,
|
||||
sdl2::keyboard::Scancode::AcSearch => Scancode::AcSearch,
|
||||
sdl2::keyboard::Scancode::AcHome => Scancode::AcHome,
|
||||
sdl2::keyboard::Scancode::AcBack => Scancode::AcBack,
|
||||
sdl2::keyboard::Scancode::AcForward => Scancode::AcForward,
|
||||
sdl2::keyboard::Scancode::AcStop => Scancode::AcStop,
|
||||
sdl2::keyboard::Scancode::AcRefresh => Scancode::AcRefresh,
|
||||
sdl2::keyboard::Scancode::AcBookmarks => Scancode::AcBookmarks,
|
||||
sdl2::keyboard::Scancode::BrightnessDown => Scancode::BrightnessDown,
|
||||
sdl2::keyboard::Scancode::BrightnessUp => Scancode::BrightnessUp,
|
||||
sdl2::keyboard::Scancode::DisplaySwitch => Scancode::DisplaySwitch,
|
||||
sdl2::keyboard::Scancode::KbdIllumToggle => Scancode::KbdIllumToggle,
|
||||
sdl2::keyboard::Scancode::KbdIllumDown => Scancode::KbdIllumDown,
|
||||
sdl2::keyboard::Scancode::KbdIllumUp => Scancode::KbdIllumUp,
|
||||
sdl2::keyboard::Scancode::Eject => Scancode::Eject,
|
||||
sdl2::keyboard::Scancode::Sleep => Scancode::Sleep,
|
||||
sdl2::keyboard::Scancode::App1 => Scancode::App1,
|
||||
sdl2::keyboard::Scancode::App2 => Scancode::App2,
|
||||
sdl2::keyboard::Scancode::Num => Scancode::Num,
|
||||
}
|
||||
}
|
||||
fn from(value: sdl2::keyboard::Scancode) -> Self {
|
||||
match value {
|
||||
sdl2::keyboard::Scancode::A => Scancode::A,
|
||||
sdl2::keyboard::Scancode::B => Scancode::B,
|
||||
sdl2::keyboard::Scancode::C => Scancode::C,
|
||||
sdl2::keyboard::Scancode::D => Scancode::D,
|
||||
sdl2::keyboard::Scancode::E => Scancode::E,
|
||||
sdl2::keyboard::Scancode::F => Scancode::F,
|
||||
sdl2::keyboard::Scancode::G => Scancode::G,
|
||||
sdl2::keyboard::Scancode::H => Scancode::H,
|
||||
sdl2::keyboard::Scancode::I => Scancode::I,
|
||||
sdl2::keyboard::Scancode::J => Scancode::J,
|
||||
sdl2::keyboard::Scancode::K => Scancode::K,
|
||||
sdl2::keyboard::Scancode::L => Scancode::L,
|
||||
sdl2::keyboard::Scancode::M => Scancode::M,
|
||||
sdl2::keyboard::Scancode::N => Scancode::N,
|
||||
sdl2::keyboard::Scancode::O => Scancode::O,
|
||||
sdl2::keyboard::Scancode::P => Scancode::P,
|
||||
sdl2::keyboard::Scancode::Q => Scancode::Q,
|
||||
sdl2::keyboard::Scancode::R => Scancode::R,
|
||||
sdl2::keyboard::Scancode::S => Scancode::S,
|
||||
sdl2::keyboard::Scancode::T => Scancode::T,
|
||||
sdl2::keyboard::Scancode::U => Scancode::U,
|
||||
sdl2::keyboard::Scancode::V => Scancode::V,
|
||||
sdl2::keyboard::Scancode::W => Scancode::W,
|
||||
sdl2::keyboard::Scancode::X => Scancode::X,
|
||||
sdl2::keyboard::Scancode::Y => Scancode::Y,
|
||||
sdl2::keyboard::Scancode::Z => Scancode::Z,
|
||||
sdl2::keyboard::Scancode::Num1 => Scancode::Num1,
|
||||
sdl2::keyboard::Scancode::Num2 => Scancode::Num2,
|
||||
sdl2::keyboard::Scancode::Num3 => Scancode::Num3,
|
||||
sdl2::keyboard::Scancode::Num4 => Scancode::Num4,
|
||||
sdl2::keyboard::Scancode::Num5 => Scancode::Num5,
|
||||
sdl2::keyboard::Scancode::Num6 => Scancode::Num6,
|
||||
sdl2::keyboard::Scancode::Num7 => Scancode::Num7,
|
||||
sdl2::keyboard::Scancode::Num8 => Scancode::Num8,
|
||||
sdl2::keyboard::Scancode::Num9 => Scancode::Num9,
|
||||
sdl2::keyboard::Scancode::Num0 => Scancode::Num0,
|
||||
sdl2::keyboard::Scancode::Return => Scancode::Return,
|
||||
sdl2::keyboard::Scancode::Escape => Scancode::Escape,
|
||||
sdl2::keyboard::Scancode::Backspace => Scancode::Backspace,
|
||||
sdl2::keyboard::Scancode::Tab => Scancode::Tab,
|
||||
sdl2::keyboard::Scancode::Space => Scancode::Space,
|
||||
sdl2::keyboard::Scancode::Minus => Scancode::Minus,
|
||||
sdl2::keyboard::Scancode::Equals => Scancode::Equals,
|
||||
sdl2::keyboard::Scancode::LeftBracket => Scancode::LeftBracket,
|
||||
sdl2::keyboard::Scancode::RightBracket => Scancode::RightBracket,
|
||||
sdl2::keyboard::Scancode::Backslash => Scancode::Backslash,
|
||||
sdl2::keyboard::Scancode::NonUsHash => Scancode::NonUsHash,
|
||||
sdl2::keyboard::Scancode::Semicolon => Scancode::Semicolon,
|
||||
sdl2::keyboard::Scancode::Apostrophe => Scancode::Apostrophe,
|
||||
sdl2::keyboard::Scancode::Grave => Scancode::Grave,
|
||||
sdl2::keyboard::Scancode::Comma => Scancode::Comma,
|
||||
sdl2::keyboard::Scancode::Period => Scancode::Period,
|
||||
sdl2::keyboard::Scancode::Slash => Scancode::Slash,
|
||||
sdl2::keyboard::Scancode::CapsLock => Scancode::CapsLock,
|
||||
sdl2::keyboard::Scancode::F1 => Scancode::F1,
|
||||
sdl2::keyboard::Scancode::F2 => Scancode::F2,
|
||||
sdl2::keyboard::Scancode::F3 => Scancode::F3,
|
||||
sdl2::keyboard::Scancode::F4 => Scancode::F4,
|
||||
sdl2::keyboard::Scancode::F5 => Scancode::F5,
|
||||
sdl2::keyboard::Scancode::F6 => Scancode::F6,
|
||||
sdl2::keyboard::Scancode::F7 => Scancode::F7,
|
||||
sdl2::keyboard::Scancode::F8 => Scancode::F8,
|
||||
sdl2::keyboard::Scancode::F9 => Scancode::F9,
|
||||
sdl2::keyboard::Scancode::F10 => Scancode::F10,
|
||||
sdl2::keyboard::Scancode::F11 => Scancode::F11,
|
||||
sdl2::keyboard::Scancode::F12 => Scancode::F12,
|
||||
sdl2::keyboard::Scancode::PrintScreen => Scancode::PrintScreen,
|
||||
sdl2::keyboard::Scancode::ScrollLock => Scancode::ScrollLock,
|
||||
sdl2::keyboard::Scancode::Pause => Scancode::Pause,
|
||||
sdl2::keyboard::Scancode::Insert => Scancode::Insert,
|
||||
sdl2::keyboard::Scancode::Home => Scancode::Home,
|
||||
sdl2::keyboard::Scancode::PageUp => Scancode::PageUp,
|
||||
sdl2::keyboard::Scancode::Delete => Scancode::Delete,
|
||||
sdl2::keyboard::Scancode::End => Scancode::End,
|
||||
sdl2::keyboard::Scancode::PageDown => Scancode::PageDown,
|
||||
sdl2::keyboard::Scancode::Right => Scancode::Right,
|
||||
sdl2::keyboard::Scancode::Left => Scancode::Left,
|
||||
sdl2::keyboard::Scancode::Down => Scancode::Down,
|
||||
sdl2::keyboard::Scancode::Up => Scancode::Up,
|
||||
sdl2::keyboard::Scancode::NumLockClear => Scancode::NumLockClear,
|
||||
sdl2::keyboard::Scancode::KpDivide => Scancode::KpDivide,
|
||||
sdl2::keyboard::Scancode::KpMultiply => Scancode::KpMultiply,
|
||||
sdl2::keyboard::Scancode::KpMinus => Scancode::KpMinus,
|
||||
sdl2::keyboard::Scancode::KpPlus => Scancode::KpPlus,
|
||||
sdl2::keyboard::Scancode::KpEnter => Scancode::KpEnter,
|
||||
sdl2::keyboard::Scancode::Kp1 => Scancode::Kp1,
|
||||
sdl2::keyboard::Scancode::Kp2 => Scancode::Kp2,
|
||||
sdl2::keyboard::Scancode::Kp3 => Scancode::Kp3,
|
||||
sdl2::keyboard::Scancode::Kp4 => Scancode::Kp4,
|
||||
sdl2::keyboard::Scancode::Kp5 => Scancode::Kp5,
|
||||
sdl2::keyboard::Scancode::Kp6 => Scancode::Kp6,
|
||||
sdl2::keyboard::Scancode::Kp7 => Scancode::Kp7,
|
||||
sdl2::keyboard::Scancode::Kp8 => Scancode::Kp8,
|
||||
sdl2::keyboard::Scancode::Kp9 => Scancode::Kp9,
|
||||
sdl2::keyboard::Scancode::Kp0 => Scancode::Kp0,
|
||||
sdl2::keyboard::Scancode::KpPeriod => Scancode::KpPeriod,
|
||||
sdl2::keyboard::Scancode::NonUsBackslash => Scancode::NonUsBackslash,
|
||||
sdl2::keyboard::Scancode::Application => Scancode::Application,
|
||||
sdl2::keyboard::Scancode::Power => Scancode::Power,
|
||||
sdl2::keyboard::Scancode::KpEquals => Scancode::KpEquals,
|
||||
sdl2::keyboard::Scancode::F13 => Scancode::F13,
|
||||
sdl2::keyboard::Scancode::F14 => Scancode::F14,
|
||||
sdl2::keyboard::Scancode::F15 => Scancode::F15,
|
||||
sdl2::keyboard::Scancode::F16 => Scancode::F16,
|
||||
sdl2::keyboard::Scancode::F17 => Scancode::F17,
|
||||
sdl2::keyboard::Scancode::F18 => Scancode::F18,
|
||||
sdl2::keyboard::Scancode::F19 => Scancode::F19,
|
||||
sdl2::keyboard::Scancode::F20 => Scancode::F20,
|
||||
sdl2::keyboard::Scancode::F21 => Scancode::F21,
|
||||
sdl2::keyboard::Scancode::F22 => Scancode::F22,
|
||||
sdl2::keyboard::Scancode::F23 => Scancode::F23,
|
||||
sdl2::keyboard::Scancode::F24 => Scancode::F24,
|
||||
sdl2::keyboard::Scancode::Execute => Scancode::Execute,
|
||||
sdl2::keyboard::Scancode::Help => Scancode::Help,
|
||||
sdl2::keyboard::Scancode::Menu => Scancode::Menu,
|
||||
sdl2::keyboard::Scancode::Select => Scancode::Select,
|
||||
sdl2::keyboard::Scancode::Stop => Scancode::Stop,
|
||||
sdl2::keyboard::Scancode::Again => Scancode::Again,
|
||||
sdl2::keyboard::Scancode::Undo => Scancode::Undo,
|
||||
sdl2::keyboard::Scancode::Cut => Scancode::Cut,
|
||||
sdl2::keyboard::Scancode::Copy => Scancode::Copy,
|
||||
sdl2::keyboard::Scancode::Paste => Scancode::Paste,
|
||||
sdl2::keyboard::Scancode::Find => Scancode::Find,
|
||||
sdl2::keyboard::Scancode::Mute => Scancode::Mute,
|
||||
sdl2::keyboard::Scancode::VolumeUp => Scancode::VolumeUp,
|
||||
sdl2::keyboard::Scancode::VolumeDown => Scancode::VolumeDown,
|
||||
sdl2::keyboard::Scancode::KpComma => Scancode::KpComma,
|
||||
sdl2::keyboard::Scancode::KpEqualsAS400 => Scancode::KpEqualsAS400,
|
||||
sdl2::keyboard::Scancode::International1 => Scancode::International1,
|
||||
sdl2::keyboard::Scancode::International2 => Scancode::International2,
|
||||
sdl2::keyboard::Scancode::International3 => Scancode::International3,
|
||||
sdl2::keyboard::Scancode::International4 => Scancode::International4,
|
||||
sdl2::keyboard::Scancode::International5 => Scancode::International5,
|
||||
sdl2::keyboard::Scancode::International6 => Scancode::International6,
|
||||
sdl2::keyboard::Scancode::International7 => Scancode::International7,
|
||||
sdl2::keyboard::Scancode::International8 => Scancode::International8,
|
||||
sdl2::keyboard::Scancode::International9 => Scancode::International9,
|
||||
sdl2::keyboard::Scancode::Lang1 => Scancode::Lang1,
|
||||
sdl2::keyboard::Scancode::Lang2 => Scancode::Lang2,
|
||||
sdl2::keyboard::Scancode::Lang3 => Scancode::Lang3,
|
||||
sdl2::keyboard::Scancode::Lang4 => Scancode::Lang4,
|
||||
sdl2::keyboard::Scancode::Lang5 => Scancode::Lang5,
|
||||
sdl2::keyboard::Scancode::Lang6 => Scancode::Lang6,
|
||||
sdl2::keyboard::Scancode::Lang7 => Scancode::Lang7,
|
||||
sdl2::keyboard::Scancode::Lang8 => Scancode::Lang8,
|
||||
sdl2::keyboard::Scancode::Lang9 => Scancode::Lang9,
|
||||
sdl2::keyboard::Scancode::AltErase => Scancode::AltErase,
|
||||
sdl2::keyboard::Scancode::SysReq => Scancode::SysReq,
|
||||
sdl2::keyboard::Scancode::Cancel => Scancode::Cancel,
|
||||
sdl2::keyboard::Scancode::Clear => Scancode::Clear,
|
||||
sdl2::keyboard::Scancode::Prior => Scancode::Prior,
|
||||
sdl2::keyboard::Scancode::Return2 => Scancode::Return2,
|
||||
sdl2::keyboard::Scancode::Separator => Scancode::Separator,
|
||||
sdl2::keyboard::Scancode::Out => Scancode::Out,
|
||||
sdl2::keyboard::Scancode::Oper => Scancode::Oper,
|
||||
sdl2::keyboard::Scancode::ClearAgain => Scancode::ClearAgain,
|
||||
sdl2::keyboard::Scancode::CrSel => Scancode::CrSel,
|
||||
sdl2::keyboard::Scancode::ExSel => Scancode::ExSel,
|
||||
sdl2::keyboard::Scancode::Kp00 => Scancode::Kp00,
|
||||
sdl2::keyboard::Scancode::Kp000 => Scancode::Kp000,
|
||||
sdl2::keyboard::Scancode::ThousandsSeparator => Scancode::ThousandsSeparator,
|
||||
sdl2::keyboard::Scancode::DecimalSeparator => Scancode::DecimalSeparator,
|
||||
sdl2::keyboard::Scancode::CurrencyUnit => Scancode::CurrencyUnit,
|
||||
sdl2::keyboard::Scancode::CurrencySubUnit => Scancode::CurrencySubUnit,
|
||||
sdl2::keyboard::Scancode::KpLeftParen => Scancode::KpLeftParen,
|
||||
sdl2::keyboard::Scancode::KpRightParen => Scancode::KpRightParen,
|
||||
sdl2::keyboard::Scancode::KpLeftBrace => Scancode::KpLeftBrace,
|
||||
sdl2::keyboard::Scancode::KpRightBrace => Scancode::KpRightBrace,
|
||||
sdl2::keyboard::Scancode::KpTab => Scancode::KpTab,
|
||||
sdl2::keyboard::Scancode::KpBackspace => Scancode::KpBackspace,
|
||||
sdl2::keyboard::Scancode::KpA => Scancode::KpA,
|
||||
sdl2::keyboard::Scancode::KpB => Scancode::KpB,
|
||||
sdl2::keyboard::Scancode::KpC => Scancode::KpC,
|
||||
sdl2::keyboard::Scancode::KpD => Scancode::KpD,
|
||||
sdl2::keyboard::Scancode::KpE => Scancode::KpE,
|
||||
sdl2::keyboard::Scancode::KpF => Scancode::KpF,
|
||||
sdl2::keyboard::Scancode::KpXor => Scancode::KpXor,
|
||||
sdl2::keyboard::Scancode::KpPower => Scancode::KpPower,
|
||||
sdl2::keyboard::Scancode::KpPercent => Scancode::KpPercent,
|
||||
sdl2::keyboard::Scancode::KpLess => Scancode::KpLess,
|
||||
sdl2::keyboard::Scancode::KpGreater => Scancode::KpGreater,
|
||||
sdl2::keyboard::Scancode::KpAmpersand => Scancode::KpAmpersand,
|
||||
sdl2::keyboard::Scancode::KpDblAmpersand => Scancode::KpDblAmpersand,
|
||||
sdl2::keyboard::Scancode::KpVerticalBar => Scancode::KpVerticalBar,
|
||||
sdl2::keyboard::Scancode::KpDblVerticalBar => Scancode::KpDblVerticalBar,
|
||||
sdl2::keyboard::Scancode::KpColon => Scancode::KpColon,
|
||||
sdl2::keyboard::Scancode::KpHash => Scancode::KpHash,
|
||||
sdl2::keyboard::Scancode::KpSpace => Scancode::KpSpace,
|
||||
sdl2::keyboard::Scancode::KpAt => Scancode::KpAt,
|
||||
sdl2::keyboard::Scancode::KpExclam => Scancode::KpExclam,
|
||||
sdl2::keyboard::Scancode::KpMemStore => Scancode::KpMemStore,
|
||||
sdl2::keyboard::Scancode::KpMemRecall => Scancode::KpMemRecall,
|
||||
sdl2::keyboard::Scancode::KpMemClear => Scancode::KpMemClear,
|
||||
sdl2::keyboard::Scancode::KpMemAdd => Scancode::KpMemAdd,
|
||||
sdl2::keyboard::Scancode::KpMemSubtract => Scancode::KpMemSubtract,
|
||||
sdl2::keyboard::Scancode::KpMemMultiply => Scancode::KpMemMultiply,
|
||||
sdl2::keyboard::Scancode::KpMemDivide => Scancode::KpMemDivide,
|
||||
sdl2::keyboard::Scancode::KpPlusMinus => Scancode::KpPlusMinus,
|
||||
sdl2::keyboard::Scancode::KpClear => Scancode::KpClear,
|
||||
sdl2::keyboard::Scancode::KpClearEntry => Scancode::KpClearEntry,
|
||||
sdl2::keyboard::Scancode::KpBinary => Scancode::KpBinary,
|
||||
sdl2::keyboard::Scancode::KpOctal => Scancode::KpOctal,
|
||||
sdl2::keyboard::Scancode::KpDecimal => Scancode::KpDecimal,
|
||||
sdl2::keyboard::Scancode::KpHexadecimal => Scancode::KpHexadecimal,
|
||||
sdl2::keyboard::Scancode::LCtrl => Scancode::LCtrl,
|
||||
sdl2::keyboard::Scancode::LShift => Scancode::LShift,
|
||||
sdl2::keyboard::Scancode::LAlt => Scancode::LAlt,
|
||||
sdl2::keyboard::Scancode::LGui => Scancode::LGui,
|
||||
sdl2::keyboard::Scancode::RCtrl => Scancode::RCtrl,
|
||||
sdl2::keyboard::Scancode::RShift => Scancode::RShift,
|
||||
sdl2::keyboard::Scancode::RAlt => Scancode::RAlt,
|
||||
sdl2::keyboard::Scancode::RGui => Scancode::RGui,
|
||||
sdl2::keyboard::Scancode::Mode => Scancode::Mode,
|
||||
sdl2::keyboard::Scancode::AudioNext => Scancode::AudioNext,
|
||||
sdl2::keyboard::Scancode::AudioPrev => Scancode::AudioPrev,
|
||||
sdl2::keyboard::Scancode::AudioStop => Scancode::AudioStop,
|
||||
sdl2::keyboard::Scancode::AudioPlay => Scancode::AudioPlay,
|
||||
sdl2::keyboard::Scancode::AudioMute => Scancode::AudioMute,
|
||||
sdl2::keyboard::Scancode::MediaSelect => Scancode::MediaSelect,
|
||||
sdl2::keyboard::Scancode::Www => Scancode::Www,
|
||||
sdl2::keyboard::Scancode::Mail => Scancode::Mail,
|
||||
sdl2::keyboard::Scancode::Calculator => Scancode::Calculator,
|
||||
sdl2::keyboard::Scancode::Computer => Scancode::Computer,
|
||||
sdl2::keyboard::Scancode::AcSearch => Scancode::AcSearch,
|
||||
sdl2::keyboard::Scancode::AcHome => Scancode::AcHome,
|
||||
sdl2::keyboard::Scancode::AcBack => Scancode::AcBack,
|
||||
sdl2::keyboard::Scancode::AcForward => Scancode::AcForward,
|
||||
sdl2::keyboard::Scancode::AcStop => Scancode::AcStop,
|
||||
sdl2::keyboard::Scancode::AcRefresh => Scancode::AcRefresh,
|
||||
sdl2::keyboard::Scancode::AcBookmarks => Scancode::AcBookmarks,
|
||||
sdl2::keyboard::Scancode::BrightnessDown => Scancode::BrightnessDown,
|
||||
sdl2::keyboard::Scancode::BrightnessUp => Scancode::BrightnessUp,
|
||||
sdl2::keyboard::Scancode::DisplaySwitch => Scancode::DisplaySwitch,
|
||||
sdl2::keyboard::Scancode::KbdIllumToggle => Scancode::KbdIllumToggle,
|
||||
sdl2::keyboard::Scancode::KbdIllumDown => Scancode::KbdIllumDown,
|
||||
sdl2::keyboard::Scancode::KbdIllumUp => Scancode::KbdIllumUp,
|
||||
sdl2::keyboard::Scancode::Eject => Scancode::Eject,
|
||||
sdl2::keyboard::Scancode::Sleep => Scancode::Sleep,
|
||||
sdl2::keyboard::Scancode::App1 => Scancode::App1,
|
||||
sdl2::keyboard::Scancode::App2 => Scancode::App2,
|
||||
sdl2::keyboard::Scancode::Num => Scancode::Num,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,41 +5,41 @@ pub mod mouse;
|
|||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
pub enum ButtonState {
|
||||
Idle,
|
||||
Pressed,
|
||||
Held,
|
||||
Released,
|
||||
Idle,
|
||||
Pressed,
|
||||
Held,
|
||||
Released,
|
||||
}
|
||||
|
||||
/// Common trait for input device implementations.
|
||||
pub trait InputDevice {
|
||||
/// Performs internal house-keeping necessary for properly reporting the current state of this
|
||||
/// input device. Normally this should be called on the device before all of this frame's
|
||||
/// input events have been processed via `handle_event`.
|
||||
fn update(&mut self);
|
||||
/// Performs internal house-keeping necessary for properly reporting the current state of this
|
||||
/// input device. Normally this should be called on the device before all of this frame's
|
||||
/// input events have been processed via `handle_event`.
|
||||
fn update(&mut self);
|
||||
}
|
||||
|
||||
/// Container for all available input devices available for applications to use.
|
||||
pub struct InputDevices {
|
||||
pub keyboard: keyboard::Keyboard,
|
||||
pub mouse: mouse::Mouse,
|
||||
pub keyboard: keyboard::Keyboard,
|
||||
pub mouse: mouse::Mouse,
|
||||
}
|
||||
|
||||
impl InputDevice for InputDevices {
|
||||
fn update(&mut self) {
|
||||
self.keyboard.update();
|
||||
self.mouse.update();
|
||||
}
|
||||
fn update(&mut self) {
|
||||
self.keyboard.update();
|
||||
self.mouse.update();
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemEventHandler for InputDevices {
|
||||
fn handle_event(&mut self, event: &SystemEvent) -> bool {
|
||||
if self.keyboard.handle_event(event) {
|
||||
return true;
|
||||
}
|
||||
if self.mouse.handle_event(event) {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
fn handle_event(&mut self, event: &SystemEvent) -> bool {
|
||||
if self.keyboard.handle_event(event) {
|
||||
return true;
|
||||
}
|
||||
if self.mouse.handle_event(event) {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
|
@ -13,23 +13,23 @@ bitflags! {
|
|||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[repr(u8)]
|
||||
pub enum MouseButton {
|
||||
Unknown = 0,
|
||||
Left = sdl2::mouse::MouseButton::Left as u8,
|
||||
Middle = sdl2::mouse::MouseButton::Middle as u8,
|
||||
Right = sdl2::mouse::MouseButton::Right as u8,
|
||||
X1 = sdl2::mouse::MouseButton::X1 as u8,
|
||||
X2 = sdl2::mouse::MouseButton::X2 as u8,
|
||||
Unknown = 0,
|
||||
Left = sdl2::mouse::MouseButton::Left as u8,
|
||||
Middle = sdl2::mouse::MouseButton::Middle as u8,
|
||||
Right = sdl2::mouse::MouseButton::Right as u8,
|
||||
X1 = sdl2::mouse::MouseButton::X1 as u8,
|
||||
X2 = sdl2::mouse::MouseButton::X2 as u8,
|
||||
}
|
||||
|
||||
impl From<sdl2::mouse::MouseButton> for MouseButton {
|
||||
fn from(value: sdl2::mouse::MouseButton) -> Self {
|
||||
match value {
|
||||
sdl2::mouse::MouseButton::Unknown => MouseButton::Unknown,
|
||||
sdl2::mouse::MouseButton::Left => MouseButton::Left,
|
||||
sdl2::mouse::MouseButton::Middle => MouseButton::Middle,
|
||||
sdl2::mouse::MouseButton::Right => MouseButton::Right,
|
||||
sdl2::mouse::MouseButton::X1 => MouseButton::X1,
|
||||
sdl2::mouse::MouseButton::X2 => MouseButton::X2
|
||||
}
|
||||
}
|
||||
fn from(value: sdl2::mouse::MouseButton) -> Self {
|
||||
match value {
|
||||
sdl2::mouse::MouseButton::Unknown => MouseButton::Unknown,
|
||||
sdl2::mouse::MouseButton::Left => MouseButton::Left,
|
||||
sdl2::mouse::MouseButton::Middle => MouseButton::Middle,
|
||||
sdl2::mouse::MouseButton::Right => MouseButton::Right,
|
||||
sdl2::mouse::MouseButton::X1 => MouseButton::X1,
|
||||
sdl2::mouse::MouseButton::X2 => MouseButton::X2
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,22 +17,22 @@ const DEFAULT_MOUSE_CURSOR_HEIGHT: usize = 16;
|
|||
|
||||
#[rustfmt::skip]
|
||||
const DEFAULT_MOUSE_CURSOR: [u8; DEFAULT_MOUSE_CURSOR_WIDTH * DEFAULT_MOUSE_CURSOR_HEIGHT] = [
|
||||
0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x0f,0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x0f,0x00,0x00,0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0x00,0x00,0xff,0xff,0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0x00,0x0f,0x0f,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
|
||||
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
];
|
||||
|
||||
/// Holds the current state of the mouse.
|
||||
|
@ -44,287 +44,287 @@ const DEFAULT_MOUSE_CURSOR: [u8; DEFAULT_MOUSE_CURSOR_WIDTH * DEFAULT_MOUSE_CURS
|
|||
/// [`System`]: crate::System
|
||||
#[derive(Debug)]
|
||||
pub struct Mouse {
|
||||
x: i32,
|
||||
y: i32,
|
||||
x_delta: i32,
|
||||
y_delta: i32,
|
||||
buttons: [ButtonState; MAX_BUTTONS],
|
||||
cursor: Bitmap,
|
||||
cursor_background: Bitmap,
|
||||
cursor_hotspot_x: u32,
|
||||
cursor_hotspot_y: u32,
|
||||
cursor_enabled: bool,
|
||||
x: i32,
|
||||
y: i32,
|
||||
x_delta: i32,
|
||||
y_delta: i32,
|
||||
buttons: [ButtonState; MAX_BUTTONS],
|
||||
cursor: Bitmap,
|
||||
cursor_background: Bitmap,
|
||||
cursor_hotspot_x: u32,
|
||||
cursor_hotspot_y: u32,
|
||||
cursor_enabled: bool,
|
||||
}
|
||||
|
||||
impl Mouse {
|
||||
pub fn new() -> Mouse {
|
||||
let (cursor, cursor_background, cursor_hotspot_x, cursor_hotspot_y) =
|
||||
Self::get_default_mouse_cursor();
|
||||
pub fn new() -> Mouse {
|
||||
let (cursor, cursor_background, cursor_hotspot_x, cursor_hotspot_y) =
|
||||
Self::get_default_mouse_cursor();
|
||||
|
||||
Mouse {
|
||||
x: 0,
|
||||
y: 0,
|
||||
x_delta: 0,
|
||||
y_delta: 0,
|
||||
buttons: [ButtonState::Idle; MAX_BUTTONS],
|
||||
cursor,
|
||||
cursor_background,
|
||||
cursor_hotspot_x,
|
||||
cursor_hotspot_y,
|
||||
cursor_enabled: false,
|
||||
}
|
||||
}
|
||||
Mouse {
|
||||
x: 0,
|
||||
y: 0,
|
||||
x_delta: 0,
|
||||
y_delta: 0,
|
||||
buttons: [ButtonState::Idle; MAX_BUTTONS],
|
||||
cursor,
|
||||
cursor_background,
|
||||
cursor_hotspot_x,
|
||||
cursor_hotspot_y,
|
||||
cursor_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current x coordinate of the mouse cursor.
|
||||
#[inline]
|
||||
pub fn x(&self) -> i32 {
|
||||
self.x
|
||||
}
|
||||
/// Returns the current x coordinate of the mouse cursor.
|
||||
#[inline]
|
||||
pub fn x(&self) -> i32 {
|
||||
self.x
|
||||
}
|
||||
|
||||
/// Returns the current y coordinate of the mouse cursor.
|
||||
#[inline]
|
||||
pub fn y(&self) -> i32 {
|
||||
self.y
|
||||
}
|
||||
/// Returns the current y coordinate of the mouse cursor.
|
||||
#[inline]
|
||||
pub fn y(&self) -> i32 {
|
||||
self.y
|
||||
}
|
||||
|
||||
/// Returns the amount of pixels along the x-axis that the mouse cursor moved since the last
|
||||
/// time that the mouse state was updated.
|
||||
#[inline]
|
||||
pub fn x_delta(&self) -> i32 {
|
||||
self.x_delta
|
||||
}
|
||||
/// Returns the amount of pixels along the x-axis that the mouse cursor moved since the last
|
||||
/// time that the mouse state was updated.
|
||||
#[inline]
|
||||
pub fn x_delta(&self) -> i32 {
|
||||
self.x_delta
|
||||
}
|
||||
|
||||
/// Returns the amount of pixels along the y-axis that the mouse cursor moved since the last
|
||||
/// time that the mouse state was updated.
|
||||
#[inline]
|
||||
pub fn y_delta(&self) -> i32 {
|
||||
self.y_delta
|
||||
}
|
||||
/// Returns the amount of pixels along the y-axis that the mouse cursor moved since the last
|
||||
/// time that the mouse state was updated.
|
||||
#[inline]
|
||||
pub fn y_delta(&self) -> i32 {
|
||||
self.y_delta
|
||||
}
|
||||
|
||||
/// Returns true if the given button was just pressed or is being held down.
|
||||
#[inline]
|
||||
pub fn is_button_down(&self, button: usize) -> bool {
|
||||
matches!(
|
||||
/// Returns true if the given button was just pressed or is being held down.
|
||||
#[inline]
|
||||
pub fn is_button_down(&self, button: usize) -> bool {
|
||||
matches!(
|
||||
self.buttons[button],
|
||||
ButtonState::Pressed | ButtonState::Held
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given button was not just pressed and is not being held down.
|
||||
#[inline]
|
||||
pub fn is_button_up(&self, button: usize) -> bool {
|
||||
matches!(
|
||||
/// Returns true if the given button was not just pressed and is not being held down.
|
||||
#[inline]
|
||||
pub fn is_button_up(&self, button: usize) -> bool {
|
||||
matches!(
|
||||
self.buttons[button],
|
||||
ButtonState::Released | ButtonState::Idle
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given button was just pressed (not being held down, yet).
|
||||
#[inline]
|
||||
pub fn is_button_pressed(&self, button: usize) -> bool {
|
||||
self.buttons[button] == ButtonState::Pressed
|
||||
}
|
||||
/// Returns true if the given button was just pressed (not being held down, yet).
|
||||
#[inline]
|
||||
pub fn is_button_pressed(&self, button: usize) -> bool {
|
||||
self.buttons[button] == ButtonState::Pressed
|
||||
}
|
||||
|
||||
/// Returns true if the given button was just released.
|
||||
#[inline]
|
||||
pub fn is_button_released(&self, button: usize) -> bool {
|
||||
self.buttons[button] == ButtonState::Released
|
||||
}
|
||||
/// Returns true if the given button was just released.
|
||||
#[inline]
|
||||
pub fn is_button_released(&self, button: usize) -> bool {
|
||||
self.buttons[button] == ButtonState::Released
|
||||
}
|
||||
|
||||
/// Returns a reference to the current mouse cursor bitmap.
|
||||
#[inline]
|
||||
pub fn cursor_bitmap(&self) -> &Bitmap {
|
||||
&self.cursor
|
||||
}
|
||||
/// Returns a reference to the current mouse cursor bitmap.
|
||||
#[inline]
|
||||
pub fn cursor_bitmap(&self) -> &Bitmap {
|
||||
&self.cursor
|
||||
}
|
||||
|
||||
/// Returns the current mouse cursor's "hotspot" x coordinate.
|
||||
#[inline]
|
||||
pub fn cursor_hotspot_x(&self) -> u32 {
|
||||
self.cursor_hotspot_x
|
||||
}
|
||||
/// Returns the current mouse cursor's "hotspot" x coordinate.
|
||||
#[inline]
|
||||
pub fn cursor_hotspot_x(&self) -> u32 {
|
||||
self.cursor_hotspot_x
|
||||
}
|
||||
|
||||
/// Returns the current mouse cursor's "hotspot" y coordinate.
|
||||
#[inline]
|
||||
pub fn cursor_hotspot_y(&self) -> u32 {
|
||||
self.cursor_hotspot_y
|
||||
}
|
||||
/// Returns the current mouse cursor's "hotspot" y coordinate.
|
||||
#[inline]
|
||||
pub fn cursor_hotspot_y(&self) -> u32 {
|
||||
self.cursor_hotspot_y
|
||||
}
|
||||
|
||||
/// Returns true if mouse cursor bitmap rendering is enabled.
|
||||
#[inline]
|
||||
pub fn is_cursor_enabled(&self) -> bool {
|
||||
self.cursor_enabled
|
||||
}
|
||||
/// Returns true if mouse cursor bitmap rendering is enabled.
|
||||
#[inline]
|
||||
pub fn is_cursor_enabled(&self) -> bool {
|
||||
self.cursor_enabled
|
||||
}
|
||||
|
||||
/// Enables or disables mouse cursor bitmap rendering.
|
||||
#[inline]
|
||||
pub fn enable_cursor(&mut self, enable: bool) {
|
||||
self.cursor_enabled = enable;
|
||||
}
|
||||
/// Enables or disables mouse cursor bitmap rendering.
|
||||
#[inline]
|
||||
pub fn enable_cursor(&mut self, enable: bool) {
|
||||
self.cursor_enabled = enable;
|
||||
}
|
||||
|
||||
/// Sets the [`Bitmap`] used to display the mouse cursor and the "hotspot" coordinate. The
|
||||
/// bitmap provided here should be set up to use color 255 as the transparent color.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `cursor`: the bitmap to be used to display the mouse cursor on screen
|
||||
/// * `hotspot_x`: the "hotspot" x coordinate
|
||||
/// * `hotspot_y`: the "hotspot" y coordinate.
|
||||
pub fn set_mouse_cursor(&mut self, cursor: Bitmap, hotspot_x: u32, hotspot_y: u32) {
|
||||
self.cursor = cursor;
|
||||
self.cursor_background = Bitmap::new(self.cursor.width(), self.cursor.height()).unwrap();
|
||||
self.cursor_hotspot_x = hotspot_x;
|
||||
self.cursor_hotspot_y = hotspot_y;
|
||||
}
|
||||
/// Sets the [`Bitmap`] used to display the mouse cursor and the "hotspot" coordinate. The
|
||||
/// bitmap provided here should be set up to use color 255 as the transparent color.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `cursor`: the bitmap to be used to display the mouse cursor on screen
|
||||
/// * `hotspot_x`: the "hotspot" x coordinate
|
||||
/// * `hotspot_y`: the "hotspot" y coordinate.
|
||||
pub fn set_mouse_cursor(&mut self, cursor: Bitmap, hotspot_x: u32, hotspot_y: u32) {
|
||||
self.cursor = cursor;
|
||||
self.cursor_background = Bitmap::new(self.cursor.width(), self.cursor.height()).unwrap();
|
||||
self.cursor_hotspot_x = hotspot_x;
|
||||
self.cursor_hotspot_y = hotspot_y;
|
||||
}
|
||||
|
||||
/// Resets the mouse cursor bitmap and "hotspot" coordinate back to the default settings.
|
||||
pub fn set_default_mouse_cursor(&mut self) {
|
||||
let (cursor, background, hotspot_x, hotspot_y) = Self::get_default_mouse_cursor();
|
||||
self.cursor = cursor;
|
||||
self.cursor_background = background;
|
||||
self.cursor_hotspot_x = hotspot_x;
|
||||
self.cursor_hotspot_y = hotspot_y;
|
||||
}
|
||||
/// Resets the mouse cursor bitmap and "hotspot" coordinate back to the default settings.
|
||||
pub fn set_default_mouse_cursor(&mut self) {
|
||||
let (cursor, background, hotspot_x, hotspot_y) = Self::get_default_mouse_cursor();
|
||||
self.cursor = cursor;
|
||||
self.cursor_background = background;
|
||||
self.cursor_hotspot_x = hotspot_x;
|
||||
self.cursor_hotspot_y = hotspot_y;
|
||||
}
|
||||
|
||||
fn get_default_mouse_cursor() -> (Bitmap, Bitmap, u32, u32) {
|
||||
let mut cursor = Bitmap::new(
|
||||
DEFAULT_MOUSE_CURSOR_WIDTH as u32,
|
||||
DEFAULT_MOUSE_CURSOR_HEIGHT as u32,
|
||||
)
|
||||
.unwrap();
|
||||
cursor.pixels_mut().copy_from_slice(&DEFAULT_MOUSE_CURSOR);
|
||||
fn get_default_mouse_cursor() -> (Bitmap, Bitmap, u32, u32) {
|
||||
let mut cursor = Bitmap::new(
|
||||
DEFAULT_MOUSE_CURSOR_WIDTH as u32,
|
||||
DEFAULT_MOUSE_CURSOR_HEIGHT as u32,
|
||||
)
|
||||
.unwrap();
|
||||
cursor.pixels_mut().copy_from_slice(&DEFAULT_MOUSE_CURSOR);
|
||||
|
||||
let cursor_background = Bitmap::new(cursor.width(), cursor.height()).unwrap();
|
||||
let cursor_background = Bitmap::new(cursor.width(), cursor.height()).unwrap();
|
||||
|
||||
(
|
||||
cursor,
|
||||
cursor_background,
|
||||
DEFAULT_MOUSE_CURSOR_HOTSPOT_X,
|
||||
DEFAULT_MOUSE_CURSOR_HOTSPOT_Y,
|
||||
)
|
||||
}
|
||||
(
|
||||
cursor,
|
||||
cursor_background,
|
||||
DEFAULT_MOUSE_CURSOR_HOTSPOT_X,
|
||||
DEFAULT_MOUSE_CURSOR_HOTSPOT_Y,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_cursor_render_position(&self) -> (i32, i32) {
|
||||
(
|
||||
self.x - self.cursor_hotspot_x as i32,
|
||||
self.y - self.cursor_hotspot_y as i32,
|
||||
)
|
||||
}
|
||||
#[inline]
|
||||
fn get_cursor_render_position(&self) -> (i32, i32) {
|
||||
(
|
||||
self.x - self.cursor_hotspot_x as i32,
|
||||
self.y - self.cursor_hotspot_y as i32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Renders the mouse cursor bitmap onto the destination bitmap at the mouse's current
|
||||
/// position. The destination bitmap specified is assumed to be the [`System`]'s video
|
||||
/// backbuffer bitmap. The background on the destination bitmap is saved internally and a
|
||||
/// subsequent call to [`Mouse::hide_cursor`] will restore the background.
|
||||
///
|
||||
/// If mouse cursor rendering is not currently enabled, this method does nothing.
|
||||
///
|
||||
/// Applications will not normally need to call this method, as if mouse cursor rendering is
|
||||
/// enabled, this will be automatically handled by [`System::display`].
|
||||
///
|
||||
/// [`System`]: crate::System
|
||||
/// [`System::display`]: crate::System::display
|
||||
pub fn render_cursor(&mut self, dest: &mut Bitmap) {
|
||||
if !self.cursor_enabled {
|
||||
return;
|
||||
}
|
||||
/// Renders the mouse cursor bitmap onto the destination bitmap at the mouse's current
|
||||
/// position. The destination bitmap specified is assumed to be the [`System`]'s video
|
||||
/// backbuffer bitmap. The background on the destination bitmap is saved internally and a
|
||||
/// subsequent call to [`Mouse::hide_cursor`] will restore the background.
|
||||
///
|
||||
/// If mouse cursor rendering is not currently enabled, this method does nothing.
|
||||
///
|
||||
/// Applications will not normally need to call this method, as if mouse cursor rendering is
|
||||
/// enabled, this will be automatically handled by [`System::display`].
|
||||
///
|
||||
/// [`System`]: crate::System
|
||||
/// [`System::display`]: crate::System::display
|
||||
pub fn render_cursor(&mut self, dest: &mut Bitmap) {
|
||||
if !self.cursor_enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
let (x, y) = self.get_cursor_render_position();
|
||||
let (x, y) = self.get_cursor_render_position();
|
||||
|
||||
// preserve existing background first
|
||||
self.cursor_background.blit_region(
|
||||
BlitMethod::Solid,
|
||||
&dest,
|
||||
&Rect::new(x, y, self.cursor.width(), self.cursor.height()),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
// preserve existing background first
|
||||
self.cursor_background.blit_region(
|
||||
BlitMethod::Solid,
|
||||
&dest,
|
||||
&Rect::new(x, y, self.cursor.width(), self.cursor.height()),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
dest.blit(BlitMethod::Transparent(255), &self.cursor, x, y);
|
||||
}
|
||||
dest.blit(BlitMethod::Transparent(255), &self.cursor, x, y);
|
||||
}
|
||||
|
||||
/// Restores the original destination bitmap contents where the mouse cursor bitmap was
|
||||
/// rendered to during the previous call to [`Mouse::render_cursor`]. The destination bitmap
|
||||
/// specified is assumed to be the [`System`]'s video backbuffer bitmap.
|
||||
///
|
||||
/// If mouse cursor rendering is not currently enabled, this method does nothing.
|
||||
///
|
||||
/// Applications will not normally need to call this method, as if mouse cursor rendering is
|
||||
/// enabled, this will be automatically handled by [`System::display`].
|
||||
///
|
||||
/// [`System`]: crate::System
|
||||
/// [`System::display`]: crate::System::display
|
||||
pub fn hide_cursor(&mut self, dest: &mut Bitmap) {
|
||||
if !self.cursor_enabled {
|
||||
return;
|
||||
}
|
||||
/// Restores the original destination bitmap contents where the mouse cursor bitmap was
|
||||
/// rendered to during the previous call to [`Mouse::render_cursor`]. The destination bitmap
|
||||
/// specified is assumed to be the [`System`]'s video backbuffer bitmap.
|
||||
///
|
||||
/// If mouse cursor rendering is not currently enabled, this method does nothing.
|
||||
///
|
||||
/// Applications will not normally need to call this method, as if mouse cursor rendering is
|
||||
/// enabled, this will be automatically handled by [`System::display`].
|
||||
///
|
||||
/// [`System`]: crate::System
|
||||
/// [`System::display`]: crate::System::display
|
||||
pub fn hide_cursor(&mut self, dest: &mut Bitmap) {
|
||||
if !self.cursor_enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
let (x, y) = self.get_cursor_render_position();
|
||||
dest.blit(BlitMethod::Solid, &self.cursor_background, x, y);
|
||||
}
|
||||
let (x, y) = self.get_cursor_render_position();
|
||||
dest.blit(BlitMethod::Solid, &self.cursor_background, x, y);
|
||||
}
|
||||
|
||||
fn update_button_state(&mut self, button: u32, is_pressed: bool) {
|
||||
let button_state = &mut self.buttons[button as usize];
|
||||
*button_state = if is_pressed {
|
||||
match *button_state {
|
||||
ButtonState::Pressed => ButtonState::Held,
|
||||
ButtonState::Held => ButtonState::Held,
|
||||
_ => ButtonState::Pressed,
|
||||
}
|
||||
} else {
|
||||
match *button_state {
|
||||
ButtonState::Pressed | ButtonState::Held => ButtonState::Released,
|
||||
ButtonState::Released => ButtonState::Idle,
|
||||
ButtonState::Idle => ButtonState::Idle,
|
||||
}
|
||||
}
|
||||
}
|
||||
fn update_button_state(&mut self, button: u32, is_pressed: bool) {
|
||||
let button_state = &mut self.buttons[button as usize];
|
||||
*button_state = if is_pressed {
|
||||
match *button_state {
|
||||
ButtonState::Pressed => ButtonState::Held,
|
||||
ButtonState::Held => ButtonState::Held,
|
||||
_ => ButtonState::Pressed,
|
||||
}
|
||||
} else {
|
||||
match *button_state {
|
||||
ButtonState::Pressed | ButtonState::Held => ButtonState::Released,
|
||||
ButtonState::Released => ButtonState::Idle,
|
||||
ButtonState::Idle => ButtonState::Idle,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InputDevice for Mouse {
|
||||
fn update(&mut self) {
|
||||
self.x_delta = 0;
|
||||
self.y_delta = 0;
|
||||
for state in self.buttons.iter_mut() {
|
||||
*state = match *state {
|
||||
ButtonState::Pressed => ButtonState::Held,
|
||||
ButtonState::Released => ButtonState::Idle,
|
||||
otherwise => otherwise,
|
||||
}
|
||||
}
|
||||
}
|
||||
fn update(&mut self) {
|
||||
self.x_delta = 0;
|
||||
self.y_delta = 0;
|
||||
for state in self.buttons.iter_mut() {
|
||||
*state = match *state {
|
||||
ButtonState::Pressed => ButtonState::Held,
|
||||
ButtonState::Released => ButtonState::Idle,
|
||||
otherwise => otherwise,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemEventHandler for Mouse {
|
||||
fn handle_event(&mut self, event: &SystemEvent) -> bool {
|
||||
match event {
|
||||
SystemEvent::Mouse(MouseEvent::MouseMotion {
|
||||
x,
|
||||
y,
|
||||
x_delta,
|
||||
y_delta,
|
||||
buttons,
|
||||
}) => {
|
||||
self.x = *x;
|
||||
self.y = *y;
|
||||
self.x_delta = *x_delta;
|
||||
self.y_delta = *y_delta;
|
||||
fn handle_event(&mut self, event: &SystemEvent) -> bool {
|
||||
match event {
|
||||
SystemEvent::Mouse(MouseEvent::MouseMotion {
|
||||
x,
|
||||
y,
|
||||
x_delta,
|
||||
y_delta,
|
||||
buttons,
|
||||
}) => {
|
||||
self.x = *x;
|
||||
self.y = *y;
|
||||
self.x_delta = *x_delta;
|
||||
self.y_delta = *y_delta;
|
||||
|
||||
self.update_button_state(MouseButton::Left as u32, buttons.contains(MouseButtons::LEFT_BUTTON));
|
||||
self.update_button_state(MouseButton::Middle as u32, buttons.contains(MouseButtons::MIDDLE_BUTTON));
|
||||
self.update_button_state(MouseButton::Right as u32, buttons.contains(MouseButtons::RIGHT_BUTTON));
|
||||
self.update_button_state(MouseButton::X1 as u32, buttons.contains(MouseButtons::X1));
|
||||
self.update_button_state(MouseButton::X2 as u32, buttons.contains(MouseButtons::X2));
|
||||
true
|
||||
}
|
||||
SystemEvent::Mouse(MouseEvent::MouseButtonDown { button, .. }) => {
|
||||
self.update_button_state(*button as u32, true);
|
||||
true
|
||||
}
|
||||
SystemEvent::Mouse(MouseEvent::MouseButtonUp { button, .. }) => {
|
||||
self.update_button_state(*button as u32, false);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
self.update_button_state(MouseButton::Left as u32, buttons.contains(MouseButtons::LEFT_BUTTON));
|
||||
self.update_button_state(MouseButton::Middle as u32, buttons.contains(MouseButtons::MIDDLE_BUTTON));
|
||||
self.update_button_state(MouseButton::Right as u32, buttons.contains(MouseButtons::RIGHT_BUTTON));
|
||||
self.update_button_state(MouseButton::X1 as u32, buttons.contains(MouseButtons::X1));
|
||||
self.update_button_state(MouseButton::X2 as u32, buttons.contains(MouseButtons::X2));
|
||||
true
|
||||
}
|
||||
SystemEvent::Mouse(MouseEvent::MouseButtonDown { button, .. }) => {
|
||||
self.update_button_state(*button as u32, true);
|
||||
true
|
||||
}
|
||||
SystemEvent::Mouse(MouseEvent::MouseButtonUp { button, .. }) => {
|
||||
self.update_button_state(*button as u32, false);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,329 +20,329 @@ pub mod event;
|
|||
pub mod input_devices;
|
||||
|
||||
fn is_x11_compositor_skipping_problematic() -> bool {
|
||||
/*
|
||||
this is _probably_ a bit of a hack.
|
||||
/*
|
||||
this is _probably_ a bit of a hack.
|
||||
|
||||
currently on linux systems, SDL2 (2.0.8+), tries to "skip" (disable) the X11 server
|
||||
compositor when starting up. this is to reduce/remove any added latency from the SDL program
|
||||
that is usually introduced by the compositor when it is enabled for the window. if SDL did
|
||||
disable the compositor in this manner, it will re-enable it when SDL shuts down. the
|
||||
intention is for the compositor to be disabled for just the SDL window(s) only and to affect
|
||||
nothing else running concurrently.
|
||||
currently on linux systems, SDL2 (2.0.8+), tries to "skip" (disable) the X11 server
|
||||
compositor when starting up. this is to reduce/remove any added latency from the SDL program
|
||||
that is usually introduced by the compositor when it is enabled for the window. if SDL did
|
||||
disable the compositor in this manner, it will re-enable it when SDL shuts down. the
|
||||
intention is for the compositor to be disabled for just the SDL window(s) only and to affect
|
||||
nothing else running concurrently.
|
||||
|
||||
this works great for several desktop environments, but it unfortunately has a global effect
|
||||
on KDE/Kwin, where users may notice a visible screen flicker, other concurrently running
|
||||
applications may exhibit visual artifacts/weirdness, and (all?) other application windows
|
||||
while the SDL app is running will also have the compositor disabled for them too.
|
||||
this works great for several desktop environments, but it unfortunately has a global effect
|
||||
on KDE/Kwin, where users may notice a visible screen flicker, other concurrently running
|
||||
applications may exhibit visual artifacts/weirdness, and (all?) other application windows
|
||||
while the SDL app is running will also have the compositor disabled for them too.
|
||||
|
||||
not great! this function is a quick, hacky, and probably-not-bullet-proof method to detect
|
||||
if KDE/Kwin is the current desktop environment. in the future other known problem
|
||||
configurations could be added here and/or this could/should be updated with a better method
|
||||
to check for this.
|
||||
*/
|
||||
match std::env::consts::OS {
|
||||
"linux"|"freebsd"|"netbsd"|"openbsd" => {
|
||||
match std::env::var("XDG_SESSION_DESKTOP") {
|
||||
Ok(value) => value.eq_ignore_ascii_case("KDE"),
|
||||
Err(_) => false
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
not great! this function is a quick, hacky, and probably-not-bullet-proof method to detect
|
||||
if KDE/Kwin is the current desktop environment. in the future other known problem
|
||||
configurations could be added here and/or this could/should be updated with a better method
|
||||
to check for this.
|
||||
*/
|
||||
match std::env::consts::OS {
|
||||
"linux" | "freebsd" | "netbsd" | "openbsd" => {
|
||||
match std::env::var("XDG_SESSION_DESKTOP") {
|
||||
Ok(value) => value.eq_ignore_ascii_case("KDE"),
|
||||
Err(_) => false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SystemError {
|
||||
#[error("System init error: {0}")]
|
||||
InitError(String),
|
||||
#[error("System init error: {0}")]
|
||||
InitError(String),
|
||||
|
||||
#[error("System display error: {0}")]
|
||||
DisplayError(String),
|
||||
#[error("System display error: {0}")]
|
||||
DisplayError(String),
|
||||
|
||||
#[error("System audio error: {0}")]
|
||||
AudioError(#[from] crate::audio::AudioError),
|
||||
#[error("System audio error: {0}")]
|
||||
AudioError(#[from] crate::audio::AudioError),
|
||||
}
|
||||
|
||||
/// Builder for configuring and constructing an instance of [`System`].
|
||||
#[derive(Debug)]
|
||||
pub struct SystemBuilder {
|
||||
window_title: String,
|
||||
vsync: bool,
|
||||
target_framerate: Option<u32>,
|
||||
initial_scale_factor: u32,
|
||||
resizable: bool,
|
||||
show_mouse: bool,
|
||||
relative_mouse_scaling: bool,
|
||||
integer_scaling: bool,
|
||||
skip_x11_compositor: bool,
|
||||
window_title: String,
|
||||
vsync: bool,
|
||||
target_framerate: Option<u32>,
|
||||
initial_scale_factor: u32,
|
||||
resizable: bool,
|
||||
show_mouse: bool,
|
||||
relative_mouse_scaling: bool,
|
||||
integer_scaling: bool,
|
||||
skip_x11_compositor: bool,
|
||||
}
|
||||
|
||||
impl SystemBuilder {
|
||||
/// Returns a new [`SystemBuilder`] with a default configuration.
|
||||
pub fn new() -> SystemBuilder {
|
||||
SystemBuilder {
|
||||
window_title: String::new(),
|
||||
vsync: false,
|
||||
target_framerate: None,
|
||||
initial_scale_factor: DEFAULT_SCALE_FACTOR,
|
||||
resizable: true,
|
||||
show_mouse: false,
|
||||
relative_mouse_scaling: true,
|
||||
integer_scaling: false,
|
||||
skip_x11_compositor: !is_x11_compositor_skipping_problematic(),
|
||||
}
|
||||
}
|
||||
/// Returns a new [`SystemBuilder`] with a default configuration.
|
||||
pub fn new() -> SystemBuilder {
|
||||
SystemBuilder {
|
||||
window_title: String::new(),
|
||||
vsync: false,
|
||||
target_framerate: None,
|
||||
initial_scale_factor: DEFAULT_SCALE_FACTOR,
|
||||
resizable: true,
|
||||
show_mouse: false,
|
||||
relative_mouse_scaling: true,
|
||||
integer_scaling: false,
|
||||
skip_x11_compositor: !is_x11_compositor_skipping_problematic(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the window title for the [`System`] to be built.
|
||||
pub fn window_title(&mut self, window_title: &str) -> &mut SystemBuilder {
|
||||
self.window_title = window_title.to_string();
|
||||
self
|
||||
}
|
||||
/// Set the window title for the [`System`] to be built.
|
||||
pub fn window_title(&mut self, window_title: &str) -> &mut SystemBuilder {
|
||||
self.window_title = window_title.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables V-Sync for the [`System`] to be built. Enabling V-sync automatically
|
||||
/// disables `target_framerate`.
|
||||
pub fn vsync(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.vsync = enable;
|
||||
self.target_framerate = None;
|
||||
self
|
||||
}
|
||||
/// Enables or disables V-Sync for the [`System`] to be built. Enabling V-sync automatically
|
||||
/// disables `target_framerate`.
|
||||
pub fn vsync(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.vsync = enable;
|
||||
self.target_framerate = None;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a target framerate for the [`System`] being built to run at. This is intended to be
|
||||
/// used when V-sync is not desired, so setting a target framerate automatically disables
|
||||
/// `vsync`.
|
||||
pub fn target_framerate(&mut self, target_framerate: u32) -> &mut SystemBuilder {
|
||||
self.target_framerate = Some(target_framerate);
|
||||
self.vsync = false;
|
||||
self
|
||||
}
|
||||
/// Sets a target framerate for the [`System`] being built to run at. This is intended to be
|
||||
/// used when V-sync is not desired, so setting a target framerate automatically disables
|
||||
/// `vsync`.
|
||||
pub fn target_framerate(&mut self, target_framerate: u32) -> &mut SystemBuilder {
|
||||
self.target_framerate = Some(target_framerate);
|
||||
self.vsync = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets an integer scaling factor for the [`System`] being built to up-scale the virtual
|
||||
/// framebuffer to when displaying it on screen.
|
||||
pub fn scale_factor(&mut self, scale_factor: u32) -> &mut SystemBuilder {
|
||||
self.initial_scale_factor = scale_factor;
|
||||
self
|
||||
}
|
||||
/// Sets an integer scaling factor for the [`System`] being built to up-scale the virtual
|
||||
/// framebuffer to when displaying it on screen.
|
||||
pub fn scale_factor(&mut self, scale_factor: u32) -> &mut SystemBuilder {
|
||||
self.initial_scale_factor = scale_factor;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the window will be resizable by the user for the [`System`] being built.
|
||||
pub fn resizable(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.resizable = enable;
|
||||
self
|
||||
}
|
||||
/// Sets whether the window will be resizable by the user for the [`System`] being built.
|
||||
pub fn resizable(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.resizable = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables mouse cursor display by the operating system when the cursor is over
|
||||
/// the window for the [`System`] being built. Disable this if you intend to render your own
|
||||
/// custom mouse cursor.
|
||||
pub fn show_mouse(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.show_mouse = enable;
|
||||
self
|
||||
}
|
||||
/// Enables or disables mouse cursor display by the operating system when the cursor is over
|
||||
/// the window for the [`System`] being built. Disable this if you intend to render your own
|
||||
/// custom mouse cursor.
|
||||
pub fn show_mouse(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.show_mouse = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables automatic DPI scaling of mouse relative movement values (delta values)
|
||||
/// available via the [`Mouse`] input device.
|
||||
pub fn relative_mouse_scaling(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.relative_mouse_scaling = enable;
|
||||
self
|
||||
}
|
||||
/// Enables or disables automatic DPI scaling of mouse relative movement values (delta values)
|
||||
/// available via the [`Mouse`] input device.
|
||||
pub fn relative_mouse_scaling(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.relative_mouse_scaling = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables restricting the final rendered output to always be integer scaled,
|
||||
/// even if that result will not fully fill the area of the window.
|
||||
pub fn integer_scaling(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.integer_scaling = enable;
|
||||
self
|
||||
}
|
||||
/// Enables or disables restricting the final rendered output to always be integer scaled,
|
||||
/// even if that result will not fully fill the area of the window.
|
||||
pub fn integer_scaling(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.integer_scaling = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables skipping the X11 server compositor on Linux systems only. This can be
|
||||
/// set to manually control the underlying SDL hint that is used to control this setting. The
|
||||
/// default setting that [`SystemBuilder`] configures is to follow the SDL default, except where
|
||||
/// the setting affects the system globally (in certain desktop environments, e.g. KDE/Kwin)
|
||||
/// which may be undesired by end-users, at the cost of some additional input latency.
|
||||
pub fn skip_x11_compositor(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.skip_x11_compositor = enable;
|
||||
self
|
||||
}
|
||||
/// Enables or disables skipping the X11 server compositor on Linux systems only. This can be
|
||||
/// set to manually control the underlying SDL hint that is used to control this setting. The
|
||||
/// default setting that [`SystemBuilder`] configures is to follow the SDL default, except where
|
||||
/// the setting affects the system globally (in certain desktop environments, e.g. KDE/Kwin)
|
||||
/// which may be undesired by end-users, at the cost of some additional input latency.
|
||||
pub fn skip_x11_compositor(&mut self, enable: bool) -> &mut SystemBuilder {
|
||||
self.skip_x11_compositor = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds and returns a [`System`] from the current configuration.
|
||||
pub fn build(&self) -> Result<System, SystemError> {
|
||||
// todo: maybe let this be customized in the future, or at least halved so a 160x120 mode can be available ... ?
|
||||
let screen_width = SCREEN_WIDTH;
|
||||
let screen_height = SCREEN_HEIGHT;
|
||||
let texture_pixel_size = 4; // 32-bit ARGB format
|
||||
/// Builds and returns a [`System`] from the current configuration.
|
||||
pub fn build(&self) -> Result<System, SystemError> {
|
||||
// todo: maybe let this be customized in the future, or at least halved so a 160x120 mode can be available ... ?
|
||||
let screen_width = SCREEN_WIDTH;
|
||||
let screen_height = SCREEN_HEIGHT;
|
||||
let texture_pixel_size = 4; // 32-bit ARGB format
|
||||
|
||||
sdl2::hint::set(
|
||||
"SDL_MOUSE_RELATIVE_SCALING",
|
||||
if self.relative_mouse_scaling {
|
||||
"1"
|
||||
} else {
|
||||
"0"
|
||||
},
|
||||
);
|
||||
sdl2::hint::set(
|
||||
"SDL_MOUSE_RELATIVE_SCALING",
|
||||
if self.relative_mouse_scaling {
|
||||
"1"
|
||||
} else {
|
||||
"0"
|
||||
},
|
||||
);
|
||||
|
||||
sdl2::hint::set(
|
||||
"SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR",
|
||||
if self.skip_x11_compositor {
|
||||
"1"
|
||||
} else {
|
||||
"0"
|
||||
}
|
||||
);
|
||||
sdl2::hint::set(
|
||||
"SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR",
|
||||
if self.skip_x11_compositor {
|
||||
"1"
|
||||
} else {
|
||||
"0"
|
||||
},
|
||||
);
|
||||
|
||||
// build all the individual SDL subsystems
|
||||
// build all the individual SDL subsystems
|
||||
|
||||
let sdl_context = match sdl2::init() {
|
||||
Ok(sdl_context) => sdl_context,
|
||||
Err(message) => return Err(SystemError::InitError(message)),
|
||||
};
|
||||
let sdl_context = match sdl2::init() {
|
||||
Ok(sdl_context) => sdl_context,
|
||||
Err(message) => return Err(SystemError::InitError(message)),
|
||||
};
|
||||
|
||||
let sdl_timer_subsystem = match sdl_context.timer() {
|
||||
Ok(timer_subsystem) => timer_subsystem,
|
||||
Err(message) => return Err(SystemError::InitError(message)),
|
||||
};
|
||||
let sdl_timer_subsystem = match sdl_context.timer() {
|
||||
Ok(timer_subsystem) => timer_subsystem,
|
||||
Err(message) => return Err(SystemError::InitError(message)),
|
||||
};
|
||||
|
||||
let sdl_video_subsystem = match sdl_context.video() {
|
||||
Ok(video_subsystem) => video_subsystem,
|
||||
Err(message) => return Err(SystemError::InitError(message)),
|
||||
};
|
||||
let sdl_video_subsystem = match sdl_context.video() {
|
||||
Ok(video_subsystem) => video_subsystem,
|
||||
Err(message) => return Err(SystemError::InitError(message)),
|
||||
};
|
||||
|
||||
let sdl_event_pump = match sdl_context.event_pump() {
|
||||
Ok(event_pump) => event_pump,
|
||||
Err(message) => return Err(SystemError::InitError(message)),
|
||||
};
|
||||
let sdl_event_pump = match sdl_context.event_pump() {
|
||||
Ok(event_pump) => event_pump,
|
||||
Err(message) => return Err(SystemError::InitError(message)),
|
||||
};
|
||||
|
||||
let sdl_audio_subsystem = match sdl_context.audio() {
|
||||
Ok(audio_subsystem) => audio_subsystem,
|
||||
Err(message) => return Err(SystemError::InitError(message)),
|
||||
};
|
||||
let sdl_audio_subsystem = match sdl_context.audio() {
|
||||
Ok(audio_subsystem) => audio_subsystem,
|
||||
Err(message) => return Err(SystemError::InitError(message)),
|
||||
};
|
||||
|
||||
// create the window
|
||||
// create the window
|
||||
|
||||
let window_width = screen_width * self.initial_scale_factor;
|
||||
let window_height = screen_height * self.initial_scale_factor;
|
||||
let mut window_builder = &mut (sdl_video_subsystem.window(
|
||||
self.window_title.as_str(),
|
||||
window_width,
|
||||
window_height,
|
||||
));
|
||||
if self.resizable {
|
||||
window_builder = window_builder.resizable();
|
||||
}
|
||||
let sdl_window = match window_builder.build() {
|
||||
Ok(window) => window,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
let window_width = screen_width * self.initial_scale_factor;
|
||||
let window_height = screen_height * self.initial_scale_factor;
|
||||
let mut window_builder = &mut (sdl_video_subsystem.window(
|
||||
self.window_title.as_str(),
|
||||
window_width,
|
||||
window_height,
|
||||
));
|
||||
if self.resizable {
|
||||
window_builder = window_builder.resizable();
|
||||
}
|
||||
let sdl_window = match window_builder.build() {
|
||||
Ok(window) => window,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
|
||||
sdl_context.mouse().show_cursor(self.show_mouse);
|
||||
sdl_context.mouse().show_cursor(self.show_mouse);
|
||||
|
||||
// turn the window into a canvas (under the hood, an SDL Renderer that owns the window)
|
||||
// turn the window into a canvas (under the hood, an SDL Renderer that owns the window)
|
||||
|
||||
let mut canvas_builder = sdl_window.into_canvas();
|
||||
if self.vsync {
|
||||
canvas_builder = canvas_builder.present_vsync();
|
||||
}
|
||||
let mut sdl_canvas = match canvas_builder.build() {
|
||||
Ok(canvas) => canvas,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
if let Err(error) = sdl_canvas.set_logical_size(screen_width, screen_height) {
|
||||
return Err(SystemError::InitError(error.to_string()));
|
||||
};
|
||||
let mut canvas_builder = sdl_window.into_canvas();
|
||||
if self.vsync {
|
||||
canvas_builder = canvas_builder.present_vsync();
|
||||
}
|
||||
let mut sdl_canvas = match canvas_builder.build() {
|
||||
Ok(canvas) => canvas,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
if let Err(error) = sdl_canvas.set_logical_size(screen_width, screen_height) {
|
||||
return Err(SystemError::InitError(error.to_string()));
|
||||
};
|
||||
|
||||
// TODO: newer versions of rust-sdl2 support this directly off the WindowCanvas struct
|
||||
unsafe {
|
||||
sdl2::sys::SDL_RenderSetIntegerScale(
|
||||
sdl_canvas.raw(),
|
||||
if self.integer_scaling {
|
||||
sdl2::sys::SDL_bool::SDL_TRUE
|
||||
} else {
|
||||
sdl2::sys::SDL_bool::SDL_FALSE
|
||||
},
|
||||
);
|
||||
}
|
||||
// TODO: newer versions of rust-sdl2 support this directly off the WindowCanvas struct
|
||||
unsafe {
|
||||
sdl2::sys::SDL_RenderSetIntegerScale(
|
||||
sdl_canvas.raw(),
|
||||
if self.integer_scaling {
|
||||
sdl2::sys::SDL_bool::SDL_TRUE
|
||||
} else {
|
||||
sdl2::sys::SDL_bool::SDL_FALSE
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// create an SDL texture which we will be uploading to every frame to display the
|
||||
// application's framebuffer
|
||||
// create an SDL texture which we will be uploading to every frame to display the
|
||||
// application's framebuffer
|
||||
|
||||
let sdl_texture = match sdl_canvas.create_texture_streaming(
|
||||
Some(PixelFormatEnum::ARGB8888),
|
||||
screen_width,
|
||||
screen_height,
|
||||
) {
|
||||
Ok(texture) => texture,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
let sdl_texture_pitch = (sdl_texture.query().width * texture_pixel_size) as usize;
|
||||
let sdl_texture = match sdl_canvas.create_texture_streaming(
|
||||
Some(PixelFormatEnum::ARGB8888),
|
||||
screen_width,
|
||||
screen_height,
|
||||
) {
|
||||
Ok(texture) => texture,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
let sdl_texture_pitch = (sdl_texture.query().width * texture_pixel_size) as usize;
|
||||
|
||||
// create a raw 32-bit RGBA buffer that will be used as the temporary source for
|
||||
// SDL texture uploads each frame. necessary as applications are dealing with 8-bit indexed
|
||||
// bitmaps, not 32-bit RGBA pixels, so this temporary buffer is where we convert the final
|
||||
// application framebuffer to 32-bit RGBA pixels before it is uploaded to the SDL texture
|
||||
let texture_pixels_size = (screen_width * screen_height * texture_pixel_size) as usize;
|
||||
let texture_pixels = vec![0u32; texture_pixels_size].into_boxed_slice();
|
||||
// create a raw 32-bit RGBA buffer that will be used as the temporary source for
|
||||
// SDL texture uploads each frame. necessary as applications are dealing with 8-bit indexed
|
||||
// bitmaps, not 32-bit RGBA pixels, so this temporary buffer is where we convert the final
|
||||
// application framebuffer to 32-bit RGBA pixels before it is uploaded to the SDL texture
|
||||
let texture_pixels_size = (screen_width * screen_height * texture_pixel_size) as usize;
|
||||
let texture_pixels = vec![0u32; texture_pixels_size].into_boxed_slice();
|
||||
|
||||
// create the Bitmap object that will be exposed to the application acting as the system
|
||||
// backbuffer
|
||||
// create the Bitmap object that will be exposed to the application acting as the system
|
||||
// backbuffer
|
||||
|
||||
let framebuffer = match Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT) {
|
||||
Ok(bmp) => bmp,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
let framebuffer = match Bitmap::new(SCREEN_WIDTH, SCREEN_HEIGHT) {
|
||||
Ok(bmp) => bmp,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
|
||||
// create the default palette, initialized to the VGA default palette. also exposed to the
|
||||
// application for manipulation
|
||||
// create the default palette, initialized to the VGA default palette. also exposed to the
|
||||
// application for manipulation
|
||||
|
||||
let palette = match Palette::new_vga_palette() {
|
||||
Ok(palette) => palette,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
let palette = match Palette::new_vga_palette() {
|
||||
Ok(palette) => palette,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
|
||||
// create the default font, initialized to the VGA BIOS default font.
|
||||
// create the default font, initialized to the VGA BIOS default font.
|
||||
|
||||
let font = match BitmaskFont::new_vga_font() {
|
||||
Ok(font) => font,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
let font = match BitmaskFont::new_vga_font() {
|
||||
Ok(font) => font,
|
||||
Err(error) => return Err(SystemError::InitError(error.to_string())),
|
||||
};
|
||||
|
||||
let audio_spec = AudioSpecDesired {
|
||||
freq: Some(TARGET_AUDIO_FREQUENCY as i32),
|
||||
channels: Some(TARGET_AUDIO_CHANNELS),
|
||||
samples: None,
|
||||
};
|
||||
let mut audio = Audio::new(audio_spec, &sdl_audio_subsystem)?;
|
||||
audio.resume();
|
||||
let audio_queue = AudioQueue::new(&audio);
|
||||
let audio_spec = AudioSpecDesired {
|
||||
freq: Some(TARGET_AUDIO_FREQUENCY as i32),
|
||||
channels: Some(TARGET_AUDIO_CHANNELS),
|
||||
samples: None,
|
||||
};
|
||||
let mut audio = Audio::new(audio_spec, &sdl_audio_subsystem)?;
|
||||
audio.resume();
|
||||
let audio_queue = AudioQueue::new(&audio);
|
||||
|
||||
let event_pump = SystemEventPump::from(sdl_event_pump);
|
||||
let event_pump = SystemEventPump::from(sdl_event_pump);
|
||||
|
||||
// create input device objects, exposed to the application
|
||||
// create input device objects, exposed to the application
|
||||
|
||||
let keyboard = Keyboard::new();
|
||||
let mouse = Mouse::new();
|
||||
let keyboard = Keyboard::new();
|
||||
let mouse = Mouse::new();
|
||||
|
||||
let input_devices = InputDevices {
|
||||
keyboard,
|
||||
mouse,
|
||||
};
|
||||
let input_devices = InputDevices {
|
||||
keyboard,
|
||||
mouse,
|
||||
};
|
||||
|
||||
Ok(System {
|
||||
sdl_context,
|
||||
sdl_audio_subsystem,
|
||||
sdl_video_subsystem,
|
||||
sdl_timer_subsystem,
|
||||
sdl_canvas,
|
||||
sdl_texture,
|
||||
sdl_texture_pitch,
|
||||
texture_pixels,
|
||||
audio,
|
||||
audio_queue,
|
||||
video: framebuffer,
|
||||
palette,
|
||||
font,
|
||||
input_devices,
|
||||
event_pump,
|
||||
target_framerate: self.target_framerate,
|
||||
target_framerate_delta: None,
|
||||
next_tick: 0,
|
||||
})
|
||||
}
|
||||
Ok(System {
|
||||
sdl_context,
|
||||
sdl_audio_subsystem,
|
||||
sdl_video_subsystem,
|
||||
sdl_timer_subsystem,
|
||||
sdl_canvas,
|
||||
sdl_texture,
|
||||
sdl_texture_pitch,
|
||||
texture_pixels,
|
||||
audio,
|
||||
audio_queue,
|
||||
video: framebuffer,
|
||||
palette,
|
||||
font,
|
||||
input_devices,
|
||||
event_pump,
|
||||
target_framerate: self.target_framerate,
|
||||
target_framerate_delta: None,
|
||||
next_tick: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds all primary structures necessary for interacting with the operating system and for
|
||||
|
@ -350,200 +350,200 @@ impl SystemBuilder {
|
|||
/// "virtual machine" exposed by this library.
|
||||
#[allow(dead_code)]
|
||||
pub struct System {
|
||||
sdl_context: Sdl,
|
||||
sdl_audio_subsystem: AudioSubsystem,
|
||||
sdl_video_subsystem: VideoSubsystem,
|
||||
sdl_timer_subsystem: TimerSubsystem,
|
||||
sdl_canvas: WindowCanvas,
|
||||
sdl_texture: Texture,
|
||||
sdl_texture_pitch: usize,
|
||||
sdl_context: Sdl,
|
||||
sdl_audio_subsystem: AudioSubsystem,
|
||||
sdl_video_subsystem: VideoSubsystem,
|
||||
sdl_timer_subsystem: TimerSubsystem,
|
||||
sdl_canvas: WindowCanvas,
|
||||
sdl_texture: Texture,
|
||||
sdl_texture_pitch: usize,
|
||||
|
||||
texture_pixels: Box<[u32]>,
|
||||
texture_pixels: Box<[u32]>,
|
||||
|
||||
target_framerate: Option<u32>,
|
||||
target_framerate_delta: Option<i64>,
|
||||
next_tick: i64,
|
||||
target_framerate: Option<u32>,
|
||||
target_framerate_delta: Option<i64>,
|
||||
next_tick: i64,
|
||||
|
||||
/// An [`Audio`] instance that allows interacting with the system's audio output device.
|
||||
pub audio: Audio,
|
||||
/// An [`Audio`] instance that allows interacting with the system's audio output device.
|
||||
pub audio: Audio,
|
||||
|
||||
/// An [`AudioQueue`] instance that can queue up playback/stop commands to be issued to the
|
||||
/// system's [`Audio`] instance a bit more flexibly. If you use this, your application must
|
||||
/// manually call [`AudioQueue::apply`] or [`AudioQueue::apply_to_device`] in your loop to
|
||||
/// flush the queued commands, otherwise this queue will not do anything.
|
||||
pub audio_queue: AudioQueue,
|
||||
/// An [`AudioQueue`] instance that can queue up playback/stop commands to be issued to the
|
||||
/// system's [`Audio`] instance a bit more flexibly. If you use this, your application must
|
||||
/// manually call [`AudioQueue::apply`] or [`AudioQueue::apply_to_device`] in your loop to
|
||||
/// flush the queued commands, otherwise this queue will not do anything.
|
||||
pub audio_queue: AudioQueue,
|
||||
|
||||
/// The primary backbuffer [`Bitmap`] that will be rendered to the screen whenever
|
||||
/// [`System::display`] is called. Regardless of the actual window size, this bitmap is always
|
||||
/// [`SCREEN_WIDTH`]x[`SCREEN_HEIGHT`] pixels in size.
|
||||
pub video: Bitmap,
|
||||
/// The primary backbuffer [`Bitmap`] that will be rendered to the screen whenever
|
||||
/// [`System::display`] is called. Regardless of the actual window size, this bitmap is always
|
||||
/// [`SCREEN_WIDTH`]x[`SCREEN_HEIGHT`] pixels in size.
|
||||
pub video: Bitmap,
|
||||
|
||||
/// The [`Palette`] that will be used in conjunction with the `video` backbuffer to
|
||||
/// render the final output to the screen whenever [`System::display`] is called.
|
||||
pub palette: Palette,
|
||||
/// The [`Palette`] that will be used in conjunction with the `video` backbuffer to
|
||||
/// render the final output to the screen whenever [`System::display`] is called.
|
||||
pub palette: Palette,
|
||||
|
||||
/// A pre-loaded [`Font`] that can be used for text rendering.
|
||||
pub font: BitmaskFont,
|
||||
/// A pre-loaded [`Font`] that can be used for text rendering.
|
||||
pub font: BitmaskFont,
|
||||
|
||||
/// Contains instances representing the current state of the input devices available.
|
||||
/// To ensure these are updated each frame, ensure that you are either calling
|
||||
/// [`System::do_events`] or manually implementing an event polling loop which calls
|
||||
/// [`InputDevices::update`] and [`InputDevices::handle_event`].
|
||||
pub input_devices: InputDevices,
|
||||
/// Contains instances representing the current state of the input devices available.
|
||||
/// To ensure these are updated each frame, ensure that you are either calling
|
||||
/// [`System::do_events`] or manually implementing an event polling loop which calls
|
||||
/// [`InputDevices::update`] and [`InputDevices::handle_event`].
|
||||
pub input_devices: InputDevices,
|
||||
|
||||
pub event_pump: SystemEventPump,
|
||||
pub event_pump: SystemEventPump,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for System {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("System")
|
||||
.field("audio", &self.audio)
|
||||
.field("audio_queue", &self.audio_queue)
|
||||
.field("video", &self.video)
|
||||
.field("palette", &self.palette)
|
||||
.field("font", &self.font)
|
||||
//.field("keyboard", &self.keyboard)
|
||||
//.field("mouse", &self.mouse)
|
||||
.field("target_framerate", &self.target_framerate)
|
||||
.field("target_framerate_delta", &self.target_framerate_delta)
|
||||
.field("next_tick", &self.next_tick)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("System")
|
||||
.field("audio", &self.audio)
|
||||
.field("audio_queue", &self.audio_queue)
|
||||
.field("video", &self.video)
|
||||
.field("palette", &self.palette)
|
||||
.field("font", &self.font)
|
||||
//.field("keyboard", &self.keyboard)
|
||||
//.field("mouse", &self.mouse)
|
||||
.field("target_framerate", &self.target_framerate)
|
||||
.field("target_framerate_delta", &self.target_framerate_delta)
|
||||
.field("next_tick", &self.next_tick)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl System {
|
||||
/// Takes the `video` backbuffer bitmap and `palette` and renders it to the window, up-scaled
|
||||
/// to fill the window (preserving aspect ratio of course). If V-sync is enabled, this method
|
||||
/// will block to wait for V-sync. Otherwise, if a target framerate was configured a delay
|
||||
/// might be used to try to meet that framerate.
|
||||
pub fn display(&mut self) -> Result<(), SystemError> {
|
||||
self.input_devices.mouse.render_cursor(&mut self.video);
|
||||
/// Takes the `video` backbuffer bitmap and `palette` and renders it to the window, up-scaled
|
||||
/// to fill the window (preserving aspect ratio of course). If V-sync is enabled, this method
|
||||
/// will block to wait for V-sync. Otherwise, if a target framerate was configured a delay
|
||||
/// might be used to try to meet that framerate.
|
||||
pub fn display(&mut self) -> Result<(), SystemError> {
|
||||
self.input_devices.mouse.render_cursor(&mut self.video);
|
||||
|
||||
// convert application framebuffer to 32-bit RGBA pixels, and then upload it to the SDL
|
||||
// texture so it will be displayed on screen
|
||||
// convert application framebuffer to 32-bit RGBA pixels, and then upload it to the SDL
|
||||
// texture so it will be displayed on screen
|
||||
|
||||
self.video
|
||||
.copy_as_argb_to(&mut self.texture_pixels, &self.palette);
|
||||
self.video
|
||||
.copy_as_argb_to(&mut self.texture_pixels, &self.palette);
|
||||
|
||||
let texture_pixels = self.texture_pixels.as_byte_slice();
|
||||
if let Err(error) = self
|
||||
.sdl_texture
|
||||
.update(None, texture_pixels, self.sdl_texture_pitch)
|
||||
{
|
||||
return Err(SystemError::DisplayError(error.to_string()));
|
||||
}
|
||||
self.sdl_canvas.clear();
|
||||
if let Err(error) = self.sdl_canvas.copy(&self.sdl_texture, None, None) {
|
||||
return Err(SystemError::DisplayError(error));
|
||||
}
|
||||
self.sdl_canvas.present();
|
||||
let texture_pixels = self.texture_pixels.as_byte_slice();
|
||||
if let Err(error) = self
|
||||
.sdl_texture
|
||||
.update(None, texture_pixels, self.sdl_texture_pitch)
|
||||
{
|
||||
return Err(SystemError::DisplayError(error.to_string()));
|
||||
}
|
||||
self.sdl_canvas.clear();
|
||||
if let Err(error) = self.sdl_canvas.copy(&self.sdl_texture, None, None) {
|
||||
return Err(SystemError::DisplayError(error));
|
||||
}
|
||||
self.sdl_canvas.present();
|
||||
|
||||
self.input_devices.mouse.hide_cursor(&mut self.video);
|
||||
self.input_devices.mouse.hide_cursor(&mut self.video);
|
||||
|
||||
// if a specific target framerate is desired, apply some loop timing/delay to achieve it
|
||||
// TODO: do this better. delaying when running faster like this is a poor way to do this..
|
||||
// if a specific target framerate is desired, apply some loop timing/delay to achieve it
|
||||
// TODO: do this better. delaying when running faster like this is a poor way to do this..
|
||||
|
||||
if let Some(target_framerate) = self.target_framerate {
|
||||
if self.target_framerate_delta.is_some() {
|
||||
// normal path for every other loop iteration except the first
|
||||
let delay = self.next_tick - self.ticks() as i64;
|
||||
if delay < 0 {
|
||||
// this loop iteration took too long, no need to delay
|
||||
self.next_tick -= delay;
|
||||
} else {
|
||||
// this loop iteration completed before next_tick time, delay by the remainder
|
||||
// time period so we're running at about the desired framerate
|
||||
self.delay(((delay * 1000) / self.tick_frequency() as i64) as u32);
|
||||
}
|
||||
} else {
|
||||
// this branch will occur on the first main loop iteration. we use the fact that
|
||||
// target_framerate_delta was not yet set to avoid doing any delay on the first
|
||||
// loop, just in case there was some other processing between the System struct
|
||||
// being created and the actual beginning of the first loop ...
|
||||
self.target_framerate_delta =
|
||||
Some((self.tick_frequency() / target_framerate as u64) as i64);
|
||||
}
|
||||
if let Some(target_framerate) = self.target_framerate {
|
||||
if self.target_framerate_delta.is_some() {
|
||||
// normal path for every other loop iteration except the first
|
||||
let delay = self.next_tick - self.ticks() as i64;
|
||||
if delay < 0 {
|
||||
// this loop iteration took too long, no need to delay
|
||||
self.next_tick -= delay;
|
||||
} else {
|
||||
// this loop iteration completed before next_tick time, delay by the remainder
|
||||
// time period so we're running at about the desired framerate
|
||||
self.delay(((delay * 1000) / self.tick_frequency() as i64) as u32);
|
||||
}
|
||||
} else {
|
||||
// this branch will occur on the first main loop iteration. we use the fact that
|
||||
// target_framerate_delta was not yet set to avoid doing any delay on the first
|
||||
// loop, just in case there was some other processing between the System struct
|
||||
// being created and the actual beginning of the first loop ...
|
||||
self.target_framerate_delta =
|
||||
Some((self.tick_frequency() / target_framerate as u64) as i64);
|
||||
}
|
||||
|
||||
// expected time for the next display() call to happen by
|
||||
self.next_tick = (self.ticks() as i64) + self.target_framerate_delta.unwrap();
|
||||
}
|
||||
// expected time for the next display() call to happen by
|
||||
self.next_tick = (self.ticks() as i64) + self.target_framerate_delta.unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks for and responds to all SDL2 events waiting in the queue. Each event is passed to
|
||||
/// all [`InputDevice`]'s automatically to ensure input device state is up to date. Returns
|
||||
/// true if a [`SystemEvent::Quit`] event is encountered, in which case, the application
|
||||
/// should quit. Otherwise, returns false.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use libretrogd::system::*;
|
||||
///
|
||||
/// let mut system = SystemBuilder::new().window_title("Example").build().unwrap();
|
||||
///
|
||||
/// while !system.do_events() {
|
||||
/// // ... the body of your main loop here ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If your application needs to react to [`SystemEvent`]s, then instead of using
|
||||
/// [`System::do_events`], you should instead manually take care of event polling in your
|
||||
/// main loop. For example:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use libretrogd::system::*;
|
||||
///
|
||||
/// let mut system = SystemBuilder::new().window_title("Example").build().unwrap();
|
||||
///
|
||||
/// 'mainloop: loop {
|
||||
/// system.input_devices.update();
|
||||
/// for event in system.event_pump.poll_iter() {
|
||||
/// system.input_devices.handle_event(&event);
|
||||
/// match event {
|
||||
/// SystemEvent::Quit => {
|
||||
/// break 'mainloop
|
||||
/// },
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // ...the rest of the body of your main loop here ...
|
||||
/// }
|
||||
/// ```
|
||||
pub fn do_events(&mut self) -> bool {
|
||||
let mut should_quit = false;
|
||||
self.input_devices.update();
|
||||
for event in self.event_pump.poll_iter() {
|
||||
self.input_devices.handle_event(&event);
|
||||
if event == SystemEvent::Quit {
|
||||
should_quit = true;
|
||||
}
|
||||
}
|
||||
should_quit
|
||||
}
|
||||
/// Checks for and responds to all SDL2 events waiting in the queue. Each event is passed to
|
||||
/// all [`InputDevice`]'s automatically to ensure input device state is up to date. Returns
|
||||
/// true if a [`SystemEvent::Quit`] event is encountered, in which case, the application
|
||||
/// should quit. Otherwise, returns false.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use libretrogd::system::*;
|
||||
///
|
||||
/// let mut system = SystemBuilder::new().window_title("Example").build().unwrap();
|
||||
///
|
||||
/// while !system.do_events() {
|
||||
/// // ... the body of your main loop here ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If your application needs to react to [`SystemEvent`]s, then instead of using
|
||||
/// [`System::do_events`], you should instead manually take care of event polling in your
|
||||
/// main loop. For example:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use libretrogd::system::*;
|
||||
///
|
||||
/// let mut system = SystemBuilder::new().window_title("Example").build().unwrap();
|
||||
///
|
||||
/// 'mainloop: loop {
|
||||
/// system.input_devices.update();
|
||||
/// for event in system.event_pump.poll_iter() {
|
||||
/// system.input_devices.handle_event(&event);
|
||||
/// match event {
|
||||
/// SystemEvent::Quit => {
|
||||
/// break 'mainloop
|
||||
/// },
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // ...the rest of the body of your main loop here ...
|
||||
/// }
|
||||
/// ```
|
||||
pub fn do_events(&mut self) -> bool {
|
||||
let mut should_quit = false;
|
||||
self.input_devices.update();
|
||||
for event in self.event_pump.poll_iter() {
|
||||
self.input_devices.handle_event(&event);
|
||||
if event == SystemEvent::Quit {
|
||||
should_quit = true;
|
||||
}
|
||||
}
|
||||
should_quit
|
||||
}
|
||||
|
||||
/// Convenience method that applies any [`AudioBuffer`]s that may have been queued up on
|
||||
/// [`System::audio_queue`] to the system audio device so that they will be played. Do not
|
||||
/// call this when you already have an active lock on an [`AudioDevice`].
|
||||
pub fn apply_audio_queue(&mut self) -> Result<(), AudioDeviceError> {
|
||||
self.audio_queue.apply(&mut self.audio)
|
||||
}
|
||||
/// Convenience method that applies any [`AudioBuffer`]s that may have been queued up on
|
||||
/// [`System::audio_queue`] to the system audio device so that they will be played. Do not
|
||||
/// call this when you already have an active lock on an [`AudioDevice`].
|
||||
pub fn apply_audio_queue(&mut self) -> Result<(), AudioDeviceError> {
|
||||
self.audio_queue.apply(&mut self.audio)
|
||||
}
|
||||
|
||||
pub fn ticks(&self) -> u64 {
|
||||
self.sdl_timer_subsystem.performance_counter()
|
||||
}
|
||||
pub fn ticks(&self) -> u64 {
|
||||
self.sdl_timer_subsystem.performance_counter()
|
||||
}
|
||||
|
||||
pub fn tick_frequency(&self) -> u64 {
|
||||
self.sdl_timer_subsystem.performance_frequency()
|
||||
}
|
||||
pub fn tick_frequency(&self) -> u64 {
|
||||
self.sdl_timer_subsystem.performance_frequency()
|
||||
}
|
||||
|
||||
/// Returns the number of milliseconds elapsed since SDL was initialized.
|
||||
pub fn millis(&self) -> u32 {
|
||||
self.sdl_timer_subsystem.ticks()
|
||||
}
|
||||
/// Returns the number of milliseconds elapsed since SDL was initialized.
|
||||
pub fn millis(&self) -> u32 {
|
||||
self.sdl_timer_subsystem.ticks()
|
||||
}
|
||||
|
||||
/// Delays (blocks) for about the number of milliseconds specified.
|
||||
pub fn delay(&mut self, millis: u32) {
|
||||
self.sdl_timer_subsystem.delay(millis);
|
||||
}
|
||||
/// Delays (blocks) for about the number of milliseconds specified.
|
||||
pub fn delay(&mut self, millis: u32) {
|
||||
self.sdl_timer_subsystem.delay(millis);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
pub trait ReadFixedLengthByteArray {
|
||||
fn read_bytes<const N: usize>(&mut self) -> Result<[u8; N], std::io::Error>;
|
||||
fn read_bytes<const N: usize>(&mut self) -> Result<[u8; N], std::io::Error>;
|
||||
}
|
||||
|
||||
impl<T: std::io::Read> ReadFixedLengthByteArray for T {
|
||||
fn read_bytes<const N: usize>(&mut self) -> Result<[u8; N], std::io::Error> {
|
||||
assert_ne!(N, 0);
|
||||
let mut array = [0u8; N];
|
||||
self.read_exact(&mut array)?;
|
||||
Ok(array)
|
||||
}
|
||||
fn read_bytes<const N: usize>(&mut self) -> Result<[u8; N], std::io::Error> {
|
||||
assert_ne!(N, 0);
|
||||
let mut array = [0u8; N];
|
||||
self.read_exact(&mut array)?;
|
||||
Ok(array)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,20 +3,20 @@ use std::io::{Error, SeekFrom};
|
|||
/// Provides a convenience method for determining the total size of a stream. This is provided
|
||||
/// as a temporary alternative to [std::io::Seek::stream_len] which is currently marked unstable.
|
||||
pub trait StreamSize {
|
||||
fn stream_size(&mut self) -> Result<u64, std::io::Error>;
|
||||
fn stream_size(&mut self) -> Result<u64, std::io::Error>;
|
||||
}
|
||||
|
||||
impl<T: std::io::Read + std::io::Seek> StreamSize for T {
|
||||
fn stream_size(&mut self) -> Result<u64, Error> {
|
||||
let old_pos = self.stream_position()?;
|
||||
let len = self.seek(SeekFrom::End(0))?;
|
||||
fn stream_size(&mut self) -> Result<u64, Error> {
|
||||
let old_pos = self.stream_position()?;
|
||||
let len = self.seek(SeekFrom::End(0))?;
|
||||
|
||||
// Avoid seeking a third time when we were already at the end of the
|
||||
// stream. The branch is usually way cheaper than a seek operation.
|
||||
if old_pos != len {
|
||||
self.seek(SeekFrom::Start(old_pos))?;
|
||||
}
|
||||
// Avoid seeking a third time when we were already at the end of the
|
||||
// stream. The branch is usually way cheaper than a seek operation.
|
||||
if old_pos != len {
|
||||
self.seek(SeekFrom::Start(old_pos))?;
|
||||
}
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -10,31 +10,31 @@ pub mod lzwgif;
|
|||
pub mod packbits;
|
||||
|
||||
pub fn rnd_value<N: SampleUniform + PartialOrd>(low: N, high: N) -> N {
|
||||
rand::thread_rng().gen_range(low..=high)
|
||||
rand::thread_rng().gen_range(low..=high)
|
||||
}
|
||||
|
||||
/// Returns the absolute difference between two unsigned values. This is just here as a temporary
|
||||
/// alternative to the `abs_diff` method currently provided by Rust but that is marked unstable.
|
||||
#[inline]
|
||||
pub fn abs_diff<N: Unsigned + PartialOrd>(a: N, b: N) -> N {
|
||||
if a < b {
|
||||
b - a
|
||||
} else {
|
||||
a - b
|
||||
}
|
||||
if a < b {
|
||||
b - a
|
||||
} else {
|
||||
a - b
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsAny {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
}
|
||||
|
||||
impl<A: Any> AsAny for A {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self as &dyn Any
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self as &dyn Any
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self as &mut dyn Any
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self as &mut dyn Any
|
||||
}
|
||||
}
|
|
@ -3,224 +3,224 @@ use thiserror::Error;
|
|||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PackBitsError {
|
||||
#[error("PackBits I/O error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
#[error("PackBits I/O error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
enum PackMode {
|
||||
Dump,
|
||||
Run,
|
||||
Dump,
|
||||
Run,
|
||||
}
|
||||
|
||||
pub fn pack_bits<S, D>(src: &mut S, dest: &mut D, src_length: usize) -> Result<(), PackBitsError>
|
||||
where
|
||||
S: ReadBytesExt,
|
||||
D: WriteBytesExt,
|
||||
where
|
||||
S: ReadBytesExt,
|
||||
D: WriteBytesExt,
|
||||
{
|
||||
const MIN_RUN: usize = 3;
|
||||
const MAX_RUN: usize = 128;
|
||||
const MAX_BUFFER: usize = 128;
|
||||
const MIN_RUN: usize = 3;
|
||||
const MAX_RUN: usize = 128;
|
||||
const MAX_BUFFER: usize = 128;
|
||||
|
||||
if src_length == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let mut bytes_left = src_length;
|
||||
if src_length == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let mut bytes_left = src_length;
|
||||
|
||||
let mut buffer = [0u8; (MAX_RUN * 2)];
|
||||
let mut buffer = [0u8; (MAX_RUN * 2)];
|
||||
|
||||
// read the first byte from the source, just to start things off before we get into the loop
|
||||
buffer[0] = src.read_u8()?;
|
||||
bytes_left -= 1;
|
||||
// read the first byte from the source, just to start things off before we get into the loop
|
||||
buffer[0] = src.read_u8()?;
|
||||
bytes_left -= 1;
|
||||
|
||||
let mut mode = PackMode::Dump;
|
||||
let mut run_end = 1; // 1 because we already read the first byte into the buffer
|
||||
let mut run_start = 0;
|
||||
let mut previous_byte = buffer[0];
|
||||
let mut mode = PackMode::Dump;
|
||||
let mut run_end = 1; // 1 because we already read the first byte into the buffer
|
||||
let mut run_start = 0;
|
||||
let mut previous_byte = buffer[0];
|
||||
|
||||
while bytes_left > 0 {
|
||||
let byte = src.read_u8()?;
|
||||
while bytes_left > 0 {
|
||||
let byte = src.read_u8()?;
|
||||
|
||||
buffer[run_end] = byte;
|
||||
run_end += 1;
|
||||
buffer[run_end] = byte;
|
||||
run_end += 1;
|
||||
|
||||
match mode {
|
||||
// "dump" mode. keep collecting any bytes and write them as-is until we detect the
|
||||
// start of a run of identical bytes
|
||||
PackMode::Dump => {
|
||||
if run_end > MAX_BUFFER {
|
||||
// we need to flush the temp buffer to the destination
|
||||
dest.write_u8((run_end - 2) as u8)?;
|
||||
dest.write_all(&buffer[0..run_end])?;
|
||||
match mode {
|
||||
// "dump" mode. keep collecting any bytes and write them as-is until we detect the
|
||||
// start of a run of identical bytes
|
||||
PackMode::Dump => {
|
||||
if run_end > MAX_BUFFER {
|
||||
// we need to flush the temp buffer to the destination
|
||||
dest.write_u8((run_end - 2) as u8)?;
|
||||
dest.write_all(&buffer[0..run_end])?;
|
||||
|
||||
buffer[0] = byte;
|
||||
run_end = 1;
|
||||
run_start = 0;
|
||||
} else if byte == previous_byte {
|
||||
// detected the start of a run of identical bytes
|
||||
if (run_end - run_start) >= MIN_RUN {
|
||||
if run_start > 0 {
|
||||
// we've found a run, flush the buffer we have currently so we can
|
||||
// start tracking the length of this run
|
||||
dest.write_u8((run_start - 1) as u8)?;
|
||||
dest.write_all(&buffer[0..run_start])?;
|
||||
}
|
||||
mode = PackMode::Run;
|
||||
} else if run_start == 0 {
|
||||
mode = PackMode::Run;
|
||||
}
|
||||
} else {
|
||||
run_start = run_end - 1;
|
||||
}
|
||||
}
|
||||
// "run" mode. keep counting up bytes as long as they are identical to each other. when
|
||||
// we find the end of a run, write out the run info and switch back to dump mode
|
||||
PackMode::Run => {
|
||||
// check for the end of a run of identical bytes
|
||||
if (byte != previous_byte) || ((run_end - run_start) > MAX_RUN) {
|
||||
// the identical byte run has ended, write it out to the destination
|
||||
// (this is just two bytes, the count and the actual byte)
|
||||
dest.write_i8(-((run_end - run_start - 2) as i8))?;
|
||||
dest.write_u8(previous_byte)?;
|
||||
buffer[0] = byte;
|
||||
run_end = 1;
|
||||
run_start = 0;
|
||||
} else if byte == previous_byte {
|
||||
// detected the start of a run of identical bytes
|
||||
if (run_end - run_start) >= MIN_RUN {
|
||||
if run_start > 0 {
|
||||
// we've found a run, flush the buffer we have currently so we can
|
||||
// start tracking the length of this run
|
||||
dest.write_u8((run_start - 1) as u8)?;
|
||||
dest.write_all(&buffer[0..run_start])?;
|
||||
}
|
||||
mode = PackMode::Run;
|
||||
} else if run_start == 0 {
|
||||
mode = PackMode::Run;
|
||||
}
|
||||
} else {
|
||||
run_start = run_end - 1;
|
||||
}
|
||||
}
|
||||
// "run" mode. keep counting up bytes as long as they are identical to each other. when
|
||||
// we find the end of a run, write out the run info and switch back to dump mode
|
||||
PackMode::Run => {
|
||||
// check for the end of a run of identical bytes
|
||||
if (byte != previous_byte) || ((run_end - run_start) > MAX_RUN) {
|
||||
// the identical byte run has ended, write it out to the destination
|
||||
// (this is just two bytes, the count and the actual byte)
|
||||
dest.write_i8(-((run_end - run_start - 2) as i8))?;
|
||||
dest.write_u8(previous_byte)?;
|
||||
|
||||
// clear the temp buffer for our switch back to "dump" mode
|
||||
buffer[0] = byte;
|
||||
run_end = 1;
|
||||
run_start = 0;
|
||||
mode = PackMode::Dump;
|
||||
}
|
||||
}
|
||||
};
|
||||
// clear the temp buffer for our switch back to "dump" mode
|
||||
buffer[0] = byte;
|
||||
run_end = 1;
|
||||
run_start = 0;
|
||||
mode = PackMode::Dump;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
previous_byte = byte;
|
||||
bytes_left -= 1;
|
||||
}
|
||||
previous_byte = byte;
|
||||
bytes_left -= 1;
|
||||
}
|
||||
|
||||
// the source bytes have all been read, but we still might have to flush the temp buffer
|
||||
// out to the destination, or finish writing out a run of identical bytes that was at the very
|
||||
// end of the source
|
||||
match mode {
|
||||
PackMode::Dump => {
|
||||
dest.write_u8((run_end - 1) as u8)?;
|
||||
dest.write_all(&buffer[0..run_end])?;
|
||||
}
|
||||
PackMode::Run => {
|
||||
dest.write_i8(-((run_end - run_start - 1) as i8))?;
|
||||
dest.write_u8(previous_byte)?;
|
||||
}
|
||||
};
|
||||
// the source bytes have all been read, but we still might have to flush the temp buffer
|
||||
// out to the destination, or finish writing out a run of identical bytes that was at the very
|
||||
// end of the source
|
||||
match mode {
|
||||
PackMode::Dump => {
|
||||
dest.write_u8((run_end - 1) as u8)?;
|
||||
dest.write_all(&buffer[0..run_end])?;
|
||||
}
|
||||
PackMode::Run => {
|
||||
dest.write_i8(-((run_end - run_start - 1) as i8))?;
|
||||
dest.write_u8(previous_byte)?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unpack_bits<S, D>(
|
||||
src: &mut S,
|
||||
dest: &mut D,
|
||||
unpacked_length: usize,
|
||||
src: &mut S,
|
||||
dest: &mut D,
|
||||
unpacked_length: usize,
|
||||
) -> Result<(), PackBitsError>
|
||||
where
|
||||
S: ReadBytesExt,
|
||||
D: WriteBytesExt,
|
||||
where
|
||||
S: ReadBytesExt,
|
||||
D: WriteBytesExt,
|
||||
{
|
||||
let mut buffer = [0u8; 128];
|
||||
let mut bytes_written = 0;
|
||||
let mut buffer = [0u8; 128];
|
||||
let mut bytes_written = 0;
|
||||
|
||||
while bytes_written < unpacked_length {
|
||||
// read the next "code" byte that determines how to process the subsequent byte(s)
|
||||
let byte = src.read_u8()?;
|
||||
while bytes_written < unpacked_length {
|
||||
// read the next "code" byte that determines how to process the subsequent byte(s)
|
||||
let byte = src.read_u8()?;
|
||||
|
||||
if byte > 128 {
|
||||
// 129-255 = repeat the next byte 257-n times
|
||||
let run_length = (257 - byte as u32) as usize;
|
||||
if byte > 128 {
|
||||
// 129-255 = repeat the next byte 257-n times
|
||||
let run_length = (257 - byte as u32) as usize;
|
||||
|
||||
// read the next byte from the source and repeat it the specified number of times
|
||||
let byte = src.read_u8()?;
|
||||
buffer.fill(byte);
|
||||
dest.write_all(&buffer[0..run_length])?;
|
||||
bytes_written += run_length;
|
||||
} else if byte < 128 {
|
||||
// 0-128 = copy next n-1 bytes from src to dest as-is
|
||||
let run_length = (byte + 1) as usize;
|
||||
// read the next byte from the source and repeat it the specified number of times
|
||||
let byte = src.read_u8()?;
|
||||
buffer.fill(byte);
|
||||
dest.write_all(&buffer[0..run_length])?;
|
||||
bytes_written += run_length;
|
||||
} else if byte < 128 {
|
||||
// 0-128 = copy next n-1 bytes from src to dest as-is
|
||||
let run_length = (byte + 1) as usize;
|
||||
|
||||
src.read_exact(&mut buffer[0..run_length])?;
|
||||
dest.write_all(&buffer[0..run_length])?;
|
||||
bytes_written += run_length;
|
||||
}
|
||||
src.read_exact(&mut buffer[0..run_length])?;
|
||||
dest.write_all(&buffer[0..run_length])?;
|
||||
bytes_written += run_length;
|
||||
}
|
||||
|
||||
// note that byte == 128 is a no-op (does it even appear in any files ???)
|
||||
}
|
||||
// note that byte == 128 is a no-op (does it even appear in any files ???)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
use std::io::Cursor;
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
struct TestData<'a> {
|
||||
packed: &'a [u8],
|
||||
unpacked: &'a [u8],
|
||||
}
|
||||
struct TestData<'a> {
|
||||
packed: &'a [u8],
|
||||
unpacked: &'a [u8],
|
||||
}
|
||||
|
||||
static TEST_DATA: &[TestData] = &[
|
||||
TestData {
|
||||
packed: &[
|
||||
0xfe, 0xaa, 0x02, 0x80, 0x00, 0x2a, 0xfd, 0xaa, 0x03, 0x80, 0x00, 0x2a, 0x22, 0xf7,
|
||||
0xaa,
|
||||
],
|
||||
unpacked: &[
|
||||
0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0x22,
|
||||
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0x00, 0xaa],
|
||||
unpacked: &[0xaa],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0xf9, 0xaa],
|
||||
unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0xf9, 0xaa, 0x00, 0xbb],
|
||||
unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0x07, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8],
|
||||
unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0x08, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8],
|
||||
unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0x06, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xfe, 0xa8],
|
||||
unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa8],
|
||||
},
|
||||
];
|
||||
static TEST_DATA: &[TestData] = &[
|
||||
TestData {
|
||||
packed: &[
|
||||
0xfe, 0xaa, 0x02, 0x80, 0x00, 0x2a, 0xfd, 0xaa, 0x03, 0x80, 0x00, 0x2a, 0x22, 0xf7,
|
||||
0xaa,
|
||||
],
|
||||
unpacked: &[
|
||||
0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x2a, 0x22,
|
||||
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0x00, 0xaa],
|
||||
unpacked: &[0xaa],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0xf9, 0xaa],
|
||||
unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0xf9, 0xaa, 0x00, 0xbb],
|
||||
unpacked: &[0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0x07, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8],
|
||||
unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0x08, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8],
|
||||
unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8],
|
||||
},
|
||||
TestData {
|
||||
packed: &[0x06, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xfe, 0xa8],
|
||||
unpacked: &[0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa8],
|
||||
},
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn packs() -> Result<(), PackBitsError> {
|
||||
for TestData { packed, unpacked } in TEST_DATA {
|
||||
let mut src = Cursor::new(*unpacked);
|
||||
let mut dest = vec![0u8; 0];
|
||||
pack_bits(&mut src, &mut dest, unpacked.len())?;
|
||||
assert_eq!(dest, *packed);
|
||||
}
|
||||
#[test]
|
||||
fn packs() -> Result<(), PackBitsError> {
|
||||
for TestData { packed, unpacked } in TEST_DATA {
|
||||
let mut src = Cursor::new(*unpacked);
|
||||
let mut dest = vec![0u8; 0];
|
||||
pack_bits(&mut src, &mut dest, unpacked.len())?;
|
||||
assert_eq!(dest, *packed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unpacks() -> Result<(), PackBitsError> {
|
||||
for TestData { packed, unpacked } in TEST_DATA {
|
||||
let mut src = Cursor::new(*packed);
|
||||
let mut dest = vec![0u8; 0];
|
||||
unpack_bits(&mut src, &mut dest, unpacked.len())?;
|
||||
assert_eq!(dest, *unpacked);
|
||||
}
|
||||
#[test]
|
||||
fn unpacks() -> Result<(), PackBitsError> {
|
||||
for TestData { packed, unpacked } in TEST_DATA {
|
||||
let mut src = Cursor::new(*packed);
|
||||
let mut dest = vec![0u8; 0];
|
||||
unpack_bits(&mut src, &mut dest, unpacked.len())?;
|
||||
assert_eq!(dest, *unpacked);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue