Add basic eraser
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
mod canvas_rasterizer;
|
||||
mod disk_format;
|
||||
mod tool;
|
||||
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
@@ -21,12 +22,13 @@ use egui::{
|
||||
use eyre::{Context, bail};
|
||||
use eyre::{OptionExt, eyre};
|
||||
use half::f16;
|
||||
use serde::Serialize;
|
||||
use zerocopy::{FromBytes, IntoBytes};
|
||||
|
||||
use crate::{custom_code_block::try_from_custom_code_block, rasterizer};
|
||||
use crate::{custom_code_block::write_custom_code_block, util::random_id};
|
||||
|
||||
use self::tool::{ToolEvent, Tool};
|
||||
|
||||
const HANDWRITING_MIN_HEIGHT: f32 = 100.0;
|
||||
const HANDWRITING_BOTTOM_PADDING: f32 = 80.0;
|
||||
const HANDWRITING_MARGIN: f32 = 0.05;
|
||||
@@ -79,6 +81,14 @@ struct Ephemeral {
|
||||
|
||||
canvas_rasterizer: CanvasRasterizer,
|
||||
|
||||
tool: Tool,
|
||||
|
||||
/// Tool position in canvas space.
|
||||
tool_position: Option<Pos2>,
|
||||
|
||||
/// Tool position last frame, in canvas space.
|
||||
last_tool_position: Option<Pos2>,
|
||||
|
||||
/// The stroke that is currently being drawed.
|
||||
current_stroke: Vec<Pos2>,
|
||||
|
||||
@@ -129,6 +139,9 @@ impl Default for Ephemeral {
|
||||
Self {
|
||||
id: random_id(),
|
||||
canvas_rasterizer: Default::default(),
|
||||
tool: Tool::Eraser,
|
||||
tool_position: None,
|
||||
last_tool_position: None,
|
||||
current_stroke: Default::default(),
|
||||
tessellator: None,
|
||||
mesh: Default::default(),
|
||||
@@ -173,6 +186,14 @@ impl Handwriting {
|
||||
let _ = Clipboard::new().unwrap().set_text(text);
|
||||
}
|
||||
|
||||
let (label, switch_to_tool) = match self.e.tool {
|
||||
Tool::Pencil => ("eraser", Tool::Eraser),
|
||||
Tool::Eraser => ("pencil", Tool::Pencil),
|
||||
};
|
||||
if ui.button(label).clicked() {
|
||||
self.e.tool = switch_to_tool;
|
||||
}
|
||||
|
||||
let vertex_count: usize = self.e.mesh.indices.len() / 3;
|
||||
ui.label(format!("vertices: {vertex_count}"));
|
||||
})
|
||||
@@ -212,6 +233,8 @@ impl Handwriting {
|
||||
emath::RectTransform::from_to(Rect::from_min_size(Pos2::ZERO, size), response.rect);
|
||||
let from_screen = to_screen.inverse();
|
||||
|
||||
self.e.last_tool_position = self.e.tool_position;
|
||||
|
||||
// Was the user in the process of drawing a stroke last frame?
|
||||
let was_drawing = !self.e.current_stroke.is_empty();
|
||||
|
||||
@@ -271,10 +294,21 @@ impl Handwriting {
|
||||
|
||||
// Process input events and turn them into strokes
|
||||
for event in events {
|
||||
let mut last_canvas_pos = self.e.current_stroke.last().copied();
|
||||
process_event(&mut last_canvas_pos, from_screen, &event, |tool_event| {
|
||||
if self.on_tool_event(tool_event) {
|
||||
hw_response.changed = true; // FIXME: ugly
|
||||
let mut last_tool_position = self.e.last_tool_position;
|
||||
process_event(&mut last_tool_position, from_screen, &event, |tool_event| {
|
||||
self.e.tool_position = tool_event.position();
|
||||
match self.e.tool {
|
||||
Tool::Pencil => {
|
||||
hw_response.changed |=
|
||||
tool::pencil::on_tool_event(self, tool_event);
|
||||
|
||||
}
|
||||
Tool::Eraser => {
|
||||
if tool::eraser::on_tool_event(self, tool_event) {
|
||||
self.e.refresh_texture = true;
|
||||
hw_response.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -333,6 +367,12 @@ impl Handwriting {
|
||||
// Draw the texture
|
||||
self.e.canvas_rasterizer.show(ui.ctx(), &painter, mesh_rect);
|
||||
|
||||
if let Some(tool_position) = self.e.tool_position && let Tool::Eraser = self.e.tool {
|
||||
let pos = to_screen * tool_position;
|
||||
let shape = Shape::circle_stroke(pos, tool::eraser::RADIUS, style.stroke);
|
||||
painter.add(shape);
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
|
||||
@@ -512,34 +552,6 @@ impl Handwriting {
|
||||
|
||||
bytes.into_boxed_slice()
|
||||
}
|
||||
|
||||
/// Handle a [ToolEvent]. Returns true if a stroke was completed.
|
||||
fn on_tool_event(&mut self, tool_event: ToolEvent) -> bool {
|
||||
match tool_event {
|
||||
ToolEvent::Press { at } => {
|
||||
debug_assert!(self.e.current_stroke.is_empty());
|
||||
self.push_to_stroke(at);
|
||||
false
|
||||
}
|
||||
ToolEvent::Move { to } => {
|
||||
self.push_to_stroke(to);
|
||||
false
|
||||
}
|
||||
ToolEvent::Release => {
|
||||
debug_assert!(!self.e.current_stroke.is_empty());
|
||||
self.strokes.push(mem::take(&mut self.e.current_stroke));
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple event that can defines how a tool (e.g. the pen) is used on a [Handwriting].
|
||||
#[derive(Serialize)]
|
||||
enum ToolEvent {
|
||||
Press { at: Pos2 },
|
||||
Move { to: Pos2 },
|
||||
Release,
|
||||
}
|
||||
|
||||
/// Convert [egui::Event]s to [ToolEvent]s.
|
||||
|
||||
Reference in New Issue
Block a user