• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Xevion / Pac-Man / 17310465987

28 Aug 2025 11:35PM UTC coverage: 48.731% (-4.0%) from 52.708%
17310465987

push

github

Xevion
fix: add expected MovementModifiers to spawn_test_player to fix movement tests

1114 of 2286 relevant lines covered (48.73%)

1148.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/systems/debug.rs
1
//! Debug rendering system
2
use std::cmp::Ordering;
3

4
use crate::constants::BOARD_PIXEL_OFFSET;
5
use crate::map::builder::Map;
6
use crate::systems::{BackbufferResource, Collider, CursorPosition, Position, SystemTimings};
7
use bevy_ecs::resource::Resource;
8
use bevy_ecs::system::{NonSendMut, Query, Res};
9
use glam::{IVec2, UVec2, Vec2};
10
use sdl2::pixels::Color;
11
use sdl2::rect::{Point, Rect};
12
use sdl2::render::{Canvas, Texture, TextureCreator};
13
use sdl2::ttf::Font;
14
use sdl2::video::{Window, WindowContext};
15

16
#[derive(Resource, Default, Debug, Copy, Clone)]
17
pub struct DebugState {
18
    pub enabled: bool,
19
}
20

21
fn f32_to_u8(value: f32) -> u8 {
×
22
    (value * 255.0) as u8
×
23
}
×
24

