overflow_debug/
overflow_debug.rs

1//! Tests how different transforms behave when clipped with `Overflow::Hidden`
2
3use bevy::{input::common_conditions::input_just_pressed, prelude::*, ui::widget::TextUiWriter};
4use std::f32::consts::{FRAC_PI_2, PI, TAU};
5
6const CONTAINER_SIZE: f32 = 150.0;
7const HALF_CONTAINER_SIZE: f32 = CONTAINER_SIZE / 2.0;
8const LOOP_LENGTH: f32 = 4.0;
9
10fn main() {
11    App::new()
12        .add_plugins(DefaultPlugins)
13        .init_resource::<AnimationState>()
14        .add_systems(Startup, setup)
15        .add_systems(
16            Update,
17            (
18                toggle_overflow.run_if(input_just_pressed(KeyCode::KeyO)),
19                next_container_size.run_if(input_just_pressed(KeyCode::KeyS)),
20                update_transform::<Move>,
21                update_transform::<Scale>,
22                update_transform::<Rotate>,
23                update_animation,
24            ),
25        )
26        .run();
27}
28
29#[derive(Component)]
30struct Instructions;
31
32#[derive(Resource, Default)]
33struct AnimationState {
34    playing: bool,
35    paused_at: f32,
36    paused_total: f32,
37    t: f32,
38}
39
40#[derive(Component)]
41struct Container(u8);
42
43trait UpdateTransform {
44    fn update(&self, t: f32, transform: &mut Transform);
45}
46
47#[derive(Component)]
48struct Move;
49
50impl UpdateTransform for Move {
51    fn update(&self, t: f32, transform: &mut Transform) {
52        transform.translation.x = ops::sin(t * TAU - FRAC_PI_2) * HALF_CONTAINER_SIZE;
53        transform.translation.y = -ops::cos(t * TAU - FRAC_PI_2) * HALF_CONTAINER_SIZE;
54    }
55}
56
57#[derive(Component)]
58struct Scale;
59
60impl UpdateTransform for Scale {
61    fn update(&self, t: f32, transform: &mut Transform) {
62        transform.scale.x = 1.0 + 0.5 * ops::cos(t * TAU).max(0.0);
63        transform.scale.y = 1.0 + 0.5 * ops::cos(t * TAU + PI).max(0.0);
64    }
65}
66
67#[derive(Component)]
68struct Rotate;
69
70impl UpdateTransform for Rotate {
71    fn update(&self, t: f32, transform: &mut Transform) {
72        transform.rotation =
73            Quat::from_axis_angle(Vec3::Z, (ops::cos(t * TAU) * 45.0).to_radians());
74    }
75}
76
77fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
78    // Camera
79
80    commands.spawn(Camera2d);
81
82    // Instructions
83
84    let text_font = TextFont::default();
85
86    commands
87        .spawn((
88            Text::new(
89                "Next Overflow Setting (O)\nNext Container Size (S)\nToggle Animation (space)\n\n",
90            ),
91            text_font.clone(),
92            Node {
93                position_type: PositionType::Absolute,
94                top: Val::Px(12.0),
95                left: Val::Px(12.0),
96                ..default()
97            },
98            Instructions,
99        ))
100        .with_child((
101            TextSpan::new(format!("{:?}", Overflow::clip())),
102            text_font.clone(),
103        ));
104
105    // Overflow Debug
106
107    commands
108        .spawn(Node {
109            width: Val::Percent(100.),
110            height: Val::Percent(100.),
111            justify_content: JustifyContent::Center,
112            align_items: AlignItems::Center,
113            ..default()
114        })
115        .with_children(|parent| {
116            parent
117                .spawn(Node {
118                    display: Display::Grid,
119                    grid_template_columns: RepeatedGridTrack::px(3, CONTAINER_SIZE),
120                    grid_template_rows: RepeatedGridTrack::px(2, CONTAINER_SIZE),
121                    row_gap: Val::Px(80.),
122                    column_gap: Val::Px(80.),
123                    ..default()
124                })
125                .with_children(|parent| {
126                    spawn_image(parent, &asset_server, Move);
127                    spawn_image(parent, &asset_server, Scale);
128                    spawn_image(parent, &asset_server, Rotate);
129
130                    spawn_text(parent, &asset_server, Move);
131                    spawn_text(parent, &asset_server, Scale);
132                    spawn_text(parent, &asset_server, Rotate);
133                });
134        });
135}
136
137fn spawn_image(
138    parent: &mut ChildSpawnerCommands,
139    asset_server: &Res<AssetServer>,
140    update_transform: impl UpdateTransform + Component,
141) {
142    spawn_container(parent, update_transform, |parent| {
143        parent.spawn((
144            ImageNode::new(asset_server.load("branding/bevy_logo_dark_big.png")),
145            Node {
146                height: Val::Px(100.),
147                position_type: PositionType::Absolute,
148                top: Val::Px(-50.),
149                left: Val::Px(-200.),
150                ..default()
151            },
152        ));
153    });
154}
155
156fn spawn_text(
157    parent: &mut ChildSpawnerCommands,
158    asset_server: &Res<AssetServer>,
159    update_transform: impl UpdateTransform + Component,
160) {
161    spawn_container(parent, update_transform, |parent| {
162        parent.spawn((
163            Text::new("Bevy"),
164            TextFont {
165                font: asset_server.load("fonts/FiraSans-Bold.ttf"),
166                font_size: 100.0,
167                ..default()
168            },
169        ));
170    });
171}
172
173fn spawn_container(
174    parent: &mut ChildSpawnerCommands,
175    update_transform: impl UpdateTransform + Component,
176    spawn_children: impl FnOnce(&mut ChildSpawnerCommands),
177) {
178    let mut transform = Transform::default();
179
180    update_transform.update(0.0, &mut transform);
181
182    parent
183        .spawn((
184            Node {
185                width: Val::Percent(100.),
186                height: Val::Percent(100.),
187                align_items: AlignItems::Center,
188                justify_content: JustifyContent::Center,
189                overflow: Overflow::clip(),
190                ..default()
191            },
192            BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
193            Container(0),
194        ))
195        .with_children(|parent| {
196            parent
197                .spawn((
198                    Node {
199                        align_items: AlignItems::Center,
200                        justify_content: JustifyContent::Center,
201                        top: Val::Px(transform.translation.x),
202                        left: Val::Px(transform.translation.y),
203                        ..default()
204                    },
205                    transform,
206                    update_transform,
207                ))
208                .with_children(spawn_children);
209        });
210}
211
212fn update_animation(
213    mut animation: ResMut<AnimationState>,
214    time: Res<Time>,
215    keys: Res<ButtonInput<KeyCode>>,
216) {
217    let delta = time.elapsed_secs();
218
219    if keys.just_pressed(KeyCode::Space) {
220        animation.playing = !animation.playing;
221
222        if !animation.playing {
223            animation.paused_at = delta;
224        } else {
225            animation.paused_total += delta - animation.paused_at;
226        }
227    }
228
229    if animation.playing {
230        animation.t = (delta - animation.paused_total) % LOOP_LENGTH / LOOP_LENGTH;
231    }
232}
233
234fn update_transform<T: UpdateTransform + Component>(
235    animation: Res<AnimationState>,
236    mut containers: Query<(&mut Transform, &mut Node, &ComputedNode, &T)>,
237) {
238    for (mut transform, mut node, computed_node, update_transform) in &mut containers {
239        update_transform.update(animation.t, &mut transform);
240
241        node.left = Val::Px(transform.translation.x * computed_node.inverse_scale_factor());
242        node.top = Val::Px(transform.translation.y * computed_node.inverse_scale_factor());
243    }
244}
245
246fn toggle_overflow(
247    mut containers: Query<&mut Node, With<Container>>,
248    instructions: Single<Entity, With<Instructions>>,
249    mut writer: TextUiWriter,
250) {
251    for mut node in &mut containers {
252        node.overflow = match node.overflow {
253            Overflow {
254                x: OverflowAxis::Visible,
255                y: OverflowAxis::Visible,
256            } => Overflow::clip_y(),
257            Overflow {
258                x: OverflowAxis::Visible,
259                y: OverflowAxis::Clip,
260            } => Overflow::clip_x(),
261            Overflow {
262                x: OverflowAxis::Clip,
263                y: OverflowAxis::Visible,
264            } => Overflow::clip(),
265            _ => Overflow::visible(),
266        };
267
268        let entity = *instructions;
269        *writer.text(entity, 1) = format!("{:?}", node.overflow);
270    }
271}
272
273fn next_container_size(mut containers: Query<(&mut Node, &mut Container)>) {
274    for (mut node, mut container) in &mut containers {
275        container.0 = (container.0 + 1) % 3;
276
277        node.width = match container.0 {
278            2 => Val::Percent(30.),
279            _ => Val::Percent(100.),
280        };
281        node.height = match container.0 {
282            1 => Val::Percent(30.),
283            _ => Val::Percent(100.),
284        };
285    }
286}