#[repr(C)]pub struct Rect {
pub min: Vec2,
pub max: Vec2,
}Expand description
An axis-aligned rectangle: min is the top-left corner, max the
bottom-right (y grows downward, like window pixels).
let r = Rect::new(10.0, 20.0, 100.0, 50.0); // x, y, width, height
assert_eq!(r.size(), vec2(100.0, 50.0));
assert!(r.contains(vec2(50.0, 40.0)));Fields§
§min: Vec2Top-left corner (smallest x and y).
max: Vec2Bottom-right corner (largest x and y).
Implementations§
Source§impl Rect
impl Rect
Sourcepub fn new(x: f32, y: f32, width: f32, height: f32) -> Self
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self
A rectangle from its top-left corner and its size.
let button = Rect::new(20.0, 20.0, 120.0, 32.0);Examples found in repository?
examples/flow.rs (line 226)
225fn hud(ctx: &mut Ctx) {
226 let panel = Rect::new(12.0, 12.0, 260.0, 104.0);
227 ctx.ui_panel(panel);
228 let at = panel.min + ctx.ui_style.padding;
229 let agents = ctx.world.query::<&Agent>().count();
230 let ms = ctx.world.resource::<Paths>().compute_ms;
231 let text = format!(
232 "{agents} agents, {:.0} FPS\nfield: {}x{} cells, {ms:.2} ms\nclick: new goal H: distances",
233 ctx.fps(),
234 MAP.x,
235 MAP.y
236 );
237 ctx.ui_label(at, &text);
238}More examples
examples/tiles.rs (line 148)
138fn hud(ctx: &mut Ctx) {
139 let r = ctx.render_stats();
140 let text = format!(
141 "{} sprites drawn ({} draw calls)\nzoom {:.2}x, {:.0} FPS\nleft mouse: paint, right drag: pan, wheel: zoom",
142 r.sprites,
143 r.draw_calls,
144 ctx.camera.zoom,
145 ctx.fps()
146 );
147 let size = ctx.measure_text(&text, ctx.ui_style.text_size) + 2.0 * ctx.ui_style.padding;
148 let panel = Rect::new(12.0, 12.0, size.x, size.y);
149 ctx.ui_panel(panel);
150 ctx.ui_label(panel.min + ctx.ui_style.padding, &text);
151}examples/ants.rs (line 417)
413fn hud(ctx: &mut Ctx) {
414 let ants = ctx.world.query::<&Ant>().count();
415 let nests = ctx.world.query::<&Nest>().count();
416 let food = delivered(&mut ctx.world);
417 let panel = Rect::new(12.0, 12.0, 260.0, 156.0);
418 ctx.ui_panel(panel);
419 let mut at = panel.min + ctx.ui_style.padding;
420 let speed = if ctx.is_paused() {
421 "paused".to_string()
422 } else {
423 format!("{}x", ctx.sim_speed())
424 };
425 let stats = format!(
426 "{ants} ants in {nests} colonies\n{food} food delivered\nspeed {speed}, {:.0} FPS",
427 ctx.fps()
428 );
429 at.y += ctx.ui_label(at, &stats).y + 8.0;
430 let pause = if ctx.is_paused() { "Run" } else { "Pause" };
431 if ctx.ui_button(Rect::new(at.x, at.y, 72.0, 30.0), pause) {
432 if ctx.is_paused() { ctx.resume() } else { ctx.pause() }
433 }
434 for (i, speed) in [1.0f32, 4.0, 16.0].into_iter().enumerate() {
435 let r = Rect::new(at.x + 80.0 + i as f32 * 54.0, at.y, 48.0, 30.0);
436 if ctx.ui_button(r, &format!("{speed}x")) {
437 ctx.set_sim_speed(speed);
438 ctx.resume();
439 }
440 }
441}examples/ui.rs (line 27)
20fn scene(ctx: &mut Ctx) {
21 let t = ctx.time() as f32;
22
23 // The world: a grid of tiles, a few shapes, a crab in orbit.
24 for y in 0..20 {
25 for x in 0..20 {
26 let shade = if (x + y) % 2 == 0 { 0.16 } else { 0.19 };
27 let tile = Rect::new(x as f32 * 64.0, y as f32 * 64.0, 64.0, 64.0);
28 ctx.draw_rect(tile, Color::rgb(shade, shade + 0.03, shade));
29 }
30 }
31 let center = vec2(640.0, 640.0);
32 ctx.draw_circle(center, 180.0, Color::hex(0x3a86ff).with_alpha(0.15));
33 let orbit = center + Vec2::from_angle(t * 0.6) * 180.0;
34 ctx.draw_line(center, orbit, 3.0, Color::hex(0xffbe0b));
35 let crab = ctx.world.resource::<Scene>().crab;
36 ctx.draw_ex(
37 &crab,
38 orbit,
39 DrawParams {
40 rotation: t,
41 ..Default::default()
42 },
43 );
44
45 // A label that sticks to the crab: world position -> window pixels.
46 let above = ctx.camera.world_to_screen(orbit) + vec2(-28.0, -40.0);
47 ctx.draw_text("orbiter", above, 16.0, Color::hex(0xffbe0b));
48
49 if ctx.world.resource::<Scene>().follow {
50 ctx.camera.look_at(orbit);
51 }
52 if ctx.mouse_pressed(MouseButton::Left) && !ctx.mouse_over_ui() {
53 let at = ctx.mouse_world();
54 ctx.world.spawn((Transform2D::at(at), Sprite::new(crab)));
55 let plop = ctx.world.resource::<Scene>().plop;
56 let speed = 0.8 + ctx.random().stream(0).f32() * 0.4;
57 ctx.play_sound_ex(
58 &plop,
59 SoundParams {
60 speed,
61 ..Default::default()
62 },
63 );
64 ctx.world.resource_mut::<Scene>().dropped += 1;
65 }
66}
67// ANCHOR_END: scene
68
69// ANCHOR: hud
70fn hud(ctx: &mut Ctx) {
71 let panel = Rect::new(12.0, 12.0, 230.0, 172.0);
72 ctx.ui_panel(panel);
73 let mut at = panel.min + ctx.ui_style.padding;
74 let (zoom, dropped) = (ctx.camera.zoom, ctx.world.resource::<Scene>().dropped);
75 let lines = format!("zoom {zoom:.2}x\ncrabs dropped: {dropped}\n{:.0} FPS", ctx.fps());
76 at.y += ctx.ui_label(at, &lines).y + 8.0;
77
78 let follow = ctx.world.resource::<Scene>().follow;
79 let label = if follow { "Stop following" } else { "Follow orbiter" };
80 if ctx.ui_button(Rect::new(at.x, at.y, 210.0, 30.0), label) {
81 ctx.world.resource_mut::<Scene>().follow = !follow;
82 }
83 at.y += 38.0;
84 if ctx.ui_button(Rect::new(at.x, at.y, 210.0, 30.0), "Reset camera") {
85 ctx.camera.zoom = 1.0;
86 ctx.camera.position = Vec2::ZERO;
87 ctx.world.resource_mut::<Scene>().follow = false;
88 }
89}examples/battle.rs (line 592)
552fn draw_map(ctx: &mut Ctx) -> impl FnMut(&mut Ctx) + use<> {
553 let size = ctx.world.resource::<Battle>().nav.size();
554 let mut pixels = vec![0u8; (size.x * size.y * 4) as usize];
555 {
556 let nav = &ctx.world.resource::<Battle>().nav;
557 for y in 0..size.y {
558 for x in 0..size.x {
559 // A little per-cell noise so the ground doesn't look flat.
560 let n = ((x.wrapping_mul(73_856_093) ^ y.wrapping_mul(19_349_663)) % 9) as u8;
561 let px = match nav.cost(x, y).unwrap() {
562 NavGrid::WALL => [52 + n, 56 + n, 66 + n, 255],
563 MUD => [74 + n, 56 + n, 34, 255],
564 _ => [30 + n, 46 + n, 32, 255],
565 };
566 pixels[((y * size.x + x) * 4) as usize..][..4].copy_from_slice(&px);
567 }
568 }
569 }
570 let tex = ctx.create_texture(size.x, size.y, &pixels);
571 ctx.set_texture_filter(&tex, TextureFilter::Nearest);
572 move |ctx: &mut Ctx| {
573 ctx.clear(Color::BLACK);
574 let world = size.as_vec2() * CELL;
575 ctx.draw_ex(
576 &tex,
577 world / 2.0,
578 DrawParams {
579 scale: Vec2::splat(CELL),
580 z: -10.0,
581 ..Default::default()
582 },
583 );
584 let rally = ctx.world.resource::<Battle>().rally[0];
585 let mut bases = [(Vec2::ZERO, 0.0, Team::Red); 2];
586 ctx.world.query::<(&Transform2D, &Base)>().for_each(|(t, b)| {
587 bases[b.team.index()] = (t.position, b.hp / b.max_hp, b.team);
588 });
589 for (at, health, team) in bases {
590 ctx.draw_circle(at, BASE_RADIUS, team.color().with_alpha(0.35));
591 ctx.draw_circle(at, BASE_RADIUS * 0.4, team.color());
592 let bar = Rect::new(at.x - BASE_RADIUS, at.y - BASE_RADIUS - 8.0, BASE_RADIUS * 2.0, 3.0);
593 ctx.draw_rect(bar, Color::rgba(0.0, 0.0, 0.0, 0.6));
594 ctx.draw_rect(
595 Rect::new(bar.min.x, bar.min.y, bar.size().x * health, 3.0),
596 team.color(),
597 );
598 }
599 ctx.draw_circle(rally, 5.0, Color::rgba(1.0, 0.9, 0.3, 0.9));
600 }
601}
602
603fn hud(ctx: &mut Ctx) {
604 let field_ms = ctx.world.resource::<Battle>().field_ms;
605 let s = ctx.world.resource::<Score>();
606 let (alive, won, winner) = (s.alive, s.rounds_won, s.winner);
607 let sim_ms: f32 = ctx.system_timings().iter().filter(|t| t.sim).map(|t| t.avg_ms).sum();
608 let speed = if ctx.is_paused() {
609 "paused".to_string()
610 } else {
611 format!("{}x", ctx.sim_speed())
612 };
613 let panel = Rect::new(12.0, 12.0, 300.0, 150.0);
614 ctx.ui_panel(panel);
615 let at = panel.min + ctx.ui_style.padding;
616 let text = format!(
617 "red {} vs blue {} (rounds {}-{})\nsim {sim_ms:.2} ms/tick, {:.0} FPS, {speed}\nfield {field_ms:.2} ms\nclick: rally A: attack D: defend\nR: restart 1-3: speed",
618 alive[0],
619 alive[1],
620 won[0],
621 won[1],
622 ctx.fps()
623 );
624 ctx.ui_label(at, &text);
625 if let Some(team) = winner {
626 let msg = if team == Team::Red {
627 "Red wins! R for another round"
628 } else {
629 "Blue wins. R to try again"
630 };
631 let center = ctx.window_size() / 2.0;
632 let size = ctx.measure_text(msg, 32.0);
633 ctx.draw_text(msg, center - size / 2.0, 32.0, team.color());
634 }
635}examples/physics.rs (line 164)
134fn draw(ctx: &mut Ctx) {
135 ctx.clear(Color::hex(0x1d2330));
136 let shapes: Vec<(Transform2D, Collider, bool, f32)> = ctx
137 .world
138 .query::<(&Transform2D, &Collider, &RigidBody, Option<&Bell>)>()
139 .iter()
140 .map(|(t, c, b, bell)| (*t, *c, b.kind == BodyKind::Fixed, bell.map_or(0.0, |b| b.rang)))
141 .collect();
142 for (t, c, fixed, rang) in shapes {
143 let color = if c.sensor {
144 Color::rgba(1.0, 0.3 + 0.7 * (1.0 - rang), 0.3, 0.35)
145 } else if fixed {
146 Color::hex(0x4a5568)
147 } else {
148 Color::hex(0xf6ad55)
149 };
150 match c.shape {
151 Shape::Circle { radius } => {
152 ctx.draw_circle(t.position, radius, color);
153 // A spoke shows the spin.
154 let spoke = t.position + Vec2::from_angle(t.rotation) * radius;
155 ctx.draw_line(t.position, spoke, 2.0, Color::hex(0x1d2330));
156 }
157 Shape::Rect { size } => {
158 // Four corners turned with the body, drawn as edges.
159 let (sin, cos) = t.rotation.sin_cos();
160 let turn = |v: Vec2| t.position + vec2(v.x * cos - v.y * sin, v.x * sin + v.y * cos);
161 let h = size / 2.0;
162 let corners = [vec2(-h.x, -h.y), vec2(h.x, -h.y), vec2(h.x, h.y), vec2(-h.x, h.y)].map(turn);
163 if fixed || c.sensor {
164 ctx.draw_rect(Rect::new(t.position.x - h.x, t.position.y - h.y, size.x, size.y), color);
165 } else {
166 for i in 0..4 {
167 ctx.draw_line(corners[i], corners[(i + 1) % 4], 3.0, color);
168 }
169 }
170 }
171 Shape::Capsule { height, radius } => {
172 let up = Vec2::from_angle(t.rotation + std::f32::consts::FRAC_PI_2) * height / 2.0;
173 ctx.draw_line(t.position - up, t.position + up, radius * 2.0, color);
174 }
175 Shape::Polygon { .. } => {
176 let (sin, cos) = t.rotation.sin_cos();
177 let turn = |v: Vec2| t.position + vec2(v.x * cos - v.y * sin, v.x * sin + v.y * cos);
178 let corners: Vec<Vec2> = c.shape.points().iter().map(|&p| turn(p + c.offset)).collect();
179 for i in 0..corners.len() {
180 ctx.draw_line(corners[i], corners[(i + 1) % corners.len()], 3.0, color);
181 }
182 }
183 _ => {} // shapes added in later versions
184 }
185 }
186 if let Some(hit) = laser(ctx) {
187 ctx.draw_line(ctx.mouse_world(), hit.point, 1.0, Color::rgba(1.0, 0.2, 0.2, 0.8));
188 ctx.draw_circle(hit.point, 4.0, Color::rgb(1.0, 0.2, 0.2));
189 }
190 let bodies = ctx.physics.body_count();
191 ctx.draw_text(
192 &format!(
193 "{bodies} bodies | left click: crate right click: ball middle click: rock space: blast R: reset"
194 ),
195 vec2(30.0, 30.0),
196 18.0,
197 Color::WHITE,
198 );
199}Sourcepub fn size(&self) -> Vec2
pub fn size(&self) -> Vec2
Width and height.
assert_eq!(Rect::new(0.0, 0.0, 4.0, 3.0).size(), vec2(4.0, 3.0));Examples found in repository?
examples/battle.rs (line 595)
552fn draw_map(ctx: &mut Ctx) -> impl FnMut(&mut Ctx) + use<> {
553 let size = ctx.world.resource::<Battle>().nav.size();
554 let mut pixels = vec![0u8; (size.x * size.y * 4) as usize];
555 {
556 let nav = &ctx.world.resource::<Battle>().nav;
557 for y in 0..size.y {
558 for x in 0..size.x {
559 // A little per-cell noise so the ground doesn't look flat.
560 let n = ((x.wrapping_mul(73_856_093) ^ y.wrapping_mul(19_349_663)) % 9) as u8;
561 let px = match nav.cost(x, y).unwrap() {
562 NavGrid::WALL => [52 + n, 56 + n, 66 + n, 255],
563 MUD => [74 + n, 56 + n, 34, 255],
564 _ => [30 + n, 46 + n, 32, 255],
565 };
566 pixels[((y * size.x + x) * 4) as usize..][..4].copy_from_slice(&px);
567 }
568 }
569 }
570 let tex = ctx.create_texture(size.x, size.y, &pixels);
571 ctx.set_texture_filter(&tex, TextureFilter::Nearest);
572 move |ctx: &mut Ctx| {
573 ctx.clear(Color::BLACK);
574 let world = size.as_vec2() * CELL;
575 ctx.draw_ex(
576 &tex,
577 world / 2.0,
578 DrawParams {
579 scale: Vec2::splat(CELL),
580 z: -10.0,
581 ..Default::default()
582 },
583 );
584 let rally = ctx.world.resource::<Battle>().rally[0];
585 let mut bases = [(Vec2::ZERO, 0.0, Team::Red); 2];
586 ctx.world.query::<(&Transform2D, &Base)>().for_each(|(t, b)| {
587 bases[b.team.index()] = (t.position, b.hp / b.max_hp, b.team);
588 });
589 for (at, health, team) in bases {
590 ctx.draw_circle(at, BASE_RADIUS, team.color().with_alpha(0.35));
591 ctx.draw_circle(at, BASE_RADIUS * 0.4, team.color());
592 let bar = Rect::new(at.x - BASE_RADIUS, at.y - BASE_RADIUS - 8.0, BASE_RADIUS * 2.0, 3.0);
593 ctx.draw_rect(bar, Color::rgba(0.0, 0.0, 0.0, 0.6));
594 ctx.draw_rect(
595 Rect::new(bar.min.x, bar.min.y, bar.size().x * health, 3.0),
596 team.color(),
597 );
598 }
599 ctx.draw_circle(rally, 5.0, Color::rgba(1.0, 0.9, 0.3, 0.9));
600 }
601}Sourcepub fn center(&self) -> Vec2
pub fn center(&self) -> Vec2
The middle point.
assert_eq!(Rect::new(0.0, 0.0, 4.0, 2.0).center(), vec2(2.0, 1.0));Trait Implementations§
impl Copy for Rect
Source§impl<'de> Deserialize<'de> for Rect
impl<'de> Deserialize<'de> for Rect
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Deserialize this value from the given Serde deserializer. Read more
impl StructuralPartialEq for Rect
Auto Trait Implementations§
impl Forget for Rect
impl Freeze for Rect
impl RefUnwindSafe for Rect
impl Send for Rect
impl Sync for Rect
impl Unpin for Rect
impl UnsafeUnpin for Rect
impl UnwindSafe for Rect
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> Component for T
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Converts
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Converts
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Converts
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Converts
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
fn into_sample(self) -> T
impl<T> MaybeSync for T
impl<T> MaybeSync for T
Source§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<R, P> ReadPrimitive<R> for P
impl<R, P> ReadPrimitive<R> for P
Source§fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
Read this value from the supplied reader. Same as
ReadEndian::read_from_little_endian().impl<T> Scalar for T
impl<T> SerializableAny for T
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
The inverse inclusion map: attempts to construct
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
Checks if
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
Use with care! Same as
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
The inclusion map: converts
self to the equivalent element of its superset.