diff --git a/common/src/chunkid.rs b/common/src/chunkid.rs index 4c2124a4..9a5e4025 100644 --- a/common/src/chunkid.rs +++ b/common/src/chunkid.rs @@ -45,7 +45,7 @@ impl ChunkID { pub fn bbox(self) -> AABB { let ll = self.corner(); - AABB::new(ll, ll + Vec2::splat(Self::SIZE_F32)) + AABB::new_ll_size(ll, Vec2::splat(Self::SIZE_F32)) } pub fn convert_up(self) -> ChunkID { @@ -97,7 +97,7 @@ mod tests { assert_eq!(c.center(), vec2(-8.0, 24.0)); assert_eq!( c.bbox(), - geom::AABB::new(vec2(-16.0, 16.0), vec2(0.0, 32.0)) + geom::AABB::new_ll_ur(vec2(-16.0, 16.0), vec2(0.0, 32.0)) ); assert_eq!(c.size(), 16.0); @@ -107,7 +107,7 @@ mod tests { assert_eq!(c.center(), vec2(-16.0, 48.0)); assert_eq!( c.bbox(), - geom::AABB::new(vec2(-32.0, 32.0), vec2(0.0, 64.0)) + geom::AABB::new_ll_ur(vec2(-32.0, 32.0), vec2(0.0, 64.0)) ); assert_eq!(c.size(), 32.0); } diff --git a/geom/src/aabb.rs b/geom/src/aabb.rs index b3b5c3ed..670b6944 100644 --- a/geom/src/aabb.rs +++ b/geom/src/aabb.rs @@ -12,10 +12,20 @@ pub struct AABB { impl AABB { /// Create a new `AABB`. #[inline] - pub const fn new(ll: Vec2, ur: Vec2) -> Self { + pub fn new_ll_ur(ll: Vec2, ur: Vec2) -> Self { + debug_assert!(ll.x <= ur.x); + debug_assert!(ll.y <= ur.y); AABB { ll, ur } } + /// Create a new `AABB`. + #[inline] + pub fn new_ll_size(ll: Vec2, size: Vec2) -> Self { + debug_assert!(size.x >= 0.0); + debug_assert!(size.y >= 0.0); + AABB { ll, ur: ll + size } + } + /// Create a new `AABB`. #[inline] pub fn centered(pos: Vec2, size: Vec2) -> Self { @@ -126,10 +136,10 @@ impl AABB { #[inline] /// Checks whether the `AABB` contains a `Point` pub fn contains_within(&self, point: Vec2, tolerance: f32) -> bool { - point.x >= self.ll.x - tolerance - && point.x <= self.ur.x + tolerance - && point.y <= self.ur.y + tolerance - && point.y >= self.ll.y - tolerance + (point.x >= self.ll.x - tolerance) + & (point.x <= self.ur.x + tolerance) + & (point.y <= self.ur.y + tolerance) + & (point.y >= self.ll.y - tolerance) } #[inline] diff --git a/geom/src/boldspline.rs b/geom/src/boldspline.rs index 7f5455ca..4624bc8c 100644 --- a/geom/src/boldspline.rs +++ b/geom/src/boldspline.rs @@ -165,7 +165,7 @@ impl Intersect for BoldSpline { s.to = s.to.rotated_by(rot); s.to_derivative = s.to_derivative.rotated_by(rot); - let aabb = AABB::new(Vec2::ZERO, vec2(w * w, w * h)); + let aabb = AABB::new_ll_ur(Vec2::ZERO, vec2(w * w, w * h)); BoldSpline { spline: s, diff --git a/geom/src/heightmap.rs b/geom/src/heightmap.rs index 7668f108..aabaa20f 100644 --- a/geom/src/heightmap.rs +++ b/geom/src/heightmap.rs @@ -46,7 +46,7 @@ impl HeightmapChunk pub fn rect(id: HeightmapChunkID) -> AABB { let ll = vec2(id.0 as f32 * SIZE as f32, id.1 as f32 * SIZE as f32); let ur = ll + vec2(SIZE as f32, SIZE as f32); - AABB::new(ll, ur) + AABB::new_ll_ur(ll, ur) } #[inline] @@ -141,7 +141,7 @@ impl Heightmap { #[inline] pub fn bounds(&self) -> AABB { - AABB::new( + AABB::new_ll_ur( vec2(0.0, 0.0), vec2(self.w as f32 * SIZE as f32, self.h as f32 * SIZE as f32), ) diff --git a/geom/src/obb.rs b/geom/src/obb.rs index 160d013c..5ab7a3f0 100644 --- a/geom/src/obb.rs +++ b/geom/src/obb.rs @@ -169,7 +169,7 @@ impl Shape for OBB { None => unsafe { unreachable_unchecked() }, }; - AABB::new(min, max) + AABB::new_ll_ur(min, max) } } @@ -247,7 +247,7 @@ impl Intersect for OBB { src: (shape.src - self.corners[0]).rotated_by(axis[0].flipy()), dst: (shape.dst - self.corners[0]).rotated_by(axis[0].flipy()), }; - AABB::new(Vec2::ZERO, vec2(w * w, h * w)).intersects(&tr) + AABB::new_ll_ur(Vec2::ZERO, vec2(w * w, h * w)).intersects(&tr) } } diff --git a/geom/src/polygon.rs b/geom/src/polygon.rs index 14677d15..48912f10 100644 --- a/geom/src/polygon.rs +++ b/geom/src/polygon.rs @@ -421,7 +421,7 @@ impl Shape for Polygon { None => return AABB::zero(), }; - AABB::new(min, max) + AABB::new_ll_ur(min, max) } } diff --git a/geom/src/polyline.rs b/geom/src/polyline.rs index 8a64c7b7..a601521c 100644 --- a/geom/src/polyline.rs +++ b/geom/src/polyline.rs @@ -292,7 +292,7 @@ impl PolyLine { None => unsafe { unreachable_unchecked() }, }; - AABB::new(min, max) + AABB::new_ll_ur(min, max) } pub fn n_points(&self) -> usize { diff --git a/geom/src/segment.rs b/geom/src/segment.rs index f18117e4..28893fbc 100644 --- a/geom/src/segment.rs +++ b/geom/src/segment.rs @@ -218,7 +218,7 @@ impl Segmentd { impl Shape for Segment { #[inline] fn bbox(&self) -> AABB { - AABB::new(self.src, self.dst) + AABB::new_ll_ur(self.src.min(self.dst), self.src.max(self.dst)) } } diff --git a/geom/src/spline.rs b/geom/src/spline.rs index 2f47c870..9041216d 100644 --- a/geom/src/spline.rs +++ b/geom/src/spline.rs @@ -111,7 +111,7 @@ impl Spline { } } - AABB::new(mi, ma) + AABB::new_ll_ur(mi, ma) } pub fn wide_bbox(&self) -> AABB { @@ -119,7 +119,7 @@ impl Spline { let p2 = self.to - self.to_derivative; let mi = self.from.min(self.to).min(p1).min(p2); let ma = self.from.max(self.to).max(p1).max(p2); - AABB::new(mi, ma) + AABB::new_ll_ur(mi, ma) } pub fn is_steep(&self, thickness: f32) -> bool { diff --git a/goryak/src/lib.rs b/goryak/src/lib.rs index 2875f2b6..8af078d1 100644 --- a/goryak/src/lib.rs +++ b/goryak/src/lib.rs @@ -11,6 +11,7 @@ mod layout; mod roundrect; mod scroll; mod selectable_label; +mod sized_canvas; mod text; mod theme; mod tooltip; @@ -30,6 +31,7 @@ pub use layout::*; pub use roundrect::*; pub use scroll::*; pub use selectable_label::*; +pub use sized_canvas::*; pub use text::*; pub use theme::*; pub use util::*; diff --git a/goryak/src/sized_canvas.rs b/goryak/src/sized_canvas.rs new file mode 100644 index 00000000..a2ad1d82 --- /dev/null +++ b/goryak/src/sized_canvas.rs @@ -0,0 +1,81 @@ +use std::fmt::Debug; +use yakui_core::geometry::{Constraints, Vec2}; +use yakui_core::widget::{LayoutContext, PaintContext, Widget}; +use yakui_core::Response; +use yakui_widgets::util::widget; + +type DrawCallback = Box) + 'static>; + +/** +Allows the user to draw arbitrary graphics in a region. + +Responds with [SizedCanvasResponse]. + */ +pub struct SizedCanvas { + draw: Option, + pub size: Vec2, +} + +impl Debug for SizedCanvas { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SizedCanvas").finish() + } +} + +impl SizedCanvas { + pub fn new(size: Vec2, draw: impl Fn(&mut PaintContext<'_>) + 'static) -> Self { + Self { + draw: Some(Box::new(draw)), + size, + } + } + + pub fn show(self) -> Response { + widget::(self) + } +} + +pub fn sized_canvas( + size: Vec2, + draw: impl Fn(&mut PaintContext<'_>) + 'static, +) -> Response { + SizedCanvas::new(size, draw).show() +} + +#[derive(Debug)] +pub struct SizedCanvasWidget { + props: SizedCanvas, +} + +pub type SizedCanvasResponse = (); + +impl Widget for SizedCanvasWidget { + type Props<'a> = SizedCanvas; + type Response = SizedCanvasResponse; + + fn new() -> Self { + Self { + props: SizedCanvas { + draw: None, + size: Default::default(), + }, + } + } + + fn update(&mut self, props: Self::Props<'_>) -> Self::Response { + self.props = props; + } + + fn layout(&self, ctx: LayoutContext<'_>, _: Constraints) -> Vec2 { + ctx.layout.enable_clipping(ctx.dom); + self.props.size + } + + fn paint(&self, mut ctx: PaintContext<'_>) { + if let Some(draw) = &self.props.draw { + draw(&mut ctx); + } + + self.default_paint(ctx); + } +} diff --git a/native_app/src/audio/ambient.rs b/native_app/src/audio/ambient.rs index 17343cf5..86d428e2 100644 --- a/native_app/src/audio/ambient.rs +++ b/native_app/src/audio/ambient.rs @@ -47,7 +47,7 @@ impl Ambient { } // Forest - let bbox = AABB::new(eye.xy() - Vec2::splat(100.0), eye.xy() + Vec2::splat(100.0)); + let bbox = AABB::centered(eye.xy(), Vec2::splat(200.0)); let mut volume = lerp(1.0, 0.0, h / 300.0); if volume > 0.0 { diff --git a/native_app/src/audio/car_sounds.rs b/native_app/src/audio/car_sounds.rs index f596887f..66d14ac3 100644 --- a/native_app/src/audio/car_sounds.rs +++ b/native_app/src/audio/car_sounds.rs @@ -1,7 +1,7 @@ use crate::uiworld::UiWorld; use engine::{AudioContext, AudioKind, Gain, GainControl}; use flat_spatial::grid::GridHandle; -use geom::{Camera, AABB}; +use geom::{Camera, Vec2, AABB}; use oddio::{Cycle, Mixed, Seek, Speed, SpeedControl}; use simulation::transportation::TransportGrid; use simulation::Simulation; @@ -37,7 +37,7 @@ impl CarSounds { pub fn update(&mut self, sim: &Simulation, uiworld: &UiWorld, ctx: &mut AudioContext) { let transport_grid = sim.read::(); let campos = uiworld.read::().eye(); - let cambox = AABB::new(campos.xy(), campos.xy()).expand(100.0); + let cambox = AABB::centered(campos.xy(), Vec2::splat(200.0)); const HEAR_RADIUS: f32 = 200.0; diff --git a/native_app/src/newgui/hud/windows/economy.rs b/native_app/src/newgui/hud/windows/economy.rs index 7726105c..06e5e36e 100644 --- a/native_app/src/newgui/hud/windows/economy.rs +++ b/native_app/src/newgui/hud/windows/economy.rs @@ -1,14 +1,17 @@ use std::collections::HashSet; +use engine::Tesselator; +use geom::AABB; +use yakui::paint::PaintMesh; use yakui::widgets::{CountGrid, List, Pad}; use yakui::{ - canvas, colored_box, constrained, use_state, Color, Constraints, CrossAxisAlignment, + colored_box, constrained, use_state, Color, Constraints, CrossAxisAlignment, MainAxisAlignItems, MainAxisSize, Vec2, }; use goryak::{ constrained_viewport, mincolumn, minrow, on_primary_container, padxy, pady, - selectable_label_primary, textc, Scrollable, Window, + selectable_label_primary, sized_canvas, textc, Scrollable, Window, }; use prototypes::{ItemID, DELTA_F64}; use simulation::economy::{ @@ -118,11 +121,34 @@ pub fn economy(uiw: &UiWorld, _: &Simulation) { pady(5.0, || { colored_box(Color::BLACK, Vec2::new(300.0, 200.0)); // plot placeholder }); - canvas(|paint| { - //let tess = Tesselator::new( - // Some(AABB::new((0.0, 0.0).into(), (300.0, 200.0).into())), - // 1.0, - //); + sized_canvas(Vec2::new(300.0, 200.0), |paint| { + let rect = paint.layout.get(paint.dom.current()).unwrap().rect; + + let mut vertices = Vec::new(); + let mut indices = Vec::new(); + + let cull_rect = AABB::new_ll_size( + <[f32; 2]>::from(rect.pos()).into(), + <[f32; 2]>::from(rect.size()).into(), + ); + let mut tess = + Tesselator::new(&mut vertices, &mut indices, Some(cull_rect), 15.0); + tess.set_color([1.0f32, 1.0, 1.0, 1.0]); + + let [x, y]: [f32; 2] = rect.pos().into(); + + tess.draw_circle((x + 50.0, y + 50.0, 0.0).into(), 50.0); + + paint.paint.add_mesh(PaintMesh::new( + vertices.into_iter().map(|v| { + yakui::paint::Vertex::new( + [v.position[0], v.position[1]], + v.uv, + v.color, + ) + }), + indices.into_iter().map(|x| x as _), + )); }); let scroll_constrained = Constraints { diff --git a/native_app/src/rendering/orbit_camera.rs b/native_app/src/rendering/orbit_camera.rs index 782ddf87..b9c33cc5 100644 --- a/native_app/src/rendering/orbit_camera.rs +++ b/native_app/src/rendering/orbit_camera.rs @@ -35,7 +35,7 @@ impl OrbitCamera { pub fn cull_tess(&self, tess: &mut Tesselator) { let p = self.camera.pos; - tess.cull_rect = Some(AABB::new(p.xy(), p.xy()).expand(2000.0)); + tess.cull_rect = Some(AABB::centered(p.xy(), Vec2::splat(4000.0))); tess.zoom = 1000.0 / self.height().max(1.0); } diff --git a/simulation/src/map/procgen/building.rs b/simulation/src/map/procgen/building.rs index 5fd3c38b..39648358 100644 --- a/simulation/src/map/procgen/building.rs +++ b/simulation/src/map/procgen/building.rs @@ -15,7 +15,7 @@ impl ColoredMesh { minmax(self.faces.iter().flat_map(|x| &x.0).map(|x| x.xy())), return AABB::zero() ); - AABB::new(ll, ur) + AABB::new_ll_ur(ll, ur) } pub fn translate(&mut self, off: Vec2) {