use egui::{PointerState, Ui};
use plotters::{
coord::Shift,
prelude::{DrawingArea, IntoDrawingArea},
};
use crate::EguiBackend;
pub const DEFAULT_MOVE_SCALE: f32 = 0.01;
pub const DEFAULT_SCROLL_SCALE: f32 = 0.001;
#[derive(Debug, Copy, Clone)]
pub struct Transform {
pub pitch: f64,
pub yaw: f64,
pub scale: f64,
pub x: i32,
pub y: i32,
}
impl Default for Transform {
fn default() -> Self {
Self {
pitch: 0.0,
yaw: 0.0,
scale: 1.0,
x: 0,
y: 0,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum MouseButton {
Primary,
Middle,
Secondary,
}
impl MouseButton {
pub fn is_down(&self, pointer: &PointerState) -> bool {
match self {
Self::Primary => pointer.primary_down(),
Self::Middle => pointer.middle_down(),
Self::Secondary => pointer.secondary_down(),
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct MouseConfig {
drag: bool,
rotate: bool,
zoom: bool,
yaw_scale: f32,
pitch_scale: f32,
zoom_scale: f32,
drag_bind: MouseButton,
rotate_bind: MouseButton,
}
impl Default for MouseConfig {
fn default() -> Self {
Self {
drag: false,
rotate: false,
zoom: false,
yaw_scale: DEFAULT_MOVE_SCALE,
pitch_scale: DEFAULT_MOVE_SCALE,
zoom_scale: DEFAULT_SCROLL_SCALE,
drag_bind: MouseButton::Middle,
rotate_bind: MouseButton::Primary,
}
}
}
impl MouseConfig {
#[inline]
pub fn enabled() -> Self {
Self {
drag: true,
rotate: true,
zoom: true,
yaw_scale: DEFAULT_MOVE_SCALE,
pitch_scale: DEFAULT_MOVE_SCALE,
zoom_scale: DEFAULT_SCROLL_SCALE,
drag_bind: MouseButton::Middle,
rotate_bind: MouseButton::Primary,
}
}
#[inline]
fn set_enable_all(&mut self) {
self.set_drag(true);
self.set_zoom(true);
self.set_rotate(true);
}
#[inline]
pub fn enable_all(mut self) -> Self {
self.set_enable_all();
self
}
#[inline]
pub fn set_drag(&mut self, drag: bool) {
self.drag = drag
}
#[inline]
pub fn drag(mut self, drag: bool) -> Self {
self.set_drag(drag);
self
}
#[inline]
pub fn set_rotate(&mut self, rotate: bool) {
self.rotate = rotate
}
#[inline]
pub fn rotate(mut self, rotate: bool) -> Self {
self.set_rotate(rotate);
self
}
#[inline]
pub fn set_zoom(&mut self, zoom: bool) {
self.zoom = zoom;
}
#[inline]
pub fn zoom(mut self, zoom: bool) -> Self {
self.set_zoom(zoom);
self
}
#[inline]
pub fn set_pitch_scale(&mut self, scale: f32) {
self.pitch_scale = scale
}
#[inline]
pub fn pitch_scale(mut self, scale: f32) -> Self {
self.set_pitch_scale(scale);
self
}
}
pub struct Chart<Data> {
transform: Transform,
mouse: MouseConfig,
builder_cb: Option<Box<dyn FnMut(&mut DrawingArea<EguiBackend, Shift>, &Transform, &Data)>>,
data: Data,
}
impl<Data> Chart<Data> {
pub fn new(data: Data) -> Self {
Self {
transform: Transform::default(),
mouse: MouseConfig::default(),
builder_cb: None,
data,
}
}
#[inline]
pub fn set_mouse(&mut self, mouse: MouseConfig) {
self.mouse = mouse
}
#[inline]
pub fn mouse(mut self, mouse: MouseConfig) -> Self {
self.set_mouse(mouse);
self
}
#[inline]
pub fn set_builder_cb(
&mut self,
builder_cb: Box<dyn FnMut(&mut DrawingArea<EguiBackend, Shift>, &Transform, &Data)>,
) {
self.builder_cb = Some(builder_cb)
}
#[inline]
pub fn builder_cb(
mut self,
builder_cb: Box<dyn FnMut(&mut DrawingArea<EguiBackend, Shift>, &Transform, &Data)>,
) -> Self {
self.set_builder_cb(builder_cb);
self
}
#[inline]
pub fn set_pitch(&mut self, pitch: f64) {
self.transform.pitch = pitch
}
#[inline]
pub fn pitch(mut self, pitch: f64) -> Self {
self.set_pitch(pitch);
self
}
#[inline]
pub fn set_yaw(&mut self, yaw: f64) {
self.transform.yaw = yaw
}
#[inline]
pub fn yaw(mut self, yaw: f64) -> Self {
self.set_yaw(yaw);
self
}
#[inline]
pub fn set_scale(&mut self, scale: f64) {
self.transform.scale = scale
}
#[inline]
pub fn scale(mut self, scale: f64) -> Self {
self.set_scale(scale);
self
}
#[inline]
pub fn get_data(&self) -> &Data {
&self.data
}
#[inline]
pub fn get_data_mut(&mut self) -> &mut Data {
&mut self.data
}
pub fn draw(&mut self, ui: &Ui) {
let transform = &mut self.transform;
ui.input(|input| {
let pointer = &input.pointer;
let delta = pointer.delta();
if self.mouse.rotate && self.mouse.rotate_bind.is_down(pointer) {
let pitch_delta = delta.y * self.mouse.pitch_scale;
let yaw_delta = delta.x * self.mouse.yaw_scale;
transform.pitch += pitch_delta as f64;
transform.yaw += -yaw_delta as f64;
}
if self.mouse.drag && self.mouse.drag_bind.is_down(pointer) {
let x_delta = delta.x;
let y_delta = delta.y;
transform.x += x_delta as i32;
transform.y += y_delta as i32;
}
if self.mouse.zoom {
let scale_delta = input.smooth_scroll_delta.y * self.mouse.zoom_scale;
transform.scale = (transform.scale + scale_delta as f64).abs();
}
});
let mut area = EguiBackend::new(ui)
.offset((transform.x, transform.y))
.scale(transform.scale as f32)
.into_drawing_area();
if let Some(cb) = &mut self.builder_cb {
cb(&mut area, transform, &self.data);
}
area.present().unwrap();
}
}