use crate::math::{DQuat, Pos2, screen_to_world};
use crate::subgizmo::common::{draw_circle, pick_circle};
use crate::subgizmo::{SubGizmoConfig, SubGizmoKind};
use crate::{GizmoDrawData, GizmoResult, config::PreparedGizmoConfig, gizmo::Ray};
use ecolor::Color32;
use super::common::PickResult;
pub(crate) type ArcballSubGizmo = SubGizmoConfig<Arcball>;
#[derive(Default, Debug, Copy, Clone)]
pub(crate) struct ArcballState {
last_pos: Pos2,
total_rotation: DQuat,
}
#[derive(Default, Debug, Copy, Clone)]
pub(crate) struct Arcball;
impl SubGizmoKind for Arcball {
type Params = ();
type State = ArcballState;
type PickPreview = PickResult;
fn pick_preview(subgizmo: &SubGizmoConfig<Self>, ray: Ray) -> super::common::PickResult
where
Self: Sized,
{
pick_circle(
&subgizmo.config,
ray,
arcball_radius(&subgizmo.config),
true,
)
}
fn pick(subgizmo: &mut ArcballSubGizmo, ray: Ray) -> Option<f64> {
let pick_result = Self::pick_preview(subgizmo, ray);
subgizmo.state.last_pos = ray.screen_pos;
if !pick_result.picked {
return None;
}
Some(f64::MAX)
}
fn update(subgizmo: &mut ArcballSubGizmo, ray: Ray) -> Option<GizmoResult> {
let dir = ray.screen_pos - subgizmo.state.last_pos;
let rotation_delta = if dir.length_sq() > f32::EPSILON {
let mat = subgizmo.config.view_projection.inverse();
let a = screen_to_world(subgizmo.config.viewport, mat, ray.screen_pos, 0.0);
let b = screen_to_world(subgizmo.config.viewport, mat, subgizmo.state.last_pos, 0.0);
let origin = subgizmo.config.view_forward();
let a = (a - origin).normalize();
let b = (b - origin).normalize();
DQuat::from_axis_angle(a.cross(b).normalize(), a.dot(b).acos() * 10.0)
} else {
DQuat::IDENTITY
};
subgizmo.state.last_pos = ray.screen_pos;
subgizmo.state.total_rotation = rotation_delta.mul_quat(subgizmo.state.total_rotation);
Some(GizmoResult::Arcball {
delta: rotation_delta.into(),
total: subgizmo.state.total_rotation.into(),
})
}
fn draw(subgizmo: &ArcballSubGizmo) -> GizmoDrawData {
draw_circle(
&subgizmo.config,
Color32::WHITE.gamma_multiply(if subgizmo.focused { 0.10 } else { 0.0 }),
arcball_radius(&subgizmo.config),
true,
)
}
}
pub(crate) fn arcball_radius(config: &PreparedGizmoConfig) -> f64 {
(config.scale_factor * (config.visuals.gizmo_size + config.visuals.stroke_width - 5.0)) as f64
}