25
/// Resource to hold the debug texture for persistent rendering
26
pub struct DebugTextureResource(pub Texture<'static>);
27

28
/// Resource to hold the debug font
29
pub struct DebugFontResource(pub Font<'static, 'static>);
30

31
/// Transforms a position from logical canvas coordinates to output canvas coordinates (with board offset)
32
fn transform_position_with_offset(pos: Vec2, scale: f32) -> IVec2 {
×
33
    ((pos + BOARD_PIXEL_OFFSET.as_vec2()) * scale).as_ivec2()
×
34
}
×
35

36
/// Renders timing information in the top-left corner of the screen
37
fn render_timing_display(
×
38
    canvas: &mut Canvas<Window>,
×
39
    texture_creator: &mut TextureCreator<WindowContext>,
×
40
    timings: &SystemTimings,
×
41
    font: &Font,
×
42
) {
×
43
    // Format timing information using the formatting module
×
44
    let lines = timings.format_timing_display();
×
45
    let line_height = 14; // Approximate line height for 12pt font
×
46
    let padding = 10;
×
47

×
48
    // Calculate background dimensions
×
49
    let max_width = lines
×
50
        .iter()
×
51
        .filter(|l| !l.is_empty()) // Don't consider empty lines for width
×
52
        .map(|line| font.size_of(line).unwrap().0)
×
53
        .max()
×
54
        .unwrap_or(0);
×
55

×
56
    // Only draw background if there is text to display
×
57
    if max_width > 0 {
×
58
        let total_height = (lines.len() as u32) * line_height as u32;
×
59
        let bg_padding = 5;
×
60

×
61
        // Draw background
×
62
        let bg_rect = Rect::new(
×
63
            padding - bg_padding,
×
64
            padding - bg_padding,
×
65
            max_width + (bg_padding * 2) as u32,
×
66
            total_height + bg_padding as u32,
×
67
        );
×
68
        canvas.set_blend_mode(sdl2::render::BlendMode::Blend);
×
69
        canvas.set_draw_color(Color::RGBA(40, 40, 40, 180));
×
70
        canvas.fill_rect(bg_rect).unwrap();
×
71
    }
×
72

73
    for (i, line) in lines.iter().enumerate() {
×
74
        if line.is_empty() {
×
75
            continue;
×
76
        }
×
77

×
78
        // Render each line
×
79
        let surface = font.render(line).blended(Color::RGBA(255, 255, 255, 200)).unwrap();
×
80
        let texture = texture_creator.create_texture_from_surface(&surface).unwrap();
×
81

×
82
        // Position each line below the previous one
×
83
        let y_pos = padding + (i * line_height) as i32;
×
84
        let dest = Rect::new(padding, y_pos, texture.query().width, texture.query().height);
×
85
        canvas.copy(&texture, None, dest).unwrap();
×
86
    }
87
}
×
88

89
#[allow(clippy::too_many_arguments)]
90
pub fn debug_render_system(
×
91
    mut canvas: NonSendMut<&mut Canvas<Window>>,
×
92
    backbuffer: NonSendMut<BackbufferResource>,
×
93
    mut debug_texture: NonSendMut<DebugTextureResource>,
×
94
    debug_font: NonSendMut<DebugFontResource>,
×
95
    debug_state: Res<DebugState>,
×
96
    timings: Res<SystemTimings>,
×
97
    map: Res<Map>,
×
98
    colliders: Query<(&Collider, &Position)>,
×
99
    cursor: Res<CursorPosition>,
×
100
) {
×
101
    if !debug_state.enabled {
×
102
        return;
×
103
    }
×
104
    let scale =
×
105
        (UVec2::from(canvas.output_size().unwrap()).as_vec2() / UVec2::from(canvas.logical_size()).as_vec2()).min_element();
×
106

×
107
    // Copy the current backbuffer to the debug texture
×
108
    canvas
×
109
        .with_texture_canvas(&mut debug_texture.0, |debug_canvas| {
×
110
            // Clear the debug canvas
×
111
            debug_canvas.set_draw_color(Color::BLACK);
×
112
            debug_canvas.clear();
×
113

×
114
            // Copy the backbuffer to the debug canvas
×
115
            debug_canvas.copy(&backbuffer.0, None, None).unwrap();
×
116
        })
×
117
        .unwrap();
×
118

×
119
    // Get texture creator before entering the closure to avoid borrowing conflicts
×
120
    let mut texture_creator = canvas.texture_creator();
×
121
    let font = &debug_font.0;
×
122

123
    let cursor_world_pos = match *cursor {
×
124
        CursorPosition::None => None,
×
125
        CursorPosition::Some { position, .. } => Some(position - BOARD_PIXEL_OFFSET.as_vec2()),
×
126
    };
127

128
    // Draw debug info on the high-resolution debug texture
129
    canvas
×
130
        .with_texture_canvas(&mut debug_texture.0, |debug_canvas| {
×
131
            // Find the closest node to the cursor
132

133
            let closest_node = if let Some(cursor_world_pos) = cursor_world_pos {
×
134
                map.graph
×
135
                    .nodes()
×
136
                    .map(|node| node.position.distance(cursor_world_pos))
×
137
                    .enumerate()
×
138
                    .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Less))
×
139
                    .map(|(id, _)| id)
×
140
            } else {
141
                None
×
142
            };
143

144
            debug_canvas.set_draw_color(Color::GREEN);
×
145
            for (collider, position) in colliders.iter() {
×
146
                let pos = position.get_pixel_position(&map.graph).unwrap();
×
147

×
148
                // Transform position and size using common methods
×
149
                let pos = (pos * scale).as_ivec2();
×
150
                let size = (collider.size * scale) as u32;
×
151

×
152
                let rect = Rect::from_center(Point::from((pos.x, pos.y)), size, size);
×
153
                debug_canvas.draw_rect(rect).unwrap();
×
154
            }
×
155

156
            debug_canvas.set_draw_color(Color {
×
157
                a: f32_to_u8(0.4),
×
158
                ..Color::RED
×
159
            });
×
160
            debug_canvas.set_blend_mode(sdl2::render::BlendMode::Blend);
×
161
            for (start_node, end_node) in map.graph.edges() {
×
162
                let start_node_model = map.graph.get_node(start_node).unwrap();
×
163
                let end_node = map.graph.get_node(end_node.target).unwrap().position;
×
164

×
165
                // Transform positions using common method
×
166
                let start = transform_position_with_offset(start_node_model.position, scale);
×
167
                let end = transform_position_with_offset(end_node, scale);
×
168

×
169
                debug_canvas
×
170
                    .draw_line(Point::from((start.x, start.y)), Point::from((end.x, end.y)))
×
171
                    .unwrap();
×
172
            }
×
173

174
            for (id, node) in map.graph.nodes().enumerate() {
×
175
                let pos = node.position;
×
176

×
177
                // Set color based on whether the node is the closest to the cursor
×
178
                debug_canvas.set_draw_color(Color {
×
179
                    a: f32_to_u8(if Some(id) == closest_node { 0.75 } else { 0.6 }),
×
180
                    ..(if Some(id) == closest_node {
×
181
                        Color::YELLOW
×
182
                    } else {
183
                        Color::BLUE
×
184
                    })
185
                });
186

187
                // Transform position using common method
188
                let pos = transform_position_with_offset(pos, scale);
×
189
                let size = (2.0 * scale) as u32;
×
190

×
191
                debug_canvas
×
192
                    .fill_rect(Rect::new(pos.x - (size as i32 / 2), pos.y - (size as i32 / 2), size, size))
×
193
                    .unwrap();
×
194
            }
195

196
            // Render node ID if a node is highlighted
197
            if let Some(closest_node_id) = closest_node {
×
198
                let node = map.graph.get_node(closest_node_id).unwrap();
×
199
                let pos = transform_position_with_offset(node.position, scale);
×
200

×
201
                let surface = font
×
202
                    .render(&closest_node_id.to_string())
×
203
                    .blended(Color {
×
204
                        a: f32_to_u8(0.4),
×
205
                        ..Color::WHITE
×
206
                    })
×
207
                    .unwrap();
×
208
                let texture = texture_creator.create_texture_from_surface(&surface).unwrap();
×
209
                let dest = Rect::new(pos.x + 10, pos.y - 5, texture.query().width, texture.query().height);
×
210
                debug_canvas.copy(&texture, None, dest).unwrap();
×
211
            }
×
212

213
            // Render timing information in the top-left corner
214
            render_timing_display(debug_canvas, &mut texture_creator, &timings, font);
×
215
        })
×
216
        .unwrap();
×
217

×
218
    // Draw the debug texture directly onto the main canvas at full resolution
×
219
    canvas.copy(&debug_texture.0, None, None).unwrap();
×
220
    canvas.present();
×
221
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc