knit-graphs 0.0.11__py3-none-any.whl → 0.0.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- knit_graphs/Course.py +63 -42
- knit_graphs/Knit_Graph.py +62 -160
- knit_graphs/Knit_Graph_Visualizer.py +51 -63
- knit_graphs/Loop.py +122 -116
- knit_graphs/Pull_Direction.py +3 -2
- knit_graphs/Yarn.py +246 -233
- knit_graphs/artin_wale_braids/Loop_Braid_Graph.py +42 -62
- knit_graphs/artin_wale_braids/Wale.py +61 -82
- knit_graphs/artin_wale_braids/Wale_Braid.py +7 -2
- knit_graphs/artin_wale_braids/Wale_Braid_Word.py +35 -22
- knit_graphs/artin_wale_braids/Wale_Group.py +46 -39
- knit_graphs/basic_knit_graph_generators.py +89 -168
- knit_graphs/directed_loop_graph.py +454 -0
- knit_graphs/knit_graph_builder.py +187 -0
- knit_graphs/knit_graph_errors/__init__.py +0 -0
- knit_graphs/knit_graph_errors/knit_graph_error.py +30 -0
- {knit_graphs-0.0.11.dist-info → knit_graphs-0.0.12.dist-info}/METADATA +1 -1
- {knit_graphs-0.0.11.dist-info → knit_graphs-0.0.12.dist-info}/RECORD +20 -16
- {knit_graphs-0.0.11.dist-info → knit_graphs-0.0.12.dist-info}/LICENSE +0 -0
- {knit_graphs-0.0.11.dist-info → knit_graphs-0.0.12.dist-info}/WHEEL +0 -0
knit_graphs/Yarn.py
CHANGED
|
@@ -8,15 +8,17 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
from collections.abc import Iterator
|
|
10
10
|
from dataclasses import dataclass
|
|
11
|
-
from typing import TYPE_CHECKING, cast
|
|
12
|
-
|
|
13
|
-
from networkx import DiGraph, dfs_edges, dfs_preorder_nodes
|
|
11
|
+
from typing import TYPE_CHECKING, Self, TypeVar, cast, overload
|
|
14
12
|
|
|
13
|
+
from knit_graphs.directed_loop_graph import Directed_Loop_Graph, Float_Edge
|
|
14
|
+
from knit_graphs.knit_graph_errors.knit_graph_error import Use_Cut_Yarn_ValueError
|
|
15
15
|
from knit_graphs.Loop import Loop
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
18
|
from knit_graphs.Knit_Graph import Knit_Graph
|
|
19
19
|
|
|
20
|
+
LoopT = TypeVar("LoopT", bound=Loop)
|
|
21
|
+
|
|
20
22
|
|
|
21
23
|
@dataclass(frozen=True)
|
|
22
24
|
class Yarn_Properties:
|
|
@@ -26,8 +28,6 @@ class Yarn_Properties:
|
|
|
26
28
|
"""
|
|
27
29
|
|
|
28
30
|
name: str = "yarn" # name (str): The name or identifier for this yarn type.
|
|
29
|
-
plies: int = 2 # plies (int): The number of individual strands twisted together to form the yarn.
|
|
30
|
-
weight: float = 28 # weight (float): The weight category or thickness of the yarn.
|
|
31
31
|
color: str = "green" # color (str): The color of the yarn for visualization purposes.
|
|
32
32
|
|
|
33
33
|
def __str__(self) -> str:
|
|
@@ -36,7 +36,7 @@ class Yarn_Properties:
|
|
|
36
36
|
Returns:
|
|
37
37
|
str: String representation in format "name(plies-weight,color)".
|
|
38
38
|
"""
|
|
39
|
-
return f"{self.name}({self.
|
|
39
|
+
return f"{self.name}({self.color})"
|
|
40
40
|
|
|
41
41
|
def __repr__(self) -> str:
|
|
42
42
|
"""Get the name of the yarn for debugging purposes.
|
|
@@ -64,13 +64,7 @@ class Yarn_Properties:
|
|
|
64
64
|
Returns:
|
|
65
65
|
bool: True if all properties (name, plies, weight, color) are equal, False otherwise.
|
|
66
66
|
"""
|
|
67
|
-
return (
|
|
68
|
-
isinstance(other, Yarn_Properties)
|
|
69
|
-
and self.name == other.name
|
|
70
|
-
and self.plies == other.plies
|
|
71
|
-
and self.weight == other.weight
|
|
72
|
-
and self.color == other.color
|
|
73
|
-
)
|
|
67
|
+
return isinstance(other, Yarn_Properties) and self.name == other.name and self.color == other.color
|
|
74
68
|
|
|
75
69
|
def __hash__(self) -> int:
|
|
76
70
|
"""Get hash value for use in sets and dictionaries.
|
|
@@ -78,53 +72,57 @@ class Yarn_Properties:
|
|
|
78
72
|
Returns:
|
|
79
73
|
int: Hash value based on all yarn properties.
|
|
80
74
|
"""
|
|
81
|
-
return hash((self.name, self.
|
|
75
|
+
return hash((self.name, self.color))
|
|
82
76
|
|
|
83
77
|
|
|
84
|
-
class Yarn:
|
|
78
|
+
class Yarn(Directed_Loop_Graph[LoopT, Float_Edge[LoopT]]):
|
|
85
79
|
"""A class to represent a yarn structure as a sequence of connected loops.
|
|
86
80
|
|
|
87
81
|
The Yarn class manages a directed graph of loops representing the physical yarn path through a knitted structure.
|
|
88
82
|
It maintains the sequential order of loops and their floating relationships, providing methods for navigation and manipulation of the yarn structure.
|
|
89
83
|
|
|
90
84
|
Attributes:
|
|
91
|
-
loop_graph (DiGraph): The directed graph loops connected by yarn-wise float edges.
|
|
92
85
|
properties (Yarn_Properties): The physical and visual properties of this yarn.
|
|
93
86
|
"""
|
|
94
87
|
|
|
95
|
-
FRONT_LOOPS: str = "Front_Loops"
|
|
96
|
-
_BACK_LOOPS: str = "Back_Loops"
|
|
97
|
-
|
|
98
88
|
def __init__(
|
|
99
89
|
self,
|
|
100
|
-
yarn_properties:
|
|
101
|
-
knit_graph:
|
|
90
|
+
yarn_properties: Yarn_Properties | None = None,
|
|
91
|
+
knit_graph: Knit_Graph[LoopT] | None = None,
|
|
92
|
+
instance: int = 0,
|
|
93
|
+
loop_class: type[LoopT] | None = None,
|
|
102
94
|
):
|
|
103
95
|
"""Initialize a yarn with the specified properties and optional knit graph association.
|
|
104
96
|
|
|
105
97
|
Args:
|
|
106
98
|
yarn_properties (None | Yarn_Properties, optional): The properties defining this yarn. If None, uses default properties. Defaults to standard properties.
|
|
107
99
|
knit_graph (None | Knit_Graph, optional): The knit graph that will own this yarn. Can be None for standalone yarns. Defaults to None.
|
|
100
|
+
instance (int, optional): The instance of this yarn. As new yarns are formed by cuts, the instance will increase. Defaults to 0 (first instance of this yarn).
|
|
101
|
+
loop_class (type[LoopT], optional): The type of loop to create by default in this class. Defaults to Loop class.
|
|
108
102
|
"""
|
|
109
|
-
|
|
103
|
+
super().__init__()
|
|
104
|
+
self._instance: int = instance
|
|
105
|
+
self._is_cut: bool = False
|
|
106
|
+
self._loop_class: type[LoopT] = loop_class if loop_class is not None else cast(type[LoopT], Loop)
|
|
110
107
|
if yarn_properties is None:
|
|
111
108
|
yarn_properties = Yarn_Properties.default_yarn()
|
|
112
109
|
self.properties: Yarn_Properties = yarn_properties
|
|
113
|
-
self._first_loop:
|
|
114
|
-
self._last_loop:
|
|
115
|
-
self._knit_graph:
|
|
110
|
+
self._first_loop: LoopT | None = None
|
|
111
|
+
self._last_loop: LoopT | None = None
|
|
112
|
+
self._knit_graph: Knit_Graph[LoopT] = knit_graph if knit_graph is not None else Knit_Graph[LoopT]()
|
|
113
|
+
if self not in self.knit_graph.yarns:
|
|
114
|
+
self.knit_graph.add_yarn(self)
|
|
116
115
|
|
|
117
116
|
@property
|
|
118
|
-
def knit_graph(self) ->
|
|
119
|
-
"""
|
|
120
|
-
|
|
117
|
+
def knit_graph(self) -> Knit_Graph[LoopT]:
|
|
118
|
+
"""
|
|
121
119
|
Returns:
|
|
122
|
-
|
|
120
|
+
Knit_Graph | Knit_Graph: The knit graph that owns this yarn, or None if not associated with a graph.
|
|
123
121
|
"""
|
|
124
122
|
return self._knit_graph
|
|
125
123
|
|
|
126
124
|
@knit_graph.setter
|
|
127
|
-
def knit_graph(self, knit_graph: Knit_Graph) -> None:
|
|
125
|
+
def knit_graph(self, knit_graph: Knit_Graph[LoopT]) -> None:
|
|
128
126
|
"""Set the knit graph that owns this yarn.
|
|
129
127
|
|
|
130
128
|
Args:
|
|
@@ -132,88 +130,8 @@ class Yarn:
|
|
|
132
130
|
"""
|
|
133
131
|
self._knit_graph = knit_graph
|
|
134
132
|
|
|
135
|
-
def has_float(self, u: Loop, v: Loop) -> bool:
|
|
136
|
-
"""Check if there is a float edge between two loops on this yarn.
|
|
137
|
-
|
|
138
|
-
Args:
|
|
139
|
-
u (Loop): The first loop to check for float connection.
|
|
140
|
-
v (Loop): The second loop to check for float connection.
|
|
141
|
-
|
|
142
|
-
Returns:
|
|
143
|
-
bool: True if there is a float edge between the loops, False otherwise.
|
|
144
|
-
"""
|
|
145
|
-
return bool(self.loop_graph.has_edge(u, v))
|
|
146
|
-
|
|
147
|
-
def add_loop_in_front_of_float(self, front_loop: Loop, u: Loop, v: Loop) -> None:
|
|
148
|
-
"""Record that a loop falls in front of the float between two other loops.
|
|
149
|
-
|
|
150
|
-
Args:
|
|
151
|
-
front_loop (Loop): The loop that is positioned in front of the float.
|
|
152
|
-
u (Loop): The first loop in the float pair.
|
|
153
|
-
v (Loop): The second loop in the float pair.
|
|
154
|
-
"""
|
|
155
|
-
if not self.has_float(u, v):
|
|
156
|
-
if self.has_float(v, u):
|
|
157
|
-
self.add_loop_in_front_of_float(front_loop, v, u)
|
|
158
|
-
else:
|
|
159
|
-
return
|
|
160
|
-
self.loop_graph.edges[u, v][self.FRONT_LOOPS].add(front_loop)
|
|
161
|
-
front_loop.add_loop_in_front_of_float(u, v)
|
|
162
|
-
|
|
163
|
-
def add_loop_behind_float(self, back_loop: Loop, u: Loop, v: Loop) -> None:
|
|
164
|
-
"""Record that a loop falls behind the float between two other loops.
|
|
165
|
-
|
|
166
|
-
Args:
|
|
167
|
-
back_loop (Loop): The loop that is positioned behind the float.
|
|
168
|
-
u (Loop): The first loop in the float pair.
|
|
169
|
-
v (Loop): The second loop in the float pair.
|
|
170
|
-
"""
|
|
171
|
-
if not self.has_float(u, v):
|
|
172
|
-
if self.has_float(v, u):
|
|
173
|
-
self.add_loop_behind_float(back_loop, v, u)
|
|
174
|
-
else:
|
|
175
|
-
return
|
|
176
|
-
self.loop_graph.edges[u, v][self._BACK_LOOPS].add(back_loop)
|
|
177
|
-
back_loop.add_loop_behind_float(u, v)
|
|
178
|
-
|
|
179
|
-
def get_loops_in_front_of_float(self, u: Loop, v: Loop) -> set[Loop]:
|
|
180
|
-
"""Get all loops positioned in front of the float between two loops.
|
|
181
|
-
|
|
182
|
-
Args:
|
|
183
|
-
u (Loop): The first loop in the float pair.
|
|
184
|
-
v (Loop): The second loop in the float pair.
|
|
185
|
-
|
|
186
|
-
Returns:
|
|
187
|
-
set[Loop]: Set of loops positioned in front of the float between u and v, or empty set if no float exists.
|
|
188
|
-
"""
|
|
189
|
-
if not self.has_float(u, v):
|
|
190
|
-
if self.has_float(v, u):
|
|
191
|
-
return self.get_loops_in_front_of_float(v, u)
|
|
192
|
-
else:
|
|
193
|
-
return set()
|
|
194
|
-
else:
|
|
195
|
-
return cast(set[Loop], self.loop_graph.edges[u, v][self.FRONT_LOOPS])
|
|
196
|
-
|
|
197
|
-
def get_loops_behind_float(self, u: Loop, v: Loop) -> set[Loop]:
|
|
198
|
-
"""Get all loops positioned behind the float between two loops.
|
|
199
|
-
|
|
200
|
-
Args:
|
|
201
|
-
u (Loop): The first loop in the float pair.
|
|
202
|
-
v (Loop): The second loop in the float pair.
|
|
203
|
-
|
|
204
|
-
Returns:
|
|
205
|
-
set[Loop]: Set of loops positioned behind the float between u and v, or empty set if no float exists.
|
|
206
|
-
"""
|
|
207
|
-
if not self.has_float(u, v):
|
|
208
|
-
if self.has_float(v, u):
|
|
209
|
-
return self.get_loops_behind_float(v, u)
|
|
210
|
-
else:
|
|
211
|
-
return set()
|
|
212
|
-
else:
|
|
213
|
-
return cast(set[Loop], self.loop_graph.edges[u, v][self._BACK_LOOPS])
|
|
214
|
-
|
|
215
133
|
@property
|
|
216
|
-
def last_loop(self) ->
|
|
134
|
+
def last_loop(self) -> LoopT | None:
|
|
217
135
|
"""Get the most recently added loop at the end of the yarn.
|
|
218
136
|
|
|
219
137
|
Returns:
|
|
@@ -222,7 +140,7 @@ class Yarn:
|
|
|
222
140
|
return self._last_loop
|
|
223
141
|
|
|
224
142
|
@property
|
|
225
|
-
def first_loop(self) ->
|
|
143
|
+
def first_loop(self) -> LoopT | None:
|
|
226
144
|
"""Get the first loop at the beginning of the yarn.
|
|
227
145
|
|
|
228
146
|
Returns:
|
|
@@ -239,6 +157,14 @@ class Yarn:
|
|
|
239
157
|
"""
|
|
240
158
|
return self.last_loop is not None
|
|
241
159
|
|
|
160
|
+
@property
|
|
161
|
+
def is_cut(self) -> bool:
|
|
162
|
+
"""
|
|
163
|
+
Returns:
|
|
164
|
+
bool: True if yarn has been cut and will no longer form loops, False otherwise.
|
|
165
|
+
"""
|
|
166
|
+
return self._is_cut
|
|
167
|
+
|
|
242
168
|
@property
|
|
243
169
|
def yarn_id(self) -> str:
|
|
244
170
|
"""Get the string identifier for this yarn.
|
|
@@ -248,105 +174,194 @@ class Yarn:
|
|
|
248
174
|
"""
|
|
249
175
|
return str(self.properties)
|
|
250
176
|
|
|
251
|
-
|
|
252
|
-
|
|
177
|
+
@property
|
|
178
|
+
def float_iter(self) -> Iterator[tuple[LoopT, LoopT]]:
|
|
179
|
+
"""
|
|
180
|
+
Returns:
|
|
181
|
+
Iterator[tuple[Loop, Loop]]: An iterator over tuples of connected loops representing the yarn path.
|
|
182
|
+
"""
|
|
183
|
+
if self.first_loop is None:
|
|
184
|
+
return iter([])
|
|
185
|
+
return self.dfs_edges(self.first_loop)
|
|
186
|
+
|
|
187
|
+
def loops_in_front_of_floats(self) -> Iterator[tuple[LoopT, LoopT, set[LoopT]]]:
|
|
188
|
+
"""Get all float segments with loops positioned in front of them.
|
|
253
189
|
|
|
254
190
|
Returns:
|
|
255
|
-
|
|
191
|
+
list[tuple[Loop, Loop, set[Loop]]]: List of tuples containing the two loops defining each float and the set of loops positioned in front of that float.
|
|
192
|
+
Only includes floats that have loops in front of them.
|
|
256
193
|
"""
|
|
257
|
-
return self.
|
|
194
|
+
return ((u, v, d.front_loops) for u, v, d in self.edge_iter if len(d.front_loops) > 0)
|
|
258
195
|
|
|
259
|
-
def
|
|
260
|
-
"""Get
|
|
196
|
+
def loops_behind_floats(self) -> Iterator[tuple[LoopT, LoopT, set[LoopT]]]:
|
|
197
|
+
"""Get all float segments with loops positioned behind them.
|
|
261
198
|
|
|
262
199
|
Returns:
|
|
263
|
-
|
|
200
|
+
list[tuple[Loop, Loop, set[Loop]]]: List of tuples containing the two loops defining each float and the set of loops positioned behind that float.
|
|
201
|
+
Only includes floats that have loops behind them.
|
|
264
202
|
"""
|
|
265
|
-
return
|
|
203
|
+
return ((u, v, d.back_loops) for u, v, d in self.edge_iter if len(d.back_loops) > 0)
|
|
266
204
|
|
|
267
|
-
def
|
|
268
|
-
"""
|
|
205
|
+
def next_loop(self, loop: LoopT) -> LoopT | None:
|
|
206
|
+
"""
|
|
207
|
+
Args:
|
|
208
|
+
loop (LoopT): The loop to find the next loop from.
|
|
269
209
|
|
|
270
210
|
Returns:
|
|
271
|
-
|
|
211
|
+
LoopT | None: The next loop on yarn after the specified loop, or None if it's the last loop.
|
|
212
|
+
|
|
213
|
+
Raises:
|
|
214
|
+
KeyError: If the specified loop is not on this yarn.
|
|
272
215
|
"""
|
|
273
|
-
|
|
216
|
+
if loop not in self:
|
|
217
|
+
raise KeyError(f"Loop {loop} is not on Yarn")
|
|
218
|
+
successors = self.successors(loop)
|
|
219
|
+
return successors.pop() if len(successors) > 0 else None
|
|
274
220
|
|
|
275
|
-
def
|
|
276
|
-
"""
|
|
221
|
+
def prior_loop(self, loop: LoopT) -> LoopT | None:
|
|
222
|
+
"""
|
|
223
|
+
Args:
|
|
224
|
+
loop (Loop): The loop to find the prior loop from.
|
|
277
225
|
|
|
278
226
|
Returns:
|
|
279
|
-
|
|
227
|
+
Loop | None: The prior loop on yarn before the specified loop, or None if it's the first loop.
|
|
228
|
+
|
|
229
|
+
Raises:
|
|
230
|
+
KeyError: If the specified loop is not on this yarn.
|
|
280
231
|
"""
|
|
281
|
-
|
|
232
|
+
if loop not in self:
|
|
233
|
+
raise KeyError(f"Loop {loop} is not on Yarn")
|
|
234
|
+
predecessors = self.predecessors(loop)
|
|
235
|
+
if len(predecessors) > 0:
|
|
236
|
+
return predecessors.pop()
|
|
237
|
+
else:
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
def has_float(self, u: LoopT, v: LoopT) -> bool:
|
|
241
|
+
"""Check if there is a float edge between two loops on this yarn.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
u (Loop): The first loop to check for float connection.
|
|
245
|
+
v (Loop): The second loop to check for float connection.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
bool: True if there is a float edge between the loops, False otherwise.
|
|
249
|
+
"""
|
|
250
|
+
return bool(self.has_edge(u, v))
|
|
251
|
+
|
|
252
|
+
def get_loops_in_front_of_float(self, u: LoopT, v: LoopT) -> set[LoopT]:
|
|
253
|
+
"""Get all loops positioned in front of the float between two loops.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
u (Loop): The first loop in the float pair.
|
|
257
|
+
v (Loop): The second loop in the float pair.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
set[Loop]: Set of loops positioned in front of the float between u and v, or empty set if no float exists.
|
|
261
|
+
"""
|
|
262
|
+
if not self.has_float(u, v):
|
|
263
|
+
if self.has_float(v, u):
|
|
264
|
+
return self.get_loops_in_front_of_float(v, u)
|
|
265
|
+
else:
|
|
266
|
+
return set()
|
|
267
|
+
else:
|
|
268
|
+
return self.get_edge(u, v).front_loops
|
|
269
|
+
|
|
270
|
+
def get_loops_behind_float(self, u: LoopT, v: LoopT) -> set[LoopT]:
|
|
271
|
+
"""Get all loops positioned behind the float between two loops.
|
|
282
272
|
|
|
283
|
-
|
|
273
|
+
Args:
|
|
274
|
+
u (Loop): The first loop in the float pair.
|
|
275
|
+
v (Loop): The second loop in the float pair.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
set[Loop]: Set of loops positioned behind the float between u and v, or empty set if no float exists.
|
|
279
|
+
"""
|
|
280
|
+
if not self.has_float(u, v):
|
|
281
|
+
if self.has_float(v, u):
|
|
282
|
+
return self.get_loops_behind_float(v, u)
|
|
283
|
+
else:
|
|
284
|
+
return set()
|
|
285
|
+
else:
|
|
286
|
+
return self.get_edge(u, v).back_loops
|
|
287
|
+
|
|
288
|
+
def make_loop_on_end(self) -> LoopT:
|
|
284
289
|
"""Create and add a new loop at the end of the yarn.
|
|
285
290
|
|
|
286
291
|
Returns:
|
|
287
292
|
Loop: The newly created loop that was added to the end of this yarn.
|
|
288
293
|
"""
|
|
289
294
|
loop_id = self._next_loop_id()
|
|
290
|
-
|
|
291
|
-
return self.add_loop_to_end(loop)
|
|
295
|
+
return self.add_loop_to_end(self._new_loop(loop_id))
|
|
292
296
|
|
|
293
297
|
def _next_loop_id(self) -> int:
|
|
294
|
-
"""
|
|
295
|
-
|
|
298
|
+
"""
|
|
296
299
|
Returns:
|
|
297
300
|
int: The ID of the next loop to be added to this yarn based on the knit graph or, if no knit graph is associated with this yarn, based on the last loop on this yarn.
|
|
298
301
|
"""
|
|
299
|
-
if self.knit_graph is
|
|
300
|
-
if self.knit_graph.last_loop is None:
|
|
301
|
-
return 0
|
|
302
|
-
else:
|
|
303
|
-
return self.knit_graph.last_loop.loop_id + 1
|
|
304
|
-
elif self.last_loop is None:
|
|
302
|
+
if self.knit_graph.last_loop is None:
|
|
305
303
|
return 0
|
|
306
304
|
else:
|
|
307
|
-
return self.last_loop.loop_id + 1
|
|
305
|
+
return self.knit_graph.last_loop.loop_id + 1
|
|
306
|
+
|
|
307
|
+
def _new_loop(self, loop_id: int) -> LoopT:
|
|
308
|
+
"""
|
|
309
|
+
Args:
|
|
310
|
+
loop_id (int): The loop id to create a new loop.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
LoopT: A loop on this yarn with the given id.
|
|
314
|
+
|
|
315
|
+
Raises:
|
|
316
|
+
Use_Cut_Yarn_ValueError: If the yarn has been cut and can no longer form loops.
|
|
317
|
+
"""
|
|
318
|
+
if self.is_cut:
|
|
319
|
+
raise Use_Cut_Yarn_ValueError(self)
|
|
320
|
+
elif self.first_loop is not None:
|
|
321
|
+
return self.first_loop.__class__(loop_id, self, self.knit_graph) # type: ignore[arg-type]
|
|
322
|
+
else:
|
|
323
|
+
return self._loop_class(loop_id, self, self.knit_graph) # type: ignore[arg-type]
|
|
308
324
|
|
|
309
|
-
def remove_loop(self, loop:
|
|
325
|
+
def remove_loop(self, loop: LoopT | int) -> None:
|
|
310
326
|
"""
|
|
311
327
|
Remove the given loop from the yarn.
|
|
312
328
|
Reconnects any neighboring loops to form a new float with the positioned in-front-of or behind the original floats positioned accordingly.
|
|
313
329
|
Resets the first_loop and last_loop properties if the removed loop was the tail of the yarn.
|
|
314
330
|
Args:
|
|
315
|
-
loop (
|
|
331
|
+
loop (LoopT): The loop to remove from the yarn.
|
|
316
332
|
|
|
317
333
|
Raises:
|
|
318
334
|
KeyError: The given loop does not exist in the yarn.
|
|
319
335
|
"""
|
|
320
336
|
if loop not in self:
|
|
321
337
|
raise KeyError(f"Loop {loop} does not exist on yarn {self}.")
|
|
338
|
+
elif isinstance(loop, int):
|
|
339
|
+
loop = self[loop]
|
|
322
340
|
prior_loop = self.prior_loop(loop)
|
|
323
341
|
next_loop = self.next_loop(loop)
|
|
324
|
-
if
|
|
342
|
+
if prior_loop is not None and next_loop is not None: # Loop is between two floats to be merged.
|
|
325
343
|
front_of_float_loops = self.get_loops_in_front_of_float(prior_loop, loop)
|
|
326
344
|
front_of_float_loops.update(self.get_loops_in_front_of_float(loop, next_loop))
|
|
327
345
|
back_of_float_loops = self.get_loops_behind_float(prior_loop, loop)
|
|
328
346
|
back_of_float_loops.update(self.get_loops_behind_float(loop, next_loop))
|
|
329
|
-
|
|
330
|
-
self.
|
|
347
|
+
super().remove_loop(loop)
|
|
348
|
+
self.add_edge(
|
|
331
349
|
prior_loop,
|
|
332
350
|
next_loop,
|
|
333
|
-
|
|
334
|
-
Back_Loops=back_of_float_loops,
|
|
351
|
+
Float_Edge[LoopT](front_loops=front_of_float_loops, back_loops=back_of_float_loops),
|
|
335
352
|
)
|
|
336
353
|
for front_loop in front_of_float_loops:
|
|
337
354
|
front_loop.add_loop_in_front_of_float(prior_loop, next_loop)
|
|
338
355
|
for back_loop in back_of_float_loops:
|
|
339
356
|
back_loop.add_loop_behind_float(prior_loop, next_loop)
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
def add_loop_to_end(self, loop: Loop) -> Loop:
|
|
357
|
+
else:
|
|
358
|
+
super().remove_loop(loop)
|
|
359
|
+
if next_loop is None: # This was the last loop, make the prior loop the last loop.
|
|
360
|
+
self._last_loop = prior_loop
|
|
361
|
+
if prior_loop is None: # This was the first loop, make the next loop the first loop.
|
|
362
|
+
self._first_loop = next_loop
|
|
363
|
+
|
|
364
|
+
def add_loop_to_end(self, loop: LoopT) -> LoopT:
|
|
350
365
|
"""Add an existing loop to the end of this yarn and associated knit graph.
|
|
351
366
|
|
|
352
367
|
Args:
|
|
@@ -360,130 +375,128 @@ class Yarn:
|
|
|
360
375
|
self.knit_graph.add_loop(loop)
|
|
361
376
|
return loop
|
|
362
377
|
|
|
363
|
-
def insert_loop(self, loop:
|
|
378
|
+
def insert_loop(self, loop: LoopT, prior_loop: LoopT | None = None) -> None:
|
|
364
379
|
"""Insert a loop into the yarn sequence after the specified prior loop.
|
|
365
380
|
|
|
366
381
|
Args:
|
|
367
382
|
loop (Loop): The loop to be added to this yarn.
|
|
368
383
|
prior_loop (Loop | None): The loop that should come before this loop on the yarn. If None, defaults to the last loop (adding to end of yarn).
|
|
369
384
|
"""
|
|
370
|
-
|
|
371
|
-
if
|
|
385
|
+
super().add_loop(loop)
|
|
386
|
+
if self.last_loop is None:
|
|
372
387
|
self._last_loop = loop
|
|
373
388
|
self._first_loop = loop
|
|
374
389
|
return
|
|
375
|
-
|
|
390
|
+
if prior_loop is None:
|
|
376
391
|
prior_loop = self.last_loop
|
|
377
|
-
|
|
392
|
+
super().add_edge(prior_loop, loop, Float_Edge[LoopT]())
|
|
378
393
|
if prior_loop == self.last_loop:
|
|
379
394
|
self._last_loop = loop
|
|
380
395
|
|
|
381
|
-
def
|
|
382
|
-
"""
|
|
396
|
+
def add_loop_in_front_of_float(self, front_loop: LoopT, start_of_float: LoopT) -> None:
|
|
397
|
+
"""Record that a loop falls in front of the float between two other loops.
|
|
383
398
|
|
|
384
399
|
Args:
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
Returns:
|
|
388
|
-
Loop | None: The next loop on yarn after the specified loop, or None if it's the last loop.
|
|
389
|
-
|
|
390
|
-
Raises:
|
|
391
|
-
KeyError: If the specified loop is not on this yarn.
|
|
400
|
+
front_loop (Loop): The loop that is positioned in front of the float.
|
|
401
|
+
start_of_float (Loop): The first loop in the float pair.
|
|
392
402
|
"""
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
return cast(Loop, successors[0])
|
|
398
|
-
else:
|
|
399
|
-
return None
|
|
400
|
-
|
|
401
|
-
def prior_loop(self, loop: Loop) -> Loop | None:
|
|
402
|
-
"""Get the loop that precedes the specified loop on this yarn.
|
|
403
|
+
end_of_float = self.next_loop(start_of_float)
|
|
404
|
+
if end_of_float is not None:
|
|
405
|
+
self.get_edge(start_of_float, end_of_float).add_loop_in_front_of_float(front_loop)
|
|
406
|
+
front_loop.add_loop_in_front_of_float(start_of_float, end_of_float)
|
|
403
407
|
|
|
408
|
+
def remove_loop_relative_to_floats(self, loop: LoopT) -> None:
|
|
409
|
+
"""
|
|
410
|
+
Removes the given loop from positions relative to floats along this yarn.
|
|
404
411
|
Args:
|
|
405
|
-
loop (
|
|
406
|
-
|
|
407
|
-
Returns:
|
|
408
|
-
Loop | None: The prior loop on yarn before the specified loop, or None if it's the first loop.
|
|
409
|
-
|
|
410
|
-
Raises:
|
|
411
|
-
KeyError: If the specified loop is not on this yarn.
|
|
412
|
+
loop (LoopT): The loop to remove from positions relative to floats on this yarn.
|
|
412
413
|
"""
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
predecessors = [*self.loop_graph.predecessors(loop)]
|
|
416
|
-
if len(predecessors) > 0:
|
|
417
|
-
return cast(Loop, predecessors[0])
|
|
418
|
-
else:
|
|
419
|
-
return None
|
|
414
|
+
for _u, _v, float_data in self.edge_iter:
|
|
415
|
+
float_data.remove_loop_relative_to_floats(loop)
|
|
420
416
|
|
|
421
|
-
def
|
|
422
|
-
"""
|
|
417
|
+
def add_loop_behind_float(self, back_loop: LoopT, start_of_float: LoopT) -> None:
|
|
418
|
+
"""Record that a loop falls in front of the float between two other loops.
|
|
423
419
|
|
|
424
420
|
Args:
|
|
425
|
-
|
|
421
|
+
back_loop (Loop): The loop that is positioned behind the float.
|
|
422
|
+
start_of_float (Loop): The first loop in the float pair.
|
|
423
|
+
"""
|
|
424
|
+
end_of_float = self.next_loop(start_of_float)
|
|
425
|
+
if end_of_float is not None:
|
|
426
|
+
self.get_edge(start_of_float, end_of_float).add_loop_behind_float(back_loop)
|
|
427
|
+
back_loop.add_loop_behind_float(start_of_float, end_of_float)
|
|
428
|
+
|
|
429
|
+
def cut_yarn(self) -> Self:
|
|
430
|
+
"""Cut yarn to make it no longer active and create a new yarn instance of the same type.
|
|
426
431
|
|
|
427
432
|
Returns:
|
|
428
|
-
|
|
433
|
+
Yarn[LoopT]: New yarn of the same type after cutting this yarn.
|
|
429
434
|
"""
|
|
430
|
-
|
|
435
|
+
self._is_cut = True
|
|
436
|
+
return self.__class__(self.properties, self.knit_graph, self._instance + 1, self._loop_class)
|
|
431
437
|
|
|
432
|
-
def
|
|
433
|
-
"""
|
|
438
|
+
def __str__(self) -> str:
|
|
439
|
+
"""Get the string representation of this yarn.
|
|
434
440
|
|
|
435
441
|
Returns:
|
|
436
|
-
|
|
442
|
+
str: The yarn identifier string.
|
|
437
443
|
"""
|
|
438
|
-
return
|
|
444
|
+
return self.yarn_id
|
|
439
445
|
|
|
440
|
-
def
|
|
441
|
-
"""
|
|
446
|
+
def __repr__(self) -> str:
|
|
447
|
+
"""Get the representation string of this yarn for debugging.
|
|
442
448
|
|
|
443
449
|
Returns:
|
|
444
|
-
|
|
450
|
+
str: The representation of the yarn properties.
|
|
445
451
|
"""
|
|
446
|
-
return
|
|
452
|
+
return repr(self.properties)
|
|
447
453
|
|
|
448
|
-
def
|
|
449
|
-
"""Get
|
|
454
|
+
def __hash__(self) -> int:
|
|
455
|
+
"""Get the hash value of this yarn for use in sets and dictionaries.
|
|
450
456
|
|
|
451
457
|
Returns:
|
|
452
|
-
|
|
453
|
-
Only includes floats that have loops in front of them.
|
|
458
|
+
int: Hash value based on the yarn properties.
|
|
454
459
|
"""
|
|
455
|
-
return
|
|
456
|
-
(u, v, self.get_loops_in_front_of_float(u, v))
|
|
457
|
-
for u, v in self.edge_iter()
|
|
458
|
-
if len(self.get_loops_in_front_of_float(u, v)) > 0
|
|
459
|
-
]
|
|
460
|
+
return hash((self._instance, self.properties))
|
|
460
461
|
|
|
461
|
-
def
|
|
462
|
-
"""
|
|
462
|
+
def __iter__(self) -> Iterator[LoopT]:
|
|
463
|
+
"""Iterate over loops on this yarn in sequence from first to last.
|
|
463
464
|
|
|
464
465
|
Returns:
|
|
465
|
-
|
|
466
|
-
Only includes floats that have loops behind them.
|
|
466
|
+
Iterator[Loop]: An iterator over the loops on this yarn in their natural sequence order.
|
|
467
467
|
"""
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
468
|
+
if self.first_loop is None:
|
|
469
|
+
return iter([])
|
|
470
|
+
return self.dfs_preorder_loops(self.first_loop)
|
|
471
|
+
|
|
472
|
+
@overload
|
|
473
|
+
def __getitem__(self, item: int) -> LoopT: ...
|
|
474
|
+
|
|
475
|
+
@overload
|
|
476
|
+
def __getitem__(self, item: tuple[LoopT | int, LoopT | int]) -> Float_Edge[LoopT]: ...
|
|
477
|
+
|
|
478
|
+
@overload
|
|
479
|
+
def __getitem__(self, item: slice) -> list[LoopT]: ...
|
|
473
480
|
|
|
474
|
-
def __getitem__(
|
|
481
|
+
def __getitem__(
|
|
482
|
+
self, item: int | tuple[LoopT | int, LoopT | int] | slice
|
|
483
|
+
) -> LoopT | Float_Edge[LoopT] | list[LoopT]:
|
|
475
484
|
"""Get a loop by its ID from this yarn.
|
|
476
485
|
|
|
477
486
|
Args:
|
|
478
|
-
item (int
|
|
487
|
+
item (int | tuple[LoopT | int, LoopT | int] | slice):
|
|
488
|
+
The loop ,loop ID, or float between two loops to retrieve from this yarn.
|
|
489
|
+
If given a slice, it will retrieve the elements between the specified indices in the standard ordering of loops along the yarn.
|
|
479
490
|
|
|
480
491
|
Returns:
|
|
481
|
-
|
|
492
|
+
LoopT: The loop on the yarn with the matching ID.
|
|
493
|
+
Float_Edge[LoopT]: The float data for the given pair of loops forming a float.
|
|
494
|
+
list[LoopT]: The loops in the slice of the yarn based on their ordering along the yarn.
|
|
482
495
|
|
|
483
496
|
Raises:
|
|
484
|
-
KeyError: If the
|
|
497
|
+
KeyError: If the item is not found on this yarn.
|
|
485
498
|
"""
|
|
486
|
-
if item
|
|
487
|
-
|
|
499
|
+
if isinstance(item, slice):
|
|
500
|
+
return list(self)[item]
|
|
488
501
|
else:
|
|
489
|
-
return
|
|
502
|
+
return super().__getitem__(item)
|