use crate::camera::camera::Camera;
use crate::interaction::gizmo::{GizmoAxis, project_drag_onto_axis};
pub fn angular_rotation_from_cursor(
cursor_viewport: Option<glam::Vec2>,
pointer_delta: glam::Vec2,
gizmo_center: glam::Vec3,
axis_world: glam::Vec3,
view_proj: glam::Mat4,
viewport_size: glam::Vec2,
camera_view: glam::Mat4,
) -> f32 {
const MIN_RADIUS: f32 = 10.0;
let cursor = match cursor_viewport {
Some(c) => c,
None => return 0.0,
};
let ndc = view_proj.project_point3(gizmo_center);
let center_screen = glam::Vec2::new(
(ndc.x + 1.0) * 0.5 * viewport_size.x,
(1.0 - ndc.y) * 0.5 * viewport_size.y,
);
let r_curr = cursor - center_screen;
let r_prev = r_curr - pointer_delta;
if r_curr.length() < MIN_RADIUS || r_prev.length() < MIN_RADIUS {
return 0.0;
}
let cross2d = r_prev.x * r_curr.y - r_prev.y * r_curr.x;
let dot = r_prev.dot(r_curr);
let screen_angle = cross2d.atan2(dot);
let axis_z_cam = (camera_view * axis_world.extend(0.0)).z;
if axis_z_cam >= 0.0 { -screen_angle } else { screen_angle }
}
pub fn constrained_translation(
pointer_delta: glam::Vec2,
axis: Option<GizmoAxis>,
exclude_axis: bool,
gizmo_center: glam::Vec3,
camera: &Camera,
viewport_size: glam::Vec2,
) -> glam::Vec3 {
let pan_scale = 2.0 * camera.distance * (camera.fov_y / 2.0).tan()
/ viewport_size.y.max(1.0);
let camera_right = camera.right();
let camera_up = camera.up();
let camera_view = camera.view_matrix();
let view_proj = camera.proj_matrix() * camera_view;
match axis {
None => {
camera_right * pointer_delta.x * pan_scale - camera_up * pointer_delta.y * pan_scale
}
Some(ax) => {
if exclude_axis {
let mut world_delta = camera_right * pointer_delta.x * pan_scale
- camera_up * pointer_delta.y * pan_scale;
match ax {
GizmoAxis::X => world_delta.x = 0.0,
GizmoAxis::Y => world_delta.y = 0.0,
GizmoAxis::Z | GizmoAxis::None => world_delta.z = 0.0,
_ => world_delta.z = 0.0,
}
world_delta
} else {
let axis_world = gizmo_axis_to_vec3(ax);
let amount = project_drag_onto_axis(
pointer_delta,
axis_world,
view_proj,
gizmo_center,
viewport_size,
);
axis_world * amount
}
}
}
}
pub fn constrained_scale(
pointer_delta: glam::Vec2,
axis: Option<GizmoAxis>,
exclude_axis: bool,
position: glam::Vec3,
view_proj: glam::Mat4,
viewport_size: glam::Vec2,
) -> glam::Vec3 {
const MIN_SCALE: f32 = 0.001;
let sensitivity = 8.0 / viewport_size.x.max(1.0);
match axis {
None => {
let factor = (1.0 + pointer_delta.x * sensitivity).max(MIN_SCALE);
glam::Vec3::splat(factor)
}
Some(ax) => {
if exclude_axis {
let factor = (1.0 + pointer_delta.x * sensitivity).max(MIN_SCALE);
let mut scale = glam::Vec3::ONE;
match ax {
GizmoAxis::X => { scale.y = factor; scale.z = factor; }
GizmoAxis::Y => { scale.x = factor; scale.z = factor; }
GizmoAxis::Z | GizmoAxis::None => { scale.x = factor; scale.y = factor; }
_ => { scale.x = factor; scale.y = factor; }
}
scale
} else {
let axis_world = gizmo_axis_to_vec3(ax);
let base_ndc = view_proj.project_point3(position);
let tip_ndc = view_proj.project_point3(position + axis_world);
let base_screen = glam::Vec2::new(
(base_ndc.x + 1.0) * 0.5 * viewport_size.x,
(1.0 - base_ndc.y) * 0.5 * viewport_size.y,
);
let tip_screen = glam::Vec2::new(
(tip_ndc.x + 1.0) * 0.5 * viewport_size.x,
(1.0 - tip_ndc.y) * 0.5 * viewport_size.y,
);
let axis_screen = tip_screen - base_screen;
let axis_screen_len = axis_screen.length();
let amount = if axis_screen_len > 1e-4 {
pointer_delta.dot(axis_screen / axis_screen_len) / viewport_size.x.max(1.0) * 8.0
} else {
0.0
};
let factor = (1.0 + amount).max(MIN_SCALE);
let mut scale = glam::Vec3::ONE;
match ax {
GizmoAxis::X => scale.x = factor,
GizmoAxis::Y => scale.y = factor,
GizmoAxis::Z | GizmoAxis::None => scale.z = factor,
_ => scale.z = factor,
}
scale
}
}
}
}
pub(super) fn gizmo_axis_to_vec3(axis: GizmoAxis) -> glam::Vec3 {
match axis {
GizmoAxis::X => glam::Vec3::X,
GizmoAxis::Y => glam::Vec3::Y,
GizmoAxis::Z | GizmoAxis::None => glam::Vec3::Z,
_ => glam::Vec3::Z,
}
}
pub(super) fn excluded_axes(axis: GizmoAxis) -> (glam::Vec3, glam::Vec3) {
match axis {
GizmoAxis::X => (glam::Vec3::Y, glam::Vec3::Z),
GizmoAxis::Y => (glam::Vec3::X, glam::Vec3::Z),
GizmoAxis::Z | GizmoAxis::None => (glam::Vec3::X, glam::Vec3::Y),
_ => (glam::Vec3::X, glam::Vec3::Y),
}
}
pub(super) use crate::interaction::gizmo::project_drag_onto_rotation as drag_onto_rotation;