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/Loop.py
CHANGED
|
@@ -6,9 +6,13 @@ Loops are the fundamental building blocks of knitted structures and maintain rel
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
from typing import TYPE_CHECKING, Self, cast
|
|
11
|
+
|
|
12
|
+
from knit_graphs.Pull_Direction import Pull_Direction
|
|
10
13
|
|
|
11
14
|
if TYPE_CHECKING:
|
|
15
|
+
from knit_graphs.Knit_Graph import Knit_Graph
|
|
12
16
|
from knit_graphs.Yarn import Yarn
|
|
13
17
|
|
|
14
18
|
|
|
@@ -19,94 +23,94 @@ class Loop:
|
|
|
19
23
|
Each loop maintains its position in the yarn sequence and its relationships to other loops through stitch connections and floating elements.
|
|
20
24
|
|
|
21
25
|
Attributes:
|
|
22
|
-
yarn (Yarn): The yarn that creates and holds this loop.
|
|
26
|
+
yarn (Yarn[Self]): The yarn that creates and holds this loop.
|
|
23
27
|
parent_loops (list[Loop]): The list of parent loops that this loop is connected to through stitch edges.
|
|
24
|
-
front_floats (dict[Loop, set[Loop]]):
|
|
25
|
-
back_floats (dict[Loop, set[Loop]]):
|
|
28
|
+
front_floats (dict[Loop, set[Loop]]): Mapping of loops involved in floats in front of this loop to the paired loops in the float.
|
|
29
|
+
back_floats (dict[Loop, set[Loop]]): Mapping of loops involved in floats behind this loop to the paired loops in the float.
|
|
26
30
|
"""
|
|
27
31
|
|
|
28
|
-
def __init__(self, loop_id: int, yarn: Yarn) -> None:
|
|
32
|
+
def __init__(self, loop_id: int, yarn: Yarn[Self], knit_graph: Knit_Graph[Self]) -> None:
|
|
29
33
|
"""Construct a Loop object with the specified identifier and yarn.
|
|
30
34
|
|
|
31
35
|
Args:
|
|
32
36
|
loop_id (int): A unique identifier for the loop, must be non-negative.
|
|
33
|
-
yarn (
|
|
37
|
+
yarn (Typed_Yarn): The yarn that creates and holds this loop.
|
|
34
38
|
"""
|
|
35
39
|
if loop_id < 0:
|
|
36
40
|
raise ValueError(f"Loop identifier must be non-negative but got {loop_id}")
|
|
37
41
|
self._loop_id: int = loop_id
|
|
38
|
-
self.yarn: Yarn = yarn
|
|
39
|
-
self.
|
|
40
|
-
self.
|
|
41
|
-
self.
|
|
42
|
+
self.yarn: Yarn[Self] = yarn
|
|
43
|
+
self._knit_graph: Knit_Graph[Self] = knit_graph
|
|
44
|
+
self.parent_loops: list[Self] = []
|
|
45
|
+
self.front_floats: dict[Self, set[Self]] = {}
|
|
46
|
+
self.back_floats: dict[Self, set[Self]] = {}
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
@property
|
|
49
|
+
def loop_id(self) -> int:
|
|
50
|
+
"""Get the unique identifier of this loop.
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
Returns:
|
|
53
|
+
int: The id of the loop.
|
|
54
|
+
"""
|
|
55
|
+
return self._loop_id
|
|
47
56
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
@property
|
|
58
|
+
def has_parent_loops(self) -> bool:
|
|
59
|
+
"""Check if this loop has any parent loops connected through stitch edges.
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
Returns:
|
|
62
|
+
bool: True if the loop has stitch-edge parents, False otherwise.
|
|
54
63
|
"""
|
|
55
|
-
|
|
56
|
-
raise ValueError("Loops of a float must share a yarn.")
|
|
57
|
-
if u not in self.back_floats:
|
|
58
|
-
self.back_floats[u] = set()
|
|
59
|
-
if v not in self.back_floats:
|
|
60
|
-
self.back_floats[v] = set()
|
|
61
|
-
self.back_floats[u].add(v)
|
|
62
|
-
self.back_floats[v].add(u)
|
|
64
|
+
return len(self.parent_loops) > 0
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
@property
|
|
67
|
+
def parent_loop_ids(self) -> list[int]:
|
|
65
68
|
"""
|
|
66
|
-
|
|
69
|
+
Returns:
|
|
70
|
+
list[int]: The ids of the parent loops of this loop in their stacking order.
|
|
67
71
|
"""
|
|
68
|
-
|
|
69
|
-
for u, v_loops in self.front_floats.items():
|
|
70
|
-
visited.add(u)
|
|
71
|
-
for v in v_loops:
|
|
72
|
-
if v not in visited and v in u.yarn: # float shares a yarn
|
|
73
|
-
u.yarn.loop_graph.edges[u, v]["Back_Loops"].remove(self)
|
|
74
|
-
self.front_floats = {}
|
|
75
|
-
|
|
76
|
-
def add_loop_behind_float(self, u: Loop, v: Loop) -> None:
|
|
77
|
-
"""Set this loop to be behind the float between loops u and v.
|
|
78
|
-
|
|
79
|
-
This method establishes that this loop passes behind a floating yarn segment between two other loops.
|
|
72
|
+
return [p.loop_id for p in self.parent_loops]
|
|
80
73
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
@property
|
|
75
|
+
def pull_directions(self) -> list[Pull_Direction]:
|
|
76
|
+
"""
|
|
77
|
+
Returns:
|
|
78
|
+
list[Pull_Direction]: The pull direction of the stitches formed by this loop and its parents in stacking order of its parents.
|
|
79
|
+
"""
|
|
80
|
+
return [self._knit_graph.get_pull_direction(p, cast(Self, self)) for p in self.parent_loops]
|
|
84
81
|
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
@property
|
|
83
|
+
def pull_direction(self) -> Pull_Direction:
|
|
87
84
|
"""
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
Returns:
|
|
86
|
+
Pull_Direction: The majority pull-direction of the stitches formed with the parent loops or Back-To-Front (knit-stitch) if this loop has no parents.
|
|
87
|
+
"""
|
|
88
|
+
if not self.has_parent_loops:
|
|
89
|
+
return Pull_Direction.BtF
|
|
90
|
+
elif len(self.parent_loops) == 1:
|
|
91
|
+
return self._knit_graph.get_pull_direction(self.parent_loops[0], cast(Self, self))
|
|
92
|
+
else:
|
|
93
|
+
knits = len([pd for pd in self.pull_directions if pd is Pull_Direction.BtF])
|
|
94
|
+
purls = len(self.parent_loops) - knits
|
|
95
|
+
return Pull_Direction.BtF if knits > purls else Pull_Direction.FtB
|
|
96
|
+
|
|
97
|
+
def prior_loop_on_yarn(self) -> Self | None:
|
|
98
|
+
"""Get the loop that precedes this loop on the same yarn.
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
Returns:
|
|
101
|
+
Loop | None: The prior loop on the yarn, or None if this is the first loop on the yarn.
|
|
98
102
|
"""
|
|
99
|
-
|
|
103
|
+
return self.yarn.prior_loop(self)
|
|
104
|
+
|
|
105
|
+
def next_loop_on_yarn(self) -> Self | None:
|
|
106
|
+
"""Get the loop that follows this loop on the same yarn.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Loop | None: The next loop on the yarn, or None if this is the last loop on the yarn.
|
|
100
110
|
"""
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
for v in v_loops:
|
|
105
|
-
if v not in visited and v in u.yarn: # float shares a yarn
|
|
106
|
-
u.yarn.loop_graph.edges[u, v]["Front_Loops"].remove(self)
|
|
107
|
-
self.back_floats = {}
|
|
108
|
-
|
|
109
|
-
def is_in_front_of_float(self, u: Loop, v: Loop) -> bool:
|
|
111
|
+
return self.yarn.next_loop(self)
|
|
112
|
+
|
|
113
|
+
def is_in_front_of_float(self, u: Self, v: Self) -> bool:
|
|
110
114
|
"""Check if this loop is positioned in front of the float between loops u and v.
|
|
111
115
|
|
|
112
116
|
Args:
|
|
@@ -118,7 +122,7 @@ class Loop:
|
|
|
118
122
|
"""
|
|
119
123
|
return u in self.back_floats and v in self.back_floats and v in self.back_floats[u]
|
|
120
124
|
|
|
121
|
-
def is_behind_float(self, u:
|
|
125
|
+
def is_behind_float(self, u: Self, v: Self) -> bool:
|
|
122
126
|
"""Check if this loop is positioned behind the float between loops u and v.
|
|
123
127
|
|
|
124
128
|
Args:
|
|
@@ -130,41 +134,67 @@ class Loop:
|
|
|
130
134
|
"""
|
|
131
135
|
return u in self.front_floats and v in self.front_floats and v in self.front_floats[u]
|
|
132
136
|
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
@staticmethod
|
|
138
|
+
def majority_pull_direction(loops: Sequence[Loop]) -> Pull_Direction:
|
|
139
|
+
"""
|
|
140
|
+
Args:
|
|
141
|
+
loops (Sequence[Loop]): The loops to find the majority pull direction of.
|
|
135
142
|
|
|
136
143
|
Returns:
|
|
137
|
-
|
|
144
|
+
Pull_Direction: The majority pull direction of the given loops or Back-to-Front (i.e., knit-stitch) there are no loops.
|
|
138
145
|
"""
|
|
139
|
-
|
|
146
|
+
if len(loops) == 0:
|
|
147
|
+
return Pull_Direction.BtF
|
|
148
|
+
elif len(loops) == 1:
|
|
149
|
+
return loops[0].pull_direction
|
|
150
|
+
else:
|
|
151
|
+
knits = len([l.pull_direction for l in loops if l.pull_direction is Pull_Direction.BtF])
|
|
152
|
+
purls = len(loops) - knits
|
|
153
|
+
return Pull_Direction.BtF if knits > purls else Pull_Direction.FtB
|
|
140
154
|
|
|
141
|
-
def
|
|
142
|
-
"""
|
|
155
|
+
def add_loop_in_front_of_float(self, u: Self, v: Self) -> None:
|
|
156
|
+
"""Set this loop to be in front of the float between loops u and v.
|
|
143
157
|
|
|
144
|
-
|
|
145
|
-
Loop | None: The next loop on the yarn, or None if this is the last loop on the yarn.
|
|
146
|
-
"""
|
|
147
|
-
return self.yarn.next_loop(self)
|
|
158
|
+
This method establishes that this loop passes in front of a floating yarn segment between two other loops.
|
|
148
159
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
160
|
+
Args:
|
|
161
|
+
u (Loop): The first loop in the float pair.
|
|
162
|
+
v (Loop): The second loop in the float pair.
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
ValueError: If u and v are not on the same yarn.
|
|
154
166
|
"""
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
167
|
+
if u.yarn != v.yarn:
|
|
168
|
+
raise ValueError("Loops of a float must share a yarn.")
|
|
169
|
+
if u not in self.back_floats:
|
|
170
|
+
self.back_floats[u] = set()
|
|
171
|
+
if v not in self.back_floats:
|
|
172
|
+
self.back_floats[v] = set()
|
|
173
|
+
self.back_floats[u].add(v)
|
|
174
|
+
self.back_floats[v].add(u)
|
|
158
175
|
|
|
159
|
-
def
|
|
160
|
-
"""
|
|
176
|
+
def add_loop_behind_float(self, u: Self, v: Self) -> None:
|
|
177
|
+
"""Set this loop to be behind the float between loops u and v.
|
|
161
178
|
|
|
162
|
-
|
|
163
|
-
|
|
179
|
+
This method establishes that this loop passes behind a floating yarn segment between two other loops.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
u (Loop): The first loop in the float pair.
|
|
183
|
+
v (Loop): The second loop in the float pair.
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
ValueError: If u and v are not on the same yarn.
|
|
164
187
|
"""
|
|
165
|
-
|
|
188
|
+
if u.yarn != v.yarn:
|
|
189
|
+
raise ValueError("Loops of a float must share a yarn.")
|
|
190
|
+
if u not in self.front_floats:
|
|
191
|
+
self.front_floats[u] = set()
|
|
192
|
+
if v not in self.front_floats:
|
|
193
|
+
self.front_floats[v] = set()
|
|
194
|
+
self.front_floats[u].add(v)
|
|
195
|
+
self.front_floats[v].add(u)
|
|
166
196
|
|
|
167
|
-
def add_parent_loop(self, parent:
|
|
197
|
+
def add_parent_loop(self, parent: Self, stack_position: int | None = None) -> None:
|
|
168
198
|
"""Add a parent loop to this loop's parent stack.
|
|
169
199
|
|
|
170
200
|
Args:
|
|
@@ -176,7 +206,7 @@ class Loop:
|
|
|
176
206
|
else:
|
|
177
207
|
self.parent_loops.append(parent)
|
|
178
208
|
|
|
179
|
-
def remove_parent(self, parent:
|
|
209
|
+
def remove_parent(self, parent: Self) -> None:
|
|
180
210
|
"""
|
|
181
211
|
Removes the given parent loop from the set of parents of this loop.
|
|
182
212
|
If the given loop is not a parent of this loop, nothing happens.
|
|
@@ -186,30 +216,6 @@ class Loop:
|
|
|
186
216
|
if parent in self.parent_loops:
|
|
187
217
|
self.parent_loops.remove(parent)
|
|
188
218
|
|
|
189
|
-
def ancestor_loops(self) -> set[Loop]:
|
|
190
|
-
"""
|
|
191
|
-
Returns:
|
|
192
|
-
set[Loop]: The set of loops that initiate all wales that lead to this loop. The empty set if this loop has no parents.
|
|
193
|
-
"""
|
|
194
|
-
if not self.has_parent_loops():
|
|
195
|
-
return set()
|
|
196
|
-
ancestors = set()
|
|
197
|
-
for parent_loop in self.parent_loops:
|
|
198
|
-
if parent_loop.has_parent_loops():
|
|
199
|
-
ancestors.update(parent_loop.ancestor_loops())
|
|
200
|
-
else:
|
|
201
|
-
ancestors.add(parent_loop)
|
|
202
|
-
return ancestors
|
|
203
|
-
|
|
204
|
-
@property
|
|
205
|
-
def loop_id(self) -> int:
|
|
206
|
-
"""Get the unique identifier of this loop.
|
|
207
|
-
|
|
208
|
-
Returns:
|
|
209
|
-
int: The id of the loop.
|
|
210
|
-
"""
|
|
211
|
-
return self._loop_id
|
|
212
|
-
|
|
213
219
|
def __hash__(self) -> int:
|
|
214
220
|
"""Return hash value based on loop_id for use in sets and dictionaries.
|
|
215
221
|
|
|
@@ -230,12 +236,12 @@ class Loop:
|
|
|
230
236
|
"""Check equality with another base loop based on loop_id and type.
|
|
231
237
|
|
|
232
238
|
Args:
|
|
233
|
-
other (
|
|
239
|
+
other (Self): The other loop to compare with.
|
|
234
240
|
|
|
235
241
|
Returns:
|
|
236
242
|
bool: True if both loops have the same class and loop_id, False otherwise.
|
|
237
243
|
"""
|
|
238
|
-
return isinstance(other,
|
|
244
|
+
return isinstance(other, type(self)) and self.loop_id == other.loop_id
|
|
239
245
|
|
|
240
246
|
def __lt__(self, other: Loop | int) -> bool:
|
|
241
247
|
"""Compare loop_id with another loop or integer for ordering.
|
knit_graphs/Pull_Direction.py
CHANGED
|
@@ -18,6 +18,7 @@ class Pull_Direction(Enum):
|
|
|
18
18
|
BtF = "Knit"
|
|
19
19
|
FtB = "Purl"
|
|
20
20
|
|
|
21
|
+
@property
|
|
21
22
|
def opposite(self) -> Pull_Direction:
|
|
22
23
|
"""Get the opposite pull direction of this direction.
|
|
23
24
|
|
|
@@ -35,7 +36,7 @@ class Pull_Direction(Enum):
|
|
|
35
36
|
Returns:
|
|
36
37
|
Pull_Direction: The opposite pull direction, same as calling opposite().
|
|
37
38
|
"""
|
|
38
|
-
return self.opposite
|
|
39
|
+
return self.opposite
|
|
39
40
|
|
|
40
41
|
def __invert__(self) -> Pull_Direction:
|
|
41
42
|
"""Get the opposite pull direction using the bitwise inversion operator.
|
|
@@ -43,7 +44,7 @@ class Pull_Direction(Enum):
|
|
|
43
44
|
Returns:
|
|
44
45
|
Pull_Direction: The opposite pull direction, same as calling opposite().
|
|
45
46
|
"""
|
|
46
|
-
return self.opposite
|
|
47
|
+
return self.opposite
|
|
47
48
|
|
|
48
49
|
def __str__(self) -> str:
|
|
49
50
|
"""Get the string representation of the pull direction.
|