Skip to content

Commit 6449d04

Browse files
authored
Merge [#51]: Handle character width and offscreen rendering
Merge pull request #51 from Shresht7:unicode-width
2 parents 595aef1 + 283676e commit 6449d04

4 files changed

Lines changed: 49 additions & 16 deletions

File tree

‎src/matrix/entity.rs‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ pub struct Entity {
2525

2626
/// The symbol the entity represents
2727
symbol: char,
28+
/// Width of the symbol that is visible in the terminal
29+
width: usize,
2830
/// The color of the symbol
2931
color: colors::RGBColor,
3032
/// The character set to use for the symbols
@@ -56,15 +58,21 @@ impl Entity {
5658
speed_y,
5759
color,
5860
symbol: ' ',
61+
width: config.mode.width(),
5962
mode: config.mode.clone(),
6063
frame_count: 0,
6164
switch_interval: utils::random_between::<u16>(1, config.switch_interval * config.fps),
6265
}
6366
}
6467

68+
/// Returns the width of the entity's symbol in terminal columns
69+
pub fn width(&self) -> usize {
70+
self.width
71+
}
72+
6573
/// Rain. Updates the position of the [Entity] using the rain speed.
6674
pub fn rain(&mut self) {
67-
self.x += self.speed_x;
75+
self.x += self.speed_x * self.width as f32;
6876
self.y += self.speed_y;
6977
}
7078

@@ -90,7 +98,7 @@ impl Entity {
9098
stdout: &mut std::io::Stdout,
9199
) -> std::io::Result<()> {
92100
// Don't render if the entity is off-screen
93-
if self.x < 0.0 || self.x >= columns as f32 || self.y < 0.0 || self.y >= rows as f32 {
101+
if self.is_offscreen(columns, rows) {
94102
return Ok(());
95103
}
96104

@@ -106,4 +114,12 @@ impl Entity {
106114

107115
Ok(())
108116
}
117+
118+
/// Returns true if the [Entity] is offscreen
119+
fn is_offscreen(&self, columns: i32, rows: i32) -> bool {
120+
self.x < 0.0
121+
|| self.x + self.width as f32 >= columns as f32
122+
|| self.y < 0.0
123+
|| self.y >= rows as f32
124+
}
109125
}

‎src/matrix/mod.rs‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,11 @@ impl Matrix {
152152
)))?;
153153
// Iterate over each row and pre-render random faint symbols
154154
for _ in 0..self.rows {
155-
let random_symbol_str =
156-
String::from_iter((0..self.columns).map(|_| config.mode.get_random()));
155+
let random_symbol_str = String::from_iter(
156+
(0..self.columns)
157+
.step_by(config.mode.width())
158+
.map(|_| config.mode.get_random()),
159+
);
157160
stdout
158161
.queue(Print(random_symbol_str))?
159162
.queue(MoveToNextLine(1))?;

‎src/matrix/stream.rs‎

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,16 @@ impl Stream {
8787
let color = gradient.interpolate(i as f32 / self.count as f32);
8888

8989
// Determine the entity starting x and y positions based on the direction of flow
90+
let width_adjusted_i = i as f32 * config.mode.width() as f32;
9091
let (x, y) = match config.direction {
9192
Direction::Down => (self.x, self.y - i as f32),
9293
Direction::Up => (self.x, self.y + i as f32),
93-
Direction::Right => (self.x - i as f32, self.y),
94-
Direction::Left => (self.x + i as f32, self.y),
95-
Direction::DiagonalRight => (self.x - i as f32, self.y - i as f32),
96-
Direction::DiagonalRightReverse => (self.x + i as f32, self.y + i as f32),
97-
Direction::DiagonalLeft => (self.x + i as f32, self.y - i as f32),
98-
Direction::DiagonalLeftReverse => (self.x - i as f32, self.y + i as f32),
94+
Direction::Right => (self.x - width_adjusted_i, self.y),
95+
Direction::Left => (self.x + width_adjusted_i, self.y),
96+
Direction::DiagonalRight => (self.x - width_adjusted_i, self.y - i as f32),
97+
Direction::DiagonalRightReverse => (self.x + width_adjusted_i, self.y + i as f32),
98+
Direction::DiagonalLeft => (self.x + width_adjusted_i, self.y - i as f32),
99+
Direction::DiagonalLeftReverse => (self.x - width_adjusted_i, self.y + i as f32),
99100
};
100101

101102
// Create the entity and add it to the entities vector
@@ -118,22 +119,27 @@ impl Stream {
118119
// Clean up the last entity. As the stream moves down, all entities will be overwritten
119120
// by the next frame, except for the trailing entity. So we manually overwrite it so that
120121
// the stream doesn't leave a trail.
121-
if !config.leave_trail {
122+
if !config.leave_trail && e.x >= 0.0 {
123+
let space = " ".repeat(config.mode.width());
122124
stdout
123125
.queue(cursor::MoveTo(e.x as u16, e.y as u16))?
124-
.queue(Print(" "))?;
126+
.queue(Print(space))?;
125127
}
126128

127129
// This is also a good time to check if the last entity is off the screen,
128130
// and if it is, we regenerate the stream and place it back at the top.
129131
let should_regenerate = match config.direction {
130132
Direction::Down => e.y >= rows as f32,
131133
Direction::Up => e.y < 0.0,
132-
Direction::Right => e.x >= columns as f32,
133-
Direction::Left => e.x < 0.0,
134+
Direction::Right => e.x + e.width() as f32 >= columns as f32,
135+
Direction::Left => e.x + (e.width() as f32) < 0.0,
134136
Direction::DiagonalLeft => e.x < 0.0 && e.y >= rows as f32,
135-
Direction::DiagonalLeftReverse => e.x >= columns as f32 && e.y < 0.0,
136-
Direction::DiagonalRight => e.x >= columns as f32 && e.y >= rows as f32,
137+
Direction::DiagonalLeftReverse => {
138+
e.x + e.width() as f32 >= columns as f32 && e.y < 0.0
139+
}
140+
Direction::DiagonalRight => {
141+
e.x + e.width() as f32 >= columns as f32 && e.y >= rows as f32
142+
}
137143
Direction::DiagonalRightReverse => e.x < 0.0 && e.y < 0.0,
138144
};
139145

‎src/symbols.rs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ impl FromStr for Symbols {
4545
}
4646

4747
impl Symbols {
48+
/// Get the width of the character in the symbol set
49+
pub fn width(&self) -> usize {
50+
match self {
51+
Self::Original | Self::Cursed => 2, // Katakana and Emojis
52+
_ => 1, // Default to width of 1 for most characters
53+
}
54+
}
55+
4856
/// Get a random character from the symbol set
4957
pub fn get_random(&self) -> char {
5058
match self {

0 commit comments

Comments
 (0)