1use std::cmp::{Ordering, max};
4use std::collections::{HashMap, HashSet, hash_set};
5use std::hash::{Hash, Hasher};
6use std::iter::Extend;
7use std::num::FpCategory;
8use std::ops::{Add, Index, RangeBounds};
9use std::rc::Rc;
10use std::slice::SliceIndex;
11
12#[derive(Debug, Clone, Copy, PartialEq)]
14pub struct Shaft(pub u32);
15
16impl Default for Shaft {
17 fn default() -> Shaft {
18 Shaft(1)
19 }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq)]
24pub struct Treadle(pub u32);
25impl Default for Treadle {
26 fn default() -> Treadle {
27 Treadle(1)
28 }
29}
30
31#[derive(Debug, PartialEq, Clone)]
33pub struct Threading {
34 shaft_count: u32,
35 threading: Vec<u32>,
36}
37
38impl Default for Threading {
39 fn default() -> Threading {
40 Threading {
41 shaft_count: 2,
42 threading: Vec::default(),
43 }
44 }
45}
46
47impl<R> Index<R> for Threading
48where
49 R: SliceIndex<[u32]>,
50{
51 type Output = R::Output;
52
53 fn index(&self, index: R) -> &Self::Output {
54 &self.threading[index]
55 }
56}
57
58impl Threading {
59 #[must_use]
64 pub fn new(shaft_count: u32, threading: Vec<u32>) -> Self {
65 for shaft in &threading {
66 assert!(
67 shaft <= &shaft_count,
68 "shaft count is {shaft_count} but found shaft {shaft}"
69 );
70 }
71
72 Threading {
73 shaft_count,
74 threading,
75 }
76 }
77
78 fn validate(&self, other: &[u32]) -> Result<(), usize> {
79 let index = other.iter().position(|s| s > &self.shaft_count);
80 match index {
81 None => Ok(()),
82 Some(i) => Err(i),
83 }
84 }
85
86 pub fn splice<R>(&mut self, range: R, replace_with: &[u32]) -> Result<Vec<u32>, usize>
106 where
107 R: RangeBounds<usize>,
108 {
109 self.validate(replace_with)?;
110 let replaced: Vec<u32> = self
111 .threading
112 .splice(range, replace_with.to_owned())
113 .collect();
114
115 Ok(replaced)
116 }
117
118 #[must_use]
120 pub fn len(&self) -> usize {
121 self.threading.len()
122 }
123
124 #[must_use]
126 pub fn is_empty(&self) -> bool {
127 self.threading.is_empty()
128 }
129
130 #[must_use]
132 pub fn threading(&self) -> &Vec<u32> {
133 &self.threading
134 }
135
136 pub fn push(&mut self, shaft: u32) -> Result<(), u32> {
140 if shaft > self.shaft_count {
141 return Err(shaft);
142 }
143 self.threading.push(shaft);
144 Ok(())
145 }
146
147 pub fn insert(&mut self, shaft: Shaft, index: usize) -> Result<(), Shaft> {
155 if shaft.0 > self.shaft_count {
156 return Err(shaft);
157 }
158 self.threading.insert(index, shaft.0);
159 Ok(())
160 }
161
162 pub fn try_insert(&mut self, shaft: Shaft, index: usize) -> Result<Result<(), Shaft>, usize> {
167 let len = self.threading.len();
168 if index > len {
169 Err(len)
170 } else {
171 Ok(self.insert(shaft, index))
172 }
173 }
174
175 pub fn remove(&mut self, index: usize) -> Shaft {
180 Shaft(self.threading.remove(index))
181 }
182
183 #[must_use]
185 pub fn get(&self, index: usize) -> Option<&u32> {
186 self.threading.get(index)
187 }
188
189 pub fn put(&mut self, index: usize, shaft: Shaft) -> Shaft {
194 let old = self.threading[index];
195 self.threading[index] = shaft.0;
196 Shaft(old)
197 }
198
199 pub fn try_put(&mut self, index: usize, shaft: Shaft) -> Result<Option<Shaft>, usize> {
205 let len = self.threading.len();
206 match index.cmp(&len) {
207 Ordering::Less => Ok(Some(self.put(index, shaft))),
208 Ordering::Equal => {
209 self.threading.push(shaft.0);
210 Ok(None)
211 }
212 Ordering::Greater => Err(len),
213 }
214 }
215
216 #[must_use]
218 pub fn max_shaft(&self) -> u32 {
219 let max = self.threading.iter().max();
220 *max.unwrap_or(&0)
221 }
222
223 pub fn set_shaft_count(&mut self, shaft_count: u32) -> Result<(), usize> {
231 if shaft_count == 0 {
232 panic!("shaft count is 0");
233 } else if shaft_count >= self.max_shaft() {
234 self.shaft_count = shaft_count;
235 Ok(())
236 } else {
237 let pos = self
239 .threading
240 .iter()
241 .position(|s| s > &shaft_count)
242 .unwrap();
243 Err(pos)
244 }
245 }
246
247 pub fn trim_shafts(&mut self) -> &Self {
250 if let Some(&max) = self.threading.iter().max() {
251 if max < self.shaft_count {
252 self.shaft_count = max;
253 }
254 }
255 self
256 }
257
258 #[must_use]
268 pub fn used_shafts(&self) -> HashSet<u32> {
269 let mut set = HashSet::new();
270 for shaft in &self.threading {
271 set.insert(*shaft);
272 }
273 set
274 }
275
276 #[allow(clippy::cast_possible_truncation)]
278 #[allow(clippy::missing_panics_doc)]
279 pub fn trim_and_squish_shafts(&mut self) -> &Self {
280 let mut used_shafts: Vec<u32> = self.used_shafts().into_iter().collect();
281 used_shafts.sort_unstable();
282 self.shaft_count = used_shafts.len() as u32;
283 let mapping: HashMap<u32, u32> = used_shafts
284 .into_iter()
285 .enumerate()
286 .map(|(i, s)| (s, i as u32 + 1))
287 .collect();
288
289 self.threading
290 .iter_mut()
291 .for_each(|s| *s = *mapping.get(s).unwrap());
293 self
294 }
295
296 pub fn flip_vertical(&mut self) -> &Self {
299 for thread in &mut self.threading {
300 *thread = self.shaft_count - *thread;
301 }
302 self
303 }
304
305 pub fn mirror(&mut self) -> &Self {
314 if self.threading.len() <= 1 {
315 return self;
316 }
317 let mirror_section = self.threading[..(self.threading.len() - 1)].to_vec();
318 self.threading.extend(mirror_section.iter().rev());
319
320 self
321 }
322
323 pub fn reverse(&mut self) -> &Self {
325 self.threading.reverse();
326 self
327 }
328}
329
330impl IntoIterator for Threading {
331 type Item = u32;
332 type IntoIter = std::vec::IntoIter<Self::Item>;
333
334 fn into_iter(self) -> Self::IntoIter {
335 self.threading.into_iter()
336 }
337}
338
339impl Add for &Threading {
340 type Output = Threading;
341
342 fn add(self, rhs: Self) -> Self::Output {
343 let mut threading = self.threading.clone();
344 threading.extend(&rhs.threading);
345 Threading {
346 shaft_count: max(self.shaft_count, rhs.shaft_count),
347 threading,
348 }
349 }
350}
351
352impl Add<Threading> for &Threading {
353 type Output = Threading;
354
355 fn add(self, rhs: Threading) -> Self::Output {
356 let mut threading = self.threading.clone();
357 threading.extend(rhs.threading);
358 Threading {
359 shaft_count: max(self.shaft_count, rhs.shaft_count),
360 threading,
361 }
362 }
363}
364
365impl Add for Threading {
366 type Output = Self;
367
368 fn add(self, rhs: Self) -> Self::Output {
369 let mut threading = self.threading;
370 threading.extend(rhs.threading);
371
372 Threading {
373 shaft_count: max(self.shaft_count, rhs.shaft_count),
374 threading,
375 }
376 }
377}
378
379impl Add<&Threading> for Threading {
380 type Output = Threading;
381
382 fn add(self, rhs: &Threading) -> Self::Output {
383 let mut threading = self.threading;
384 threading.extend(&rhs.threading);
385
386 Threading {
387 shaft_count: max(self.shaft_count, rhs.shaft_count),
388 threading,
389 }
390 }
391}
392
393#[derive(Debug, PartialEq, Clone)]
396pub struct TreadlingInfo {
397 shaft_count: u32,
398 rise_sink: RiseSink,
399 tie_up: TieUpKind,
400 treadling: Treadling,
401}
402
403impl Default for TreadlingInfo {
404 fn default() -> Self {
405 TreadlingInfo {
406 shaft_count: 2, rise_sink: RiseSink::default(),
408 tie_up: TieUpKind::default(),
409 treadling: Treadling::default(),
410 }
411 }
412}
413
414#[derive(Debug, Clone, Copy, PartialEq)]
416pub enum TieUpCreate {
417 Direct,
419 Indirect(u32),
421}
422
423impl TreadlingInfo {
424 #[must_use]
429 pub fn new(shaft_count: u32, tie_up: TieUpCreate, rise_sink: RiseSink) -> Self {
430 assert_ne!(shaft_count, 0, "shaft count is 0");
431 let tie_up = match tie_up {
432 TieUpCreate::Direct => TieUpKind::Direct,
433 TieUpCreate::Indirect(treadles) => {
434 assert_ne!(treadles, 0, "treadle count is 0");
435
436 TieUpKind::Indirect(TieUp {
437 treadle_count: treadles,
438 tie_up: vec![HashSet::new(); treadles as usize],
439 })
440 }
441 };
442
443 TreadlingInfo {
444 shaft_count,
445 tie_up,
446 treadling: Treadling::new(),
447 rise_sink,
448 }
449 }
450
451 #[must_use]
453 pub fn shaft_count(&self) -> u32 {
454 self.shaft_count
455 }
456
457 #[must_use]
461 pub fn max_shaft_used(&self) -> u32 {
462 match &self.tie_up {
463 TieUpKind::Direct => self.treadling.max_shaft(),
464 TieUpKind::Indirect(tie_up) => tie_up.max_shaft(),
465 }
466 }
467
468 pub fn set_shaft_count(&mut self, shaft_count: u32) -> Result<(), u32> {
473 let max = self.max_shaft_used();
474 if shaft_count >= max {
475 self.shaft_count = shaft_count;
476 Ok(())
477 } else {
478 Err(max)
479 }
480 }
481
482 #[must_use]
484 pub fn tie_up(&self) -> &TieUpKind {
485 &self.tie_up
486 }
487
488 #[must_use]
490 pub fn treadle_count(&self) -> u32 {
491 match self.tie_up {
492 TieUpKind::Direct => self.shaft_count,
493 TieUpKind::Indirect(TieUp { treadle_count, .. }) => treadle_count,
494 }
495 }
496
497 #[must_use]
499 pub fn rise_sink(&self) -> RiseSink {
500 self.rise_sink
501 }
502
503 #[must_use]
505 pub fn len(&self) -> usize {
506 self.treadling.0.len()
507 }
508
509 #[must_use]
511 pub fn is_empty(&self) -> bool {
512 self.treadling.0.len() == 0
513 }
514
515 pub fn push_single(&mut self, treadle: u32) -> Result<(), u32> {
520 if treadle > self.treadle_count() {
521 return Err(treadle);
522 } else if treadle == 0 {
523 self.treadling.0.push(HashSet::new());
525 } else {
526 self.treadling.0.push(HashSet::from([treadle]));
527 }
528 Ok(())
529 }
530
531 fn validate_treadle(&self, treadle: u32) -> Result<(), u32> {
532 if treadle == 0 || treadle > self.treadle_count() {
533 Err(treadle)
534 } else {
535 Ok(())
536 }
537 }
538
539 fn validate(&self, treadles: &HashSet<u32>) -> Result<(), u32> {
540 let t_count = self.treadle_count();
541 match treadles.iter().find(|&&t| t == 0 || t > t_count) {
542 None => Ok(()),
543 Some(&t) => Err(t),
544 }
545 }
546
547 pub fn push(&mut self, treadles: HashSet<u32>) -> Result<(), u32> {
552 self.validate(&treadles)?;
553 self.treadling.0.push(treadles);
554
555 Ok(())
556 }
557
558 pub fn toggle_treadle(&mut self, index: usize, treadle: Treadle) -> Result<bool, u32> {
565 self.validate_treadle(treadle.0)?;
566 let pick = &mut self.treadling.0[index];
567 if pick.contains(&treadle.0) {
568 pick.remove(&treadle.0);
569 Ok(false)
570 } else {
571 pick.insert(treadle.0);
572 Ok(true)
573 }
574 }
575
576 pub fn insert(&mut self, index: usize, treadles: HashSet<u32>) -> Result<(), u32> {
583 self.validate(&treadles)?;
584 self.treadling.0.insert(index, treadles);
585
586 Ok(())
587 }
588
589 pub fn splice<R>(
596 &mut self,
597 range: R,
598 replace_with: Vec<HashSet<u32>>,
599 ) -> Result<Vec<HashSet<u32>>, u32>
600 where
601 R: RangeBounds<usize>,
602 {
603 for pick in &replace_with {
604 self.validate(pick)?;
605 }
606
607 let replaced: Vec<HashSet<u32>> = self.treadling.0.splice(range, replace_with).collect();
608
609 Ok(replaced)
610 }
611
612 pub fn put(
620 &mut self,
621 index: usize,
622 treadles: HashSet<u32>,
623 ) -> Result<Option<HashSet<u32>>, u32> {
624 self.validate(&treadles)?;
625
626 match index.cmp(&self.len()) {
627 Ordering::Less => {
628 let old = self.treadling.0[index].clone();
629 self.treadling.0[index] = treadles;
630 Ok(Some(old))
631 }
632 Ordering::Equal => {
633 self.treadling.0.push(treadles);
634 Ok(None)
635 }
636 Ordering::Greater => {
637 panic!("Index {index} out of bounds")
638 }
639 }
640 }
641
642 pub fn make_rising(&mut self) {
644 match self.rise_sink {
645 RiseSink::Rising => (),
646 RiseSink::Sinking => self.invert(),
647 }
648 }
649
650 pub fn make_sinking(&mut self) {
652 match self.rise_sink {
653 RiseSink::Rising => self.invert(),
654 RiseSink::Sinking => (),
655 }
656 }
657
658 pub fn make_lift_plan(&mut self) -> bool {
661 match &mut self.tie_up {
662 TieUpKind::Direct => false,
663 TieUpKind::Indirect(tie_up) => {
664 for entry in &mut self.treadling.0 {
665 *entry = tie_up.compute_shafts(entry);
666 }
667 self.tie_up = TieUpKind::Direct;
668 true
669 }
670 }
671 }
672
673 pub fn invert(&mut self) {
675 match &mut self.tie_up {
676 TieUpKind::Direct => self.treadling.invert(self.shaft_count),
677 TieUpKind::Indirect(tie_up) => tie_up.invert(self.shaft_count),
678 }
679
680 self.rise_sink = self.rise_sink.invert();
681 }
682}
683
684impl Index<usize> for TreadlingInfo {
685 type Output = HashSet<u32>;
686 fn index(&self, index: usize) -> &Self::Output {
687 &self.treadling[index]
688 }
689}
690
691fn invert(set: &HashSet<u32>, max: u32) -> HashSet<u32> {
692 assert_ne!(max, 0, "cannot invert when max is 0");
693 let inversion = (1..=max).collect::<HashSet<u32>>();
694
695 &inversion - set
696}
697
698#[derive(Debug, Clone, PartialEq, Default)]
700pub enum TieUpKind {
701 #[default]
703 Direct,
704 Indirect(TieUp),
706}
707
708impl TieUpKind {
709 #[must_use]
711 pub fn tie_up(&self) -> Option<&TieUp> {
712 match self {
713 TieUpKind::Direct => None,
714 TieUpKind::Indirect(tie_up) => Some(tie_up),
715 }
716 }
717
718 #[must_use]
720 pub fn raw_tie_up(&self) -> Option<&Vec<HashSet<u32>>> {
721 match self {
722 TieUpKind::Direct => None,
723 TieUpKind::Indirect(tie_up) => Some(&tie_up.tie_up),
724 }
725 }
726}
727
728#[derive(Debug, Clone, PartialEq)]
730pub struct TieUp {
731 treadle_count: u32,
732 tie_up: Vec<HashSet<u32>>,
735}
736
737impl TieUp {
738 #[must_use]
740 pub fn new(treadle_count: u32) -> Self {
741 TieUp {
742 treadle_count,
743 tie_up: vec![HashSet::new(); treadle_count as usize],
744 }
745 }
746 fn invert(&mut self, shaft_count: u32) {
747 self.tie_up
748 .iter_mut()
749 .for_each(|t| *t = invert(t, shaft_count));
750 }
751
752 fn max_shaft(&self) -> u32 {
753 max_vec_hash(&self.tie_up)
754 }
755
756 #[must_use]
760 pub fn get_shafts(&self, treadle: &u32) -> Option<&HashSet<u32>> {
761 self.tie_up.get((treadle - 1) as usize)
762 }
763
764 #[must_use]
766 pub fn compute_shafts(&self, treadles: &HashSet<u32>) -> HashSet<u32> {
767 let mut shafts = HashSet::new();
768 for treadle in treadles {
769 if let Some(tied_shafts) = self.get_shafts(treadle) {
770 for shaft in tied_shafts {
771 shafts.insert(*shaft);
772 }
773 }
774 }
775 shafts
776 }
777}
778
779#[derive(Debug, Clone, PartialEq, Copy, Default)]
781pub enum RiseSink {
782 #[default]
784 Rising,
785 Sinking,
787}
788
789impl RiseSink {
790 #[must_use]
792 pub fn invert(self) -> Self {
793 match self {
794 RiseSink::Rising => Self::Sinking,
795 RiseSink::Sinking => Self::Rising,
796 }
797 }
798}
799
800#[derive(Debug, Clone, PartialEq, Default)]
802pub struct Treadling(Vec<HashSet<u32>>);
803
804fn max_vec_hash(vec: &[HashSet<u32>]) -> u32 {
805 *vec.iter()
806 .map(|s| s.iter().max().unwrap_or(&0))
807 .max()
808 .unwrap_or(&0)
809}
810
811impl Treadling {
812 fn new() -> Treadling {
813 Treadling(Vec::new())
814 }
815
816 fn invert(&mut self, shaft_count: u32) {
817 self.0.iter_mut().for_each(|t| *t = invert(t, shaft_count));
818 }
819
820 fn max_shaft(&self) -> u32 {
821 max_vec_hash(&self.0)
822 }
823}
824
825impl Add for Treadling {
826 type Output = Treadling;
827
828 fn add(self, rhs: Self) -> Self::Output {
829 let mut new = self.0;
830 new.extend(rhs.0);
831 Treadling(new)
832 }
833}
834
835impl Add<&Treadling> for Treadling {
836 type Output = Treadling;
837 fn add(self, rhs: &Treadling) -> Self::Output {
838 let mut new = self.0;
839 new.extend_from_slice(&rhs.0);
840
841 Treadling(new)
842 }
843}
844
845impl Add for &Treadling {
846 type Output = Treadling;
847
848 fn add(self, rhs: Self) -> Self::Output {
849 let mut new = self.0.clone();
850 new.extend_from_slice(&rhs.0);
851 Treadling(new)
852 }
853}
854
855impl Add<Treadling> for &Treadling {
856 type Output = Treadling;
857 fn add(self, rhs: Treadling) -> Self::Output {
858 let mut new = self.0.clone();
859 new.extend(rhs.0);
860 Treadling(new)
861 }
862}
863
864impl<S> Index<S> for Treadling
865where
866 S: SliceIndex<[HashSet<u32>]>,
867{
868 type Output = S::Output;
869 fn index(&self, index: S) -> &Self::Output {
870 &self.0[index]
871 }
872}
873
874#[derive(Debug, Clone, PartialEq, Default)]
876pub struct YarnPalette(HashSet<Rc<Yarn>>);
877
878impl YarnPalette {
879 #[must_use]
881 pub fn new() -> YarnPalette {
882 YarnPalette(HashSet::new())
883 }
884
885 #[must_use]
887 pub fn len(&self) -> usize {
888 self.0.len()
889 }
890
891 #[must_use]
893 pub fn is_empty(&self) -> bool {
894 self.0.is_empty()
895 }
896
897 pub fn remove_unused_yarns(&mut self) -> Vec<Yarn> {
913 let mut to_remove = vec![];
914
915 for yarn in &self.0 {
916 if Rc::strong_count(yarn) == 1 {
917 to_remove.push(Rc::clone(yarn));
918 }
919 }
920
921 to_remove
922 .into_iter()
923 .map(|yarn| {
924 self.0.remove(&yarn);
925 Rc::unwrap_or_clone(yarn)
927 })
928 .collect()
929 }
930
931 #[allow(clippy::missing_panics_doc)]
933 pub fn use_yarn(&mut self, yarn: Yarn) -> Rc<Yarn> {
934 if self.0.contains(&yarn) {
935 Rc::clone(self.0.get(&yarn).unwrap())
936 } else {
937 let yarn = Rc::new(yarn);
938 self.0.insert(Rc::clone(&yarn));
939 yarn
940 }
941 }
942
943 pub fn use_yarns<T>(&mut self, yarns: T) -> Vec<Rc<Yarn>>
945 where
946 T: IntoIterator<Item = Yarn>,
947 {
948 yarns.into_iter().map(|yarn| self.use_yarn(yarn)).collect()
949 }
950
951 #[must_use]
953 pub fn iter(&self) -> hash_set::Iter<Rc<Yarn>> {
954 self.0.iter()
955 }
956}
957
958impl<'a> IntoIterator for &'a YarnPalette {
959 type Item = &'a Rc<Yarn>;
960 type IntoIter = hash_set::Iter<'a, Rc<Yarn>>;
961
962 fn into_iter(self) -> Self::IntoIter {
963 self.0.iter()
964 }
965}
966
967#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
969pub struct Yarn {
970 name: Option<String>,
971 color: Color,
972 thickness: Thickness, }
974
975impl Yarn {
976 #[must_use]
978 pub fn name(&self) -> &Option<String> {
979 &self.name
980 }
981
982 #[must_use]
984 pub fn color(&self) -> &Color {
985 &self.color
986 }
987
988 #[must_use]
990 pub fn thickness(&self) -> &Thickness {
991 &self.thickness
992 }
993
994 pub fn set_name(&mut self, name: Option<String>) {
996 self.name = name;
997 }
998
999 pub fn set_color(&mut self, color: Color) {
1001 self.color = color;
1002 }
1003
1004 pub fn set_thickness(&mut self, thickness: Thickness) {
1006 self.thickness = thickness;
1007 }
1008}
1009
1010#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
1012pub struct Color(pub u8, pub u8, pub u8);
1013
1014impl Color {
1015 #[must_use]
1017 pub fn r(&self) -> u8 {
1018 self.0
1019 }
1020 #[must_use]
1022 pub fn g(&self) -> u8 {
1023 self.1
1024 }
1025 #[must_use]
1027 pub fn b(&self) -> u8 {
1028 self.2
1029 }
1030}
1031
1032#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
1034pub struct Thickness {
1035 display_width: ValidDecimal,
1036 threads_per_unit: ValidDecimal,
1037 unit: PerUnit,
1038}
1039
1040impl Thickness {
1041 #[must_use]
1043 pub fn display_width(&self) -> ValidDecimal {
1044 self.display_width
1045 }
1046
1047 #[must_use]
1049 pub fn threads_per_unit(&self) -> ValidDecimal {
1050 self.threads_per_unit
1051 }
1052
1053 #[must_use]
1055 pub fn unit(&self) -> PerUnit {
1056 self.unit
1057 }
1058}
1059
1060#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
1063pub struct ValidDecimal(f64);
1064
1065impl ValidDecimal {
1066 #[must_use]
1070 pub fn new(value: f64) -> ValidDecimal {
1071 match value.classify() {
1072 FpCategory::Nan | FpCategory::Infinite | FpCategory::Subnormal => {
1073 panic!("Invalid decimal {value}")
1074 }
1075 FpCategory::Normal if value < 0.0 => panic!("Negative decimal {value}"),
1076 _ => ValidDecimal(value),
1077 }
1078 }
1079}
1080
1081impl Eq for ValidDecimal {}
1082
1083impl Hash for ValidDecimal {
1084 fn hash<H: Hasher>(&self, state: &mut H) {
1085 self.0.to_bits().hash(state);
1086 }
1087}
1088
1089impl Default for ValidDecimal {
1090 fn default() -> Self {
1091 Self(1.0)
1092 }
1093}
1094
1095#[derive(Clone, Debug, PartialEq, Copy, Eq, Hash, Default)]
1097pub enum PerUnit {
1098 #[default]
1100 Inch,
1101 Centimeter,
1103}
1104
1105#[derive(Clone, Debug, PartialEq, Default)]
1107pub struct YarnRepeat {
1108 offset: usize, sequence: Vec<Rc<Yarn>>,
1110}
1111
1112impl YarnRepeat {
1113 #[must_use]
1115 pub fn new() -> Self {
1116 Self::default()
1117 }
1118
1119 pub fn set_sequence(&mut self, sequence: &[Rc<Yarn>]) {
1121 self.sequence = sequence.iter().map(Rc::clone).collect();
1122 }
1123
1124 pub fn set_offset(&mut self, offset: usize) {
1126 self.offset = offset;
1127 }
1128
1129 #[must_use]
1150 pub fn try_get(&self, index: usize) -> Option<&Rc<Yarn>> {
1151 if self.sequence.is_empty() {
1152 return None;
1153 }
1154 let seq_index = (self.offset + index) % self.sequence.len();
1155 self.sequence.get(seq_index)
1156 }
1157
1158 #[must_use]
1163 pub fn get(&self, index: usize) -> &Rc<Yarn> {
1164 assert!(!self.sequence.is_empty(), "Empty YarnSequence");
1165 let seq_index = (self.offset + index) % self.sequence.len();
1166 &self.sequence[seq_index]
1167 }
1168
1169 #[must_use]
1171 pub fn offset(&self) -> usize {
1172 self.offset
1173 }
1174
1175 #[must_use]
1177 pub fn sequence(&self) -> &Vec<Rc<Yarn>> {
1178 &self.sequence
1179 }
1180}
1181
1182#[derive(Clone, Debug, PartialEq, Default)]
1184pub struct YarnSequence {
1185 default_sequence: YarnRepeat,
1186 exceptions: HashMap<usize, Rc<Yarn>>,
1187}
1188
1189impl YarnSequence {
1190 #[must_use]
1192 pub fn default_sequence(&self) -> &YarnRepeat {
1193 &self.default_sequence
1194 }
1195
1196 #[must_use]
1198 pub fn exceptions(&self) -> &HashMap<usize, Rc<Yarn>> {
1199 &self.exceptions
1200 }
1201
1202 pub fn set_default_sequence(&mut self, default_sequence: YarnRepeat) {
1204 self.default_sequence = default_sequence;
1205 }
1206
1207 pub fn set_exceptions(&mut self, exceptions: HashMap<usize, Rc<Yarn>>) {
1209 self.exceptions = exceptions;
1210 }
1211
1212 pub fn set_yarn(&mut self, index: usize, yarn: Rc<Yarn>) {
1214 self.exceptions.insert(index, yarn);
1215 }
1216
1217 pub fn set_repeat(&mut self, repeat: &[Rc<Yarn>]) {
1219 self.default_sequence.set_sequence(repeat);
1220 }
1221
1222 pub fn set_offset(&mut self, offset: usize) {
1224 self.default_sequence.offset = offset;
1225 }
1226
1227 #[must_use]
1230 pub fn try_get(&self, index: usize) -> Option<&Rc<Yarn>> {
1231 if self.exceptions.contains_key(&index) {
1232 self.exceptions.get(&index)
1233 } else {
1234 self.default_sequence.try_get(index)
1235 }
1236 }
1237
1238 #[must_use]
1242 pub fn get(&self, index: usize) -> &Rc<Yarn> {
1243 self.exceptions
1244 .get(&index)
1245 .unwrap_or_else(|| self.default_sequence.get(index))
1246 }
1247}
1248
1249#[cfg(test)]
1250mod tests {
1251 use super::*;
1252
1253 #[test]
1254 fn test_palette_borrows() {
1255 let mut palette = YarnPalette::new();
1256 let yarn = palette.use_yarn(Yarn {
1257 name: None,
1258 color: Color(0, 0, 0),
1259 thickness: Thickness::default(),
1260 });
1261 let yarn2 = palette.use_yarn(Yarn {
1262 name: None,
1263 color: Color(255, 255, 255),
1264 thickness: Thickness::default(),
1265 });
1266 assert_ne!(yarn, yarn2);
1267 }
1268
1269 #[test]
1270 #[should_panic(expected = "shaft count is 2 but found shaft 3")]
1271 fn test_validate_threading() {
1272 let _ = Threading::new(2, vec![1, 2, 3, 4]);
1273 }
1274
1275 #[test]
1276 fn test_add_threading() {
1277 let threading_1 = Threading::new(4, vec![1, 2, 3, 4]);
1278 let threading_2 = Threading::new(6, vec![3, 4, 5, 6, 1]);
1279 assert_eq!(
1280 threading_1 + threading_2,
1281 Threading::new(6, vec![1, 2, 3, 4, 3, 4, 5, 6, 1])
1282 );
1283 }
1284
1285 #[test]
1286 fn test_thread_indexing() {
1287 let threading = Threading::new(4, vec![1, 2, 3, 4]);
1288 assert_eq!(threading[0], 1);
1289 assert_eq!(threading[0..1], [1]);
1290 }
1291
1292 #[test]
1293 fn test_treadling_indexing() {
1294 let treadling = Treadling(vec![
1295 HashSet::from([1]),
1296 HashSet::from([2]),
1297 HashSet::from([3]),
1298 ]);
1299 assert_eq!(treadling[0], HashSet::from([1]));
1300 assert_eq!(treadling[..2], [HashSet::from([1]), HashSet::from([2])]);
1301 }
1302
1303 #[test]
1304 fn test_squish_threading() {
1305 let mut threading = Threading::new(8, vec![1, 3, 4, 6, 3]);
1306 assert_eq!(
1307 threading.trim_and_squish_shafts(),
1308 &Threading::new(4, vec![1, 2, 3, 4, 2])
1309 );
1310 }
1311
1312 #[test]
1313 fn test_invert_set() {
1314 let set = HashSet::from([1, 3, 5, 7]);
1315 assert_eq!(invert(&set, 8), HashSet::from([2, 4, 6, 8]));
1316 }
1317
1318 #[test]
1319 #[should_panic(expected = "cannot invert when max is 0")]
1320 fn test_invert_panic() {
1321 let set = HashSet::from([1, 3, 5, 7]);
1322 let _ = invert(&set, 0);
1323 }
1324
1325 #[test]
1326 fn test_invert_tie_up() {
1327 let mut tie_up = TieUp {
1328 treadle_count: 4,
1329 tie_up: vec![
1330 HashSet::from([1, 2]),
1331 HashSet::from([2, 3]),
1332 HashSet::from([3, 4]),
1333 HashSet::from([4, 1]),
1334 ],
1335 };
1336 tie_up.invert(4);
1337 assert_eq!(
1338 tie_up,
1339 TieUp {
1340 treadle_count: 4,
1341 tie_up: vec![
1342 HashSet::from([3, 4]),
1343 HashSet::from([1, 4]),
1344 HashSet::from([1, 2]),
1345 HashSet::from([2, 3])
1346 ]
1347 }
1348 );
1349 }
1350}