torque_tracker_engine/project/
pattern.rs1use std::ops::{Index, IndexMut};
2
3use crate::project::note_event::NoteEvent;
4use crate::project::Song;
5
6#[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 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 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 pub fn set_length(&mut self, new_len: u16) {
60 assert!(new_len <= Self::MAX_ROWS);
61 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 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 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 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 fn index(&self, index: u16) -> &Self::Output {
136 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 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}