Skip to content

Commit

Permalink
Merge branch 'johan/flame-top-row'
Browse files Browse the repository at this point in the history
This change gets us no cloud if there's no system load.

Before this change we got one pixel of cloud in this case.
  • Loading branch information
walles committed Jul 9, 2023
2 parents d21dd14 + def1365 commit 4654ce2
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 68 deletions.
84 changes: 16 additions & 68 deletions libloadviz/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,6 @@ static BG_COLOR_RGB: &[u8; 3] = &[0x30, 0x30, 0x90];
static USER_LOAD_COLOR_RGB_WARMER: &[u8; 3] = &[0xff, 0xb4, 0x6b]; // 3000K
static USER_LOAD_COLOR_RGB_COOLER: &[u8; 3] = &[0xff, 0x38, 0x00]; // 1000K

static CLOUD_COLOR_DARK: &[u8; 3] = &[0x88, 0x88, 0x88];
static CLOUD_COLOR_BRIGHT: &[u8; 3] = &[0xff, 0xff, 0xff];

/// How much of the cloud should fade towards transparent?
///
/// This is a fraction of the height of the whole image, not a fraction of the
/// height of the cloud.
///
/// Lower values make the cloud edge sharper.
static CLOUD_TRANSPARENT_FRACTION: f32 = 0.4;

pub struct Renderer {
noise: FastNoise,
}
Expand All @@ -31,6 +20,8 @@ impl Default for Renderer {
}
}

mod cloud;

impl Renderer {
/// Don't call this! It's public for benchmarking purposes only.
///
Expand Down Expand Up @@ -83,61 +74,6 @@ impl Renderer {
}
}

fn get_cloud_pixel(
&self,
viz_loads: &Vec<CpuLoad>,
dt_seconds: f32,
pixel_x: usize,
pixel_y_from_top: usize,
width: usize,
height: usize,
) -> Option<[u8; 3]> {
// Higher number = more details.
let detail = 5.0 / width as f32;

// Higher speed number = faster cloud turbulence.
let speed = 0.3;

let x_fraction_0_to_1 = pixel_x as f32 / (width as f32 - 1.0);
let cpu_load = get_load(viz_loads, x_fraction_0_to_1);

// Compute the sysload height for this load
let cloud_height_pixels = cpu_load.system_0_to_1 * height as f32;
if pixel_y_from_top as f32 > cloud_height_pixels {
return None;
}

// Noise output is -1 to 1, deciphered from here:
// https://github.com/amethyst/bracket-lib/blob/0d2d5e6a9a8e7c7ae3710cfef85be4cab0109a27/bracket-noise/examples/simplex_fractal.rs#L34-L39
let noise_m1_to_1 = self.noise.get_noise3d(
detail * pixel_x as f32,
detail * pixel_y_from_top as f32,
speed * dt_seconds,
);

let brightness_0_to_1 = (noise_m1_to_1 + 1.0) / 2.0;
let color = interpolate(brightness_0_to_1, CLOUD_COLOR_DARK, CLOUD_COLOR_BRIGHT);

let transparency_height_pixels = CLOUD_TRANSPARENT_FRACTION * height as f32;
let opaque_height_pixels = cloud_height_pixels - transparency_height_pixels;
if (pixel_y_from_top as f32) < opaque_height_pixels {
// Cloud interior
return Some(color);
}

// When we get here, we're closer to the edge of the cloud

// 0-1, higher means more transparent
let alpha = (pixel_y_from_top as f32 - opaque_height_pixels) / transparency_height_pixels;

// Replace dark with transparent. Towards the edge of the cloud, we won't
// see as many dark colors since the sun won't be blocked by thick cloud
// parts.
let color = interpolate(alpha * (1.0 - brightness_0_to_1), &color, BG_COLOR_RGB);

return Some(interpolate(alpha, &color, BG_COLOR_RGB));
}

