sim_lib_pitch_serial/
segment.rs1use sim_lib_pitch_core::PitchClass;
4use sim_lib_pitch_namer_forte::lookup_forte_label;
5use sim_lib_pitch_set::{IntervalVector, PitchClassMask, SetClass, SetEquivalence, classify_set};
6
7use crate::{OrderedIntervalString, RowError, ToneRow, interval::ordered_intervals_vec};
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11pub enum RowSegmentSource {
12 Contiguous {
14 start: usize,
16 len: usize,
18 },
19 Wrapped {
21 start: usize,
23 len: usize,
25 },
26 Indexed,
28}
29
30#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct RowSegment {
33 source: RowSegmentSource,
34 ordinals: Vec<u8>,
35 classes: Vec<PitchClass>,
36 mask: PitchClassMask,
37 interval_vector: IntervalVector,
38 set_class: SetClass,
39 forte_label: Option<&'static str>,
40}
41
42impl RowSegment {
43 pub const fn source(&self) -> &RowSegmentSource {
45 &self.source
46 }
47
48 pub fn ordinals(&self) -> &[u8] {
50 &self.ordinals
51 }
52
53 pub fn classes(&self) -> &[PitchClass] {
55 &self.classes
56 }
57
58 pub const fn mask(&self) -> PitchClassMask {
60 self.mask
61 }
62
63 pub const fn interval_vector(&self) -> IntervalVector {
65 self.interval_vector
66 }
67
68 pub fn set_class(&self) -> &SetClass {
70 &self.set_class
71 }
72
73 pub const fn forte_label(&self) -> Option<&'static str> {
75 self.forte_label
76 }
77
78 pub fn ordered_intervals(&self) -> Vec<u8> {
80 ordered_intervals_vec(&self.classes)
81 }
82
83 pub(crate) fn new(
84 source: RowSegmentSource,
85 ordinals: Vec<u8>,
86 classes: Vec<PitchClass>,
87 ) -> Self {
88 let mask = PitchClassMask::from_pitch_classes(&classes);
89 let set_class = classify_set(mask, SetEquivalence::TranspositionInversion);
90 Self {
91 source,
92 ordinals,
93 classes,
94 mask,
95 interval_vector: mask.interval_vector(),
96 forte_label: lookup_forte_label(mask),
97 set_class,
98 }
99 }
100}
101
102impl ToneRow {
103 pub fn segment(&self, start: usize, len: usize) -> Result<RowSegment, RowError> {
105 if start > self.classes().len() || start + len > self.classes().len() {
106 return Err(RowError::SegmentOutOfBounds { start, len });
107 }
108 let ordinals = (start..start + len).map(|ordinal| ordinal as u8).collect();
109 let classes = self.classes()[start..start + len].to_vec();
110 Ok(RowSegment::new(
111 RowSegmentSource::Contiguous { start, len },
112 ordinals,
113 classes,
114 ))
115 }
116
117 pub fn wrapped_segment(&self, start: usize, len: usize) -> Result<RowSegment, RowError> {
119 if start >= self.classes().len() {
120 return Err(RowError::InvalidOrdinal { ordinal: start });
121 }
122 if len > self.classes().len() {
123 return Err(RowError::WrappedSegmentTooLong { len });
124 }
125 let ordinals = (0..len)
126 .map(|offset| ((start + offset) % self.classes().len()) as u8)
127 .collect::<Vec<_>>();
128 let classes = ordinals
129 .iter()
130 .map(|ordinal| self.classes()[usize::from(*ordinal)])
131 .collect();
132 Ok(RowSegment::new(
133 RowSegmentSource::Wrapped { start, len },
134 ordinals,
135 classes,
136 ))
137 }
138
139 pub fn indexed_segment(&self, ordinals: &[usize]) -> Result<RowSegment, RowError> {
141 let mut classes = Vec::with_capacity(ordinals.len());
142 let mut stored_ordinals = Vec::with_capacity(ordinals.len());
143 for ordinal in ordinals {
144 let Some(class) = self.classes().get(*ordinal).copied() else {
145 return Err(RowError::InvalidOrdinal { ordinal: *ordinal });
146 };
147 classes.push(class);
148 stored_ordinals.push(*ordinal as u8);
149 }
150 Ok(RowSegment::new(
151 RowSegmentSource::Indexed,
152 stored_ordinals,
153 classes,
154 ))
155 }
156
157 pub fn ordered_intervals(&self) -> OrderedIntervalString {
159 OrderedIntervalString::of_row(self)
160 }
161}