mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-27 00:12:55 +03:00
Make the lasso tool (buried in an old experiment in story mapping) a bit more usable, and lift it to widgetry
This commit is contained in:
parent
9c0aedb575
commit
78e4fc3f80
@ -1,9 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use geom::{Distance, LonLat, PolyLine, Pt2D, Ring};
|
||||
use geom::{Distance, LonLat, Pt2D, Ring};
|
||||
use map_gui::render::DrawOptions;
|
||||
use map_gui::tools::{ChooseSomething, PromptInput};
|
||||
use widgetry::mapspace::{ObjectID, World, WorldOutcome};
|
||||
use widgetry::tools::Lasso;
|
||||
use widgetry::{
|
||||
lctrl, Choice, Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
||||
Line, Outcome, Panel, SimpleState, State, Text, TextBox, VerticalAlignment, Widget,
|
||||
@ -405,14 +406,14 @@ struct DrawFreehand {
|
||||
|
||||
impl State<App> for DrawFreehand {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||
if let Some(result) = self.lasso.event(ctx) {
|
||||
if let Some(polygon) = self.lasso.event(ctx) {
|
||||
let idx = self.new_idx;
|
||||
return Transition::Multi(vec![
|
||||
Transition::Pop,
|
||||
Transition::ModifyState(Box::new(move |state, ctx, app| {
|
||||
let editor = state.downcast_mut::<StoryMapEditor>().unwrap();
|
||||
editor.story.markers.push(Marker {
|
||||
pts: result.into_points(),
|
||||
pts: polygon.into_points(),
|
||||
label: String::new(),
|
||||
});
|
||||
|
||||
@ -435,78 +436,3 @@ impl State<App> for DrawFreehand {
|
||||
self.lasso.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO This should totally be an widgetry tool
|
||||
struct Lasso {
|
||||
pl: Option<PolyLine>,
|
||||
}
|
||||
|
||||
impl Lasso {
|
||||
fn new() -> Lasso {
|
||||
Lasso { pl: None }
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx) -> Option<Ring> {
|
||||
if self.pl.is_none() {
|
||||
if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
|
||||
if ctx.input.left_mouse_button_pressed() {
|
||||
self.pl = Some(PolyLine::must_new(vec![pt, pt.offset(0.1, 0.0)]));
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
if ctx.input.left_mouse_button_released() {
|
||||
return Some(simplify(self.pl.take().unwrap().into_points()));
|
||||
}
|
||||
|
||||
let current_pl = self.pl.as_ref().unwrap();
|
||||
if ctx.redo_mouseover() {
|
||||
if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
|
||||
if let Ok(pl) = PolyLine::new(vec![current_pl.last_pt(), pt]) {
|
||||
// Did we make a crossing?
|
||||
if let Some((hit, _)) = current_pl.intersection(&pl) {
|
||||
if let Some(slice) = current_pl.get_slice_starting_at(hit) {
|
||||
return Some(simplify(slice.into_points()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut pts = current_pl.points().clone();
|
||||
pts.push(pt);
|
||||
if let Ok(new) = PolyLine::new(pts) {
|
||||
self.pl = Some(new);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
if let Some(ref pl) = self.pl {
|
||||
g.draw_polygon(
|
||||
Color::RED.alpha(0.8),
|
||||
pl.make_polygons(Distance::meters(5.0) / g.canvas.cam_zoom),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn simplify(mut raw: Vec<Pt2D>) -> Ring {
|
||||
// TODO This is eating some of the shapes entirely. Wasn't meant for this.
|
||||
if false {
|
||||
let pts = raw
|
||||
.into_iter()
|
||||
.map(|pt| lttb::DataPoint::new(pt.x(), pt.y()))
|
||||
.collect();
|
||||
let mut downsampled = Vec::new();
|
||||
for pt in lttb::lttb(pts, 50) {
|
||||
downsampled.push(Pt2D::new(pt.x, pt.y));
|
||||
}
|
||||
downsampled.push(downsampled[0]);
|
||||
Ring::must_new(downsampled)
|
||||
} else {
|
||||
raw.push(raw[0]);
|
||||
Ring::must_new(raw)
|
||||
}
|
||||
}
|
||||
|
60
widgetry/src/tools/lasso.rs
Normal file
60
widgetry/src/tools/lasso.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use geom::{Distance, PolyLine, Polygon, Pt2D, Ring};
|
||||
|
||||
use crate::{Color, EventCtx, GfxCtx};
|
||||
|
||||
/// Draw freehand polygons
|
||||
pub struct Lasso {
|
||||
points: Vec<Pt2D>,
|
||||
}
|
||||
|
||||
impl Lasso {
|
||||
pub fn new() -> Lasso {
|
||||
Lasso { points: Vec::new() }
|
||||
}
|
||||
|
||||
/// When this returns a polygon, the interaction is finished
|
||||
pub fn event(&mut self, ctx: &mut EventCtx) -> Option<Polygon> {
|
||||
if self.points.is_empty() {
|
||||
if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
|
||||
if ctx.input.left_mouse_button_pressed() {
|
||||
self.points.push(pt);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
if ctx.input.left_mouse_button_released() {
|
||||
return self.finalize();
|
||||
}
|
||||
|
||||
if ctx.redo_mouseover() {
|
||||
if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
|
||||
if self.points.last().as_ref().unwrap().dist_to(pt) > Distance::meters(0.1) {
|
||||
self.points.push(pt);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
if let Ok(pl) = PolyLine::new(self.points.clone()) {
|
||||
g.draw_polygon(
|
||||
Color::RED.alpha(0.8),
|
||||
pl.make_polygons(Distance::meters(5.0) / g.canvas.cam_zoom),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> Option<Polygon> {
|
||||
// TODO It's better if the user doesn't close the polygon themselves. When they try to,
|
||||
// usually the result is the smaller polygon chunk
|
||||
self.points.push(self.points[0]);
|
||||
Some(
|
||||
Ring::new(std::mem::take(&mut self.points))
|
||||
.ok()?
|
||||
.into_polygon()
|
||||
.simplify(1.0),
|
||||
)
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
mod lasso;
|
||||
mod load;
|
||||
mod popup;
|
||||
pub(crate) mod screenshot;
|
||||
mod url;
|
||||
pub(crate) mod warper;
|
||||
|
||||
pub use lasso::Lasso;
|
||||
pub use load::{FileLoader, FutureLoader, RawBytes};
|
||||
pub use popup::PopupMsg;
|
||||
pub use url::URLManager;
|
||||
|
Loading…
Reference in New Issue
Block a user