fn get_flame_pixel(
&self,
viz_loads: &Vec<CpuLoad>,
Expand Down Expand Up @@ -188,7 +124,7 @@ impl Renderer {
// Pick the load to show
let dx_pixels = noise1_m1_to_1 * distortion_pixel_radius;
let distorted_pixel_x = pixel_x as f32 + dx_pixels;
let x_fraction_0_to_1 = distorted_pixel_x / (width as f32 - 1.0);
let x_fraction_0_to_1 = pixel_to_fraction(distorted_pixel_x, width);
let cpu_load = get_load(viz_loads, x_fraction_0_to_1);

let highest_possible_flame_height_pixels =
Expand All @@ -210,7 +146,7 @@ impl Renderer {
// Figure out how to color the current pixel
let dy_pixels = noise2_m1_to_1 * distortion_pixel_radius;
let distorted_pixel_y = pixel_y_from_bottom as f32 + dy_pixels;
let y_from_bottom_0_to_1 = distorted_pixel_y / height as f32;
let y_from_bottom_0_to_1 = pixel_to_fraction(distorted_pixel_y, height);
if y_from_bottom_0_to_1 > cpu_load.user_0_to_1 {
return None;
}
Expand Down Expand Up @@ -306,6 +242,10 @@ fn interpolate(factor_0_to_1: f32, color1: &[u8; 3], color2: &[u8; 3]) -> [u8; 3
return result;
}

fn pixel_to_fraction(pixel: f32, maxpixel: usize) -> f32 {
return (pixel + 1.0) / (maxpixel + 2) as f32;
}

#[cfg(test)]
mod tests {
use crate::cpuload::CpuLoad;
Expand Down Expand Up @@ -428,4 +368,12 @@ mod tests {
}
);
}

#[test]
fn test_pixel_to_fraction() {
// Fractions should be evenly spaced
assert_eq!(0.25, pixel_to_fraction(0.0, 2));
assert_eq!(0.50, pixel_to_fraction(1.0, 2));
assert_eq!(0.75, pixel_to_fraction(2.0, 2));
}
}
87 changes: 87 additions & 0 deletions libloadviz/src/renderer/cloud.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use crate::cpuload::CpuLoad;

use super::{get_load, interpolate, pixel_to_fraction, Renderer, BG_COLOR_RGB};

static CLOUD_COLOR_DARK: &[u8; 3] = &[0x88, 0x88, 0x88];
static CLOUD_COLOR_BRIGHT: &[u8; 3] = &[0xff, 0xff, 0xff];

/// How much of the cloud should fade towards transparent?
///
/// This is a fraction of the height of the whole image, not a fraction of the
/// height of the cloud.
///
/// Lower values make the cloud edge sharper.
static CLOUD_TRANSPARENT_FRACTION: f32 = 0.4;

impl Renderer {
pub(super) fn get_cloud_pixel(
&self,
viz_loads: &Vec<CpuLoad>,
dt_seconds: f32,
pixel_x: usize,
pixel_y_from_top: usize,
width: usize,
height: usize,
) -> Option<[u8; 3]> {
// Higher number = more details.
let detail = 5.0 / width as f32;

// Higher speed number = faster cloud turbulence.
let speed = 0.3;

let x_fraction_0_to_1 = pixel_to_fraction(pixel_x as f32, width);
let cpu_load = get_load(viz_loads, x_fraction_0_to_1);

// Compute the sysload height for this load
let cloud_height_0_to_1 = cpu_load.system_0_to_1;
let y_from_top_0_to_1 = pixel_to_fraction(pixel_y_from_top as f32, height);
if y_from_top_0_to_1 > cloud_height_0_to_1 {
return None;
}

// Noise output is -1 to 1, deciphered from here:
// https://github.com/amethyst/bracket-lib/blob/0d2d5e6a9a8e7c7ae3710cfef85be4cab0109a27/bracket-noise/examples/simplex_fractal.rs#L34-L39
let noise_m1_to_1 = self.noise.get_noise3d(
detail * pixel_x as f32,
detail * pixel_y_from_top as f32,
speed * dt_seconds,
);

let brightness_0_to_1 = (noise_m1_to_1 + 1.0) / 2.0;
let color = interpolate(brightness_0_to_1, CLOUD_COLOR_DARK, CLOUD_COLOR_BRIGHT);

let opaque_height_0_to_1 = cloud_height_0_to_1 - CLOUD_TRANSPARENT_FRACTION;
if y_from_top_0_to_1 < opaque_height_0_to_1 {
// Cloud interior
return Some(color);
}

// When we get here, we're closer to the edge of the cloud

// 0-1, higher means more transparent
let alpha = (y_from_top_0_to_1 - opaque_height_0_to_1) / CLOUD_TRANSPARENT_FRACTION;

// Replace dark with transparent. Towards the edge of the cloud, we won't
// see as many dark colors since the sun won't be blocked by thick cloud
// parts.
let color = interpolate(alpha * (1.0 - brightness_0_to_1), &color, BG_COLOR_RGB);

return Some(interpolate(alpha, &color, BG_COLOR_RGB));
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_no_load_no_cloud() {
let viz_loads = vec![CpuLoad {
user_0_to_1: 0.0,
system_0_to_1: 0.0,
}];
let renderer: Renderer = Default::default();
let pixel = renderer.get_cloud_pixel(&viz_loads, 0.0, 0, 0, 1, 1);
assert_eq!(pixel, None);
}
}

0 comments on commit 4654ce2

Please sign in to comment.