1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// ComparisonView - Constructor, frame accessors, navigation methods
impl ComparisonView {
/// Create a new comparison view from two recordings
///
/// # Example
/// ```ignore
/// use pmat::services::dap::{Recording, ComparisonView};
///
/// let recording_a = Recording::new("program_a".to_string(), vec![]);
/// let recording_b = Recording::new("program_b".to_string(), vec![]);
///
/// let comparison = ComparisonView::new(recording_a, recording_b);
/// ```
pub fn new(recording_a: Recording, recording_b: Recording) -> Self {
let name_a = recording_a.metadata().program.clone();
let name_b = recording_b.metadata().program.clone();
Self {
player_a: TimelinePlayer::new(recording_a),
player_b: TimelinePlayer::new(recording_b),
sync_mode: SyncMode::ByFrame,
name_a,
name_b,
}
}
/// Get current frame number for recording A
pub fn current_frame_a(&self) -> usize {
self.player_a.current_frame()
}
/// Get current frame number for recording B
pub fn current_frame_b(&self) -> usize {
self.player_b.current_frame()
}
/// Get total frames for recording A
pub fn total_frames_a(&self) -> usize {
self.player_a.total_frames()
}
/// Get total frames for recording B
pub fn total_frames_b(&self) -> usize {
self.player_b.total_frames()
}
/// Get minimum frame count of both recordings
pub fn total_frames_min(&self) -> usize {
self.total_frames_a().min(self.total_frames_b())
}
/// Get maximum frame count of both recordings
pub fn total_frames_max(&self) -> usize {
self.total_frames_a().max(self.total_frames_b())
}
/// Check if recording A is exhausted (at or past its last frame)
pub fn recording_a_exhausted(&self) -> bool {
let total = self.total_frames_a();
if total == 0 {
return true;
}
self.current_frame_a() >= total - 1
}
/// Check if recording B is exhausted (at or past its last frame)
pub fn recording_b_exhausted(&self) -> bool {
let total = self.total_frames_b();
if total == 0 {
return true;
}
self.current_frame_b() >= total - 1
}
/// Get current synchronization mode
pub fn sync_mode(&self) -> SyncMode {
self.sync_mode
}
/// Set synchronization mode
pub fn set_sync_mode(&mut self, mode: SyncMode) {
self.sync_mode = mode;
}
/// Advance both recordings to next frame
///
/// Returns Ok(()) if successful, Err if either recording is exhausted
pub fn next_frame(&mut self) -> Result<()> {
// Attempt to advance both
let a_result = self.player_a.next_frame();
let b_result = self.player_b.next_frame();
// If either failed, return error
if a_result.is_none() && b_result.is_none() {
anyhow::bail!("Both recordings exhausted");
}
Ok(())
}
/// Move both recordings to previous frame
///
/// Returns Ok(()) if successful, Err if either recording is at frame 0
pub fn prev_frame(&mut self) -> Result<()> {
// Attempt to move both back
let a_result = self.player_a.prev_frame();
let b_result = self.player_b.prev_frame();
// If both failed (at start), return error
if a_result.is_none() && b_result.is_none() {
anyhow::bail!("Both recordings at start");
}
Ok(())
}
/// Jump both recordings to specific frame
///
/// Returns Ok(()) if successful, Err if frame is out of bounds for both
pub fn jump_to(&mut self, frame: usize) -> Result<()> {
// Jump both players
let a_result = self.player_a.jump_to(frame);
let b_result = self.player_b.jump_to(frame);
// If both failed, return error
if a_result.is_err() && b_result.is_err() {
anyhow::bail!("Frame {} out of bounds for both recordings", frame);
}
Ok(())
}
}