weave_draft/
lib.rs

1#![warn(missing_docs)]
2#![warn(clippy::pedantic)]
3
4//! # weave-draft
5//!
6//! `weave-draft` is a crate for representing and manipulating weaving drafts
7//!
8//! ## Crate features
9//!
10//! ### `wif`
11//!
12//! Enable this to convert to the standard `.wif` format
13
14use crate::data::{Shaft, Treadle};
15#[doc(inline)]
16pub use data::RiseSink;
17#[doc(inline)]
18pub use data::Threading;
19#[doc(inline)]
20pub use data::TieUp;
21#[doc(inline)]
22pub use data::TieUpCreate;
23#[doc(inline)]
24pub use data::TieUpKind;
25#[doc(inline)]
26pub use data::TreadlingInfo;
27use std::cmp::Ordering;
28use std::collections::HashSet;
29use std::ops::RangeBounds;
30
31mod data;
32
33/// Structure holding all elements of a draft
34#[derive(Clone, Debug, PartialEq)]
35pub struct Draft {
36    threading: Threading,
37    treadling: TreadlingInfo,
38}
39
40/// An enum representing the axes of a weaving draft
41pub enum WeavingAxis {
42    /// Threading
43    Threading,
44    /// Treadling
45    Treadling,
46}
47
48impl Draft {
49    /// Create an empty draft with the given options
50    #[must_use]
51    pub fn new(shaft_count: u32, tie_up_create: TieUpCreate, rise_sink: RiseSink) -> Self {
52        Self {
53            threading: Threading::new(shaft_count, Vec::new()),
54            treadling: TreadlingInfo::new(shaft_count, tie_up_create, rise_sink),
55        }
56    }
57
58    /// Get the threading
59    #[must_use]
60    pub fn threading(&self) -> &Threading {
61        &self.threading
62    }
63
64    /// Get the treadling
65    #[must_use]
66    pub fn treadling_info(&self) -> &TreadlingInfo {
67        &self.treadling
68    }
69
70    /// Get the tie-up
71    #[must_use]
72    pub fn tie_up(&self) -> &TieUpKind {
73        self.treadling.tie_up()
74    }
75
76    /// Make rising shaft
77    pub fn make_rising(&mut self) {
78        self.treadling.make_rising();
79    }
80
81    /// Make sinking shaft
82    pub fn make_sinking(&mut self) {
83        self.treadling.make_sinking();
84    }
85
86    /// Goes from a treadling to a lift plan. Returns false if already a lift plan,
87    /// true if conversion happened
88    pub fn make_lift_plan(&mut self) -> bool {
89        self.treadling.make_lift_plan()
90    }
91
92    /// Max shaft used in threading or treadling
93    #[must_use]
94    pub fn max_shaft(&self) -> (WeavingAxis, u32) {
95        let treadling_max = self.treadling.max_shaft_used();
96        let threading_max = self.threading.max_shaft();
97
98        match treadling_max.cmp(&threading_max) {
99            Ordering::Less | Ordering::Equal => (WeavingAxis::Threading, threading_max),
100            Ordering::Greater => (WeavingAxis::Treadling, treadling_max),
101        }
102    }
103
104    /// Sets shaft count on threading and treadling. Only succeeds when non-destructive
105    ///
106    /// # Errors
107    /// If shafts greater than the given count are in use, returns an error with the max used shaft
108    /// and the axis it's used on
109    ///
110    /// # Panics
111    /// If shaft count is 0
112    pub fn set_shaft_count(&mut self, shaft_count: u32) -> Result<(), (WeavingAxis, u32)> {
113        let (axis, max) = self.max_shaft();
114        if shaft_count >= max {
115            self.treadling.set_shaft_count(shaft_count).unwrap();
116            self.threading.set_shaft_count(shaft_count).unwrap();
117            Ok(())
118        } else {
119            Err((axis, max))
120        }
121    }
122
123    /// Calls [`Threading::splice`]
124    ///
125    /// # Errors
126    /// See [`Threading::splice`]
127    pub fn splice_threading<R>(&mut self, range: R, replace_with: &[u32]) -> Result<Vec<u32>, usize>
128    where
129        R: RangeBounds<usize>,
130    {
131        self.threading.splice(range, replace_with)
132    }
133
134    /// Adds a new thread to teh end of the threading
135    ///
136    /// # Errors
137    /// Returns the shaft if greater than shaft count
138    pub fn push_threading(&mut self, shaft: u32) -> Result<(), u32> {
139        self.threading.push(shaft)
140    }
141
142    /// Insert a thread in the threading at the given index, shifting the later threads
143    ///
144    /// # Panics
145    /// If index is greater than length
146    ///
147    /// # Errors
148    /// If `shaft` is greater than `shaft_count`
149    pub fn insert_threading(&mut self, shaft: Shaft, index: usize) -> Result<(), Shaft> {
150        self.threading.insert(shaft, index)
151    }
152
153    /// Insert a thread at the given index, shifting later threads
154    ///
155    /// # Errors
156    /// Returns the current length if index is greater than length
157    pub fn try_insert_threading(
158        &mut self,
159        shaft: Shaft,
160        index: usize,
161    ) -> Result<Result<(), Shaft>, usize> {
162        self.threading.try_insert(shaft, index)
163    }
164
165    /// Remove the thread at the given index, returning it as a [Shaft]
166    ///
167    /// # Panics
168    /// If index is out of bounds
169    pub fn remove_threading(&mut self, index: usize) -> Shaft {
170        self.threading.remove(index)
171    }
172
173    /// Get threading shaft at an index
174    #[must_use]
175    pub fn get_from_threading(&self, index: usize) -> Option<&u32> {
176        self.threading.get(index)
177    }
178
179    /// Overwrite thread at given index, returns old thread value
180    ///
181    /// # Panics
182    /// If index is out of bounds
183    pub fn put_threading(&mut self, index: usize, shaft: Shaft) -> Shaft {
184        self.threading.put(index, shaft)
185    }
186
187    /// Overwrite thread at given index. Returns replaced shaft, or none if inserting at the end
188    ///
189    /// # Errors
190    ///
191    /// Returns current length if index out of bounds
192    pub fn try_put_threading(
193        &mut self,
194        index: usize,
195        shaft: Shaft,
196    ) -> Result<Option<Shaft>, usize> {
197        self.threading.try_put(index, shaft)
198    }
199
200    /// See [`Threading::flip_vertical`]
201    pub fn flip_threading_vert(&mut self) {
202        self.threading.flip_vertical();
203    }
204
205    /// See [`Threading::mirror`]
206    pub fn mirror_threading(&mut self) {
207        self.threading.mirror();
208    }
209
210    /// See [`Threading::reverse`]
211    pub fn flip_threading_horiz(&mut self) {
212        self.threading.reverse();
213    }
214
215    /// Add a new pick at the end using just the given treadle
216    ///
217    /// # Errors
218    /// If treadle is higher than number of shafts, returns treadle
219    pub fn push_single_treadling(&mut self, treadle: u32) -> Result<(), u32> {
220        self.treadling.push_single(treadle)
221    }
222
223    /// Add a new pick at the end using all given treadles/shafts
224    ///
225    /// # Errors
226    /// If any treadle is over the number of treadles/shafts, returns that value
227    pub fn push_treadling(&mut self, treadles: HashSet<u32>) -> Result<(), u32> {
228        self.treadling.push(treadles)
229    }
230
231    /// Toggle treadle at given index. Return `true` if treadle has been toggled on, `false` if toggled off
232    ///
233    /// # Errors
234    /// If treadle is invalid
235    /// # Panics
236    /// If index is out of bounds
237    pub fn toggle_treadle(&mut self, index: usize, treadle: Treadle) -> Result<bool, u32> {
238        self.treadling.toggle_treadle(index, treadle)
239    }
240
241    /// Inserts treadling at given index
242    ///
243    /// # Errors
244    /// If any treadles are invalid
245    /// # Panics
246    /// If index is out of bounds
247    pub fn insert_treadle(&mut self, index: usize, treadles: HashSet<u32>) -> Result<(), u32> {
248        self.treadling.insert(index, treadles)
249    }
250
251    /// Based on [`Vec::splice`], it splices the given sequence into the given range. It validates that
252    /// the elements in `replace_with` are inside the shaft bounds, and it returns the replaced elements.
253    ///
254    /// # Errors
255    /// If an element in `replace_with` is larger than the shaft count, returns index of first
256    /// out-of-bounds element
257    pub fn splice_treadling<R>(
258        &mut self,
259        range: R,
260        replace_with: Vec<HashSet<u32>>,
261    ) -> Result<Vec<HashSet<u32>>, u32>
262    where
263        R: RangeBounds<usize>,
264    {
265        self.treadling.splice(range, replace_with)
266    }
267    /// Overwrites the treadling at the given index with the new treadles
268    ///
269    /// # Errors
270    /// If treadling is invalid
271    ///
272    /// # Panics
273    /// If index is greater than the length of the treadling
274    pub fn put_treadling(
275        &mut self,
276        index: usize,
277        treadles: HashSet<u32>,
278    ) -> Result<Option<HashSet<u32>>, u32> {
279        self.treadling.put(index, treadles)
280    }
281}