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

Xevion / Pac-Man / 17311423744

29 Aug 2025 12:40AM UTC coverage: 48.753% (+0.02%) from 48.731%
17311423744

push

github

Xevion
feat: use backbuffer fully, proper 'present' system, debug texture draws with transparency

0 of 61 new or added lines in 3 files covered. (0.0%)

2 existing lines in 2 files now uncovered.

1114 of 2285 relevant lines covered (48.75%)

1148.76 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::{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
    mut debug_texture: NonSendMut<DebugTextureResource>,
×
93
    debug_font: NonSendMut<DebugFontResource>,
×
94
    debug_state: Res<DebugState>,
×
95
    timings: Res<SystemTimings>,
×
96
    map: Res<Map>,
×
97
    colliders: Query<(&Collider, &Position)>,
×
98
    cursor: Res<CursorPosition>,
×
99
) {
×
100
    if !debug_state.enabled {
×
101
        return;
×
102
    }
×
103
    let scale =
×
104
        (UVec2::from(canvas.output_size().unwrap()).as_vec2() / UVec2::from(canvas.logical_size()).as_vec2()).min_element();
×
105

×
106
    // Get texture creator before entering the closure to avoid borrowing conflicts
×
107
    let mut texture_creator = canvas.texture_creator();
×
108
    let font = &debug_font.0;
×
109

110
    let cursor_world_pos = match *cursor {
×
111
        CursorPosition::None => None,
×
112
        CursorPosition::Some { position, .. } => Some(position - BOARD_PIXEL_OFFSET.as_vec2()),
×
113
    };
114

115
    // Draw debug info on the high-resolution debug texture
116
    canvas
×
117
        .with_texture_canvas(&mut debug_texture.0, |debug_canvas| {
×
NEW
118
            // Clear the debug canvas
×
NEW
119
            debug_canvas.set_draw_color(Color::RGBA(0, 0, 0, 0));
×
NEW
120
            debug_canvas.clear();
×
121

122
            // Find the closest node to the cursor
123
            let closest_node = if let Some(cursor_world_pos) = cursor_world_pos {
×
124
                map.graph
×
125
                    .nodes()
×
126
                    .map(|node| node.position.distance(cursor_world_pos))
×
127
                    .enumerate()
×
128
                    .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Less))
×
129
                    .map(|(id, _)| id)
×
130
            } else {
131
                None
×
132
            };
133

134
            debug_canvas.set_draw_color(Color::GREEN);
×
135
            for (collider, position) in colliders.iter() {
×
136
                let pos = position.get_pixel_position(&map.graph).unwrap();
×
137

×
138
                // Transform position and size using common methods
×
139
                let pos = (pos * scale).as_ivec2();
×
140
                let size = (collider.size * scale) as u32;
×
141

×
142
                let rect = Rect::from_center(Point::from((pos.x, pos.y)), size, size);
×
143
                debug_canvas.draw_rect(rect).unwrap();
×
144
            }
×
145

146
            debug_canvas.set_draw_color(Color {
×
147
                a: f32_to_u8(0.4),
×
148
                ..Color::RED
×
149
            });
×
150
            debug_canvas.set_blend_mode(sdl2::render::BlendMode::Blend);
×
151
            for (start_node, end_node) in map.graph.edges() {
×
152
                let start_node_model = map.graph.get_node(start_node).unwrap();
×
153
                let end_node = map.graph.get_node(end_node.target).unwrap().position;
×
154

×
155
                // Transform positions using common method
×
156
                let start = transform_position_with_offset(start_node_model.position, scale);
×
157
                let end = transform_position_with_offset(end_node, scale);
×
158

×
159
                debug_canvas
×
160
                    .draw_line(Point::from((start.x, start.y)), Point::from((end.x, end.y)))
×
161
                    .unwrap();
×
162
            }
×
163

164
            for (id, node) in map.graph.nodes().enumerate() {
×
165
                let pos = node.position;
×
166

×
167
                // Set color based on whether the node is the closest to the cursor
×
168
                debug_canvas.set_draw_color(Color {
×
169
                    a: f32_to_u8(if Some(id) == closest_node { 0.75 } else { 0.6 }),
×
170
                    ..(if Some(id) == closest_node {
×
171
                        Color::YELLOW
×
172
                    } else {
173
                        Color::BLUE
×
174
                    })
175
                });
176

177
                // Transform position using common method
178
                let pos = transform_position_with_offset(pos, scale);
×
179
                let size = (2.0 * scale) as u32;
×
180

×
181
                debug_canvas
×
182
                    .fill_rect(Rect::new(pos.x - (size as i32 / 2), pos.y - (size as i32 / 2), size, size))
×
183
                    .unwrap();
×
184
            }
185

186
            // Render node ID if a node is highlighted
187
            if let Some(closest_node_id) = closest_node {
×
188
                let node = map.graph.get_node(closest_node_id).unwrap();
×
189
                let pos = transform_position_with_offset(node.position, scale);
×
190

×
191
                let surface = font
×
192
                    .render(&closest_node_id.to_string())
×
193
                    .blended(Color {
×
194
                        a: f32_to_u8(0.4),
×
195
                        ..Color::WHITE
×
196
                    })
×
197
                    .unwrap();
×
198
                let texture = texture_creator.create_texture_from_surface(&surface).unwrap();
×
199
                let dest = Rect::new(pos.x + 10, pos.y - 5, texture.query().width, texture.query().height);
×
200
                debug_canvas.copy(&texture, None, dest).unwrap();
×
201
            }
×
202

203
            // Render timing information in the top-left corner
204
            render_timing_display(debug_canvas, &mut texture_creator, &timings, font);
×
205
        })
×
206
        .unwrap();
×
207
}
×
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