diff --git a/ezgui/examples/demo.rs b/ezgui/examples/demo.rs index 1da01b527c..4faa5e6a9c 100644 --- a/ezgui/examples/demo.rs +++ b/ezgui/examples/demo.rs @@ -10,7 +10,7 @@ use ezgui::{ hotkey, lctrl, Btn, Checkbox, Color, Composite, Drawable, EventCtx, EventLoopMode, GeomBatch, - GfxCtx, HorizontalAlignment, Key, Line, Outcome, Plot, PlotOptions, Series, Text, TextExt, + GfxCtx, HorizontalAlignment, Key, Line, LinePlot, Outcome, PlotOptions, Series, Text, TextExt, VerticalAlignment, Widget, GUI, }; use geom::{Angle, Duration, Polygon, Pt2D, Time}; @@ -77,7 +77,7 @@ impl App { Widget::col(col2).outline(3.0, Color::BLACK).margin(5), Widget::col(col3).outline(3.0, Color::BLACK).margin(5), ]), - Plot::new( + LinePlot::new( ctx, "timeseries", vec![ @@ -103,6 +103,7 @@ impl App { // Without this, the plot doesn't stretch to cover times in between whole // seconds. max_x: Some(Time::START_OF_DAY + self.elapsed), + max_y: None, }, ), ]) @@ -230,19 +231,17 @@ fn setup_scrollable_canvas(ctx: &mut EventCtx) -> Drawable { 1.0, // Rotate Angle::ZERO, + ezgui::RewriteColor::NoOp, // Map-space (don't scale for high DPI monitors) true, ); // Text rendering also goes through lyon and usvg. - batch.add_transformed( + batch.append( Text::from(Line("Awesome vector text thanks to usvg and lyon").fg(Color::hex("#DF8C3D"))) - .render_to_batch(&ctx.prerender), - // Translate - Pt2D::new(600.0, 500.0), - // Scale - 2.0, - // Rotate - Angle::new_degs(30.0), + .render_to_batch(&ctx.prerender) + .scale(2.0) + .centered_on(Pt2D::new(600.0, 500.0)) + .rotate(Angle::new_degs(30.0)), ); // This is a bit of a hack; it's needed so that zooming in/out has reasonable limits. ctx.canvas.map_dims = (5000.0, 5000.0); diff --git a/ezgui/src/drawing.rs b/ezgui/src/drawing.rs index d72f21542b..d1bab34331 100644 --- a/ezgui/src/drawing.rs +++ b/ezgui/src/drawing.rs @@ -205,7 +205,7 @@ impl<'a> GfxCtx<'a> { Color::BLACK, Polygon::rectangle(dims.width, dims.height).translate(pt.x, pt.y), ); - batch.add_translated(txt_batch, pt.x + pad, pt.y + pad); + batch.append(txt_batch.translate(pt.x + pad, pt.y + pad)); // fork_screenspace, but with an even more prominent Z self.uniforms.transform = [0.0, 0.0, 1.0]; diff --git a/ezgui/src/event_ctx.rs b/ezgui/src/event_ctx.rs index 94ce5c22a8..3917238216 100644 --- a/ezgui/src/event_ctx.rs +++ b/ezgui/src/event_ctx.rs @@ -180,11 +180,7 @@ impl<'a> LoadingScreen<'a> { text::BG_COLOR, Polygon::rectangle(0.8 * g.canvas.window_width, 0.8 * g.canvas.window_height), )]); - batch.add_translated( - txt.inner_render(&g.prerender.assets, svg::LOW_QUALITY), - 0.0, - 0.0, - ); + batch.append(txt.inner_render(&g.prerender.assets, svg::LOW_QUALITY)); let draw = g.upload(batch); g.redraw_at( ScreenPt::new(0.1 * g.canvas.window_width, 0.1 * g.canvas.window_height), diff --git a/ezgui/src/geom.rs b/ezgui/src/geom.rs index 53e7f4380a..0a497e3212 100644 --- a/ezgui/src/geom.rs +++ b/ezgui/src/geom.rs @@ -32,6 +32,7 @@ impl GeomBatch { pub fn push(&mut self, color: Color, p: Polygon) { self.list.push((FancyColor::RGBA(color), p)); } + // TODO Weird API too pub fn fancy_push(&mut self, color: FancyColor, p: Polygon) { self.list.push((color, p)); } @@ -117,15 +118,6 @@ impl GeomBatch { } } - /// Transforms all colors in a batch. - pub fn rewrite_color(&mut self, transformation: RewriteColor) { - for (fancy, _) in self.list.iter_mut() { - if let FancyColor::RGBA(ref mut c) = fancy { - *c = transformation.apply(*c); - } - } - } - // TODO Weird API. /// Adds an SVG image to the current batch, applying the transformations first. pub fn add_svg( @@ -138,7 +130,7 @@ impl GeomBatch { rewrite: RewriteColor, map_space: bool, ) { - self.add_transformed( + self.append( svg::load_svg( prerender, filename, @@ -148,61 +140,60 @@ impl GeomBatch { *prerender.assets.scale_factor.borrow() }, ) - .0, - center, - scale, - rotate, - rewrite, + .0 + .scale(scale) + .centered_on(center) + .rotate(rotate) + .color(rewrite), ); } + // TODO Weird API /// Parse an SVG string and add it to the batch. pub fn add_svg_contents(&mut self, raw: Vec) { let svg_tree = usvg::Tree::from_data(&raw, &usvg::Options::default()).unwrap(); svg::add_svg_inner(self, svg_tree, svg::HIGH_QUALITY, 1.0).unwrap(); } + // TODO Weird API /// Adds geometry from another batch to the current batch, first centering it on the given /// point. pub fn add_centered(&mut self, other: GeomBatch, center: Pt2D) { - self.add_transformed(other, center, 1.0, Angle::ZERO, RewriteColor::NoOp); + self.append(other.centered_on(center)); } - /// Adds geometry from another batch to the current batch, first transforming it. The - /// translation centers on the given point. - pub fn add_transformed( - &mut self, - other: GeomBatch, - center: Pt2D, - scale: f64, - rotate: Angle, - rewrite: RewriteColor, - ) { - let dims = other.get_dims(); - let dx = center.x() - dims.width * scale / 2.0; - let dy = center.y() - dims.height * scale / 2.0; - for (mut fancy_color, mut poly) in other.consume() { - // Avoid unnecessary transformations for slight perf boost - if scale != 1.0 { - poly = poly.scale(scale); + /// Transforms all colors in a batch. + pub fn color(mut self, transformation: RewriteColor) -> GeomBatch { + for (fancy, _) in &mut self.list { + if let FancyColor::RGBA(ref mut c) = fancy { + *c = transformation.apply(*c); } - poly = poly.translate(dx, dy); - if rotate != Angle::ZERO { - poly = poly.rotate(rotate); - } - if let FancyColor::RGBA(ref mut c) = fancy_color { - *c = rewrite.apply(*c); - } - self.fancy_push(fancy_color, poly); } + self } - // TODO Weird API - /// Adds geometry from another batch to the current batch, first translating it. - pub fn add_translated(&mut self, other: GeomBatch, dx: f64, dy: f64) { - for (color, poly) in other.consume() { - self.fancy_push(color, poly.translate(dx, dy)); + /// Translates the batch to be centered on some point. + pub fn centered_on(self, center: Pt2D) -> GeomBatch { + let dims = self.get_dims(); + let dx = center.x() - dims.width / 2.0; + let dy = center.y() - dims.height / 2.0; + self.translate(dx, dy) + } + + /// Translates the batch by some offset. + pub fn translate(mut self, dx: f64, dy: f64) -> GeomBatch { + for (_, poly) in &mut self.list { + *poly = poly.translate(dx, dy); } + self + } + + /// Rotates each polygon in the batch relative to the center of that polygon. + pub fn rotate(mut self, angle: Angle) -> GeomBatch { + for (_, poly) in &mut self.list { + *poly = poly.rotate(angle); + } + self } /// Scales the batch by some factor. diff --git a/ezgui/src/widgets/button.rs b/ezgui/src/widgets/button.rs index e84698c8fa..18bd2cd1f8 100644 --- a/ezgui/src/widgets/button.rs +++ b/ezgui/src/widgets/button.rs @@ -291,13 +291,8 @@ impl BtnBuilder { let geom = Polygon::rectangle(bounds.width() + 2.0 * pad, bounds.height() + 2.0 * pad); - let mut normal = GeomBatch::new(); - normal.add_translated(orig.clone(), pad, pad); - normal.rewrite_color(rewrite_normal); - - let mut hovered = GeomBatch::new(); - hovered.add_translated(orig, pad, pad); - hovered.rewrite_color(rewrite_hover); + let normal = orig.clone().translate(pad, pad).color(rewrite_normal); + let hovered = orig.translate(pad, pad).color(rewrite_hover); Button::new( ctx, @@ -325,9 +320,9 @@ impl BtnBuilder { ); let mut normal = GeomBatch::new(); - normal.add_translated(unselected_batch, horiz_padding, vert_padding); + normal.append(unselected_batch.translate(horiz_padding, vert_padding)); let mut hovered = GeomBatch::new(); - hovered.add_translated(selected_batch, horiz_padding, vert_padding); + hovered.append(selected_batch.translate(horiz_padding, vert_padding)); Button::new( ctx, @@ -357,9 +352,9 @@ impl BtnBuilder { ); let mut normal = GeomBatch::new(); - normal.add_translated(unselected_batch, horiz_padding, vert_padding); + normal.append(unselected_batch.translate(horiz_padding, vert_padding)); let mut hovered = GeomBatch::new(); - hovered.add_translated(selected_batch, horiz_padding, vert_padding); + hovered.append(selected_batch.translate(horiz_padding, vert_padding)); Button::new( ctx, @@ -390,10 +385,10 @@ impl BtnBuilder { ); let mut normal = GeomBatch::from(vec![(unselected_bg_color, geom.clone())]); - normal.add_translated(txt_batch.clone(), HORIZ_PADDING, VERT_PADDING); + normal.append(txt_batch.clone().translate(HORIZ_PADDING, VERT_PADDING)); let mut hovered = GeomBatch::from(vec![(selected_bg_color, geom.clone())]); - hovered.add_translated(txt_batch.clone(), HORIZ_PADDING, VERT_PADDING); + hovered.append(txt_batch.translate(HORIZ_PADDING, VERT_PADDING)); Button::new( ctx, diff --git a/ezgui/src/widgets/just_draw.rs b/ezgui/src/widgets/just_draw.rs index b7c8116223..3e5c05716e 100644 --- a/ezgui/src/widgets/just_draw.rs +++ b/ezgui/src/widgets/just_draw.rs @@ -35,12 +35,12 @@ impl JustDraw { })) } pub(crate) fn svg_transform(ctx: &EventCtx, filename: &str, rewrite: RewriteColor) -> Widget { - let (mut batch, bounds) = svg::load_svg( + let (batch, bounds) = svg::load_svg( ctx.prerender, filename, *ctx.prerender.assets.scale_factor.borrow(), ); - batch.rewrite_color(rewrite); + let batch = batch.color(rewrite); // TODO The dims will be wrong; it'll only look at geometry, not the padding in the image. Widget::new(Box::new(JustDraw { dims: ScreenDims::new(bounds.width(), bounds.height()), diff --git a/game/src/common/mod.rs b/game/src/common/mod.rs index e9936deebc..d15c35b8de 100644 --- a/game/src/common/mod.rs +++ b/game/src/common/mod.rs @@ -250,16 +250,18 @@ impl CommonState { app.cs.panel_bg, Polygon::rectangle(g.canvas.window_width, 1.5 * g.default_line_height()), )]); - batch.add_translated(osd.render_g(g), 10.0, 0.25 * g.default_line_height()); + batch.append( + osd.render_g(g) + .translate(10.0, 0.25 * g.default_line_height()), + ); if app.opts.dev && !g.is_screencap() { let dev_batch = Text::from(Line("DEV")).bg(Color::RED).render_g(g); let dims = dev_batch.get_dims(); - batch.add_translated( - dev_batch, + batch.append(dev_batch.translate( g.canvas.window_width - dims.width - 10.0, 0.25 * g.default_line_height(), - ); + )); } let draw = g.upload(batch); let top_left = ScreenPt::new(0.0, g.canvas.window_height - 1.5 * g.default_line_height()); diff --git a/game/src/devtools/story.rs b/game/src/devtools/story.rs index ca2705ade4..b12e843a4b 100644 --- a/game/src/devtools/story.rs +++ b/game/src/devtools/story.rs @@ -485,14 +485,12 @@ impl Marker { RewriteColor::Change(Color::hex("#5B5B5B"), Color::hex("#FE3D00")), false, ); - batch.add_transformed( + batch.append( Text::from(Line(&event)) .with_bg() - .render_to_batch(ctx.prerender), - pts[0], - 0.5, - Angle::ZERO, - RewriteColor::NoOp, + .render_to_batch(ctx.prerender) + .scale(0.5) + .centered_on(pts[0]), ); batch.unioned_polygon() } else { @@ -502,14 +500,12 @@ impl Marker { batch.push(Color::RED, o); } // TODO Refactor - batch.add_transformed( + batch.append( Text::from(Line(&event)) .with_bg() - .render_to_batch(ctx.prerender), - poly.polylabel(), - 0.5, - Angle::ZERO, - RewriteColor::NoOp, + .render_to_batch(ctx.prerender) + .scale(0.5) + .centered_on(poly.polylabel()), ); poly }; @@ -533,26 +529,22 @@ impl Marker { RewriteColor::Change(Color::hex("#5B5B5B"), app.cs.hovering), false, ); - batch.add_transformed( + batch.append( Text::from(Line(&self.event)) .with_bg() - .render_to_batch(g.prerender), - self.pts[0], - 0.75, - Angle::ZERO, - RewriteColor::NoOp, + .render_to_batch(g.prerender) + .scale(0.75) + .centered_on(self.pts[0]), ); } else { batch.push(app.cs.hovering, Polygon::new(&self.pts)); // TODO Refactor plz - batch.add_transformed( + batch.append( Text::from(Line(&self.event)) .with_bg() - .render_to_batch(g.prerender), - self.hitbox.polylabel(), - 0.75, - Angle::ZERO, - RewriteColor::NoOp, + .render_to_batch(g.prerender) + .scale(0.75) + .centered_on(self.hitbox.polylabel()), ); } batch.draw(g); diff --git a/game/src/info/intersection.rs b/game/src/info/intersection.rs index ce50363f90..1bf72a2b20 100644 --- a/game/src/info/intersection.rs +++ b/game/src/info/intersection.rs @@ -1,11 +1,8 @@ use crate::app::App; use crate::info::{header_btns, make_tabs, throughput, DataOptions, Details, Tab}; use abstutil::prettyprint_usize; -use ezgui::{ - Color, EventCtx, GeomBatch, Line, PlotOptions, RewriteColor, ScatterPlotV2, Series, Text, - Widget, -}; -use geom::{Angle, ArrowCap, Distance, PolyLine}; +use ezgui::{Color, EventCtx, GeomBatch, Line, PlotOptions, ScatterPlotV2, Series, Text, Widget}; +use geom::{ArrowCap, Distance, PolyLine}; use map_model::{IntersectionID, IntersectionType}; use std::collections::BTreeSet; @@ -138,12 +135,11 @@ pub fn current_demand( pl.make_arrow(percent * Distance::meters(3.0), ArrowCap::Triangle) .unwrap(), ); - txt_batch.add_transformed( - Text::from(Line(prettyprint_usize(demand))).render_ctx(ctx), - pl.middle(), - 0.15 / ctx.get_scale_factor(), - Angle::ZERO, - RewriteColor::NoOp, + txt_batch.append( + Text::from(Line(prettyprint_usize(demand))) + .render_ctx(ctx) + .scale(0.15 / ctx.get_scale_factor()) + .centered_on(pl.middle()), ); } batch.append(txt_batch); diff --git a/game/src/render/building.rs b/game/src/render/building.rs index 28f47fc2a6..398a1930d9 100644 --- a/game/src/render/building.rs +++ b/game/src/render/building.rs @@ -85,12 +85,10 @@ impl Renderable for DrawBuilding { if b.amenities.len() > 1 { txt.append(Line(format!(" (+{})", b.amenities.len() - 1)).fg(Color::BLACK)); } - batch.add_transformed( - txt.render_to_batch(g.prerender), - b.label_center, - 0.1, - Angle::ZERO, - RewriteColor::NoOp, + batch.append( + txt.render_to_batch(g.prerender) + .scale(0.1) + .centered_on(b.label_center), ); } *label = Some(g.prerender.upload(batch)); diff --git a/game/src/render/car.rs b/game/src/render/car.rs index fb8f84719f..b4a6f98258 100644 --- a/game/src/render/car.rs +++ b/game/src/render/car.rs @@ -135,12 +135,11 @@ impl DrawCar { if let Some(line) = input.label { // TODO Would rotation make any sense? Or at least adjust position/size while turning. // Buses are a constant length, so hardcoding this is fine. - draw_default.add_transformed( - Text::from(Line(line).fg(cs.bus_label)).render_to_batch(prerender), - input.body.dist_along(Distance::meters(9.0)).0, - 0.07, - Angle::ZERO, - RewriteColor::NoOp, + draw_default.append( + Text::from(Line(line).fg(cs.bus_label)) + .render_to_batch(prerender) + .scale(0.07) + .centered_on(input.body.dist_along(Distance::meters(9.0)).0), ); } diff --git a/game/src/render/intersection.rs b/game/src/render/intersection.rs index 48a551a9fc..1540ef5ec2 100644 --- a/game/src/render/intersection.rs +++ b/game/src/render/intersection.rs @@ -77,7 +77,7 @@ impl DrawIntersection { let zorder = i.get_zorder(map); if zorder < 0 { - default_geom.rewrite_color(RewriteColor::ChangeAlpha(0.5)); + default_geom = default_geom.color(RewriteColor::ChangeAlpha(0.5)); } DrawIntersection { @@ -152,12 +152,11 @@ impl Renderable for DrawIntersection { app.opts.traffic_signal_style.clone(), ); if app.opts.traffic_signal_style != TrafficSignalStyle::BAP { - batch.add_transformed( - Text::from(Line(format!("{}", idx + 1))).render_to_batch(g.prerender), - app.primary.map.get_i(self.id).polygon.center(), - 0.1, - Angle::ZERO, - RewriteColor::NoOp, + batch.append( + Text::from(Line(format!("{}", idx + 1))) + .render_to_batch(g.prerender) + .scale(0.1) + .centered_on(app.primary.map.get_i(self.id).polygon.center()), ); } *maybe_redraw = Some((app.primary.sim.time(), g.prerender.upload(batch))); diff --git a/game/src/render/lane.rs b/game/src/render/lane.rs index 3944c32059..18de3949d1 100644 --- a/game/src/render/lane.rs +++ b/game/src/render/lane.rs @@ -64,8 +64,7 @@ impl AlmostDrawLane { } if self.zorder < 0 { - self.draw_default - .rewrite_color(RewriteColor::ChangeAlpha(0.5)); + self.draw_default = self.draw_default.color(RewriteColor::ChangeAlpha(0.5)); } DrawLane { diff --git a/game/src/render/pedestrian.rs b/game/src/render/pedestrian.rs index aa8d27aa7a..40be6315fd 100644 --- a/game/src/render/pedestrian.rs +++ b/game/src/render/pedestrian.rs @@ -2,8 +2,8 @@ use crate::app::App; use crate::colors::ColorScheme; use crate::helpers::ID; use crate::render::{DrawOptions, Renderable, OUTLINE_THICKNESS}; -use ezgui::{Color, Drawable, GeomBatch, GfxCtx, Line, Prerender, RewriteColor, Text}; -use geom::{Angle, ArrowCap, Circle, Distance, PolyLine, Polygon}; +use ezgui::{Color, Drawable, GeomBatch, GfxCtx, Line, Prerender, Text}; +use geom::{ArrowCap, Circle, Distance, PolyLine, Polygon}; use map_model::{Map, SIDEWALK_THICKNESS}; use sim::{DrawPedCrowdInput, DrawPedestrianInput, PedCrowdLocation, PedestrianID}; @@ -222,13 +222,11 @@ impl DrawPedCrowd { let blob = pl_shifted.make_polygons(SIDEWALK_THICKNESS / 2.0); let mut batch = GeomBatch::new(); batch.push(cs.ped_crowd, blob.clone()); - batch.add_transformed( + batch.append( Text::from(Line(format!("{}", input.members.len())).fg(Color::BLACK)) - .render_to_batch(prerender), - blob.center(), - 0.02, - Angle::ZERO, - RewriteColor::NoOp, + .render_to_batch(prerender) + .scale(0.02) + .centered_on(blob.center()), ); DrawPedCrowd { diff --git a/game/src/render/road.rs b/game/src/render/road.rs index e32f789995..a4f55b7dce 100644 --- a/game/src/render/road.rs +++ b/game/src/render/road.rs @@ -2,8 +2,8 @@ use crate::app::App; use crate::colors::ColorScheme; use crate::helpers::ID; use crate::render::{DrawOptions, Renderable}; -use ezgui::{Drawable, GeomBatch, GfxCtx, Line, Prerender, RewriteColor, Text}; -use geom::{Angle, Distance, Polygon, Pt2D}; +use ezgui::{Drawable, GeomBatch, GfxCtx, Line, Prerender, Text}; +use geom::{Distance, Polygon, Pt2D}; use map_model::{LaneType, Map, Road, RoadID}; use std::cell::RefCell; @@ -62,12 +62,10 @@ impl Renderable for DrawRoad { let mut txt = Text::new().with_bg(); txt.add(Line(r.get_name())); - batch.add_transformed( - txt.render_to_batch(g.prerender), - r.center_pts.middle(), - 0.1, - Angle::ZERO, - RewriteColor::NoOp, + batch.append( + txt.render_to_batch(g.prerender) + .scale(0.1) + .centered_on(r.center_pts.middle()), ); *label = Some(g.prerender.upload(batch)); } diff --git a/game/src/sandbox/dashboards/trip_table.rs b/game/src/sandbox/dashboards/trip_table.rs index e8dade63cb..670b98977e 100644 --- a/game/src/sandbox/dashboards/trip_table.rs +++ b/game/src/sandbox/dashboards/trip_table.rs @@ -437,7 +437,7 @@ pub fn make_table( batch.autocrop_dims = false; let mut x1 = 0.0; for (col, width) in row.into_iter().zip(width_per_col.iter()) { - batch.add_translated(col.scale(1.0 / ctx.get_scale_factor()), x1, 0.0); + batch.append(col.scale(1.0 / ctx.get_scale_factor()).translate(x1, 0.0)); x1 += *width + extra_margin; } diff --git a/map_editor/src/main.rs b/map_editor/src/main.rs index 1c1017d523..4b10acde9f 100644 --- a/map_editor/src/main.rs +++ b/map_editor/src/main.rs @@ -4,10 +4,10 @@ mod world; use abstutil::{CmdArgs, Timer}; use ezgui::{ hotkey, Btn, Canvas, Choice, Color, Composite, Drawable, EventCtx, EventLoopMode, GeomBatch, - GfxCtx, HorizontalAlignment, Key, Line, Outcome, RewriteColor, ScreenPt, Text, - VerticalAlignment, Widget, Wizard, GUI, + GfxCtx, HorizontalAlignment, Key, Line, Outcome, ScreenPt, Text, VerticalAlignment, Widget, + Wizard, GUI, }; -use geom::{Angle, Distance, Line, Polygon, Pt2D}; +use geom::{Distance, Line, Polygon, Pt2D}; use map_model::raw::{OriginalBuilding, OriginalIntersection, OriginalRoad, RestrictionType}; use map_model::{osm, NORMAL_LANE_THICKNESS}; use model::{Model, ID}; @@ -657,14 +657,12 @@ fn preview_intersection(i: OriginalIntersection, model: &Model, ctx: &EventCtx) for (label, poly) in debug { let center = poly.center(); batch.push(Color::RED.alpha(0.5), poly); - batch.add_transformed( + batch.append( Text::from(Line(label)) .with_bg() - .render_to_batch(ctx.prerender), - center, - 0.1, - Angle::ZERO, - RewriteColor::NoOp, + .render_to_batch(ctx.prerender) + .scale(0.1) + .centered_on(center), ); } batch.upload(ctx)