weave_draft/
data.rs

1//! data module provides data structures used in the [`crate::Draft`]
2
3use 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/// Wrapper for shaft
13#[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/// Wrapper for Treadle
23#[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/// Threading in a weaving draft. 1 thread can only be on one shaft
32#[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    /// Constructs a new threading, verifying that the `shaft_count` is respected.
60    ///
61    /// # Panics
62    /// If there are threads outside the shaft count
63    #[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    /// Based on [`Vec::splice`], it splices the given sequence into the given range. It validates that
87    /// the elements in `replace_with` are inside the shaft bounds, and it returns the replaced elements.
88    ///
89    /// # Examples
90    /// ```
91    /// # use weave_draft::Threading;
92    /// let mut threading = Threading::new(4, vec![1,2,3,4]);
93    /// let removed = threading.splice(1..3, &[4,3,4,1]).unwrap();
94    /// assert_eq!(threading, Threading::new(4, vec![1,4,3,4,1,4]));
95    /// assert_eq!(removed, vec![2,3]);
96    ///
97    /// let error = threading.splice(1..3, &[5]).unwrap_err();
98    /// assert_eq!(error, 0);
99    /// assert_eq!(threading.len(), 6); // no removal on failure
100    /// ```
101    ///
102    /// # Errors
103    /// If an element in `replace_with` is larger than the shaft count, returns index of first
104    /// out-of-bounds element
105    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    /// Number of threads in the threading
119    #[must_use]
120    pub fn len(&self) -> usize {
121        self.threading.len()
122    }
123
124    /// Is the threading empty
125    #[must_use]
126    pub fn is_empty(&self) -> bool {
127        self.threading.is_empty()
128    }
129
130    /// Get the raw threading
131    #[must_use]
132    pub fn threading(&self) -> &Vec<u32> {
133        &self.threading
134    }
135
136    /// Add a new thread at the end of the threading
137    /// # Errors
138    /// Returns the shaft if greater than shaft count
139    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    /// Insert a thread at the given index, shifting later threads
148    ///
149    /// # Panics
150    /// If index is greater than the length
151    ///
152    /// # Errors
153    /// If `shaft` is greater than `shaft_count`
154    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    /// Insert a thread at the given index, shifting later threads
163    ///
164    /// # Errors
165    /// Returns the current length if index is greater than length
166    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    /// Remove the thread at the given index, returning it as a [Shaft]
176    ///
177    /// # Panics
178    /// If index is out of bounds
179    pub fn remove(&mut self, index: usize) -> Shaft {
180        Shaft(self.threading.remove(index))
181    }
182
183    /// Get shaft at index
184    #[must_use]
185    pub fn get(&self, index: usize) -> Option<&u32> {
186        self.threading.get(index)
187    }
188
189    /// Overwrite thread at given index, returns old thread value
190    ///
191    /// # Panics
192    /// If index is out of bounds
193    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    /// Overwrite thread at given index. Returns replaced shaft, or none if inserting at the end
200    ///
201    /// # Errors
202    ///
203    /// Returns current length if index out of bounds
204    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    /// Highest used shaft in threading
217    #[must_use]
218    pub fn max_shaft(&self) -> u32 {
219        let max = self.threading.iter().max();
220        *max.unwrap_or(&0)
221    }
222
223    /// Non-destructively set shaft count
224    ///
225    /// # Panics
226    /// If `shaft_count` is 0
227    ///
228    /// # Errors
229    /// If `shaft_count` is less than max shaft used
230    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            // guaranteed to exist because of max shaft check
238            let pos = self
239                .threading
240                .iter()
241                .position(|s| s > &shaft_count)
242                .unwrap();
243            Err(pos)
244        }
245    }
246
247    /// Shrinks the shaft count to the maximum shaft present in the threading. Returns the old
248    /// shaft count if the count changed
249    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    /// Returns set of shafts used in the threading
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// # use std::collections::HashSet;
264    /// # use weave_draft::Threading;
265    /// assert_eq!(HashSet::from([1,2,4]), Threading::new(4, vec![4, 1, 2, 1]).used_shafts());
266    /// ```
267    #[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    /// Removes any empty shafts, shifting threads down
277    #[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            // construction of map guarantees entry exists
292            .for_each(|s| *s = *mapping.get(s).unwrap());
293        self
294    }
295
296    /// Flips the threading vertically. On an 8 shaft threading, this means that shaft 1 becomes shaft 8
297    /// shaft 2 becomes shaft 7, and so on.
298    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    /// Repeats the threading in reverse, does not repeat the final/center thread
306    ///
307    /// # Examples
308    /// ```
309    /// # use weave_draft::Threading;
310    /// let mut threading = Threading::new(4, vec![1, 2, 3, 4]);
311    /// assert_eq!(threading.mirror().threading(), &vec![1, 2, 3, 4, 3, 2, 1]);
312    /// ```
313    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    /// Reverse threading horizontally
324    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/// The treadling or lift plan for a draft, also includes the tie-up and whether the loom is rising
394/// or sinking shed
395#[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, // 2 shaft is the minimum meaningful shaft count
407            rise_sink: RiseSink::default(),
408            tie_up: TieUpKind::default(),
409            treadling: Treadling::default(),
410        }
411    }
412}
413
414/// Options when creating a new [`TieUpKind`]
415#[derive(Debug, Clone, Copy, PartialEq)]
416pub enum TieUpCreate {
417    /// Create a direct tie-up
418    Direct,
419    /// Create an indirect tie-up with given number of treadles
420    Indirect(u32),
421}
422
423impl TreadlingInfo {
424    /// Construct new treadling
425    ///
426    /// # Panics
427    /// If shaft or treadle count is 0
428    #[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    /// Get the shaft count
452    #[must_use]
453    pub fn shaft_count(&self) -> u32 {
454        self.shaft_count
455    }
456
457    /// Returns max shaft used. If this is a direct tie-up, it's the max shaft in the lift plan. If
458    /// it's an indirect tie-up, it's the max shaft used in the tie-up, even if no picks use a treadle
459    /// tied to that shaft
460    #[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    /// Non-destructively sets shaft count
469    ///
470    /// # Errors
471    /// If `shaft_count` is less than the max shaft used, returns max shaft
472    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    /// Get the tie-up info
483    #[must_use]
484    pub fn tie_up(&self) -> &TieUpKind {
485        &self.tie_up
486    }
487
488    /// Get the treadle count. Returns the shaft count if directly tied up
489    #[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    /// Whether the treadling is for a rising shaft or sinking shaft loom
498    #[must_use]
499    pub fn rise_sink(&self) -> RiseSink {
500        self.rise_sink
501    }
502
503    /// Number of picks in the treadling
504    #[must_use]
505    pub fn len(&self) -> usize {
506        self.treadling.0.len()
507    }
508
509    /// Is the treadling empty
510    #[must_use]
511    pub fn is_empty(&self) -> bool {
512        self.treadling.0.len() == 0
513    }
514
515    /// Add a new pick at the end using just the given treadle
516    ///
517    /// # Errors
518    /// If treadle is higher than number of shafts, returns treadle
519    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            // tread 0 as no treadle
524            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    /// Add a new pick at the end using all given treadles/shafts
548    ///
549    /// # Errors
550    /// If any treadle is over the number of treadles/shafts, returns that value
551    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    /// Toggle treadle at given index. Return `true` if treadle has been toggled on, `false` if toggled off
559    ///
560    /// # Errors
561    /// If treadle is invalid
562    /// # Panics
563    /// If index is out of bounds
564    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    /// Inserts treadling at given index
577    ///
578    /// # Errors
579    /// If any treadles are invalid
580    /// # Panics
581    /// If index is out of bounds
582    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    /// Based on [`Vec::splice`], it splices the given sequence into the given range. It validates that
590    /// the elements in `replace_with` are inside the shaft bounds, and it returns the replaced elements.
591    ///
592    /// # Errors
593    /// If an element in `replace_with` is larger than the shaft count, returns index of first
594    /// out-of-bounds element
595    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    /// Overwrites the treadling at the given index with the new treadles
613    ///
614    /// # Errors
615    /// If treadling is invalid
616    ///
617    /// # Panics
618    /// If index is greater than the length of the treadling
619    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    /// Convert in place to a rising shaft treadling
643    pub fn make_rising(&mut self) {
644        match self.rise_sink {
645            RiseSink::Rising => (),
646            RiseSink::Sinking => self.invert(),
647        }
648    }
649
650    /// Convert in place to a sinking shaft treadling
651    pub fn make_sinking(&mut self) {
652        match self.rise_sink {
653            RiseSink::Rising => self.invert(),
654            RiseSink::Sinking => (),
655        }
656    }
657
658    /// Goes from a treadling to a lift plan. Returns false if already a lift plan,
659    /// true if conversion happened
660    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    /// Switch from rising to sinking or vice versa
674    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/// Whether the loom is a direct tie-up or whether treadles can be tied to multiple shafts
699#[derive(Debug, Clone, PartialEq, Default)]
700pub enum TieUpKind {
701    /// Direct tie up (table loom, some 4 shaft looms, dobby looms)
702    #[default]
703    Direct,
704    /// Indirect tie up (most 4+ shaft manual floor looms)
705    Indirect(TieUp),
706}
707
708impl TieUpKind {
709    /// Get [`TieUp`] if indirect
710    #[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    /// Get underlying tie up data if indirect
719    #[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/// A tie-up of a loom
729#[derive(Debug, Clone, PartialEq)]
730pub struct TieUp {
731    treadle_count: u32,
732    /// Each element in the vector corresponds to one treadle, and the hashset is which shafts it's
733    /// tied to
734    tie_up: Vec<HashSet<u32>>,
735}
736
737impl TieUp {
738    /// Create an empty tie up for the given treadle count
739    #[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    /// Returns the shafts tied up to the given treadle. Returns an empty set if treadle is out of bounds
757    ///
758    /// Note: treadles (and shafts) are 1-indexed
759    #[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    /// Computes which shafts are raised when given set of treadles are pressed
765    #[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/// Whether this draft is written for a rising shaft or sinking shaft loom
780#[derive(Debug, Clone, PartialEq, Copy, Default)]
781pub enum RiseSink {
782    /// Rising shaft loom (most US jack looms)
783    #[default]
784    Rising,
785    /// Sinking shaft loom (counterbalance, direct tie up, etc. looms)
786    Sinking,
787}
788
789impl RiseSink {
790    /// Swap to other kind
791    #[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/// Treadling/Lift Plan Sequence
801#[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/// Palette of yarns to be used in the weaving
875#[derive(Debug, Clone, PartialEq, Default)]
876pub struct YarnPalette(HashSet<Rc<Yarn>>);
877
878impl YarnPalette {
879    /// Construct a new [`YarnPalette`]
880    #[must_use]
881    pub fn new() -> YarnPalette {
882        YarnPalette(HashSet::new())
883    }
884
885    /// Number of yarns
886    #[must_use]
887    pub fn len(&self) -> usize {
888        self.0.len()
889    }
890
891    /// Is the palette empty
892    #[must_use]
893    pub fn is_empty(&self) -> bool {
894        self.0.is_empty()
895    }
896
897    /// Removes yarns that are not used outside the palette and returns a Vec of the removed yarns
898    ///
899    /// # Examples
900    /// ```
901    /// # use std::rc::Rc;
902    /// # use weave_draft::data::{Yarn, YarnPalette};
903    /// let mut palette = YarnPalette::new();
904    /// palette.use_yarn(Yarn::default());
905    /// assert_eq!(palette.remove_unused_yarns(), vec![Yarn::default()]);
906    /// assert_eq!(palette.len(), 0);
907    ///
908    /// let yarn = palette.use_yarn(Yarn::default());
909    /// assert_eq!(palette.remove_unused_yarns(), vec![]);
910    /// assert_eq!(Rc::strong_count(&yarn), 2);
911    /// ```
912    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                // Should have strong count of 1 here
926                Rc::unwrap_or_clone(yarn)
927            })
928            .collect()
929    }
930
931    /// Adds yarn to palette if not there. Returns reference to yarn owned by palette
932    #[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    /// Add multiple yarns to the palette. Returned `Vec` is in the same order as the input
944    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    /// Borrowing iterator across yarns
952    #[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/// A yarn that is used in the weaving
968#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
969pub struct Yarn {
970    name: Option<String>,
971    color: Color,
972    thickness: Thickness, // todo: Other metadata? Like fiber, source, etc
973}
974
975impl Yarn {
976    /// Get name
977    #[must_use]
978    pub fn name(&self) -> &Option<String> {
979        &self.name
980    }
981
982    /// Get color
983    #[must_use]
984    pub fn color(&self) -> &Color {
985        &self.color
986    }
987
988    /// Get Thickness
989    #[must_use]
990    pub fn thickness(&self) -> &Thickness {
991        &self.thickness
992    }
993
994    /// Set name
995    pub fn set_name(&mut self, name: Option<String>) {
996        self.name = name;
997    }
998
999    /// Set color
1000    pub fn set_color(&mut self, color: Color) {
1001        self.color = color;
1002    }
1003
1004    /// Set thickness
1005    pub fn set_thickness(&mut self, thickness: Thickness) {
1006        self.thickness = thickness;
1007    }
1008}
1009
1010/// RGB color
1011#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
1012pub struct Color(pub u8, pub u8, pub u8);
1013
1014impl Color {
1015    /// retrieve the red value
1016    #[must_use]
1017    pub fn r(&self) -> u8 {
1018        self.0
1019    }
1020    /// retrieve the green value
1021    #[must_use]
1022    pub fn g(&self) -> u8 {
1023        self.1
1024    }
1025    /// retrieve the blue value
1026    #[must_use]
1027    pub fn b(&self) -> u8 {
1028        self.2
1029    }
1030}
1031
1032/// Thickness of a yarn
1033#[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    /// The width of the thread in the displayed draft
1042    #[must_use]
1043    pub fn display_width(&self) -> ValidDecimal {
1044        self.display_width
1045    }
1046
1047    /// The number of picks/threads per unit
1048    #[must_use]
1049    pub fn threads_per_unit(&self) -> ValidDecimal {
1050        self.threads_per_unit
1051    }
1052
1053    /// Unit to be used when calculated picks/thread per inch/centimeter
1054    #[must_use]
1055    pub fn unit(&self) -> PerUnit {
1056        self.unit
1057    }
1058}
1059
1060/// Float wrapper that guarantees the value is `0` or a positive [`Normal`][FpCategory::Normal] float.
1061/// This allows for a full [`Eq`] and hash
1062#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
1063pub struct ValidDecimal(f64);
1064
1065impl ValidDecimal {
1066    /// Constructs a [`ValidDecimal`]
1067    /// # Panics
1068    /// If the value is infinite, NaN, Negative, or Subnormal
1069    #[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/// Unit used in weaving
1096#[derive(Clone, Debug, PartialEq, Copy, Eq, Hash, Default)]
1097pub enum PerUnit {
1098    /// Inches
1099    #[default]
1100    Inch,
1101    /// Centimeters
1102    Centimeter,
1103}
1104
1105/// Repeating sequence of yarns used in the warp or weft
1106#[derive(Clone, Debug, PartialEq, Default)]
1107pub struct YarnRepeat {
1108    offset: usize, // which color in the sequence to start on
1109    sequence: Vec<Rc<Yarn>>,
1110}
1111
1112impl YarnRepeat {
1113    /// Empty yarn repeat
1114    #[must_use]
1115    pub fn new() -> Self {
1116        Self::default()
1117    }
1118
1119    /// Set the repat
1120    pub fn set_sequence(&mut self, sequence: &[Rc<Yarn>]) {
1121        self.sequence = sequence.iter().map(Rc::clone).collect();
1122    }
1123
1124    /// Set the offset
1125    pub fn set_offset(&mut self, offset: usize) {
1126        self.offset = offset;
1127    }
1128
1129    /// Gets the appropriate yarn for the given index, returns `None` if the sequence is empty
1130    ///
1131    /// # Examples
1132    /// ```
1133    /// # use std::rc::Rc;
1134    /// # use weave_draft::data::{Color, Yarn, YarnRepeat};
1135    /// let mut seq = YarnRepeat::new();
1136    /// assert_eq!(seq.try_get(1), None);
1137    ///
1138    /// let yarn_1 = Rc::new(Yarn::default());
1139    /// let mut yarn_2 = Yarn::default();
1140    /// yarn_2.set_color(Color(255,255,255));
1141    /// let yarn_2 = Rc::new(yarn_2);
1142    /// seq.set_sequence(&[Rc::clone(&yarn_1), Rc::clone(&yarn_2)]);
1143    ///
1144    /// assert_eq!(seq.try_get(3).unwrap(), &yarn_2);
1145    ///
1146    /// seq.set_offset(1);
1147    /// assert_eq!(seq.try_get(3).unwrap(), &yarn_1);
1148    /// ```
1149    #[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    /// Gets the appropriate yarn for the given index
1159    ///
1160    /// # Panics
1161    /// If the sequence is empty
1162    #[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    /// get offset
1170    #[must_use]
1171    pub fn offset(&self) -> usize {
1172        self.offset
1173    }
1174
1175    /// get sequence
1176    #[must_use]
1177    pub fn sequence(&self) -> &Vec<Rc<Yarn>> {
1178        &self.sequence
1179    }
1180}
1181
1182/// Yarns used in the warp or weft
1183#[derive(Clone, Debug, PartialEq, Default)]
1184pub struct YarnSequence {
1185    default_sequence: YarnRepeat,
1186    exceptions: HashMap<usize, Rc<Yarn>>,
1187}
1188
1189impl YarnSequence {
1190    /// get sequence
1191    #[must_use]
1192    pub fn default_sequence(&self) -> &YarnRepeat {
1193        &self.default_sequence
1194    }
1195
1196    /// get exceptions to sequence
1197    #[must_use]
1198    pub fn exceptions(&self) -> &HashMap<usize, Rc<Yarn>> {
1199        &self.exceptions
1200    }
1201
1202    /// set sequence
1203    pub fn set_default_sequence(&mut self, default_sequence: YarnRepeat) {
1204        self.default_sequence = default_sequence;
1205    }
1206
1207    /// set exceptions to sequence
1208    pub fn set_exceptions(&mut self, exceptions: HashMap<usize, Rc<Yarn>>) {
1209        self.exceptions = exceptions;
1210    }
1211
1212    /// Set yarn at index
1213    pub fn set_yarn(&mut self, index: usize, yarn: Rc<Yarn>) {
1214        self.exceptions.insert(index, yarn);
1215    }
1216
1217    /// Set the default repeat
1218    pub fn set_repeat(&mut self, repeat: &[Rc<Yarn>]) {
1219        self.default_sequence.set_sequence(repeat);
1220    }
1221
1222    /// Set the repeat offset
1223    pub fn set_offset(&mut self, offset: usize) {
1224        self.default_sequence.offset = offset;
1225    }
1226
1227    /// Get the correct yarn for the index. Returns `None` if the sequence is empty and there is no
1228    /// exception for the index
1229    #[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    /// Get the correct yarn for the index.
1239    /// # Panics
1240    /// If the sequence is empty and there is no exception for the index
1241    #[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}