use bevy_camera::prelude::*;
use bevy_math::{DVec2, DVec3};
use bevy_reflect::Reflect;
#[derive(Debug, Clone, Reflect)]
pub struct ZoomLimits {
pub min_size_per_pixel: f64,
pub max_size_per_pixel: f64,
pub zoom_through_objects: bool,
}
impl Default for ZoomLimits {
fn default() -> Self {
Self {
min_size_per_pixel: 1e-6, max_size_per_pixel: 1e27, zoom_through_objects: false,
}
}
}
pub fn length_per_pixel_at_view_space_pos(camera: &Camera, view_space_pos: DVec3) -> Option<f64> {
let scaled_offset = view_space_pos.length();
let view_space_pos_offset = view_space_pos + DVec3::X * scaled_offset;
let viewport_pos = view_to_viewport(camera, view_space_pos)?;
let viewport_pos_offset = view_to_viewport(camera, view_space_pos_offset)?;
let pixels_per_world_unit = (viewport_pos_offset - viewport_pos).length();
let len_per_pixel = pixels_per_world_unit.recip().min(f64::MAX) * scaled_offset;
len_per_pixel.is_finite().then_some(len_per_pixel)
}
fn view_to_viewport(camera: &Camera, view_space_point: DVec3) -> Option<DVec2> {
let ndc_space_coords = camera
.clip_from_view()
.as_dmat4()
.project_point3(view_space_point);
let ndc_space_coords =
(!ndc_space_coords.is_nan() && ndc_space_coords.z >= 0.0 && ndc_space_coords.z <= 1.0)
.then_some(ndc_space_coords)?;
let target_size = camera.logical_viewport_size()?.as_dvec2();
let mut viewport_position = (ndc_space_coords.truncate() + DVec2::ONE) / 2.0 * target_size;
viewport_position.y = target_size.y - viewport_position.y;
Some(viewport_position)
}