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

Xevion / Pac-Man / 17012448262

16 Aug 2025 08:12PM UTC coverage: 38.85% (-12.3%) from 51.196%
17012448262

Pull #3

github

web-flow
Merge 48fd9b912 into 2f1ff85d8
Pull Request #3: ECS Refactor

161 of 1172 new or added lines in 23 files covered. (13.74%)

9 existing lines in 4 files now uncovered.

777 of 2000 relevant lines covered (38.85%)

101.8 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::components::Collider;
7
use crate::systems::input::CursorPosition;
8
use crate::systems::movement::Position;
9
use crate::systems::profiling::SystemTimings;
10
use crate::systems::render::BackbufferResource;
11
use bevy_ecs::prelude::*;
12
use glam::{IVec2, UVec2, Vec2};
13
use sdl2::pixels::Color;
14
use sdl2::rect::{Point, Rect};
15
use sdl2::render::{Canvas, Texture, TextureCreator};
16
use sdl2::ttf::Font;
17
use sdl2::video::{Window, WindowContext};
18

19
#[derive(Resource, Default, Debug, Copy, Clone, PartialEq)]
20
pub enum DebugState {
21
    #[default]
22
    Off,
23
    Graph,
24
    Collision,
25
}
26

27
impl DebugState {
NEW
28
    pub fn next(&self) -> Self {
×
NEW
29
        match self {
×
NEW
30
            DebugState::Off => DebugState::Graph,
×
NEW
31
            DebugState::Graph => DebugState::Collision,
×
NEW
32
            DebugState::Collision => DebugState::Off,
×
33
        }
NEW
34
    }
×
35
}
36

