-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from ckaznable/refactor
Code Refactor
- Loading branch information
Showing
16 changed files
with
617 additions
and
490 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,22 @@ | ||
mod app; | ||
mod cli; | ||
mod state; | ||
mod tui; | ||
mod ui; | ||
mod util; | ||
mod weather; | ||
mod widget; | ||
|
||
use anyhow::Result; | ||
use app::App; | ||
use clap::Parser; | ||
use cli::Args; | ||
use weather::Weather; | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<()> { | ||
let args = Args::parse(); | ||
let mut app = App::new(args)?; | ||
let mut app = App::new(args, Weather::from(args))?; | ||
app.run().await?; | ||
Ok(()) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
use std::cell::RefCell; | ||
use std::rc::Rc; | ||
|
||
use ratatui::layout::Rect; | ||
use tinyvec::ArrayVec; | ||
|
||
use super::DropColumn; | ||
use super::DropSpeed; | ||
|
||
pub struct RenderBuffer { | ||
pub buf: Vec<DropColumn>, | ||
pub line: Vec<DropSpeed>, | ||
} | ||
|
||
impl RenderBuffer { | ||
pub fn new(size: Rect) -> Self { | ||
let mut buf = Vec::with_capacity(size.width as usize); | ||
for _ in 0..size.width { | ||
let mut column = Vec::with_capacity(size.height as usize); | ||
for _ in 0..size.height { | ||
column.push(ArrayVec::<[DropSpeed; 3]>::default()); | ||
} | ||
|
||
buf.push(Rc::new(RefCell::new(column))); | ||
} | ||
|
||
Self { | ||
line: Vec::with_capacity(buf.len()), | ||
buf, | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
use super::{buffer::RenderBuffer, DropCell, DropColumn, DropSpeed, Mode, EachFrameImpl}; | ||
|
||
pub struct DroppingState { | ||
pub threshold: u16, | ||
pub mode: Mode, | ||
} | ||
|
||
impl DroppingState { | ||
fn gen_drop(&self, rb: &mut RenderBuffer, seed: u64) { | ||
rb.line.clear(); | ||
|
||
const GROUP_SIZE: u64 = 64; | ||
let len = rb.buf.len() as u64; | ||
let last_group = len % GROUP_SIZE; | ||
let groups = len / GROUP_SIZE + if last_group > 0 { 1 } else { 0 }; | ||
|
||
for g in 0..groups { | ||
let range = if groups.saturating_sub(1) == g { last_group } else { GROUP_SIZE }; | ||
for i in 0..range { | ||
rb.line.push(if seed & (1 << i) != 0 { | ||
Self::get_drop_speed(seed.saturating_sub(i), self.threshold) | ||
} else { | ||
DropSpeed::None | ||
}); | ||
} | ||
} | ||
} | ||
|
||
/// generate new drop line | ||
fn new_drop(&self, rb: &mut RenderBuffer, seed: u64) { | ||
self.gen_drop(rb, seed); | ||
rb.line | ||
.iter() | ||
.enumerate() | ||
.for_each(|(i, d)| { | ||
if let Some(cell) = rb.buf | ||
.get_mut(i) | ||
.unwrap() | ||
.try_borrow_mut() | ||
.unwrap() | ||
.get_mut(0) { | ||
|
||
*cell = Self::merge_drop_state(*cell, *d) | ||
}; | ||
}); | ||
} | ||
|
||
fn drop(col: &mut DropColumn, ticks: u8, mode: Mode) { | ||
let len = col.borrow().len(); | ||
|
||
for col_index in 0..len { | ||
let next_index = len.saturating_sub(col_index.saturating_add(1)); | ||
let current_index = len.saturating_sub(col_index.saturating_add(2)); | ||
let current = { col.borrow().get(current_index).cloned() }; | ||
let Some(current) = current else { continue; }; | ||
let mut column = col.try_borrow_mut().unwrap(); | ||
|
||
'state: for i in 0..current.len() { | ||
let state = match current.get(i) { | ||
Some(s) if ticks % mode.get_frame_by_speed(*s) == 0 => s, | ||
_ => continue 'state | ||
}; | ||
|
||
column[current_index] = Self::remove_drop_state(column[current_index], *state); | ||
column[next_index] = Self::merge_drop_state(column[next_index], *state); | ||
} | ||
} | ||
} | ||
|
||
#[inline] | ||
fn clean_latest_drop(col: &mut DropColumn) { | ||
let len = col.borrow().len(); | ||
if len > 0 { | ||
let mut col = col.try_borrow_mut().unwrap(); | ||
if let Some(c) = col.get_mut(len - 1) { | ||
c.clear() | ||
}; | ||
} | ||
} | ||
|
||
#[inline] | ||
fn merge_drop_state(mut cell: DropCell, state: DropSpeed) -> DropCell { | ||
if !cell.contains(&state) && state != DropSpeed::None { | ||
cell.push(state); | ||
}; | ||
|
||
cell | ||
} | ||
|
||
#[inline] | ||
fn remove_drop_state(cell: DropCell, state: DropSpeed) -> DropCell { | ||
cell.into_iter().filter(|c| *c != state).collect() | ||
} | ||
|
||
#[inline] | ||
fn get_drop_speed(num: u64, threshold: u16) -> DropSpeed { | ||
match num % threshold as u64 { | ||
0 => DropSpeed::Normal, | ||
1 => DropSpeed::Fast, | ||
2 => DropSpeed::Slow, | ||
_ => DropSpeed::None, | ||
} | ||
} | ||
} | ||
|
||
impl EachFrameImpl for DroppingState { | ||
fn on_frame(&mut self, rb: &mut super::buffer::RenderBuffer, seed: u64, frame: u8) { | ||
// each column | ||
for i in 0..rb.buf.len() { | ||
Self::clean_latest_drop(&mut rb.buf[i]); | ||
Self::drop(&mut rb.buf[i], frame, self.mode); | ||
} | ||
|
||
self.new_drop(rb, seed); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
use std::{cell::RefCell, fmt::Display, rc::Rc}; | ||
|
||
use clap::ValueEnum; | ||
use rand::{rngs::SmallRng, RngCore, SeedableRng}; | ||
use ratatui::layout::Rect; | ||
use tinyvec::ArrayVec; | ||
|
||
use self::{ | ||
buffer::RenderBuffer, | ||
timer::Timer, | ||
}; | ||
|
||
pub mod buffer; | ||
pub mod dropping; | ||
pub mod timer; | ||
pub mod wind; | ||
|
||
pub type DropCell = ArrayVec<[DropSpeed; 3]>; | ||
pub type DropColumn = Rc<RefCell<Vec<DropCell>>>; | ||
|
||
pub trait EachFrameImpl { | ||
fn on_frame(&mut self, rb: &mut RenderBuffer, seed: u64, frame: u8); | ||
} | ||
|
||
#[derive(Copy, Clone, PartialEq, Eq, Default)] | ||
pub enum DropSpeed { | ||
Fast, | ||
Normal, | ||
Slow, | ||
#[default] | ||
None, | ||
} | ||
|
||
#[derive(Copy, Clone, Default, ValueEnum, PartialEq, Eq)] | ||
pub enum Mode { | ||
#[default] | ||
Rain, | ||
Snow, | ||
Meteor, | ||
Star, | ||
} | ||
|
||
impl Display for Mode { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
let s = match *self { | ||
Mode::Rain => "rain", | ||
Mode::Snow => "snow", | ||
Mode::Meteor => "meteor", | ||
Mode::Star => "star", | ||
}; | ||
|
||
s.fmt(f) | ||
} | ||
} | ||
|
||
impl Mode { | ||
pub fn get_frame_by_speed(&self, s: DropSpeed) -> u8 { | ||
use DropSpeed::*; | ||
use Mode::*; | ||
|
||
match self { | ||
Rain => match s { | ||
Fast => 1, | ||
Normal => 2, | ||
Slow => 3, | ||
_ => 0, | ||
}, | ||
Snow => match s { | ||
Fast => 2, | ||
Normal => 4, | ||
Slow => 6, | ||
_ => 0, | ||
}, | ||
_ => 0 | ||
} | ||
} | ||
} | ||
|
||
pub struct State<T> { | ||
pub rb: RenderBuffer, | ||
pub timer: Timer, | ||
pub weather: T, | ||
frame: u8, | ||
rng: SmallRng, | ||
seed: u64, | ||
} | ||
|
||
impl<T: EachFrameImpl> State<T> { | ||
pub fn new(size: Rect, weather: T) -> Self { | ||
State { | ||
rb: RenderBuffer::new(size), | ||
rng: SmallRng::from_entropy(), | ||
frame: 0, | ||
timer: Timer::default(), | ||
seed: 0, | ||
weather, | ||
} | ||
} | ||
|
||
pub fn on_resize(&mut self, columns: u16, rows: u16) { | ||
self.rb = RenderBuffer::new(Rect { | ||
x: 0, | ||
y: 0, | ||
height: rows, | ||
width: columns, | ||
}); | ||
} | ||
|
||
pub fn tick_timer(&mut self) { | ||
self.timer = Timer::new(); | ||
} | ||
|
||
pub fn tick(&mut self) { | ||
self.frame = if self.frame > 240 { 0 } else { self.frame.saturating_add(1) }; | ||
self.seed = self.rng.next_u64(); | ||
self.weather.on_frame(&mut self.rb, self.seed, self.frame); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use std::time::SystemTime; | ||
|
||
use chrono::{DateTime, Local, Timelike}; | ||
|
||
#[derive(Copy, Clone)] | ||
pub struct Timer { | ||
pub hours: u8, | ||
pub minutes: u8, | ||
pub seconds: u8, | ||
} | ||
|
||
impl Timer { | ||
pub fn new() -> Self { | ||
Self::default() | ||
} | ||
} | ||
|
||
impl Default for Timer { | ||
fn default() -> Self { | ||
let system_time = SystemTime::now(); | ||
let datetime: DateTime<Local> = system_time.into(); | ||
Self { | ||
hours: datetime.hour() as u8, | ||
minutes: datetime.minute() as u8, | ||
seconds: datetime.second() as u8, | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.