From cd50d36faa6f68e1c738d53241335693bcd24d86 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Tue, 24 Sep 2024 15:41:52 -0400 Subject: [PATCH 01/13] eraser size scales w/ speed --- crates/rnote-engine/src/pens/eraser.rs | 63 +++++++++++++++---- .../src/pens/pensconfig/eraserconfig.rs | 9 ++- 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/crates/rnote-engine/src/pens/eraser.rs b/crates/rnote-engine/src/pens/eraser.rs index 821ef5e92c..11a47d6e74 100644 --- a/crates/rnote-engine/src/pens/eraser.rs +++ b/crates/rnote-engine/src/pens/eraser.rs @@ -20,15 +20,45 @@ pub enum EraserState { Down(Element), } +#[derive(Debug, Default, Clone, Copy)] +pub struct EraserMotion { + last_element: Option, + last_element_time: Option, + pub speed: f64, +} + +impl EraserMotion { + fn update(&mut self, element: Element) { + if let Some(last_element) = self.last_element { + if let Some(last_element_time) = self.last_element_time { + let delta = element.pos - last_element.pos; + let delta_time = Instant::now() - last_element_time; + let new_speed = delta.norm() / delta_time.as_secs_f64(); + self.speed = (self.speed * 3.0 + new_speed) / 4.0; + } + } + self.last_element = Some(element); + self.last_element_time = Some(Instant::now()); + } + + fn reset(&mut self) { + self.last_element = None; + self.last_element_time = None; + self.speed = 0.0; + } +} + #[derive(Clone, Debug)] pub struct Eraser { pub(crate) state: EraserState, + pub(crate) motion: EraserMotion, } impl Default for Eraser { fn default() -> Self { Self { state: EraserState::Up, + motion: EraserMotion::default(), } } } @@ -57,10 +87,10 @@ impl PenBehaviour for Eraser { engine_view: &mut EngineViewMut, ) -> (EventResult, WidgetFlags) { let mut widget_flags = WidgetFlags::default(); - let event_result = match (&mut self.state, event) { (EraserState::Up | EraserState::Proximity { .. }, PenEvent::Down { element, .. }) => { - widget_flags |= erase(element, engine_view); + self.motion.update(element); + widget_flags |= erase(element, self.motion.speed, engine_view); self.state = EraserState::Down(element); EventResult { handled: true, @@ -69,6 +99,7 @@ impl PenBehaviour for Eraser { } } (EraserState::Up | EraserState::Down { .. }, PenEvent::Proximity { element, .. }) => { + self.motion.update(element); self.state = EraserState::Proximity(element); EventResult { handled: false, @@ -85,7 +116,8 @@ impl PenBehaviour for Eraser { progress: PenProgress::Idle, }, (EraserState::Down(current_element), PenEvent::Down { element, .. }) => { - widget_flags |= erase(element, engine_view); + self.motion.update(element); + widget_flags |= erase(element, self.motion.speed, engine_view); *current_element = element; EventResult { handled: true, @@ -94,8 +126,9 @@ impl PenBehaviour for Eraser { } } (EraserState::Down { .. }, PenEvent::Up { element, .. }) => { - widget_flags |= - erase(element, engine_view) | engine_view.store.record(Instant::now()); + self.motion.reset(); + widget_flags |= erase(element, self.motion.speed, engine_view) + | engine_view.store.record(Instant::now()); self.state = EraserState::Up; EventResult { handled: true, @@ -109,6 +142,7 @@ impl PenBehaviour for Eraser { progress: PenProgress::InProgress, }, (EraserState::Proximity(_), PenEvent::Up { .. }) => { + self.motion.reset(); self.state = EraserState::Up; EventResult { handled: false, @@ -117,6 +151,7 @@ impl PenBehaviour for Eraser { } } (EraserState::Proximity(current_element), PenEvent::Proximity { element, .. }) => { + self.motion.update(element); *current_element = element; EventResult { handled: false, @@ -167,7 +202,7 @@ impl DrawableOnDoc for Eraser { engine_view .pens_config .eraser_config - .eraser_bounds(*current_element), + .eraser_bounds(*current_element, self.motion.speed), ), } } @@ -190,7 +225,7 @@ impl DrawableOnDoc for Eraser { let bounds = engine_view .pens_config .eraser_config - .eraser_bounds(*current_element); + .eraser_bounds(*current_element, self.motion.speed); let fill_rect = bounds.to_kurbo_rect(); let outline_rect = bounds.tightened(outline_width * 0.5).to_kurbo_rect(); @@ -202,7 +237,7 @@ impl DrawableOnDoc for Eraser { let bounds = engine_view .pens_config .eraser_config - .eraser_bounds(*current_element); + .eraser_bounds(*current_element, self.motion.speed); let fill_rect = bounds.to_kurbo_rect(); let outline_rect = bounds.tightened(outline_width * 0.5).to_kurbo_rect(); @@ -217,20 +252,26 @@ impl DrawableOnDoc for Eraser { } } -fn erase(element: Element, engine_view: &mut EngineViewMut) -> WidgetFlags { +fn erase(element: Element, speed: f64, engine_view: &mut EngineViewMut) -> WidgetFlags { // the widget_flags.store_modified flag is set in the `.trash_..()` methods let mut widget_flags = WidgetFlags::default(); match &engine_view.pens_config.eraser_config.style { EraserStyle::TrashCollidingStrokes => { widget_flags |= engine_view.store.trash_colliding_strokes( - engine_view.pens_config.eraser_config.eraser_bounds(element), + engine_view + .pens_config + .eraser_config + .eraser_bounds(element, speed), engine_view.camera.viewport(), ); } EraserStyle::SplitCollidingStrokes => { let (modified_strokes, wf) = engine_view.store.split_colliding_strokes( - engine_view.pens_config.eraser_config.eraser_bounds(element), + engine_view + .pens_config + .eraser_config + .eraser_bounds(element, speed), engine_view.camera.viewport(), ); widget_flags |= wf; diff --git a/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs b/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs index b0aa50ee35..e20b612dc2 100644 --- a/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs +++ b/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs @@ -52,8 +52,13 @@ impl EraserConfig { pub const WIDTH_MIN: f64 = 1.0; pub const WIDTH_MAX: f64 = 500.0; pub const WIDTH_DEFAULT: f64 = 12.0; + pub const SPEED_SCALING: f64 = 0.001; - pub(crate) fn eraser_bounds(&self, element: Element) -> Aabb { - Aabb::from_half_extents(element.pos.into(), na::Vector2::repeat(self.width * 0.5)) + pub(crate) fn eraser_bounds(&self, element: Element, speed: f64) -> Aabb { + let speed_scale = 1.0 + speed * Self::SPEED_SCALING; + Aabb::from_half_extents( + element.pos.into(), + na::Vector2::repeat(self.width * speed_scale * 0.5), + ) } } From 55058155bf7eb872e9a746356f84907f89fcb7f0 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Tue, 24 Sep 2024 21:07:03 -0400 Subject: [PATCH 02/13] no more magic number smoothing --- crates/rnote-engine/src/pens/eraser.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/rnote-engine/src/pens/eraser.rs b/crates/rnote-engine/src/pens/eraser.rs index 11a47d6e74..1d9e1fbb9b 100644 --- a/crates/rnote-engine/src/pens/eraser.rs +++ b/crates/rnote-engine/src/pens/eraser.rs @@ -28,13 +28,16 @@ pub struct EraserMotion { } impl EraserMotion { + pub const SMOOTHING_FACTOR: f64 = 3.0; + fn update(&mut self, element: Element) { if let Some(last_element) = self.last_element { if let Some(last_element_time) = self.last_element_time { let delta = element.pos - last_element.pos; let delta_time = Instant::now() - last_element_time; let new_speed = delta.norm() / delta_time.as_secs_f64(); - self.speed = (self.speed * 3.0 + new_speed) / 4.0; + self.speed = (self.speed * Self::SMOOTHING_FACTOR + new_speed) + / (Self::SMOOTHING_FACTOR + 1.0); } } self.last_element = Some(element); From b0b08448fd3dad52ff8770e0c6376ee72c164986 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Wed, 25 Sep 2024 11:32:35 -0400 Subject: [PATCH 03/13] Added UI toggle for speed scaling --- .../src/pens/pensconfig/eraserconfig.rs | 17 ++++++++++++----- .../rnote-ui/data/ui/penssidebar/eraserpage.ui | 12 +++++++++++- crates/rnote-ui/src/penssidebar/eraserpage.rs | 16 ++++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs b/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs index e20b612dc2..38a439f24e 100644 --- a/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs +++ b/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs @@ -37,6 +37,8 @@ pub struct EraserConfig { pub width: f64, #[serde(rename = "style")] pub style: EraserStyle, + #[serde(rename = "speed_scale")] + pub speed_scaling: bool, } impl Default for EraserConfig { @@ -44,6 +46,7 @@ impl Default for EraserConfig { Self { width: Self::WIDTH_DEFAULT, style: EraserStyle::default(), + speed_scaling: true, } } } @@ -55,10 +58,14 @@ impl EraserConfig { pub const SPEED_SCALING: f64 = 0.001; pub(crate) fn eraser_bounds(&self, element: Element, speed: f64) -> Aabb { - let speed_scale = 1.0 + speed * Self::SPEED_SCALING; - Aabb::from_half_extents( - element.pos.into(), - na::Vector2::repeat(self.width * speed_scale * 0.5), - ) + if self.speed_scaling { + let speed_scale = 1.0 + speed * Self::SPEED_SCALING; + Aabb::from_half_extents( + element.pos.into(), + na::Vector2::repeat(self.width * speed_scale * 0.5), + ) + } else { + Aabb::from_half_extents(element.pos.into(), na::Vector2::repeat(self.width * 0.5)) + } } } diff --git a/crates/rnote-ui/data/ui/penssidebar/eraserpage.ui b/crates/rnote-ui/data/ui/penssidebar/eraserpage.ui index 443449386c..3238f85a05 100644 --- a/crates/rnote-ui/data/ui/penssidebar/eraserpage.ui +++ b/crates/rnote-ui/data/ui/penssidebar/eraserpage.ui @@ -47,5 +47,15 @@ rounded-rect + + + speed_scaling_toggle + Scale eraser size with speed + workspacelistentryicon-speedometer-symbolic + + + - \ No newline at end of file + diff --git a/crates/rnote-ui/src/penssidebar/eraserpage.rs b/crates/rnote-ui/src/penssidebar/eraserpage.rs index 70f29e19ad..b78c9c3d80 100644 --- a/crates/rnote-ui/src/penssidebar/eraserpage.rs +++ b/crates/rnote-ui/src/penssidebar/eraserpage.rs @@ -18,6 +18,8 @@ mod imp { pub(crate) eraserstyle_split_colliding_strokes_toggle: TemplateChild, #[template_child] pub(crate) stroke_width_picker: TemplateChild, + #[template_child] + pub(crate) speed_scaling_toggle: TemplateChild, } #[glib::object_subclass] @@ -170,6 +172,20 @@ impl RnEraserPage { } ), ); + + imp.speed_scaling_toggle.connect_toggled(clone!( + #[weak] + appwindow, + move |speed_scaling_toggle| { + appwindow + .active_tab_wrapper() + .canvas() + .engine_mut() + .pens_config + .eraser_config + .speed_scaling = speed_scaling_toggle.is_active(); + } + )); } pub(crate) fn refresh_ui(&self, active_tab: &RnCanvasWrapper) { From 520825843332c97381590729de361e42e80b3b82 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Sat, 28 Sep 2024 12:29:49 -0400 Subject: [PATCH 04/13] refactor: cleanup EraserMotion --- crates/rnote-engine/src/pens/eraser.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/crates/rnote-engine/src/pens/eraser.rs b/crates/rnote-engine/src/pens/eraser.rs index 1d9e1fbb9b..b4a3038220 100644 --- a/crates/rnote-engine/src/pens/eraser.rs +++ b/crates/rnote-engine/src/pens/eraser.rs @@ -22,8 +22,7 @@ pub enum EraserState { #[derive(Debug, Default, Clone, Copy)] pub struct EraserMotion { - last_element: Option, - last_element_time: Option, + last_element: Option<(Element, Instant)>, pub speed: f64, } @@ -31,22 +30,18 @@ impl EraserMotion { pub const SMOOTHING_FACTOR: f64 = 3.0; fn update(&mut self, element: Element) { - if let Some(last_element) = self.last_element { - if let Some(last_element_time) = self.last_element_time { - let delta = element.pos - last_element.pos; - let delta_time = Instant::now() - last_element_time; - let new_speed = delta.norm() / delta_time.as_secs_f64(); - self.speed = (self.speed * Self::SMOOTHING_FACTOR + new_speed) - / (Self::SMOOTHING_FACTOR + 1.0); - } + if let Some((last_element, last_element_time)) = self.last_element { + let delta = element.pos - last_element.pos; + let delta_time = Instant::now() - last_element_time; + let new_speed = delta.norm() / delta_time.as_secs_f64(); + self.speed = + (self.speed * Self::SMOOTHING_FACTOR + new_speed) / (Self::SMOOTHING_FACTOR + 1.0); } - self.last_element = Some(element); - self.last_element_time = Some(Instant::now()); + self.last_element = Some((element, Instant::now())); } fn reset(&mut self) { self.last_element = None; - self.last_element_time = None; self.speed = 0.0; } } From dec32cbabcc8bc8fc18c1732e4f92e01cbc47035 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Sun, 29 Sep 2024 16:58:05 -0400 Subject: [PATCH 05/13] fix: use event instant instead of Instant::now() --- crates/rnote-engine/src/pens/eraser.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/rnote-engine/src/pens/eraser.rs b/crates/rnote-engine/src/pens/eraser.rs index b4a3038220..7a3bae3243 100644 --- a/crates/rnote-engine/src/pens/eraser.rs +++ b/crates/rnote-engine/src/pens/eraser.rs @@ -29,15 +29,15 @@ pub struct EraserMotion { impl EraserMotion { pub const SMOOTHING_FACTOR: f64 = 3.0; - fn update(&mut self, element: Element) { + fn update(&mut self, element: Element, time: Instant) { if let Some((last_element, last_element_time)) = self.last_element { let delta = element.pos - last_element.pos; - let delta_time = Instant::now() - last_element_time; + let delta_time = time - last_element_time; let new_speed = delta.norm() / delta_time.as_secs_f64(); self.speed = (self.speed * Self::SMOOTHING_FACTOR + new_speed) / (Self::SMOOTHING_FACTOR + 1.0); } - self.last_element = Some((element, Instant::now())); + self.last_element = Some((element, time)); } fn reset(&mut self) { @@ -81,13 +81,13 @@ impl PenBehaviour for Eraser { fn handle_event( &mut self, event: PenEvent, - _now: Instant, + now: Instant, engine_view: &mut EngineViewMut, ) -> (EventResult, WidgetFlags) { let mut widget_flags = WidgetFlags::default(); let event_result = match (&mut self.state, event) { (EraserState::Up | EraserState::Proximity { .. }, PenEvent::Down { element, .. }) => { - self.motion.update(element); + self.motion.update(element, now); widget_flags |= erase(element, self.motion.speed, engine_view); self.state = EraserState::Down(element); EventResult { @@ -97,7 +97,7 @@ impl PenBehaviour for Eraser { } } (EraserState::Up | EraserState::Down { .. }, PenEvent::Proximity { element, .. }) => { - self.motion.update(element); + self.motion.update(element, now); self.state = EraserState::Proximity(element); EventResult { handled: false, @@ -114,7 +114,7 @@ impl PenBehaviour for Eraser { progress: PenProgress::Idle, }, (EraserState::Down(current_element), PenEvent::Down { element, .. }) => { - self.motion.update(element); + self.motion.update(element, now); widget_flags |= erase(element, self.motion.speed, engine_view); *current_element = element; EventResult { @@ -149,7 +149,7 @@ impl PenBehaviour for Eraser { } } (EraserState::Proximity(current_element), PenEvent::Proximity { element, .. }) => { - self.motion.update(element); + self.motion.update(element, now); *current_element = element; EventResult { handled: false, From a7ef742fb990297e8a18d315ce42627bd604a70e Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Thu, 3 Oct 2024 13:34:12 -0400 Subject: [PATCH 06/13] feat: add speed limit --- crates/rnote-engine/src/pens/eraser.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/rnote-engine/src/pens/eraser.rs b/crates/rnote-engine/src/pens/eraser.rs index 7a3bae3243..d6c29fcd98 100644 --- a/crates/rnote-engine/src/pens/eraser.rs +++ b/crates/rnote-engine/src/pens/eraser.rs @@ -28,14 +28,16 @@ pub struct EraserMotion { impl EraserMotion { pub const SMOOTHING_FACTOR: f64 = 3.0; + pub const SPEED_LIMIT: f64 = 10000.0; fn update(&mut self, element: Element, time: Instant) { if let Some((last_element, last_element_time)) = self.last_element { let delta = element.pos - last_element.pos; let delta_time = time - last_element_time; let new_speed = delta.norm() / delta_time.as_secs_f64(); - self.speed = - (self.speed * Self::SMOOTHING_FACTOR + new_speed) / (Self::SMOOTHING_FACTOR + 1.0); + self.speed = Self::SPEED_LIMIT.min( + (self.speed * Self::SMOOTHING_FACTOR + new_speed) / (Self::SMOOTHING_FACTOR + 1.0), + ); } self.last_element = Some((element, time)); } From b1917fcd4df1d8123741555487f387a0bd49bbe3 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Thu, 3 Oct 2024 13:39:51 -0400 Subject: [PATCH 07/13] refactor: split eraser width and bounds funcs --- .../src/pens/pensconfig/eraserconfig.rs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs b/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs index 38a439f24e..ffdc68023e 100644 --- a/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs +++ b/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs @@ -57,15 +57,17 @@ impl EraserConfig { pub const WIDTH_DEFAULT: f64 = 12.0; pub const SPEED_SCALING: f64 = 0.001; - pub(crate) fn eraser_bounds(&self, element: Element, speed: f64) -> Aabb { - if self.speed_scaling { - let speed_scale = 1.0 + speed * Self::SPEED_SCALING; - Aabb::from_half_extents( - element.pos.into(), - na::Vector2::repeat(self.width * speed_scale * 0.5), - ) - } else { - Aabb::from_half_extents(element.pos.into(), na::Vector2::repeat(self.width * 0.5)) + fn width_from_speed(&self, speed: f64) -> f64 { + if !self.speed_scaling { + return self.width; } + self.width + Self::SPEED_SCALING * speed + } + + pub(crate) fn eraser_bounds(&self, element: Element, speed: f64) -> Aabb { + Aabb::from_half_extents( + element.pos.into(), + na::Vector2::repeat(self.width_from_speed(speed) * 0.5), + ) } } From 63ab34dbd2ea5c09551354c210fc4b7be5dc55de Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Thu, 3 Oct 2024 13:50:42 -0400 Subject: [PATCH 08/13] feat: eraser size stepping func --- crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs b/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs index ffdc68023e..70e67586e0 100644 --- a/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs +++ b/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs @@ -55,13 +55,16 @@ impl EraserConfig { pub const WIDTH_MIN: f64 = 1.0; pub const WIDTH_MAX: f64 = 500.0; pub const WIDTH_DEFAULT: f64 = 12.0; - pub const SPEED_SCALING: f64 = 0.001; + pub const SPEED_SCALING_STEP_SIZE: f64 = 10.0; + pub const SPEED_SCALING: f64 = 0.01; fn width_from_speed(&self, speed: f64) -> f64 { if !self.speed_scaling { return self.width; } - self.width + Self::SPEED_SCALING * speed + let size_increase = Self::SPEED_SCALING * speed; + let step = size_increase - (size_increase % Self::SPEED_SCALING_STEP_SIZE); + self.width + step } pub(crate) fn eraser_bounds(&self, element: Element, speed: f64) -> Aabb { From 621dce81b545e7d9e0312ab9e0cb272b50053ce9 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Thu, 3 Oct 2024 13:55:36 -0400 Subject: [PATCH 09/13] feat: separate accel/decel smoothing factors --- crates/rnote-engine/src/pens/eraser.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/rnote-engine/src/pens/eraser.rs b/crates/rnote-engine/src/pens/eraser.rs index d6c29fcd98..c701b5c348 100644 --- a/crates/rnote-engine/src/pens/eraser.rs +++ b/crates/rnote-engine/src/pens/eraser.rs @@ -27,7 +27,8 @@ pub struct EraserMotion { } impl EraserMotion { - pub const SMOOTHING_FACTOR: f64 = 3.0; + pub const SMOOTHING_FACTOR_ACCEL: f64 = 3.0; + pub const SMOOTHING_FACTOR_DECEL: f64 = 10.0; pub const SPEED_LIMIT: f64 = 10000.0; fn update(&mut self, element: Element, time: Instant) { @@ -35,9 +36,17 @@ impl EraserMotion { let delta = element.pos - last_element.pos; let delta_time = time - last_element_time; let new_speed = delta.norm() / delta_time.as_secs_f64(); - self.speed = Self::SPEED_LIMIT.min( - (self.speed * Self::SMOOTHING_FACTOR + new_speed) / (Self::SMOOTHING_FACTOR + 1.0), - ); + if new_speed > self.speed { + self.speed = Self::SPEED_LIMIT.min( + (self.speed * Self::SMOOTHING_FACTOR_ACCEL + new_speed) + / (Self::SMOOTHING_FACTOR_ACCEL + 1.0), + ); + } else { + self.speed = Self::SPEED_LIMIT.min( + (self.speed * Self::SMOOTHING_FACTOR_DECEL + new_speed) + / (Self::SMOOTHING_FACTOR_DECEL + 1.0), + ); + } } self.last_element = Some((element, time)); } From 4dd60cc36ca1e02a4738880c69cd7b50fb98cfc0 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Fri, 4 Oct 2024 15:24:17 -0400 Subject: [PATCH 10/13] refactor: move eraser size calc to EraserMotion --- crates/rnote-engine/src/pens/eraser.rs | 20 ++++++++++------ .../src/pens/pensconfig/eraserconfig.rs | 23 +++++++------------ 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/crates/rnote-engine/src/pens/eraser.rs b/crates/rnote-engine/src/pens/eraser.rs index c701b5c348..71da2cb39d 100644 --- a/crates/rnote-engine/src/pens/eraser.rs +++ b/crates/rnote-engine/src/pens/eraser.rs @@ -23,13 +23,16 @@ pub enum EraserState { #[derive(Debug, Default, Clone, Copy)] pub struct EraserMotion { last_element: Option<(Element, Instant)>, - pub speed: f64, + speed: f64, + pub added_width: f64, } impl EraserMotion { pub const SMOOTHING_FACTOR_ACCEL: f64 = 3.0; pub const SMOOTHING_FACTOR_DECEL: f64 = 10.0; pub const SPEED_LIMIT: f64 = 10000.0; + pub const SPEED_SCALING: f64 = 0.01; + pub const SPEED_SCALING_STEP_SIZE: f64 = 10.0; fn update(&mut self, element: Element, time: Instant) { if let Some((last_element, last_element_time)) = self.last_element { @@ -49,6 +52,9 @@ impl EraserMotion { } } self.last_element = Some((element, time)); + + let size_increase = Self::SPEED_SCALING * self.speed; + self.added_width = size_increase - (size_increase % Self::SPEED_SCALING_STEP_SIZE); } fn reset(&mut self) { @@ -126,7 +132,7 @@ impl PenBehaviour for Eraser { }, (EraserState::Down(current_element), PenEvent::Down { element, .. }) => { self.motion.update(element, now); - widget_flags |= erase(element, self.motion.speed, engine_view); + widget_flags |= erase(element, self.motion.added_width, engine_view); *current_element = element; EventResult { handled: true, @@ -234,7 +240,7 @@ impl DrawableOnDoc for Eraser { let bounds = engine_view .pens_config .eraser_config - .eraser_bounds(*current_element, self.motion.speed); + .eraser_bounds(*current_element, self.motion.added_width); let fill_rect = bounds.to_kurbo_rect(); let outline_rect = bounds.tightened(outline_width * 0.5).to_kurbo_rect(); @@ -246,7 +252,7 @@ impl DrawableOnDoc for Eraser { let bounds = engine_view .pens_config .eraser_config - .eraser_bounds(*current_element, self.motion.speed); + .eraser_bounds(*current_element, self.motion.added_width); let fill_rect = bounds.to_kurbo_rect(); let outline_rect = bounds.tightened(outline_width * 0.5).to_kurbo_rect(); @@ -261,7 +267,7 @@ impl DrawableOnDoc for Eraser { } } -fn erase(element: Element, speed: f64, engine_view: &mut EngineViewMut) -> WidgetFlags { +fn erase(element: Element, added_width: f64, engine_view: &mut EngineViewMut) -> WidgetFlags { // the widget_flags.store_modified flag is set in the `.trash_..()` methods let mut widget_flags = WidgetFlags::default(); @@ -271,7 +277,7 @@ fn erase(element: Element, speed: f64, engine_view: &mut EngineViewMut) -> Widge engine_view .pens_config .eraser_config - .eraser_bounds(element, speed), + .eraser_bounds(element, added_width), engine_view.camera.viewport(), ); } @@ -280,7 +286,7 @@ fn erase(element: Element, speed: f64, engine_view: &mut EngineViewMut) -> Widge engine_view .pens_config .eraser_config - .eraser_bounds(element, speed), + .eraser_bounds(element, added_width), engine_view.camera.viewport(), ); widget_flags |= wf; diff --git a/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs b/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs index 70e67586e0..245d160539 100644 --- a/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs +++ b/crates/rnote-engine/src/pens/pensconfig/eraserconfig.rs @@ -55,22 +55,15 @@ impl EraserConfig { pub const WIDTH_MIN: f64 = 1.0; pub const WIDTH_MAX: f64 = 500.0; pub const WIDTH_DEFAULT: f64 = 12.0; - pub const SPEED_SCALING_STEP_SIZE: f64 = 10.0; - pub const SPEED_SCALING: f64 = 0.01; - fn width_from_speed(&self, speed: f64) -> f64 { - if !self.speed_scaling { - return self.width; + pub(crate) fn eraser_bounds(&self, element: Element, added_width: f64) -> Aabb { + if self.speed_scaling { + Aabb::from_half_extents( + element.pos.into(), + na::Vector2::repeat((self.width + added_width) * 0.5), + ) + } else { + Aabb::from_half_extents(element.pos.into(), na::Vector2::repeat(self.width * 0.5)) } - let size_increase = Self::SPEED_SCALING * speed; - let step = size_increase - (size_increase % Self::SPEED_SCALING_STEP_SIZE); - self.width + step - } - - pub(crate) fn eraser_bounds(&self, element: Element, speed: f64) -> Aabb { - Aabb::from_half_extents( - element.pos.into(), - na::Vector2::repeat(self.width_from_speed(speed) * 0.5), - ) } } From c8c34ed0a033be487e57919394a679089f21e798 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Fri, 4 Oct 2024 15:41:23 -0400 Subject: [PATCH 11/13] feat: eraser size hysteresis --- crates/rnote-engine/src/pens/eraser.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/rnote-engine/src/pens/eraser.rs b/crates/rnote-engine/src/pens/eraser.rs index 71da2cb39d..34135cb8e6 100644 --- a/crates/rnote-engine/src/pens/eraser.rs +++ b/crates/rnote-engine/src/pens/eraser.rs @@ -54,7 +54,14 @@ impl EraserMotion { self.last_element = Some((element, time)); let size_increase = Self::SPEED_SCALING * self.speed; - self.added_width = size_increase - (size_increase % Self::SPEED_SCALING_STEP_SIZE); + let lower_bound = self.added_width - Self::SPEED_SCALING_STEP_SIZE; + let upper_bound = self.added_width + Self::SPEED_SCALING_STEP_SIZE; + if size_increase < lower_bound { + self.added_width -= Self::SPEED_SCALING_STEP_SIZE; + } + if size_increase > upper_bound { + self.added_width += Self::SPEED_SCALING_STEP_SIZE; + } } fn reset(&mut self) { From c81783bc648b4bc8f924022d4a96c8223b4ef641 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Sat, 5 Oct 2024 17:20:18 -0400 Subject: [PATCH 12/13] feat: minimum speed for scaling to occur --- crates/rnote-engine/src/pens/eraser.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/rnote-engine/src/pens/eraser.rs b/crates/rnote-engine/src/pens/eraser.rs index 34135cb8e6..f510e24367 100644 --- a/crates/rnote-engine/src/pens/eraser.rs +++ b/crates/rnote-engine/src/pens/eraser.rs @@ -31,8 +31,9 @@ impl EraserMotion { pub const SMOOTHING_FACTOR_ACCEL: f64 = 3.0; pub const SMOOTHING_FACTOR_DECEL: f64 = 10.0; pub const SPEED_LIMIT: f64 = 10000.0; + pub const MIN_SPEED: f64 = 100.0; pub const SPEED_SCALING: f64 = 0.01; - pub const SPEED_SCALING_STEP_SIZE: f64 = 10.0; + pub const SPEED_SCALING_STEP_SIZE: f64 = 25.0; fn update(&mut self, element: Element, time: Instant) { if let Some((last_element, last_element_time)) = self.last_element { @@ -53,20 +54,25 @@ impl EraserMotion { } self.last_element = Some((element, time)); - let size_increase = Self::SPEED_SCALING * self.speed; - let lower_bound = self.added_width - Self::SPEED_SCALING_STEP_SIZE; - let upper_bound = self.added_width + Self::SPEED_SCALING_STEP_SIZE; - if size_increase < lower_bound { - self.added_width -= Self::SPEED_SCALING_STEP_SIZE; - } - if size_increase > upper_bound { - self.added_width += Self::SPEED_SCALING_STEP_SIZE; + if self.speed > Self::MIN_SPEED { + let size_increase = Self::SPEED_SCALING * self.speed; + let lower_bound = self.added_width - 0.5 * Self::SPEED_SCALING_STEP_SIZE; + let upper_bound = self.added_width + 1.0 * Self::SPEED_SCALING_STEP_SIZE; + if size_increase < lower_bound { + self.added_width -= Self::SPEED_SCALING_STEP_SIZE; + } + if size_increase > upper_bound { + self.added_width += Self::SPEED_SCALING_STEP_SIZE; + } + } else { + self.added_width = 0.0; } } fn reset(&mut self) { self.last_element = None; self.speed = 0.0; + self.added_width = 0.0; } } From f7adb2acc0277f549db5f0de8764d1dff9ea6989 Mon Sep 17 00:00:00 2001 From: PerfectlyInternal Date: Wed, 9 Oct 2024 18:43:11 -0400 Subject: [PATCH 13/13] fix: make ui build after update --- crates/rnote-ui/src/penssidebar/eraserpage.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/rnote-ui/src/penssidebar/eraserpage.rs b/crates/rnote-ui/src/penssidebar/eraserpage.rs index 06188e85bc..c4968a6530 100644 --- a/crates/rnote-ui/src/penssidebar/eraserpage.rs +++ b/crates/rnote-ui/src/penssidebar/eraserpage.rs @@ -172,13 +172,12 @@ impl RnEraserPage { #[weak] appwindow, move |speed_scaling_toggle| { - appwindow - .active_tab_wrapper() - .canvas() - .engine_mut() - .pens_config - .eraser_config - .speed_scaling = speed_scaling_toggle.is_active(); + let Some(canvas) = appwindow.active_tab_canvas() else { + return; + }; + + canvas.engine_mut().pens_config.eraser_config.speed_scaling = + speed_scaling_toggle.is_active(); } )); }