From eb4060b071a000ce204017f974568eda699c7d46 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Fri, 17 Dec 2021 11:37:54 +0000 Subject: [PATCH] Improve LTN tool rendering when zoomed in. Outlines around selected objects are less thick and opaque, to cover up less of the road. --- game/src/ltn/select_boundary.rs | 42 +++++++++++++++++---------------- widgetry/src/color.rs | 5 ++++ widgetry/src/mapspace/mod.rs | 10 ++++++++ widgetry/src/mapspace/world.rs | 27 +++++++++++++-------- 4 files changed, 54 insertions(+), 30 deletions(-) diff --git a/game/src/ltn/select_boundary.rs b/game/src/ltn/select_boundary.rs index 788929a856..47137f8f9f 100644 --- a/game/src/ltn/select_boundary.rs +++ b/game/src/ltn/select_boundary.rs @@ -2,10 +2,11 @@ use std::collections::{BTreeMap, BTreeSet}; use geom::Distance; use map_model::{Block, Perimeter}; +use widgetry::mapspace::ToggleZoomed; use widgetry::mapspace::{ObjectID, World, WorldOutcome}; use widgetry::{ - Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, State, - TextExt, VerticalAlignment, Widget, + Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, State, TextExt, + VerticalAlignment, Widget, }; use crate::app::{App, Transition}; @@ -20,7 +21,7 @@ pub struct SelectBoundary { blocks: BTreeMap, world: World, selected: BTreeSet, - draw_outline: Drawable, + draw_outline: ToggleZoomed, } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -39,7 +40,7 @@ impl SelectBoundary { blocks: BTreeMap::new(), world: World::bounded(app.primary.map.get_bounds()), selected: BTreeSet::new(), - draw_outline: Drawable::empty(ctx), + draw_outline: ToggleZoomed::empty(ctx), }; ctx.loading_screen("calculate all blocks", |ctx, timer| { @@ -126,27 +127,28 @@ impl SelectBoundary { // Draw the outline of the current blocks let mut valid_blocks = 0; - let mut batch = GeomBatch::new(); + let mut batch = ToggleZoomed::builder(); for perimeter in self.merge_selected() { if let Ok(block) = perimeter.to_block(&app.primary.map) { - if let Ok(outline) = block.polygon.to_outline(Distance::meters(10.0)) { - // Alternate colors, to help people figure out where two disjoint boundaries - // exist - // TODO Ideally have more than 2 colors to cycle through - batch.push( - if valid_blocks % 2 == 0 { - Color::RED - } else { - Color::GREEN - }, - outline, - ); - } + // Alternate colors, to help people figure out where two disjoint boundaries exist + // TODO Ideally have more than 2 colors to cycle through + let color = if valid_blocks % 2 == 0 { + Color::RED + } else { + Color::GREEN + }; valid_blocks += 1; + + if let Ok(outline) = block.polygon.to_outline(Distance::meters(10.0)) { + batch.unzoomed.push(color, outline); + } + if let Ok(outline) = block.polygon.to_outline(Distance::meters(5.0)) { + batch.zoomed.push(color.alpha(0.5), outline); + } } } - self.draw_outline = batch.upload(ctx); + self.draw_outline = batch.build(ctx); self.panel = make_panel(ctx, app, valid_blocks == 1); } } @@ -212,7 +214,7 @@ impl State for SelectBoundary { fn draw(&self, g: &mut GfxCtx, _: &App) { self.world.draw(g); - g.redraw(&self.draw_outline); + self.draw_outline.draw(g); self.panel.draw(g); } } diff --git a/widgetry/src/color.rs b/widgetry/src/color.rs index d243d0e23c..832d6a3585 100644 --- a/widgetry/src/color.rs +++ b/widgetry/src/color.rs @@ -114,6 +114,11 @@ impl Color { Color::rgba_f(self.r, self.g, self.b, a) } + /// Multiply the color's current alpha by the `factor`, returning a new color. + pub fn multiply_alpha(&self, factor: f32) -> Color { + Color::rgba_f(self.r, self.g, self.b, self.a * factor) + } + pub fn hex(raw: &str) -> Color { // Skip the leading '#' let r = usize::from_str_radix(&raw[1..3], 16).unwrap(); diff --git a/widgetry/src/mapspace/mod.rs b/widgetry/src/mapspace/mod.rs index 5b02a03999..7a54862f00 100644 --- a/widgetry/src/mapspace/mod.rs +++ b/widgetry/src/mapspace/mod.rs @@ -77,6 +77,16 @@ impl ToggleZoomedBuilder { self } + /// Mark that this object will be drawn differently when zoomed and unzoomed, undoing the + /// effects of converting from a single `GeomBatch`. Idempotent. + pub fn draw_differently_zoomed(mut self) -> Self { + if self.always_draw_unzoomed { + self.always_draw_unzoomed = false; + self.zoomed = self.unzoomed.clone(); + } + self + } + pub fn build(self, ctx: &EventCtx) -> ToggleZoomed { if self.always_draw_unzoomed { assert!(self.zoomed.is_empty()); diff --git a/widgetry/src/mapspace/world.rs b/widgetry/src/mapspace/world.rs index ff0a08dc25..a62351972b 100644 --- a/widgetry/src/mapspace/world.rs +++ b/widgetry/src/mapspace/world.rs @@ -159,25 +159,32 @@ impl<'a, ID: ObjectID> ObjectBuilder<'a, ID> { self.draw_hover_rewrite(RewriteColor::ChangeAlpha(alpha)) } - /// Draw the object in a hovered state by adding an outline to the normal drawing. + /// Draw the object in a hovered state by adding an outline to the normal drawing. The + /// specified `color` and `thickness` will be used when unzoomed. For the zoomed view, the + /// color's opacity and the thickness will be halved. pub fn hover_outline(self, color: Color, thickness: Distance) -> Self { let mut draw = self .draw_normal .clone() - .expect("first specify how to draw normally"); - if let Ok(p) = self - .hitbox - .clone() - .expect("call hitbox first") - .to_outline(thickness) - { - draw = draw.push(color, p); + .expect("first specify how to draw normally") + .draw_differently_zoomed(); + let hitbox = self.hitbox.as_ref().expect("call hitbox first"); + if let (Ok(unzoomed), Ok(zoomed)) = ( + hitbox.to_outline(thickness), + hitbox.to_outline(thickness / 2.0), + ) { + draw.unzoomed.push(color, unzoomed); + draw.zoomed.push(color.multiply_alpha(0.5), zoomed); } else { warn!( "Can't hover_outline for {:?}. Falling back to a colored polygon", self.id ); - draw = GeomBatch::from(vec![(color, self.hitbox.clone().unwrap())]).into(); + draw = GeomBatch::from(vec![( + color.multiply_alpha(0.5), + self.hitbox.clone().unwrap(), + )]) + .into(); } self.draw_hovered(draw) }