37
/// Resource to hold the debug texture for persistent rendering
38
pub struct DebugTextureResource(pub Texture<'static>);
39

40
/// Resource to hold the debug font
41
pub struct DebugFontResource(pub Font<'static, 'static>);
42

43
/// Transforms a position from logical canvas coordinates to output canvas coordinates (with board offset)
NEW
44
fn transform_position_with_offset(pos: Vec2, scale: f32) -> IVec2 {
×
NEW
45
    ((pos + BOARD_PIXEL_OFFSET.as_vec2()) * scale).as_ivec2()
×
NEW
46
}
×
47

48
/// Renders timing information in the top-left corner of the screen
NEW
49
fn render_timing_display(
×
NEW
50
    canvas: &mut Canvas<Window>,
×
NEW
51
    texture_creator: &mut TextureCreator<WindowContext>,
×
NEW
52
    timings: &SystemTimings,
×
NEW
53
    font: &Font,
×
NEW
54
) {
×
NEW
55
    // Format timing information using the formatting module
×
NEW
56
    let lines = timings.format_timing_display();
×
NEW
57
    let line_height = 14; // Approximate line height for 12pt font
×
NEW
58
    let padding = 10;
×
NEW
59

×
NEW
60
    // Calculate background dimensions
×
NEW
61
    let max_width = lines
×
NEW
62
        .iter()
×
NEW
63
        .filter(|l| !l.is_empty()) // Don't consider empty lines for width
×
NEW
64
        .map(|line| font.size_of(line).unwrap().0)
×
NEW
65
        .max()
×
NEW
66
        .unwrap_or(0);
×
NEW
67

×
NEW
68
    // Only draw background if there is text to display
×
NEW
69
    if max_width > 0 {
×
NEW
70
        let total_height = (lines.len() as u32) * line_height as u32;
×
NEW
71
        let bg_padding = 5;
×
NEW
72

×
NEW
73
        // Draw background
×
NEW
74
        let bg_rect = Rect::new(
×
NEW
75
            padding - bg_padding,
×
NEW
76
            padding - bg_padding,
×
NEW
77
            max_width + (bg_padding * 2) as u32,
×
NEW
78
            total_height + bg_padding as u32,
×
NEW
79
        );
×
NEW
80
        canvas.set_blend_mode(sdl2::render::BlendMode::Blend);
×
NEW
81
        canvas.set_draw_color(Color::RGBA(40, 40, 40, 180));
×
NEW
82
        canvas.fill_rect(bg_rect).unwrap();
×
NEW
83
    }
×
84

NEW
85
    for (i, line) in lines.iter().enumerate() {
×
NEW
86
        if line.is_empty() {
×
NEW
87
            continue;
×
NEW
88
        }
×
NEW
89

×
NEW
90
        // Render each line
×
NEW
91
        let surface = font.render(line).blended(Color::RGBA(255, 255, 255, 200)).unwrap();
×
NEW
92
        let texture = texture_creator.create_texture_from_surface(&surface).unwrap();
×
NEW
93

×
NEW
94
        // Position each line below the previous one
×
NEW
95
        let y_pos = padding + (i * line_height) as i32;
×
NEW
96
        let dest = Rect::new(padding, y_pos, texture.query().width, texture.query().height);
×
NEW
97
        canvas.copy(&texture, None, dest).unwrap();
×
98
    }
NEW
99
}
×
100

101
#[allow(clippy::too_many_arguments)]
NEW
102
pub fn debug_render_system(
×
NEW
103
    mut canvas: NonSendMut<&mut Canvas<Window>>,
×
NEW
104
    backbuffer: NonSendMut<BackbufferResource>,
×
NEW
105
    mut debug_texture: NonSendMut<DebugTextureResource>,
×
NEW
106
    debug_font: NonSendMut<DebugFontResource>,
×
NEW
107
    debug_state: Res<DebugState>,
×
NEW
108
    timings: Res<SystemTimings>,
×
NEW
109
    map: Res<Map>,
×
NEW
110
    colliders: Query<(&Collider, &Position)>,
×
NEW
111
    cursor: Res<CursorPosition>,
×
NEW
112
) {
×
NEW
113
    if *debug_state == DebugState::Off {
×
NEW
114
        return;
×
NEW
115
    }
×
NEW
116
    let scale =
×
NEW
117
        (UVec2::from(canvas.output_size().unwrap()).as_vec2() / UVec2::from(canvas.logical_size()).as_vec2()).min_element();
×
NEW
118

×
NEW
119
    // Copy the current backbuffer to the debug texture
×
NEW
120
    canvas
×
NEW
121
        .with_texture_canvas(&mut debug_texture.0, |debug_canvas| {
×
NEW
122
            // Clear the debug canvas
×
NEW
123
            debug_canvas.set_draw_color(Color::BLACK);
×
NEW
124
            debug_canvas.clear();
×
NEW
125

×
NEW
126
            // Copy the backbuffer to the debug canvas
×
NEW
127
            debug_canvas.copy(&backbuffer.0, None, None).unwrap();
×
NEW
128
        })
×
NEW
129
        .unwrap();
×
NEW
130

×
NEW
131
    // Get texture creator before entering the closure to avoid borrowing conflicts
×
NEW
132
    let mut texture_creator = canvas.texture_creator();
×
NEW
133
    let font = &debug_font.0;
×
134

NEW
135
    let cursor_world_pos = match *cursor {
×
NEW
136
        CursorPosition::None => None,
×
NEW
137
        CursorPosition::Some { position, .. } => Some(position - BOARD_PIXEL_OFFSET.as_vec2()),
×
138
    };
139

140
    // Draw debug info on the high-resolution debug texture
NEW
141
    canvas
×
NEW
142
        .with_texture_canvas(&mut debug_texture.0, |debug_canvas| {
×
NEW
143
            match *debug_state {
×
144
                DebugState::Graph => {
145
                    // Find the closest node to the cursor
146

NEW
147
                    let closest_node = if let Some(cursor_world_pos) = cursor_world_pos {
×
NEW
148
                        map.graph
×
NEW
149
                            .nodes()
×
NEW
150
                            .map(|node| node.position.distance(cursor_world_pos))
×
NEW
151
                            .enumerate()
×
NEW
152
                            .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Less))
×
NEW
153
                            .map(|(id, _)| id)
×
154
                    } else {
NEW
155
                        None
×
156
                    };
157

NEW
158
                    debug_canvas.set_draw_color(Color::RED);
×
NEW
159
                    for (start_node, end_node) in map.graph.edges() {
×
NEW
160
                        let start_node_model = map.graph.get_node(start_node).unwrap();
×
NEW
161
                        let end_node = map.graph.get_node(end_node.target).unwrap().position;
×
NEW
162

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

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

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

×
NEW
175
                        // Set color based on whether the node is the closest to the cursor
×
NEW
176
                        debug_canvas.set_draw_color(if Some(id) == closest_node {
×
NEW
177
                            Color::YELLOW
×
178
                        } else {
NEW
179
                            Color::BLUE
×
180
                        });
181

182
                        // Transform position using common method
NEW
183
                        let pos = transform_position_with_offset(pos, scale);
×
NEW
184
                        let size = (3.0 * scale) as u32;
×
NEW
185

×
NEW
186
                        debug_canvas
×
NEW
187
                            .fill_rect(Rect::new(pos.x - (size as i32 / 2), pos.y - (size as i32 / 2), size, size))
×
NEW
188
                            .unwrap();
×
189
                    }
190

191
                    // Render node ID if a node is highlighted
NEW
192
                    if let Some(closest_node_id) = closest_node {
×
NEW
193
                        let node = map.graph.get_node(closest_node_id).unwrap();
×
NEW
194
                        let pos = transform_position_with_offset(node.position, scale);
×
NEW
195

×
NEW
196
                        let surface = font.render(&closest_node_id.to_string()).blended(Color::WHITE).unwrap();
×
NEW
197
                        let texture = texture_creator.create_texture_from_surface(&surface).unwrap();
×
NEW
198
                        let dest = Rect::new(pos.x + 10, pos.y - 5, texture.query().width, texture.query().height);
×
NEW
199
                        debug_canvas.copy(&texture, None, dest).unwrap();
×
NEW
200
                    }
×
201
                }
202
                DebugState::Collision => {
NEW
203
                    debug_canvas.set_draw_color(Color::GREEN);
×
NEW
204
                    for (collider, position) in colliders.iter() {
×
NEW
205
                        let pos = position.get_pixel_position(&map.graph).unwrap();
×
NEW
206

×
NEW
207
                        // Transform position and size using common methods
×
NEW
208
                        let pos = (pos * scale).as_ivec2();
×
NEW
209
                        let size = (collider.size * scale) as u32;
×
NEW
210

×
NEW
211
                        let rect = Rect::from_center(Point::from((pos.x, pos.y)), size, size);
×
NEW
212
                        debug_canvas.draw_rect(rect).unwrap();
×
NEW
213
                    }
×
214
                }
NEW
215
                _ => {}
×
216
            }
217

218
            // Render timing information in the top-left corner
NEW
219
            render_timing_display(debug_canvas, &mut texture_creator, &timings, font);
×
NEW
220
        })
×
NEW
221
        .unwrap();
×
NEW
222

×
NEW
223
    // Draw the debug texture directly onto the main canvas at full resolution
×
NEW
224
    canvas.copy(&debug_texture.0, None, None).unwrap();
×
NEW
225
}
×
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