knit-graphs 0.0.10__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.
- docs/source/conf.py +71 -71
- docs/source/index.rst +0 -4
- knit_graphs/Course.py +64 -41
- knit_graphs/Knit_Graph.py +70 -162
- knit_graphs/Knit_Graph_Visualizer.py +386 -185
- knit_graphs/Loop.py +124 -117
- knit_graphs/Pull_Direction.py +5 -2
- knit_graphs/Yarn.py +257 -219
- knit_graphs/artin_wale_braids/Crossing_Direction.py +2 -0
- knit_graphs/artin_wale_braids/Loop_Braid_Graph.py +47 -56
- knit_graphs/artin_wale_braids/Wale.py +67 -79
- knit_graphs/artin_wale_braids/Wale_Braid.py +8 -2
- knit_graphs/artin_wale_braids/Wale_Braid_Word.py +45 -31
- knit_graphs/artin_wale_braids/Wale_Group.py +53 -43
- knit_graphs/basic_knit_graph_generators.py +96 -140
- 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/py.typed +0 -0
- {knit_graphs-0.0.10.dist-info → knit_graphs-0.0.12.dist-info}/METADATA +3 -2
- {knit_graphs-0.0.10.dist-info → knit_graphs-0.0.12.dist-info}/RECORD +24 -19
- {knit_graphs-0.0.10.dist-info → knit_graphs-0.0.12.dist-info}/LICENSE +0 -0
- {knit_graphs-0.0.10.dist-info → knit_graphs-0.0.12.dist-info}/WHEEL +0 -0
knit_graphs/Loop.py
CHANGED
|
@@ -3,11 +3,16 @@
|
|
|
3
3
|
This module defines the Loop class which represents individual loops in a knitting pattern.
|
|
4
4
|
Loops are the fundamental building blocks of knitted structures and maintain relationships with parent loops, yarn connections, and float positions.
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
from __future__ import annotations
|
|
7
8
|
|
|
8
|
-
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
|
|
9
13
|
|
|
10
14
|
if TYPE_CHECKING:
|
|
15
|
+
from knit_graphs.Knit_Graph import Knit_Graph
|
|
11
16
|
from knit_graphs.Yarn import Yarn
|
|
12
17
|
|
|
13
18
|
|
|
@@ -18,94 +23,94 @@ class Loop:
|
|
|
18
23
|
Each loop maintains its position in the yarn sequence and its relationships to other loops through stitch connections and floating elements.
|
|
19
24
|
|
|
20
25
|
Attributes:
|
|
21
|
-
yarn (Yarn): The yarn that creates and holds this loop.
|
|
26
|
+
yarn (Yarn[Self]): The yarn that creates and holds this loop.
|
|
22
27
|
parent_loops (list[Loop]): The list of parent loops that this loop is connected to through stitch edges.
|
|
23
|
-
front_floats (dict[Loop, set[Loop]]):
|
|
24
|
-
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.
|
|
25
30
|
"""
|
|
26
31
|
|
|
27
|
-
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:
|
|
28
33
|
"""Construct a Loop object with the specified identifier and yarn.
|
|
29
34
|
|
|
30
35
|
Args:
|
|
31
36
|
loop_id (int): A unique identifier for the loop, must be non-negative.
|
|
32
|
-
yarn (
|
|
37
|
+
yarn (Typed_Yarn): The yarn that creates and holds this loop.
|
|
33
38
|
"""
|
|
34
39
|
if loop_id < 0:
|
|
35
40
|
raise ValueError(f"Loop identifier must be non-negative but got {loop_id}")
|
|
36
41
|
self._loop_id: int = loop_id
|
|
37
|
-
self.yarn: Yarn = yarn
|
|
38
|
-
self.
|
|
39
|
-
self.
|
|
40
|
-
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]] = {}
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
@property
|
|
49
|
+
def loop_id(self) -> int:
|
|
50
|
+
"""Get the unique identifier of this loop.
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
Returns:
|
|
53
|
+
int: The id of the loop.
|
|
54
|
+
"""
|
|
55
|
+
return self._loop_id
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
@property
|
|
58
|
+
def has_parent_loops(self) -> bool:
|
|
59
|
+
"""Check if this loop has any parent loops connected through stitch edges.
|
|
50
60
|
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
Returns:
|
|
62
|
+
bool: True if the loop has stitch-edge parents, False otherwise.
|
|
53
63
|
"""
|
|
54
|
-
|
|
55
|
-
raise ValueError("Loops of a float must share a yarn.")
|
|
56
|
-
if u not in self.back_floats:
|
|
57
|
-
self.back_floats[u] = set()
|
|
58
|
-
if v not in self.back_floats:
|
|
59
|
-
self.back_floats[v] = set()
|
|
60
|
-
self.back_floats[u].add(v)
|
|
61
|
-
self.back_floats[v].add(u)
|
|
64
|
+
return len(self.parent_loops) > 0
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
@property
|
|
67
|
+
def parent_loop_ids(self) -> list[int]:
|
|
64
68
|
"""
|
|
65
|
-
|
|
69
|
+
Returns:
|
|
70
|
+
list[int]: The ids of the parent loops of this loop in their stacking order.
|
|
66
71
|
"""
|
|
67
|
-
|
|
68
|
-
for u, v_loops in self.front_floats.items():
|
|
69
|
-
visited.add(u)
|
|
70
|
-
for v in v_loops:
|
|
71
|
-
if v not in visited and v in u.yarn: # float shares a yarn
|
|
72
|
-
u.yarn.loop_graph.edges[u, v]["Back_Loops"].remove(self)
|
|
73
|
-
self.front_floats = {}
|
|
74
|
-
|
|
75
|
-
def add_loop_behind_float(self, u: Loop, v: Loop) -> None:
|
|
76
|
-
"""Set this loop to be behind the float between loops u and v.
|
|
72
|
+
return [p.loop_id for p in self.parent_loops]
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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]
|
|
83
81
|
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
@property
|
|
83
|
+
def pull_direction(self) -> Pull_Direction:
|
|
86
84
|
"""
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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.
|
|
95
99
|
|
|
96
|
-
|
|
100
|
+
Returns:
|
|
101
|
+
Loop | None: The prior loop on the yarn, or None if this is the first loop on the yarn.
|
|
97
102
|
"""
|
|
98
|
-
|
|
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.
|
|
99
110
|
"""
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
for v in v_loops:
|
|
104
|
-
if v not in visited and v in u.yarn: # float shares a yarn
|
|
105
|
-
u.yarn.loop_graph.edges[u, v]["Front_Loops"].remove(self)
|
|
106
|
-
self.back_floats = {}
|
|
107
|
-
|
|
108
|
-
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:
|
|
109
114
|
"""Check if this loop is positioned in front of the float between loops u and v.
|
|
110
115
|
|
|
111
116
|
Args:
|
|
@@ -117,7 +122,7 @@ class Loop:
|
|
|
117
122
|
"""
|
|
118
123
|
return u in self.back_floats and v in self.back_floats and v in self.back_floats[u]
|
|
119
124
|
|
|
120
|
-
def is_behind_float(self, u:
|
|
125
|
+
def is_behind_float(self, u: Self, v: Self) -> bool:
|
|
121
126
|
"""Check if this loop is positioned behind the float between loops u and v.
|
|
122
127
|
|
|
123
128
|
Args:
|
|
@@ -129,41 +134,67 @@ class Loop:
|
|
|
129
134
|
"""
|
|
130
135
|
return u in self.front_floats and v in self.front_floats and v in self.front_floats[u]
|
|
131
136
|
|
|
132
|
-
|
|
133
|
-
|
|
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.
|
|
134
142
|
|
|
135
143
|
Returns:
|
|
136
|
-
|
|
144
|
+
Pull_Direction: The majority pull direction of the given loops or Back-to-Front (i.e., knit-stitch) there are no loops.
|
|
137
145
|
"""
|
|
138
|
-
|
|
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
|
|
139
154
|
|
|
140
|
-
def
|
|
141
|
-
"""
|
|
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.
|
|
142
157
|
|
|
143
|
-
|
|
144
|
-
Loop | None: The next loop on the yarn, or None if this is the last loop on the yarn.
|
|
145
|
-
"""
|
|
146
|
-
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.
|
|
147
159
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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.
|
|
153
166
|
"""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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)
|
|
157
175
|
|
|
158
|
-
def
|
|
159
|
-
"""
|
|
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.
|
|
160
178
|
|
|
161
|
-
|
|
162
|
-
|
|
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.
|
|
163
187
|
"""
|
|
164
|
-
|
|
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)
|
|
165
196
|
|
|
166
|
-
def add_parent_loop(self, parent:
|
|
197
|
+
def add_parent_loop(self, parent: Self, stack_position: int | None = None) -> None:
|
|
167
198
|
"""Add a parent loop to this loop's parent stack.
|
|
168
199
|
|
|
169
200
|
Args:
|
|
@@ -175,7 +206,7 @@ class Loop:
|
|
|
175
206
|
else:
|
|
176
207
|
self.parent_loops.append(parent)
|
|
177
208
|
|
|
178
|
-
def remove_parent(self, parent:
|
|
209
|
+
def remove_parent(self, parent: Self) -> None:
|
|
179
210
|
"""
|
|
180
211
|
Removes the given parent loop from the set of parents of this loop.
|
|
181
212
|
If the given loop is not a parent of this loop, nothing happens.
|
|
@@ -185,30 +216,6 @@ class Loop:
|
|
|
185
216
|
if parent in self.parent_loops:
|
|
186
217
|
self.parent_loops.remove(parent)
|
|
187
218
|
|
|
188
|
-
def ancestor_loops(self) -> set[Loop]:
|
|
189
|
-
"""
|
|
190
|
-
Returns:
|
|
191
|
-
set[Loop]: The set of loops that initiate all wales that lead to this loop. The empty set if this loop has no parents.
|
|
192
|
-
"""
|
|
193
|
-
if not self.has_parent_loops():
|
|
194
|
-
return set()
|
|
195
|
-
ancestors = set()
|
|
196
|
-
for parent_loop in self.parent_loops:
|
|
197
|
-
if parent_loop.has_parent_loops():
|
|
198
|
-
ancestors.update(parent_loop.ancestor_loops())
|
|
199
|
-
else:
|
|
200
|
-
ancestors.add(parent_loop)
|
|
201
|
-
return ancestors
|
|
202
|
-
|
|
203
|
-
@property
|
|
204
|
-
def loop_id(self) -> int:
|
|
205
|
-
"""Get the unique identifier of this loop.
|
|
206
|
-
|
|
207
|
-
Returns:
|
|
208
|
-
int: The id of the loop.
|
|
209
|
-
"""
|
|
210
|
-
return self._loop_id
|
|
211
|
-
|
|
212
219
|
def __hash__(self) -> int:
|
|
213
220
|
"""Return hash value based on loop_id for use in sets and dictionaries.
|
|
214
221
|
|
|
@@ -225,16 +232,16 @@ class Loop:
|
|
|
225
232
|
"""
|
|
226
233
|
return self.loop_id
|
|
227
234
|
|
|
228
|
-
def __eq__(self, other:
|
|
235
|
+
def __eq__(self, other: object) -> bool:
|
|
229
236
|
"""Check equality with another base loop based on loop_id and type.
|
|
230
237
|
|
|
231
238
|
Args:
|
|
232
|
-
other (
|
|
239
|
+
other (Self): The other loop to compare with.
|
|
233
240
|
|
|
234
241
|
Returns:
|
|
235
242
|
bool: True if both loops have the same class and loop_id, False otherwise.
|
|
236
243
|
"""
|
|
237
|
-
return isinstance(other,
|
|
244
|
+
return isinstance(other, type(self)) and self.loop_id == other.loop_id
|
|
238
245
|
|
|
239
246
|
def __lt__(self, other: Loop | int) -> bool:
|
|
240
247
|
"""Compare loop_id with another loop or integer for ordering.
|
knit_graphs/Pull_Direction.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This module defines the Pull_Direction enumeration which represents the two ways a loop can be pulled through other loops in knitting: from back to front (knit) or from front to back (purl).
|
|
4
4
|
"""
|
|
5
|
+
|
|
5
6
|
from __future__ import annotations
|
|
6
7
|
|
|
7
8
|
from enum import Enum
|
|
@@ -13,9 +14,11 @@ class Pull_Direction(Enum):
|
|
|
13
14
|
This enumeration represents the two directions that yarn can be pulled through loops to create different stitch types.
|
|
14
15
|
BtF (Back to Front) creates knit stitches, while FtB (Front to Back) creates purl stitches.
|
|
15
16
|
"""
|
|
17
|
+
|
|
16
18
|
BtF = "Knit"
|
|
17
19
|
FtB = "Purl"
|
|
18
20
|
|
|
21
|
+
@property
|
|
19
22
|
def opposite(self) -> Pull_Direction:
|
|
20
23
|
"""Get the opposite pull direction of this direction.
|
|
21
24
|
|
|
@@ -33,7 +36,7 @@ class Pull_Direction(Enum):
|
|
|
33
36
|
Returns:
|
|
34
37
|
Pull_Direction: The opposite pull direction, same as calling opposite().
|
|
35
38
|
"""
|
|
36
|
-
return self.opposite
|
|
39
|
+
return self.opposite
|
|
37
40
|
|
|
38
41
|
def __invert__(self) -> Pull_Direction:
|
|
39
42
|
"""Get the opposite pull direction using the bitwise inversion operator.
|
|
@@ -41,7 +44,7 @@ class Pull_Direction(Enum):
|
|
|
41
44
|
Returns:
|
|
42
45
|
Pull_Direction: The opposite pull direction, same as calling opposite().
|
|
43
46
|
"""
|
|
44
|
-
return self.opposite
|
|
47
|
+
return self.opposite
|
|
45
48
|
|
|
46
49
|
def __str__(self) -> str:
|
|
47
50
|
"""Get the string representation of the pull direction.
|