Repository: GraphiteEditor/Graphite
File: editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs (Rust)

Code before:
	}

	pub fn send_dialog_to_frontend(&self, responses: &mut VecDeque<Message>, preferences: &PreferencesMessageHandler) {
		self.send_layout(responses, LayoutTarget::DialogColumn1, preferences);
		self.send_layout_column_2(responses, LayoutTarget::DialogColumn2);
		self.send_layout_buttons(responses, LayoutTarget::DialogButtons);
		responses.add(FrontendMessage::DisplayDialog {
			icon: Self::ICON.into(),
			title: Self::TITLE.into(),
		});
	}
}
// Map the actual rate value to display value (1-100)
fn map_zoom_rate_to_display(rate: f64) -> f64 {
	let value = if rate <= 0.0001 {
		1.0
	} else if rate >= 0.05 {
		100.0
	} else {
		// Calculate the logarithmic position between 0.0001 and 0.05
		let log_min = 0.0001_f64.ln();
		let log_max = 0.05_f64.ln();
		let log_val = rate.ln();

		// Map to 1-100 range
		let normalized = (log_val - log_min) / (log_max - log_min);
		1.0 + 99.0 * normalized
	};

	value.round() 
}

// Map the display value (1-100) back to the actual rate value
fn map_display_to_zoom_rate(display: f64) -> f64 {
	if display <= 1.0 {
		0.0001
	} else if display >= 100.0 {
		0.05
	} else {
		// Normalize to 0-1 range
		let normalized = (display - 1.0) / 99.0;

		
		let log_min = 0.0001_f64.ln();
		let log_max = 0.05_f64.ln();
		let log_val = log_min + normalized * (log_max - log_min);

		// Convert back to actual value
		log_val.exp()
	}
}

Review comment (bug):
@Keavon  when you have a moment , could be please take a look if the formula of logarithmic scaling  correctly implemented for our use case.

I took reference from [here](https://stackoverflow.com/questions/28125439/algorithm-to-map-values-to-unit-scale-with-logarithmic-spacing).

This PR sets the minimal possible `zoom rate` to `0.0001` and max possible to `0.05`
the default value of `0.005` will correspond to `63` on the frontend


Code after:
	}

	fn send_layout_buttons(&self, responses: &mut VecDeque<Message>, layout_target: LayoutTarget) {
		responses.add(LayoutMessage::SendLayout {
			layout: self.layout_buttons(),
			layout_target,
		});
	}

	pub fn send_dialog_to_frontend(&self, responses: &mut VecDeque<Message>, preferences: &PreferencesMessageHandler) {
		self.send_layout(responses, LayoutTarget::DialogColumn1, preferences);
		self.send_layout_column_2(responses, LayoutTarget::DialogColumn2);
		self.send_layout_buttons(responses, LayoutTarget::DialogButtons);
		responses.add(FrontendMessage::DisplayDialog {
			icon: Self::ICON.into(),
			title: Self::TITLE.into(),
		});
	}
}

/// Maps display values (1-100) to actual zoom rates.
fn map_display_to_zoom_rate(display: f64) -> f64 {
	// Calculate the relative distance from the reference point (50)
	let distance_from_reference = display - 50.;
	let scaling_factor = (VIEWPORT_ZOOM_WHEEL_RATE_CHANGE * distance_from_reference / 50.).exp();
	VIEWPORT_ZOOM_WHEEL_RATE * scaling_factor
}

/// Maps actual zoom rates back to display values (1-100).
fn map_zoom_rate_to_display(rate: f64) -> f64 {
	// Calculate the scaling factor from the reference rate
	let scaling_factor = rate / VIEWPORT_ZOOM_WHEEL_RATE;
	let distance_from_reference = 50. * scaling_factor.ln() / VIEWPORT_ZOOM_WHEEL_RATE_CHANGE;
	let display = 50. + distance_from_reference;
	display.clamp(1., 100.).round()
}