From a005dd420f2c229ba335d28f79c331a345e327a3 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 15 Jul 2025 01:15:15 +0100 Subject: [PATCH] Fixed the `Aabb`'s size and anchor point calculations in `calculate_bounds_text2d`. --- crates/bevy_text/src/text2d.rs | 22 ++-- examples/2d/text2d.rs | 203 ++++++++------------------------- examples/testbed/2d.rs | 1 + 3 files changed, 59 insertions(+), 167 deletions(-) diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 7fa3202d18..d7555b84b8 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -17,7 +17,7 @@ use bevy_ecs::{ system::{Commands, Local, Query, Res, ResMut}, }; use bevy_image::prelude::*; -use bevy_math::Vec2; +use bevy_math::{Vec2, Vec3}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_render::sync_world::TemporaryRenderEntity; use bevy_render::view::{self, Visibility, VisibilityClass}; @@ -186,6 +186,7 @@ pub fn extract_text2d_sprite( let top_left = (Anchor::TOP_LEFT.0 - anchor.as_vec()) * size; let transform = *global_transform * GlobalTransform::from_translation(top_left.extend(0.)) * scaling; + let mut color = LinearRgba::WHITE; let mut current_span = usize::MAX; @@ -366,22 +367,17 @@ pub fn calculate_bounds_text2d( text_bounds.width.unwrap_or(layout_info.size.x), text_bounds.height.unwrap_or(layout_info.size.y), ); - let center = (-anchor.as_vec() * size + (size.y - layout_info.size.y) * Vec2::Y) - .extend(0.) - .into(); - let half_extents = (0.5 * layout_info.size).extend(0.0).into(); + let x1 = (Anchor::TOP_LEFT.0.x - anchor.as_vec().x) * size.x; + let x2 = (Anchor::TOP_LEFT.0.x - anchor.as_vec().x + 1.) * size.x; + let y1 = (Anchor::TOP_LEFT.0.y - anchor.as_vec().y) * size.y; + let y2 = (Anchor::TOP_LEFT.0.y - anchor.as_vec().y - 1.) * size.y; + let new_aabb = Aabb::from_min_max(Vec3::new(x1, y1, 0.), Vec3::new(x2, y2, 0.)); if let Some(mut aabb) = aabb { - *aabb = Aabb { - center, - half_extents, - }; + *aabb = new_aabb; } else { - commands.entity(entity).try_insert(Aabb { - center, - half_extents, - }); + commands.entity(entity).try_insert(new_aabb); } } } diff --git a/examples/2d/text2d.rs b/examples/2d/text2d.rs index 3123e8d891..f2ba1afe0a 100644 --- a/examples/2d/text2d.rs +++ b/examples/2d/text2d.rs @@ -5,177 +5,72 @@ //! For an example on how to render text as part of a user interface, independent from the world //! viewport, you may want to look at `games/contributors.rs` or `ui/text.rs`. -use bevy::{ - color::palettes::css::*, - math::ops, - prelude::*, - sprite::Anchor, - text::{FontSmoothing, LineBreak, TextBounds}, -}; +use bevy::color::palettes; +use bevy::prelude::*; +use bevy::sprite::Anchor; +use bevy::text::TextBounds; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Startup, setup) - .add_systems( - Update, - (animate_translation, animate_rotation, animate_scale), - ) .run(); } -#[derive(Component)] -struct AnimateTranslation; - -#[derive(Component)] -struct AnimateRotation; - -#[derive(Component)] -struct AnimateScale; - -fn setup(mut commands: Commands, asset_server: Res) { - let font = asset_server.load("fonts/FiraSans-Bold.ttf"); - let text_font = TextFont { - font: font.clone(), - font_size: 50.0, - ..default() - }; - let text_justification = Justify::Center; - commands.spawn(Camera2d); - // Demonstrate changing translation +fn example(commands: &mut Commands, dest: Vec3, justify: Justify) { commands.spawn(( - Text2d::new("translation"), - text_font.clone(), - TextLayout::new_with_justify(text_justification), - AnimateTranslation, - )); - // Demonstrate changing rotation - commands.spawn(( - Text2d::new("rotation"), - text_font.clone(), - TextLayout::new_with_justify(text_justification), - AnimateRotation, - )); - // Demonstrate changing scale - commands.spawn(( - Text2d::new("scale"), - text_font, - TextLayout::new_with_justify(text_justification), - Transform::from_translation(Vec3::new(400.0, 0.0, 0.0)), - AnimateScale, - )); - // Demonstrate text wrapping - let slightly_smaller_text_font = TextFont { - font, - font_size: 35.0, - ..default() - }; - let box_size = Vec2::new(300.0, 200.0); - let box_position = Vec2::new(0.0, -250.0); - commands.spawn(( - Sprite::from_color(Color::srgb(0.25, 0.25, 0.55), box_size), - Transform::from_translation(box_position.extend(0.0)), - children![( - Text2d::new("this text wraps in the box\n(Unicode linebreaks)"), - slightly_smaller_text_font.clone(), - TextLayout::new(Justify::Left, LineBreak::WordBoundary), - // Wrap text in the rectangle - TextBounds::from(box_size), - // Ensure the text is drawn on top of the box - Transform::from_translation(Vec3::Z), - )], + Sprite::from_color(palettes::css::YELLOW, 10. * Vec2::ONE), + Anchor::CENTER, + Transform::from_translation(dest), )); - let other_box_size = Vec2::new(300.0, 200.0); - let other_box_position = Vec2::new(320.0, -250.0); - commands.spawn(( - Sprite::from_color(Color::srgb(0.25, 0.25, 0.55), other_box_size), - Transform::from_translation(other_box_position.extend(0.0)), - children![( - Text2d::new("this text wraps in the box\n(AnyCharacter linebreaks)"), - slightly_smaller_text_font.clone(), - TextLayout::new(Justify::Left, LineBreak::AnyCharacter), - // Wrap text in the rectangle - TextBounds::from(other_box_size), - // Ensure the text is drawn on top of the box - Transform::from_translation(Vec3::Z), - )], - )); + for (a, bg) in [ + (Anchor::TOP_LEFT, palettes::css::DARK_SLATE_GREY), + (Anchor::TOP_RIGHT, palettes::css::DARK_OLIVEGREEN), + (Anchor::BOTTOM_RIGHT, palettes::css::DARK_SLATE_GREY), + (Anchor::BOTTOM_LEFT, palettes::css::DARK_OLIVEGREEN), + ] { + commands.spawn(( + Sprite::from_color(bg, Vec2::new(300., 75.)), + a, + Transform::from_translation(dest - Vec3::Z), + )); - // Demonstrate font smoothing off - commands.spawn(( - Text2d::new("This text has\nFontSmoothing::None\nAnd Justify::Center"), - slightly_smaller_text_font - .clone() - .with_font_smoothing(FontSmoothing::None), - TextLayout::new_with_justify(Justify::Center), - Transform::from_translation(Vec3::new(-400.0, -250.0, 0.0)), - )); - - commands - .spawn(( - Sprite { - color: Color::Srgba(LIGHT_CYAN), - custom_size: Some(Vec2::new(10., 10.)), + commands.spawn(( + Text2d(format!("L R\n{:?}\n{:?}", a.0, justify)), + TextFont { + font_size: 14.0, + ..default() + }, + TextLayout { + justify, ..Default::default() }, - Transform::from_translation(250. * Vec3::Y), - )) - .with_children(|commands| { - for (text_anchor, color) in [ - (Anchor::TOP_LEFT, Color::Srgba(LIGHT_SALMON)), - (Anchor::TOP_RIGHT, Color::Srgba(LIGHT_GREEN)), - (Anchor::BOTTOM_RIGHT, Color::Srgba(LIGHT_BLUE)), - (Anchor::BOTTOM_LEFT, Color::Srgba(LIGHT_YELLOW)), - ] { - commands - .spawn(( - Text2d::new(" Anchor".to_string()), - slightly_smaller_text_font.clone(), - text_anchor, - )) - .with_child(( - TextSpan("::".to_string()), - slightly_smaller_text_font.clone(), - TextColor(LIGHT_GREY.into()), - )) - .with_child(( - TextSpan(format!("{text_anchor:?} ")), - slightly_smaller_text_font.clone(), - TextColor(color), - )); - } - }); -} - -fn animate_translation( - time: Res