Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add smooth coloring #58

Merged
merged 1 commit into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/app_state/click_modes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub(crate) enum ClickMode {
MandelConstant,
Move,
BailOut,
Info,
}

impl ClickMode {
Expand All @@ -19,6 +20,7 @@ impl ClickMode {
ClickMode::JuliaConstant,
ClickMode::BailOut,
ClickMode::MandelConstant,
ClickMode::Info,
]
}

Expand All @@ -30,6 +32,7 @@ impl ClickMode {
("juliaconstant", Self::JuliaConstant),
("mandelconstant", Self::MandelConstant),
("bailout", Self::BailOut),
("info", Self::Info),
]
.iter()
.find(|x| x.0.starts_with(&str_.to_lowercase().replace("_", "")))
Expand Down
9 changes: 6 additions & 3 deletions src/app_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ pub(crate) struct AppState {
pub(crate) log_panel_scroll_state: Mutex<ScrollViewState>,
pub(crate) last_command: String,
pub(crate) command_input: TuiInput,
pub(crate) marker: Option<CanvasCoords>,
pub(crate) move_dist: i32,
pub(crate) scaling_factor: i32,
pub(crate) render_settings: RenderSettings,
Expand All @@ -40,7 +39,7 @@ pub(crate) struct AppState {

const DF_MOVE_DISTANCE_CPU: i32 = 8;
const DF_SCALING_FACTOR_CPU: i32 = 20;
const DF_MOVE_DISTANCE_GPU: i32 = 1;
const DF_MOVE_DISTANCE_GPU: i32 = 4;
const DF_SCALING_FACTOR_GPU: i32 = 8;

impl Default for AppState {
Expand All @@ -59,7 +58,6 @@ impl Default for AppState {
render_settings: Default::default(),
scaling_factor: DF_SCALING_FACTOR_GPU,
move_dist: DF_MOVE_DISTANCE_GPU,
marker: Default::default(),
requested_jobs: Default::default(),
click_config: Default::default(),
}
Expand Down Expand Up @@ -97,6 +95,11 @@ impl AppState {
self.set_decimal_prec(precision);
}

// Change the smoothness
if let Some(smoothness) = saved.smoothness {
self.render_settings.smoothness = smoothness;
}

// Change the canvas position
if let Some(pos) = saved.pos {
self.render_settings.pos = Complex::parse(pos)
Expand Down
30 changes: 29 additions & 1 deletion src/colors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@
use ratatui::style::Color;

/// Returns the color assiciated to the given divergence in the provided palette.
pub(crate) fn palette_color(i: i32, pal: &Palette) -> Color {
pub(crate) fn palette_color(i: i32, offset: i32, pal: &Palette, smoothing: i32) -> Color {
let d = (offset + i) as f32 / smoothing as f32;
let min = d.floor() as i32;
let max = d.ceil() as i32;
interpolate(
palette_color_at(min, pal),
palette_color_at(max, pal),
d % 1.0,
)
}

pub(crate) fn palette_color_at(i: i32, pal: &Palette) -> Color {
pal.colors[i as usize % pal.colors.len()]
}

Expand All @@ -14,6 +25,23 @@ pub(crate) fn get_palette_index_by_name(name: &str) -> Option<usize> {
.position(|pal| pal.name.to_lowercase() == name.to_lowercase())
}

pub(crate) fn interpolate_byte(b1: u8, b2: u8, p: f32) -> u8 {
let d = b2 as f32 - b1 as f32;
let incr = d * p;
(b1 as f32 + incr) as u8
}
pub(crate) fn interpolate(c1: Color, c2: Color, p: f32) -> Color {
if let (Color::Rgb(r1, g1, b1), Color::Rgb(r2, g2, b2)) = (c1, c2) {
Color::Rgb(
interpolate_byte(r1, r2, p),
interpolate_byte(g1, g2, p),
interpolate_byte(b1, b2, p),
)
} else {
panic!("Invalid color passed to interpolate()");
}
}

/// Represents a color palette.
pub(crate) struct Palette {
/// The list of palette colors, in fixed order.
Expand Down
2 changes: 1 addition & 1 deletion src/commands/gpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub(crate) fn execute_gpu(state: &mut AppState, _args: Vec<&str>) -> Result<(),
.render_settings
.initialize_gpu_sync(None)
.map_err(|err| format!("GPU mode could not be enabled: {err}"))?;
state.log_success("GPU mode initialized successfully!");
state.log_success("GPU mode initialized successfully! To benefit from high precision arithmetics you will have to disable it with the <command gpu> command.");
state.render_settings.use_gpu = true;
}
state.request_redraw();
Expand Down
4 changes: 3 additions & 1 deletion src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub(crate) mod pos;
pub(crate) mod prec;
pub(crate) mod quit;
pub(crate) mod save;
pub(crate) mod smoothness;
pub(crate) mod version;
pub(crate) mod zoom_factor;

Expand All @@ -38,7 +39,7 @@ pub(crate) struct Command {
pub(crate) accepted_arg_count: &'static [usize],
}

pub(crate) fn get_commands_list() -> [&'static Command; 18] {
pub(crate) fn get_commands_list() -> [&'static Command; 19] {
[
&help::HELP,
&quit::QUIT,
Expand All @@ -54,6 +55,7 @@ pub(crate) fn get_commands_list() -> [&'static Command; 18] {
&prec::PREC,
&max_iter::MAX_ITER,
&color::COLOR,
&smoothness::SMOOTHNESS,
&frac::FRAC,
&zoom_factor::ZOOM_FACTOR,
&move_dist::MOVE_DIST,
Expand Down
41 changes: 41 additions & 0 deletions src/commands/smoothness.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use super::{command_increment::command_increment, Command};
use crate::AppState;

const MIN_SMOOTHNESS: i32 = 1;
const MAX_SMOOTHNESS: i32 = 100;

pub(crate) fn execute_smoothness(state: &mut AppState, args: Vec<&str>) -> Result<(), String> {
let val = command_increment(
state,
state.render_settings.smoothness,
args,
MIN_SMOOTHNESS,
MAX_SMOOTHNESS,
)?;
state.render_settings.smoothness = val;
state.request_redraw();

Ok(())
}
pub(crate) const SMOOTHNESS: Command = Command {
execute: &execute_smoothness,
name: "smoothness",
aliases: &["sm"],
accepted_arg_count: &[0, 1, 2],

detailed_desc: Some(concat!(
"<green Usage: <command +/- [value]>>\n",
"<green Usage: <command [smoothness]>>\n",
"<green Usage: <command [without args]>>\n",
"- If no arguments are given, display the smoothness.\n",
"- If a value is specified directly, set the smoothness to the given value.\n",
"- If a value is specified alongside an operator, ",
"increase of decrease the smoothness by the given value.\n",
"<acc [smoothness]> must be a valid integer between <acc 1> and <acc 100>.",
)),
basic_desc: concat!(
"Changes the smoothness of the color palette, ",
"lower values will contrast the adjacent divergences ",
"while higher values will look smoother (and better in my opinion)."
),
};
35 changes: 15 additions & 20 deletions src/components/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ratatui::{
buffer::Buffer,
crossterm::event::{KeyCode, MouseButton, MouseEvent, MouseEventKind},
layout::{Alignment, Rect},
style::{Color, Style},
style::Style,
symbols::Marker,
text::Line,
widgets::{canvas::Points, Block, Widget},
Expand Down Expand Up @@ -85,6 +85,18 @@ impl<'a> Canvas<'a> {
.real()
.to_f32()
}
ClickMode::Info => {
let point = state.render_settings.coord_to_c(canvas_pos);
state.log_info_title(
"Click Info",
format!(
"Real: <acc {}>\nImag: <acc {}>\nDiverg: <acc {}>",
point.real().to_f32(),
point.imag().to_f32(),
(state.render_settings.get_frac_clos())(point, &state.render_settings),
),
)
}
}

state.request_redraw();
Expand Down Expand Up @@ -197,19 +209,12 @@ impl<'a> Canvas<'a> {
// Todo: remove duplication for + and -
// Increment color scheme offset
KeyCode::Char('-') => {
state.render_settings.color_scheme_offset =
(state.render_settings.color_scheme_offset
+ state.render_settings.get_palette().colors.len() as i32
- 1)
% state.render_settings.get_palette().colors.len() as i32;

state.render_settings.decrement_color_offset();
state.request_repaint();
}
// Increment color scheme offset
KeyCode::Char('+') => {
state.render_settings.color_scheme_offset =
(state.render_settings.color_scheme_offset + 1)
% state.render_settings.get_palette().colors.len() as i32;
state.render_settings.increment_color_offset();
state.request_repaint();
}
// Cycle through the void fills
Expand Down Expand Up @@ -319,16 +324,6 @@ impl Widget for Canvas<'_> {
coords: points,
})
}
if let Some(marker) = &self.state.marker {
// Todo: use a Painter instead
ctx.draw(&Points {
color: Color::Rgb(255, 0, 0),
coords: &[(
(marker.x + self.state.render_settings.canvas_size.x / 2) as f64,
(marker.y + self.state.render_settings.canvas_size.y / 2) as f64,
)],
})
}
});

canvas_wid.render(area, buf);
Expand Down
27 changes: 22 additions & 5 deletions src/frac_logic/render_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const BLACK: Color = Color::Rgb(0, 0, 0);
const WHITE: Color = Color::Rgb(255, 255, 255);
const DEFAULT_JULIA_CONSTANT: (f32, f32) = (-9.9418604e-1, 2.61627e-1);
const DEFAULT_MANDEL_CONSTANT: (f32, f32) = (0.0, 0.0);
const DEFAULT_SMOOTHNESS: i32 = 7;
const DEFAULT_BAILOUT: f32 = 2.0;

/// Used to group values related to fractal rendering logic.
#[derive(Clone)]
Expand All @@ -49,6 +51,7 @@ pub(crate) struct RenderSettings {
pub(crate) julia_constant: Complex,
pub(crate) mandel_constant: Complex,
pub(crate) bailout: f32,
pub(crate) smoothness: i32,
}

impl Default for RenderSettings {
Expand All @@ -68,12 +71,23 @@ impl Default for RenderSettings {
wgpu_state: WgpuState::default(),
julia_constant: Complex::with_val(DF_PREC_GPU, DEFAULT_JULIA_CONSTANT),
mandel_constant: Complex::with_val(DF_PREC_GPU, DEFAULT_MANDEL_CONSTANT),
bailout: 2.0,
bailout: DEFAULT_BAILOUT,
smoothness: DEFAULT_SMOOTHNESS,
}
}
}

impl RenderSettings {
pub(crate) fn increment_color_offset(&mut self) {
self.color_scheme_offset = (self.color_scheme_offset + 1)
% (self.get_palette().colors.len() as i32 * self.smoothness);
}
pub(crate) fn decrement_color_offset(&mut self) {
self.color_scheme_offset = (self.color_scheme_offset
+ self.get_palette().colors.len() as i32 * self.smoothness
- 1)
% (self.get_palette().colors.len() as i32 * self.smoothness);
}
/// Load the default settings for CPU mode when GPU init fails at startup.
pub(crate) fn cpu_defaults(&mut self) {
self.set_decimal_prec(DF_PREC_CPU);
Expand Down Expand Up @@ -133,9 +147,12 @@ impl RenderSettings {
VoidFill::Transparent => Color::Reset,
VoidFill::Black => BLACK,
VoidFill::White => WHITE,
VoidFill::ColorScheme => {
colors::palette_color(*diverg + self.color_scheme_offset, palette)
}
VoidFill::ColorScheme => colors::palette_color(
*diverg,
self.color_scheme_offset,
palette,
self.smoothness,
),
VoidFill::RGBNoise => Color::Rgb(
rng.gen_range(0..255),
rng.gen_range(0..255),
Expand All @@ -146,7 +163,7 @@ impl RenderSettings {
VoidFill::BlueNoise => Color::Rgb(0, 0, rng.gen_range(0..255)),
}
} else {
colors::palette_color(*diverg + self.color_scheme_offset, palette)
colors::palette_color(*diverg, self.color_scheme_offset, palette, self.smoothness)
}
}
}
2 changes: 2 additions & 0 deletions src/helpers/saved_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub(crate) struct SavedState {
pub(crate) julia_constant: Option<String>,
pub(crate) mandel_constant: Option<String>,
pub(crate) bailout: Option<f32>,
pub(crate) smoothness: Option<i32>,
}

impl From<&AppState> for SavedState {
Expand All @@ -33,6 +34,7 @@ impl From<&AppState> for SavedState {
julia_constant: Some(state.render_settings.julia_constant.to_string()),
mandel_constant: Some(state.render_settings.mandel_constant.to_string()),
bailout: Some(state.render_settings.bailout),
smoothness: Some(state.render_settings.smoothness),
}
}
}
Loading