torque_tracker_engine/project/
pattern.rs

1use std::ops::{Index, IndexMut};
2
3use crate::project::note_event::NoteEvent;
4use crate::project::Song;
5
6/// both row and channel are zero based. If this ever changes a lot of the implementations of
7/// Pattern need to be changed, because the searching starts working differently
8// don't change the Order of fields, as PartialOrd derive depends on it
9#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
10pub struct InPatternPosition {
11    pub row: u16,
12    pub channel: u8,
13}
14
15#[cfg(test)]
16mod test {
17    use crate::project::pattern::InPatternPosition;
18    #[test]
19    fn position_ord() {
20        let one_zero = InPatternPosition { row: 1, channel: 0 };
21        let zero_one = InPatternPosition { row: 0, channel: 1 };
22        assert!(one_zero > zero_one);
23    }
24}
25
26#[derive(Clone, Debug)]
27pub struct Pattern {
28    rows: u16,
29    // Events are sorted with InPatternPosition as the key.
30    data: Vec<(InPatternPosition, NoteEvent)>,
31}
32
33const fn key(data: &(InPatternPosition, NoteEvent)) -> InPatternPosition {
34    data.0
35}
36
37impl Default for Pattern {
38    fn default() -> Self {
39        Self::new(Self::DEFAULT_ROWS)
40    }
41}
42
43impl Pattern {
44    pub const MAX_ROWS: u16 = 200;
45
46    pub const DEFAULT_ROWS: u16 = 64;
47
48    /// panics if len larger than 'Self::MAX_LEN'
49    pub const fn new(len: u16) -> Self {
50        assert!(len <= Self::MAX_ROWS);
51        Self {
52            rows: len,
53            data: Vec::new(),
54        }
55    }
56
57    /// panics it the new len is larger than 'Self::MAX_LEN'
58    /// deletes the data on higher rows
59    pub fn set_length(&mut self, new_len: u16) {
60        assert!(new_len <= Self::MAX_ROWS);
61        // gets the index of the first element of the first row to be removed
62        if new_len < self.rows {
63            let idx = self.data.partition_point(|(pos, _)| pos.row < new_len);
64            self.data.truncate(idx);
65        }
66        self.rows = new_len;
67    }
68
69    /// overwrites the event if the row already has an event for that channel
70    /// panics if the row position is larger than current amount of rows
71    pub fn set_event(&mut self, position: InPatternPosition, event: NoteEvent) {
72        assert!(position.row < self.rows);
73        match self.data.binary_search_by_key(&position, key) {
74            Ok(idx) => self.data[idx].1 = event,
75            Err(idx) => self.data.insert(idx, (position, event)),
76        }
77    }
78
79    pub fn get_event(&self, index: InPatternPosition) -> Option<&NoteEvent> {
80        self.data
81            .binary_search_by_key(&index, key)
82            .ok()
83            .map(|idx| &self.data[idx].1)
84    }
85
86    pub fn get_event_mut(&mut self, index: InPatternPosition) -> Option<&mut NoteEvent> {
87        self.data
88            .binary_search_by_key(&index, key)
89            .ok()
90            .map(|idx| &mut self.data[idx].1)
91    }
92
93    /// if there is no event, does nothing
94    pub fn remove_event(&mut self, position: InPatternPosition) {
95        if let Ok(index) = self.data.binary_search_by_key(&position, key) {
96            self.data.remove(index);
97        }
98    }
99
100    pub const fn row_count(&self) -> u16 {
101        self.rows
102    }
103
104    /// Panics if the Operation is invalid
105    pub fn apply_operation(&mut self, op: PatternOperation) {
106        match op {
107            PatternOperation::SetLength { new_len } => self.set_length(new_len),
108            PatternOperation::SetEvent { position, event } => self.set_event(position, event),
109            PatternOperation::RemoveEvent { position } => self.remove_event(position),
110        }
111    }
112
113    pub const fn operation_is_valid(&self, op: &PatternOperation) -> bool {
114        match op {
115            PatternOperation::SetLength { new_len } => *new_len < Self::MAX_ROWS,
116            PatternOperation::SetEvent { position, event: _ } => {
117                position.row < self.rows && position.channel as usize <= Song::MAX_CHANNELS
118            }
119            PatternOperation::RemoveEvent { position: _ } => true,
120        }
121    }
122
123    pub fn is_empty(&self) -> bool {
124        self.data.is_empty()
125    }
126}
127
128impl Index<u16> for Pattern {
129    type Output = [(InPatternPosition, NoteEvent)];
130
131    /// # Out of Bounds
132    /// Debug: Panic
133    ///
134    /// Release: Empty slice
135    fn index(&self, index: u16) -> &Self::Output {
136        // only a debug assert because if out of bounds the output is simply empty
137        debug_assert!(index <= self.rows);
138        let start_position = self.data.partition_point(|(pos, _)| {
139            *pos < InPatternPosition {
140                row: index,
141                channel: 0,
142            }
143        });
144        // only search after start_position
145        let end_position =
146            self.data[start_position..self.data.len()].partition_point(|(pos, _)| {
147                *pos < InPatternPosition {
148                    row: index + 1,
149                    channel: 0,
150                }
151            }) + start_position;
152        &self.data[start_position..end_position]
153    }
154}
155
156impl Index<InPatternPosition> for Pattern {
157    type Output = NoteEvent;
158
159    fn index(&self, index: InPatternPosition) -> &Self::Output {
160        self.get_event(index).unwrap()
161    }
162}
163
164impl IndexMut<InPatternPosition> for Pattern {
165    fn index_mut(&mut self, index: InPatternPosition) -> &mut Self::Output {
166        self.get_event_mut(index).unwrap()
167    }
168}
169
170#[derive(Debug, Clone, Copy)]
171pub enum PatternOperation {
172    SetLength {
173        new_len: u16,
174    },
175    SetEvent {
176        position: InPatternPosition,
177        event: NoteEvent,
178    },
179    RemoveEvent {
180        position: InPatternPosition,
181    },
182}