knit-graphs 0.0.9__py3-none-any.whl → 0.0.10__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/Knit_Graph.py +68 -39
- knit_graphs/Loop.py +74 -9
- knit_graphs/Yarn.py +41 -4
- knit_graphs/artin_wale_braids/Loop_Braid_Graph.py +11 -1
- knit_graphs/artin_wale_braids/Wale.py +65 -49
- knit_graphs/artin_wale_braids/Wale_Group.py +75 -62
- {knit_graphs-0.0.9.dist-info → knit_graphs-0.0.10.dist-info}/METADATA +1 -1
- {knit_graphs-0.0.9.dist-info → knit_graphs-0.0.10.dist-info}/RECORD +10 -10
- {knit_graphs-0.0.9.dist-info → knit_graphs-0.0.10.dist-info}/LICENSE +0 -0
- {knit_graphs-0.0.9.dist-info → knit_graphs-0.0.10.dist-info}/WHEEL +0 -0
knit_graphs/Knit_Graph.py
CHANGED
|
@@ -18,9 +18,6 @@ from knit_graphs.Loop import Loop
|
|
|
18
18
|
from knit_graphs.Pull_Direction import Pull_Direction
|
|
19
19
|
from knit_graphs.Yarn import Yarn
|
|
20
20
|
|
|
21
|
-
# from knit_graphs.artin_wale_braids.Wale import Wale
|
|
22
|
-
# from knit_graphs.artin_wale_braids.Wale_Group import Wale_Group
|
|
23
|
-
|
|
24
21
|
|
|
25
22
|
class Knit_Graph:
|
|
26
23
|
"""A representation of knitted structures as connections between loops on yarns.
|
|
@@ -77,6 +74,42 @@ class Knit_Graph:
|
|
|
77
74
|
if self._last_loop is None or loop > self._last_loop:
|
|
78
75
|
self._last_loop = loop
|
|
79
76
|
|
|
77
|
+
def remove_loop(self, loop: Loop) -> None:
|
|
78
|
+
"""
|
|
79
|
+
Remove the given loop from the knit graph.
|
|
80
|
+
Args:
|
|
81
|
+
loop (Loop): The loop to be removed.
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
KeyError: If the loop is not in the knit graph.
|
|
85
|
+
|
|
86
|
+
"""
|
|
87
|
+
if loop not in self:
|
|
88
|
+
raise KeyError(f"Loop {loop} not on the knit graph")
|
|
89
|
+
self.braid_graph.remove_loop(loop) # remove any crossing associated with this loop.
|
|
90
|
+
# Remove any stitch edges involving this loop.
|
|
91
|
+
loop.remove_parent_loops()
|
|
92
|
+
if self.has_child_loop(loop):
|
|
93
|
+
child_loop = self.get_child_loop(loop)
|
|
94
|
+
assert isinstance(child_loop, Loop)
|
|
95
|
+
child_loop.remove_parent(loop)
|
|
96
|
+
self.stitch_graph.remove_node(loop)
|
|
97
|
+
# Remove loop from any floating positions
|
|
98
|
+
loop.remove_loop_from_front_floats()
|
|
99
|
+
loop.remove_loop_from_back_floats()
|
|
100
|
+
# Remove loop from yarn
|
|
101
|
+
yarn = loop.yarn
|
|
102
|
+
yarn.remove_loop(loop)
|
|
103
|
+
if len(yarn) == 0: # This was the only loop on that yarn
|
|
104
|
+
self.yarns.discard(yarn)
|
|
105
|
+
# Reset last loop
|
|
106
|
+
if loop is self.last_loop:
|
|
107
|
+
if len(self.yarns) == 0: # No loops left
|
|
108
|
+
assert len(self.stitch_graph.nodes) == 0
|
|
109
|
+
self._last_loop = None
|
|
110
|
+
else: # Set to the newest loop formed at the end of any yarns.
|
|
111
|
+
self._last_loop = max(y.last_loop for y in self.yarns if isinstance(y.last_loop, Loop))
|
|
112
|
+
|
|
80
113
|
def add_yarn(self, yarn: Yarn) -> None:
|
|
81
114
|
"""Add a yarn to the graph without adding its loops.
|
|
82
115
|
|
|
@@ -106,44 +139,31 @@ class Knit_Graph:
|
|
|
106
139
|
self.stitch_graph.add_edge(parent_loop, child_loop, pull_direction=pull_direction)
|
|
107
140
|
child_loop.add_parent_loop(parent_loop, stack_position)
|
|
108
141
|
|
|
109
|
-
def
|
|
110
|
-
"""Get
|
|
142
|
+
def get_wales_ending_with_loop(self, last_loop: Loop) -> set[Wale]:
|
|
143
|
+
"""Get all wales (vertical columns of stitches) that end at the specified loop.
|
|
111
144
|
|
|
112
145
|
Args:
|
|
113
|
-
|
|
146
|
+
last_loop (Loop): The last loop of the joined set of wales.
|
|
114
147
|
|
|
115
148
|
Returns:
|
|
116
|
-
Wale:
|
|
149
|
+
set[Wale]: The set of wales that end at this loop.
|
|
117
150
|
"""
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
assert isinstance(wale.last_loop, Loop)
|
|
123
|
-
wale.add_loop_to_end(cur_loop, self.get_pull_direction(wale.last_loop, cur_loop))
|
|
124
|
-
return wale
|
|
125
|
-
|
|
126
|
-
def get_wales_ending_with_loop(self, last_loop: Loop) -> list[Wale]:
|
|
127
|
-
"""Get all wales (vertical columns of stitches) that end at the specified loop.
|
|
151
|
+
if len(last_loop.parent_loops) == 0:
|
|
152
|
+
return {Wale(last_loop, self)}
|
|
153
|
+
ancestors = last_loop.ancestor_loops()
|
|
154
|
+
return {Wale(l, self) for l in ancestors}
|
|
128
155
|
|
|
129
|
-
|
|
130
|
-
|
|
156
|
+
def get_terminal_wales(self) -> dict[Loop, list[Wale]]:
|
|
157
|
+
"""
|
|
158
|
+
Get wale groups organized by their terminal loops.
|
|
131
159
|
|
|
132
160
|
Returns:
|
|
133
|
-
list[Wale]:
|
|
161
|
+
dict[Loop, list[Wale]]: Dictionary mapping terminal loops to list of wales that terminate that wale.
|
|
134
162
|
"""
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
wale = Wale(last_loop)
|
|
140
|
-
wale.add_loop_to_beginning(top_stitch_parent, cast(Pull_Direction, self.get_pull_direction(top_stitch_parent, last_loop)))
|
|
141
|
-
cur_loop = top_stitch_parent
|
|
142
|
-
while len(cur_loop.parent_loops) == 1: # stop at split for decrease or start of wale
|
|
143
|
-
cur_loop = cur_loop.parent_loops[0]
|
|
144
|
-
wale.add_loop_to_beginning(cur_loop, cast(Pull_Direction, self.get_pull_direction(cur_loop, cast(Loop, wale.first_loop))))
|
|
145
|
-
wales.append(wale)
|
|
146
|
-
return wales
|
|
163
|
+
wale_groups = {}
|
|
164
|
+
for loop in self.terminal_loops():
|
|
165
|
+
wale_groups[loop] = [wale for wale in self.get_wales_ending_with_loop(loop)]
|
|
166
|
+
return wale_groups
|
|
147
167
|
|
|
148
168
|
def get_courses(self) -> list[Course]:
|
|
149
169
|
"""Get all courses (horizontal rows) in the knit graph in chronological order.
|
|
@@ -164,17 +184,13 @@ class Knit_Graph:
|
|
|
164
184
|
courses.append(course)
|
|
165
185
|
return courses
|
|
166
186
|
|
|
167
|
-
def get_wale_groups(self) ->
|
|
187
|
+
def get_wale_groups(self) -> set[Wale_Group]:
|
|
168
188
|
"""Get wale groups organized by their terminal loops.
|
|
169
189
|
|
|
170
190
|
Returns:
|
|
171
|
-
|
|
191
|
+
set[Wale_Group]: The set of wale-groups that lead to the terminal loops of this graph. Each wale group represents a collection of wales that end at the same terminal loop.
|
|
172
192
|
"""
|
|
173
|
-
|
|
174
|
-
for loop in self:
|
|
175
|
-
if self.is_terminal_loop(loop):
|
|
176
|
-
wale_groups.update({loop: Wale_Group(wale, self) for wale in self.get_wales_ending_with_loop(loop)})
|
|
177
|
-
return wale_groups
|
|
193
|
+
return set(Wale_Group(l, self) for l in self.terminal_loops())
|
|
178
194
|
|
|
179
195
|
def __contains__(self, item: Loop | tuple[Loop, Loop]) -> bool:
|
|
180
196
|
"""Check if a loop is contained in the knit graph.
|
|
@@ -197,6 +213,12 @@ class Knit_Graph:
|
|
|
197
213
|
"""
|
|
198
214
|
return cast(Iterator[Loop], iter(self.stitch_graph.nodes))
|
|
199
215
|
|
|
216
|
+
def __getitem__(self, item: int) -> Loop:
|
|
217
|
+
loop = next((l for l in self if l.loop_id == item), None)
|
|
218
|
+
if loop is None:
|
|
219
|
+
raise KeyError(f"Loop of id {item} not in knit graph")
|
|
220
|
+
return loop
|
|
221
|
+
|
|
200
222
|
def sorted_loops(self) -> list[Loop]:
|
|
201
223
|
"""
|
|
202
224
|
Returns:
|
|
@@ -270,3 +292,10 @@ class Knit_Graph:
|
|
|
270
292
|
bool: True if the loop has no child loops and terminates a wale, False otherwise.
|
|
271
293
|
"""
|
|
272
294
|
return not self.has_child_loop(loop)
|
|
295
|
+
|
|
296
|
+
def terminal_loops(self) -> Iterator[Loop]:
|
|
297
|
+
"""
|
|
298
|
+
Returns:
|
|
299
|
+
Iterator[Loop]: An iterator over all terminal loops in the knit graph.
|
|
300
|
+
"""
|
|
301
|
+
return iter(l for l in self if self.is_terminal_loop(l))
|
knit_graphs/Loop.py
CHANGED
|
@@ -5,7 +5,7 @@ Loops are the fundamental building blocks of knitted structures and maintain rel
|
|
|
5
5
|
"""
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
from typing import TYPE_CHECKING
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
11
|
from knit_graphs.Yarn import Yarn
|
|
@@ -47,7 +47,12 @@ class Loop:
|
|
|
47
47
|
Args:
|
|
48
48
|
u (Loop): The first loop in the float pair.
|
|
49
49
|
v (Loop): The second loop in the float pair.
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
ValueError: If u and v are not on the same yarn.
|
|
50
53
|
"""
|
|
54
|
+
if u.yarn != v.yarn:
|
|
55
|
+
raise ValueError("Loops of a float must share a yarn.")
|
|
51
56
|
if u not in self.back_floats:
|
|
52
57
|
self.back_floats[u] = set()
|
|
53
58
|
if v not in self.back_floats:
|
|
@@ -55,6 +60,18 @@ class Loop:
|
|
|
55
60
|
self.back_floats[u].add(v)
|
|
56
61
|
self.back_floats[v].add(u)
|
|
57
62
|
|
|
63
|
+
def remove_loop_from_front_floats(self) -> None:
|
|
64
|
+
"""
|
|
65
|
+
Removes this loop from being in front of all marked floats. Mutates the yarns that own edges of those floats.
|
|
66
|
+
"""
|
|
67
|
+
visited: set[Loop] = set()
|
|
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
|
+
|
|
58
75
|
def add_loop_behind_float(self, u: Loop, v: Loop) -> None:
|
|
59
76
|
"""Set this loop to be behind the float between loops u and v.
|
|
60
77
|
|
|
@@ -63,7 +80,12 @@ class Loop:
|
|
|
63
80
|
Args:
|
|
64
81
|
u (Loop): The first loop in the float pair.
|
|
65
82
|
v (Loop): The second loop in the float pair.
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
ValueError: If u and v are not on the same yarn.
|
|
66
86
|
"""
|
|
87
|
+
if u.yarn != v.yarn:
|
|
88
|
+
raise ValueError("Loops of a float must share a yarn.")
|
|
67
89
|
if u not in self.front_floats:
|
|
68
90
|
self.front_floats[u] = set()
|
|
69
91
|
if v not in self.front_floats:
|
|
@@ -71,6 +93,18 @@ class Loop:
|
|
|
71
93
|
self.front_floats[u].add(v)
|
|
72
94
|
self.front_floats[v].add(u)
|
|
73
95
|
|
|
96
|
+
def remove_loop_from_back_floats(self) -> None:
|
|
97
|
+
"""
|
|
98
|
+
Removes this loop from being behind of all marked floats. Mutates the yarns that own edges of those floats.
|
|
99
|
+
"""
|
|
100
|
+
visited = set()
|
|
101
|
+
for u, v_loops in self.back_floats.items():
|
|
102
|
+
visited.add(u)
|
|
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
|
+
|
|
74
108
|
def is_in_front_of_float(self, u: Loop, v: Loop) -> bool:
|
|
75
109
|
"""Check if this loop is positioned in front of the float between loops u and v.
|
|
76
110
|
|
|
@@ -101,19 +135,25 @@ class Loop:
|
|
|
101
135
|
Returns:
|
|
102
136
|
Loop | None: The prior loop on the yarn, or None if this is the first loop on the yarn.
|
|
103
137
|
"""
|
|
104
|
-
|
|
105
|
-
if loop is None:
|
|
106
|
-
return None
|
|
107
|
-
else:
|
|
108
|
-
return loop
|
|
138
|
+
return self.yarn.prior_loop(self)
|
|
109
139
|
|
|
110
|
-
def next_loop_on_yarn(self) -> Loop:
|
|
140
|
+
def next_loop_on_yarn(self) -> Loop | None:
|
|
111
141
|
"""Get the loop that follows this loop on the same yarn.
|
|
112
142
|
|
|
113
143
|
Returns:
|
|
114
|
-
Loop: The next loop on the yarn, or None if this is the last loop on the yarn.
|
|
144
|
+
Loop | None: The next loop on the yarn, or None if this is the last loop on the yarn.
|
|
115
145
|
"""
|
|
116
|
-
return
|
|
146
|
+
return self.yarn.next_loop(self)
|
|
147
|
+
|
|
148
|
+
def remove_parent_loops(self) -> list[Loop]:
|
|
149
|
+
"""
|
|
150
|
+
Removes the list of parent loops from this loop.
|
|
151
|
+
Returns:
|
|
152
|
+
list[Loop]: The list of parent loops that were removed.
|
|
153
|
+
"""
|
|
154
|
+
parents = self.parent_loops
|
|
155
|
+
self.parent_loops = []
|
|
156
|
+
return parents
|
|
117
157
|
|
|
118
158
|
def has_parent_loops(self) -> bool:
|
|
119
159
|
"""Check if this loop has any parent loops connected through stitch edges.
|
|
@@ -135,6 +175,31 @@ class Loop:
|
|
|
135
175
|
else:
|
|
136
176
|
self.parent_loops.append(parent)
|
|
137
177
|
|
|
178
|
+
def remove_parent(self, parent: Loop) -> None:
|
|
179
|
+
"""
|
|
180
|
+
Removes the given parent loop from the set of parents of this loop.
|
|
181
|
+
If the given loop is not a parent of this loop, nothing happens.
|
|
182
|
+
Args:
|
|
183
|
+
parent (Loop): The parent loop to remove.
|
|
184
|
+
"""
|
|
185
|
+
if parent in self.parent_loops:
|
|
186
|
+
self.parent_loops.remove(parent)
|
|
187
|
+
|
|
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
|
+
|
|
138
203
|
@property
|
|
139
204
|
def loop_id(self) -> int:
|
|
140
205
|
"""Get the unique identifier of this loop.
|
knit_graphs/Yarn.py
CHANGED
|
@@ -82,6 +82,8 @@ class Yarn:
|
|
|
82
82
|
loop_graph (DiGraph): The directed graph loops connected by yarn-wise float edges.
|
|
83
83
|
properties (Yarn_Properties): The physical and visual properties of this yarn.
|
|
84
84
|
"""
|
|
85
|
+
FRONT_LOOPS: str = "Front_Loops"
|
|
86
|
+
_BACK_LOOPS: str = "Back_Loops"
|
|
85
87
|
|
|
86
88
|
def __init__(self, yarn_properties: None | Yarn_Properties = None, knit_graph: None | Knit_Graph = None):
|
|
87
89
|
"""Initialize a yarn with the specified properties and optional knit graph association.
|
|
@@ -141,7 +143,7 @@ class Yarn:
|
|
|
141
143
|
self.add_loop_in_front_of_float(front_loop, v, u)
|
|
142
144
|
else:
|
|
143
145
|
return
|
|
144
|
-
self.loop_graph.edges[u, v][
|
|
146
|
+
self.loop_graph.edges[u, v][self.FRONT_LOOPS].add(front_loop)
|
|
145
147
|
front_loop.add_loop_in_front_of_float(u, v)
|
|
146
148
|
|
|
147
149
|
def add_loop_behind_float(self, back_loop: Loop, u: Loop, v: Loop) -> None:
|
|
@@ -157,7 +159,7 @@ class Yarn:
|
|
|
157
159
|
self.add_loop_behind_float(back_loop, v, u)
|
|
158
160
|
else:
|
|
159
161
|
return
|
|
160
|
-
self.loop_graph.edges[u, v][
|
|
162
|
+
self.loop_graph.edges[u, v][self._BACK_LOOPS].add(back_loop)
|
|
161
163
|
back_loop.add_loop_behind_float(u, v)
|
|
162
164
|
|
|
163
165
|
def get_loops_in_front_of_float(self, u: Loop, v: Loop) -> set[Loop]:
|
|
@@ -176,7 +178,7 @@ class Yarn:
|
|
|
176
178
|
else:
|
|
177
179
|
return set()
|
|
178
180
|
else:
|
|
179
|
-
return cast(set[Loop], self.loop_graph.edges[u, v][
|
|
181
|
+
return cast(set[Loop], self.loop_graph.edges[u, v][self.FRONT_LOOPS])
|
|
180
182
|
|
|
181
183
|
def get_loops_behind_float(self, u: Loop, v: Loop) -> set[Loop]:
|
|
182
184
|
"""Get all loops positioned behind the float between two loops.
|
|
@@ -194,7 +196,7 @@ class Yarn:
|
|
|
194
196
|
else:
|
|
195
197
|
return set()
|
|
196
198
|
else:
|
|
197
|
-
return cast(set[Loop], self.loop_graph.edges[u, v][
|
|
199
|
+
return cast(set[Loop], self.loop_graph.edges[u, v][self._BACK_LOOPS])
|
|
198
200
|
|
|
199
201
|
@property
|
|
200
202
|
def last_loop(self) -> Loop | None:
|
|
@@ -290,6 +292,41 @@ class Yarn:
|
|
|
290
292
|
else:
|
|
291
293
|
return self.last_loop.loop_id + 1
|
|
292
294
|
|
|
295
|
+
def remove_loop(self, loop: Loop) -> None:
|
|
296
|
+
"""
|
|
297
|
+
Remove the given loop from the yarn.
|
|
298
|
+
Reconnects any neighboring loops to form a new float with the positioned in-front-of or behind the original floats positioned accordingly.
|
|
299
|
+
Resets the first_loop and last_loop properties if the removed loop was the tail of the yarn.
|
|
300
|
+
Args:
|
|
301
|
+
loop (Loop): The loop to remove from the yarn.
|
|
302
|
+
|
|
303
|
+
Raises:
|
|
304
|
+
KeyError: The given loop does not exist in the yarn.
|
|
305
|
+
"""
|
|
306
|
+
if loop not in self:
|
|
307
|
+
raise KeyError(f'Loop {loop} does not exist on yarn {self}.')
|
|
308
|
+
prior_loop = self.prior_loop(loop)
|
|
309
|
+
next_loop = self.next_loop(loop)
|
|
310
|
+
if isinstance(prior_loop, Loop) and isinstance(next_loop, Loop): # Loop is between two floats to be merged.
|
|
311
|
+
front_of_float_loops = self.get_loops_in_front_of_float(prior_loop, loop)
|
|
312
|
+
front_of_float_loops.update(self.get_loops_in_front_of_float(loop, next_loop))
|
|
313
|
+
back_of_float_loops = self.get_loops_behind_float(prior_loop, loop)
|
|
314
|
+
back_of_float_loops.update(self.get_loops_behind_float(loop, next_loop))
|
|
315
|
+
self.loop_graph.remove_node(loop)
|
|
316
|
+
self.loop_graph.add_edge(prior_loop, next_loop, Front_Loops=front_of_float_loops, Back_Loops=back_of_float_loops)
|
|
317
|
+
for front_loop in front_of_float_loops:
|
|
318
|
+
front_loop.add_loop_in_front_of_float(prior_loop, next_loop)
|
|
319
|
+
for back_loop in back_of_float_loops:
|
|
320
|
+
back_loop.add_loop_behind_float(prior_loop, next_loop)
|
|
321
|
+
return
|
|
322
|
+
if next_loop is None: # This was the last loop, make the prior loop the last loop.
|
|
323
|
+
assert loop is self.last_loop
|
|
324
|
+
self._last_loop = prior_loop
|
|
325
|
+
if prior_loop is None: # This was the first loop, make the next loop the first loop.
|
|
326
|
+
assert loop is self.first_loop
|
|
327
|
+
self._first_loop = next_loop
|
|
328
|
+
self.loop_graph.remove_node(loop)
|
|
329
|
+
|
|
293
330
|
def add_loop_to_end(self, loop: Loop) -> Loop:
|
|
294
331
|
"""Add an existing loop to the end of this yarn and associated knit graph.
|
|
295
332
|
|
|
@@ -19,6 +19,7 @@ class Loop_Braid_Graph:
|
|
|
19
19
|
Attributes:
|
|
20
20
|
loop_crossing_graph (DiGraph): A NetworkX directed graph storing loop crossing relationships with crossing direction attributes.
|
|
21
21
|
"""
|
|
22
|
+
_CROSSING = "crossing"
|
|
22
23
|
|
|
23
24
|
def __init__(self) -> None:
|
|
24
25
|
"""Initialize an empty loop braid graph with no crossings."""
|
|
@@ -34,6 +35,15 @@ class Loop_Braid_Graph:
|
|
|
34
35
|
"""
|
|
35
36
|
self.loop_crossing_graph.add_edge(left_loop, right_loop, crossing=crossing_direction)
|
|
36
37
|
|
|
38
|
+
def remove_loop(self, loop: Loop) -> None:
|
|
39
|
+
"""
|
|
40
|
+
Removes any crossings that involve the given loop.
|
|
41
|
+
Args:
|
|
42
|
+
loop (Loop): The loop to remove.
|
|
43
|
+
"""
|
|
44
|
+
if loop in self:
|
|
45
|
+
self.loop_crossing_graph.remove_node(loop)
|
|
46
|
+
|
|
37
47
|
def __contains__(self, item: Loop | tuple[Loop, Loop]) -> bool:
|
|
38
48
|
"""Check if a loop or loop pair is contained in the braid graph.
|
|
39
49
|
|
|
@@ -92,4 +102,4 @@ class Loop_Braid_Graph:
|
|
|
92
102
|
"""
|
|
93
103
|
if not self.loop_crossing_graph.has_edge(left_loop, right_loop):
|
|
94
104
|
self.add_crossing(left_loop, right_loop, Crossing_Direction.No_Cross)
|
|
95
|
-
return cast(Crossing_Direction, self.loop_crossing_graph[left_loop][right_loop][
|
|
105
|
+
return cast(Crossing_Direction, self.loop_crossing_graph[left_loop][right_loop][self._CROSSING])
|
|
@@ -4,13 +4,16 @@ This module defines the Wale class which represents a vertical column of stitche
|
|
|
4
4
|
"""
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
from typing import Iterator, cast
|
|
7
|
+
from typing import TYPE_CHECKING, Iterator, cast
|
|
8
8
|
|
|
9
9
|
from networkx import DiGraph, dfs_preorder_nodes
|
|
10
10
|
|
|
11
11
|
from knit_graphs.Loop import Loop
|
|
12
12
|
from knit_graphs.Pull_Direction import Pull_Direction
|
|
13
13
|
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from knit_graphs.Knit_Graph import Knit_Graph
|
|
16
|
+
|
|
14
17
|
|
|
15
18
|
class Wale:
|
|
16
19
|
"""A data structure representing stitch relationships between loops in a vertical column of a knitted structure.
|
|
@@ -23,49 +26,42 @@ class Wale:
|
|
|
23
26
|
last_loop (Loop | None): The last (top) loop in the wale sequence.
|
|
24
27
|
stitches (DiGraph): Stores the directed graph of stitch connections within this wale.
|
|
25
28
|
"""
|
|
29
|
+
_PULL_DIRECTION: str = "pull_direction"
|
|
26
30
|
|
|
27
|
-
def __init__(self, first_loop: Loop | None = None) -> None:
|
|
31
|
+
def __init__(self, first_loop: Loop, knit_graph: Knit_Graph, end_loop: Loop | None = None) -> None:
|
|
28
32
|
"""Initialize a wale optionally starting with a specified loop.
|
|
29
33
|
|
|
30
34
|
Args:
|
|
31
|
-
first_loop (Loop
|
|
35
|
+
first_loop (Loop): The initial loop to start the wale with.
|
|
36
|
+
knit_graph (Knit_Graph): The knit graph that owns this wale.
|
|
37
|
+
end_loop (Loop, optional):
|
|
38
|
+
The loop to terminate the wale with.
|
|
39
|
+
If no loop is provided or this loop is not found, the wale will terminate at the first loop with no child.
|
|
32
40
|
"""
|
|
33
41
|
self.stitches: DiGraph = DiGraph()
|
|
34
|
-
self.first_loop
|
|
35
|
-
self.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
self.stitches.add_node(first_loop)
|
|
43
|
+
self._knit_graph: Knit_Graph = knit_graph
|
|
44
|
+
self.first_loop: Loop = first_loop
|
|
45
|
+
self.last_loop: Loop = first_loop
|
|
46
|
+
self._build_wale_from_first_loop(end_loop)
|
|
47
|
+
|
|
48
|
+
def _build_wale_from_first_loop(self, end_loop: Loop | None) -> None:
|
|
49
|
+
while self._knit_graph.has_child_loop(self.last_loop):
|
|
50
|
+
child = self._knit_graph.get_child_loop(self.last_loop)
|
|
51
|
+
assert isinstance(child, Loop)
|
|
52
|
+
self.add_loop_to_end(child)
|
|
53
|
+
if end_loop is not None and child is end_loop:
|
|
54
|
+
return # found the end loop, so wrap up the wale
|
|
55
|
+
|
|
56
|
+
def add_loop_to_end(self, loop: Loop) -> None:
|
|
45
57
|
"""
|
|
46
|
-
|
|
47
|
-
self.stitches.add_node(loop)
|
|
48
|
-
self.first_loop = loop
|
|
49
|
-
self.last_loop = loop
|
|
50
|
-
else:
|
|
51
|
-
assert isinstance(pull_direction, Pull_Direction)
|
|
52
|
-
self.stitches.add_edge(self.last_loop, loop, pull_direction=pull_direction)
|
|
53
|
-
self.last_loop = loop
|
|
54
|
-
|
|
55
|
-
def add_loop_to_beginning(self, loop: Loop, pull_direction: Pull_Direction = Pull_Direction.BtF) -> None:
|
|
56
|
-
"""Add a loop to the beginning (bottom) of the wale with the specified pull direction.
|
|
58
|
+
Add a loop to the end (top) of the wale with the specified pull direction.
|
|
57
59
|
|
|
58
60
|
Args:
|
|
59
|
-
loop (Loop): The loop to add to the
|
|
60
|
-
pull_direction (Pull_Direction, optional): The direction to pull the existing first loop through this new loop. Defaults to Pull_Direction.BtF.
|
|
61
|
+
loop (Loop): The loop to add to the end of the wale.
|
|
61
62
|
"""
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
self.first_loop = loop
|
|
65
|
-
self.last_loop = loop
|
|
66
|
-
else:
|
|
67
|
-
self.stitches.add_edge(loop, self.first_loop, pull_direction=pull_direction)
|
|
68
|
-
self.first_loop = loop
|
|
63
|
+
self.stitches.add_edge(self.last_loop, loop, pull_direction=self._knit_graph.get_pull_direction(self.last_loop, loop))
|
|
64
|
+
self.last_loop = loop
|
|
69
65
|
|
|
70
66
|
def get_stitch_pull_direction(self, u: Loop, v: Loop) -> Pull_Direction:
|
|
71
67
|
"""Get the pull direction of the stitch edge between two loops in this wale.
|
|
@@ -77,10 +73,11 @@ class Wale:
|
|
|
77
73
|
Returns:
|
|
78
74
|
Pull_Direction: The pull direction of the stitch between loops u and v.
|
|
79
75
|
"""
|
|
80
|
-
return cast(Pull_Direction, self.stitches.edges[u, v][
|
|
76
|
+
return cast(Pull_Direction, self.stitches.edges[u, v][self._PULL_DIRECTION])
|
|
81
77
|
|
|
82
78
|
def split_wale(self, split_loop: Loop) -> tuple[Wale, Wale | None]:
|
|
83
|
-
"""
|
|
79
|
+
"""
|
|
80
|
+
Split this wale at the specified loop into two separate wales.
|
|
84
81
|
|
|
85
82
|
The split loop becomes the last loop of the first wale and the first loop of the second wale.
|
|
86
83
|
|
|
@@ -93,19 +90,23 @@ class Wale:
|
|
|
93
90
|
* The first wale (from start to split_loop). This will be the whole wale if the split_loop is not found.
|
|
94
91
|
* The second wale (from split_loop to end). This will be None if the split_loop is not found.
|
|
95
92
|
"""
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if l is split_loop:
|
|
101
|
-
growing_wale.add_loop_to_end(l, self.get_stitch_pull_direction(cast(Loop, growing_wale.last_loop), l))
|
|
102
|
-
growing_wale = Wale(split_loop)
|
|
103
|
-
found_loop = True
|
|
104
|
-
else:
|
|
105
|
-
growing_wale.add_loop_to_end(l, self.get_stitch_pull_direction(cast(Loop, growing_wale.last_loop), l))
|
|
106
|
-
if not found_loop:
|
|
93
|
+
if split_loop in self:
|
|
94
|
+
return (Wale(self.first_loop, self._knit_graph, end_loop=split_loop),
|
|
95
|
+
Wale(split_loop, self._knit_graph, end_loop=self.last_loop))
|
|
96
|
+
else:
|
|
107
97
|
return self, None
|
|
108
|
-
|
|
98
|
+
|
|
99
|
+
def __eq__(self, other: Wale) -> bool:
|
|
100
|
+
"""
|
|
101
|
+
Args:
|
|
102
|
+
other (Wale): The wale to compare.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
bool: True if all the loops in both wales are present and in the same order. False, otherwise.
|
|
106
|
+
"""
|
|
107
|
+
if len(self) != len(other):
|
|
108
|
+
return False
|
|
109
|
+
return not any(l != o for l, o in zip(self, other))
|
|
109
110
|
|
|
110
111
|
def __len__(self) -> int:
|
|
111
112
|
"""Get the number of loops in this wale.
|
|
@@ -149,13 +150,28 @@ class Wale:
|
|
|
149
150
|
return bool(self.stitches.has_node(item))
|
|
150
151
|
|
|
151
152
|
def __hash__(self) -> int:
|
|
152
|
-
"""
|
|
153
|
+
"""
|
|
154
|
+
Get the hash value of this wale based on its first loop.
|
|
153
155
|
|
|
154
156
|
Returns:
|
|
155
157
|
int: Hash value based on the first loop in this wale.
|
|
156
158
|
"""
|
|
157
159
|
return hash(self.first_loop)
|
|
158
160
|
|
|
161
|
+
def __str__(self) -> str:
|
|
162
|
+
"""
|
|
163
|
+
Returns:
|
|
164
|
+
str: The string representation of this wale.
|
|
165
|
+
"""
|
|
166
|
+
return f"Wale({self.first_loop}->{self.last_loop})"
|
|
167
|
+
|
|
168
|
+
def __repr__(self) -> str:
|
|
169
|
+
"""
|
|
170
|
+
Returns:
|
|
171
|
+
str: The string representation of this wale.
|
|
172
|
+
"""
|
|
173
|
+
return str(self)
|
|
174
|
+
|
|
159
175
|
def overlaps(self, other: Wale) -> bool:
|
|
160
176
|
"""Check if this wale has any loops in common with another wale.
|
|
161
177
|
|
|
@@ -24,84 +24,66 @@ class Wale_Group:
|
|
|
24
24
|
Attributes:
|
|
25
25
|
wale_graph (DiGraph): A directed graph representing the relationships between wales in this group.
|
|
26
26
|
stitch_graph (DiGraph): A directed graph of all individual stitch connections within this wale group.
|
|
27
|
-
terminal_wale (Wale | None): The topmost wale in this group, typically where multiple wales converge.
|
|
28
27
|
top_loops (dict[Loop, Wale]): Mapping from the last (top) loop of each wale to the wale itself.
|
|
29
28
|
bottom_loops (dict[Loop, Wale]): Mapping from the first (bottom) loop of each wale to the wale itself.
|
|
30
29
|
"""
|
|
31
30
|
|
|
32
|
-
def __init__(self,
|
|
31
|
+
def __init__(self, terminal_loop: Loop, knit_graph: Knit_Graph):
|
|
33
32
|
"""Initialize a wale group starting from a terminal wale and building downward.
|
|
34
33
|
|
|
35
34
|
Args:
|
|
36
|
-
|
|
35
|
+
terminal_loop (Loop): The terminal loop of this wale-group. All the wales in the group connect up to this loop.
|
|
37
36
|
knit_graph (Knit_Graph): The parent knit graph that contains this wale group.
|
|
38
37
|
"""
|
|
39
38
|
self.wale_graph: DiGraph = DiGraph()
|
|
40
39
|
self.stitch_graph: DiGraph = DiGraph()
|
|
41
40
|
self._knit_graph: Knit_Graph = knit_graph
|
|
42
|
-
self.
|
|
41
|
+
self._terminal_loop: Loop = terminal_loop
|
|
43
42
|
self.top_loops: dict[Loop, Wale] = {}
|
|
44
43
|
self.bottom_loops: dict[Loop, Wale] = {}
|
|
45
|
-
self.
|
|
44
|
+
self._build_wale_group()
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Args:
|
|
53
|
-
wale (Wale): The wale to add to this group. Empty wales are ignored and not added.
|
|
46
|
+
@property
|
|
47
|
+
def terminal_loop(self) -> Loop:
|
|
48
|
+
"""
|
|
49
|
+
Returns:
|
|
50
|
+
Loop: The loop that terminates all wales in this group.
|
|
54
51
|
"""
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
return self._terminal_loop
|
|
53
|
+
|
|
54
|
+
def _build_wale_group(self) -> None:
|
|
55
|
+
full_wales = self._knit_graph.get_wales_ending_with_loop(self._terminal_loop)
|
|
56
|
+
# Build up the stitch graph.
|
|
57
|
+
for wale in full_wales:
|
|
58
|
+
u_loops: list[Loop] = cast(list[Loop], wale[:-1])
|
|
59
|
+
v_loops: list[Loop] = cast(list[Loop], wale[1:])
|
|
60
|
+
for u, v in zip(u_loops, v_loops):
|
|
61
|
+
self.stitch_graph.add_edge(u, v, pull_direction=self._knit_graph.get_pull_direction(u, v))
|
|
62
|
+
wales_to_split = full_wales
|
|
63
|
+
while len(wales_to_split) > 0:
|
|
64
|
+
wale_to_split = wales_to_split.pop()
|
|
65
|
+
split = False
|
|
66
|
+
upper_loops = cast(list[Loop], wale_to_split[1:])
|
|
67
|
+
for loop in upper_loops: # skip first loop in each wale as it may be already connected to a discovered decrease.
|
|
68
|
+
if len(loop.parent_loops) > 1: # Focal of a decrease.
|
|
69
|
+
clean_wale, remaining_wale = wale_to_split.split_wale(loop)
|
|
70
|
+
if not self.wale_graph.has_node(clean_wale):
|
|
71
|
+
self._add_wale(clean_wale)
|
|
72
|
+
if isinstance(remaining_wale, Wale):
|
|
73
|
+
wales_to_split.add(remaining_wale)
|
|
74
|
+
split = True
|
|
75
|
+
break
|
|
76
|
+
if not split:
|
|
77
|
+
self._add_wale(wale_to_split)
|
|
78
|
+
for bot_loop, lower_wale in self.bottom_loops.items():
|
|
79
|
+
if lower_wale.last_loop in self.bottom_loops:
|
|
80
|
+
self.wale_graph.add_edge(lower_wale, self.bottom_loops[lower_wale.last_loop])
|
|
81
|
+
|
|
82
|
+
def _add_wale(self, wale: Wale) -> None:
|
|
57
83
|
self.wale_graph.add_node(wale)
|
|
58
|
-
for u, v in wale.stitches.edges:
|
|
59
|
-
self.stitch_graph.add_edge(u, v, pull_direction=wale.get_stitch_pull_direction(u, v))
|
|
60
|
-
for top_loop, other_wale in self.top_loops.items():
|
|
61
|
-
if top_loop == wale.first_loop:
|
|
62
|
-
self.wale_graph.add_edge(other_wale, wale)
|
|
63
|
-
for bot_loop, other_wale in self.bottom_loops.items():
|
|
64
|
-
if bot_loop == wale.last_loop:
|
|
65
|
-
self.wale_graph.add_edge(wale, other_wale)
|
|
66
|
-
assert isinstance(wale.last_loop, Loop)
|
|
67
84
|
self.top_loops[wale.last_loop] = wale
|
|
68
|
-
assert isinstance(wale.first_loop, Loop)
|
|
69
85
|
self.bottom_loops[wale.first_loop] = wale
|
|
70
86
|
|
|
71
|
-
def add_parent_wales(self, wale: Wale) -> list[Wale]:
|
|
72
|
-
"""Find and add all parent wales that created the given wale through decrease operations.
|
|
73
|
-
|
|
74
|
-
This method identifies wales that end at the loops that are parents of this wale's first loop, representing the wales that were decreased together to form the given wale.
|
|
75
|
-
|
|
76
|
-
Args:
|
|
77
|
-
wale (Wale): The wale to find and add parent wales for.
|
|
78
|
-
|
|
79
|
-
Returns:
|
|
80
|
-
list[Wale]: The list of parent wales that were found and added to the group.
|
|
81
|
-
"""
|
|
82
|
-
added_wales = []
|
|
83
|
-
for parent_loop in cast(Loop, wale.first_loop).parent_loops:
|
|
84
|
-
parent_wales = self._knit_graph.get_wales_ending_with_loop(parent_loop)
|
|
85
|
-
for parent_wale in parent_wales:
|
|
86
|
-
self.add_wale(parent_wale)
|
|
87
|
-
added_wales.extend(parent_wales)
|
|
88
|
-
return added_wales
|
|
89
|
-
|
|
90
|
-
def build_group_from_top_wale(self, top_wale: Wale) -> None:
|
|
91
|
-
"""Build the complete wale group by recursively finding all parent wales from the terminal wale.
|
|
92
|
-
|
|
93
|
-
This method starts with the terminal wale and recursively adds all parent wales, building the complete tree structure of wales that contribute to the terminal wale through decrease operations.
|
|
94
|
-
|
|
95
|
-
Args:
|
|
96
|
-
top_wale (Wale): The terminal wale at the top of the group structure.
|
|
97
|
-
"""
|
|
98
|
-
self.add_wale(top_wale)
|
|
99
|
-
added_wales = self.add_parent_wales(top_wale)
|
|
100
|
-
while len(added_wales) > 0:
|
|
101
|
-
next_wale = added_wales.pop()
|
|
102
|
-
more_wales = self.add_parent_wales(next_wale)
|
|
103
|
-
added_wales.extend(more_wales)
|
|
104
|
-
|
|
105
87
|
def get_loops_over_courses(self) -> list[list[Loop]]:
|
|
106
88
|
"""Get loops organized by their course (horizontal row) within this wale group.
|
|
107
89
|
|
|
@@ -110,11 +92,8 @@ class Wale_Group:
|
|
|
110
92
|
Returns:
|
|
111
93
|
list[list[Loop]]: A list where each inner list contains all loops that belong to the same course, ordered from top to bottom courses. Returns empty list if there is no terminal wale.
|
|
112
94
|
"""
|
|
113
|
-
if self.terminal_wale is None:
|
|
114
|
-
return []
|
|
115
|
-
top_loop: Loop = cast(Loop, self.terminal_wale.last_loop)
|
|
116
95
|
courses: list[list[Loop]] = []
|
|
117
|
-
cur_course: list[Loop] = [
|
|
96
|
+
cur_course: list[Loop] = [self._terminal_loop]
|
|
118
97
|
while len(cur_course) > 0:
|
|
119
98
|
courses.append(cur_course)
|
|
120
99
|
next_course = []
|
|
@@ -136,3 +115,37 @@ class Wale_Group:
|
|
|
136
115
|
path_len = sum(len(successor) for successor in dfs_preorder_nodes(self.wale_graph, wale))
|
|
137
116
|
max_len = max(max_len, path_len)
|
|
138
117
|
return max_len
|
|
118
|
+
|
|
119
|
+
def __hash__(self) -> int:
|
|
120
|
+
"""
|
|
121
|
+
Returns:
|
|
122
|
+
int: Hash value of the terminal loop of this group.
|
|
123
|
+
"""
|
|
124
|
+
return hash(self._terminal_loop)
|
|
125
|
+
|
|
126
|
+
def __contains__(self, item: Loop | int | Wale) -> bool:
|
|
127
|
+
"""
|
|
128
|
+
Args:
|
|
129
|
+
item (Loop | int | Wale): The item to check for in the wale group.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
bool: True if the given loop, loop_id (int), or wale is in this group.
|
|
133
|
+
"""
|
|
134
|
+
if isinstance(item, Loop) or isinstance(item, int):
|
|
135
|
+
return item in self.stitch_graph.nodes
|
|
136
|
+
else: # isinstance(item, Wale):
|
|
137
|
+
return item in self.wale_graph
|
|
138
|
+
|
|
139
|
+
def __str__(self) -> str:
|
|
140
|
+
"""
|
|
141
|
+
Returns:
|
|
142
|
+
str: The string representation of this wale group.
|
|
143
|
+
"""
|
|
144
|
+
return f"WG({self.terminal_loop})"
|
|
145
|
+
|
|
146
|
+
def __repr__(self) -> str:
|
|
147
|
+
"""
|
|
148
|
+
Returns:
|
|
149
|
+
str: The string representation of this wale group.
|
|
150
|
+
"""
|
|
151
|
+
return str(self)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: knit-graphs
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.10
|
|
4
4
|
Summary: A graph representation of knitted structures where each loop is a node and edges represent yarn and stitch relationships.
|
|
5
5
|
Home-page: https://mhofmann-khoury.github.io/knit_graph/
|
|
6
6
|
License: MIT
|
|
@@ -21,21 +21,21 @@ docs/source/conf.py,sha256=TZK8Rz0JNgQs2J_cUhVX5_4tmMBvRdGtILJsnAnAFWo,13350
|
|
|
21
21
|
docs/source/index.rst,sha256=D4KmCX1_d2-WrPgxSeggHHsc9-TeQwSUGYOkehRQWrw,1462
|
|
22
22
|
docs/source/installation.rst,sha256=GyNRk_oXKVgOZ4KFLAgkXLwjHYzDYsx8gcokLRrS0ZI,1247
|
|
23
23
|
knit_graphs/Course.py,sha256=hO768vPM0KFRsjCYsBTUgasqMzlvl0gsuSrsBX0UUSs,6280
|
|
24
|
-
knit_graphs/Knit_Graph.py,sha256=
|
|
24
|
+
knit_graphs/Knit_Graph.py,sha256=tzJlHGwIboItz9jg6m6vch4UX3L8vYy2EYNmYpxStkg,12367
|
|
25
25
|
knit_graphs/Knit_Graph_Visualizer.py,sha256=s2zp6Z7-ZF_52x3zTx8oQr__tQScpkGthIoWck57ykE,40227
|
|
26
|
-
knit_graphs/Loop.py,sha256=
|
|
26
|
+
knit_graphs/Loop.py,sha256=zAVz4HtbfBTlihMppcF8E1wWI95bkGjcgQ69o59rdlc,9716
|
|
27
27
|
knit_graphs/Pull_Direction.py,sha256=8xRLN-j7V1mExgmt3YjcJcTDa5rLedA2bF4wI8gd-DE,2334
|
|
28
|
-
knit_graphs/Yarn.py,sha256=
|
|
28
|
+
knit_graphs/Yarn.py,sha256=CRTFAA77_v6Z2xg1lhH6DWxRFn2HDokn_L7l_8Lxyc0,18089
|
|
29
29
|
knit_graphs/__init__.py,sha256=qzZAOxTqjgLkhoGnKNZwOkuYbKEyzfWKkKpKkV6lVfk,111
|
|
30
30
|
knit_graphs/artin_wale_braids/Crossing_Direction.py,sha256=71ckmEFfL0s25NJb8lfYwH2zku0uM1C5X9gGM7PYC5A,2820
|
|
31
|
-
knit_graphs/artin_wale_braids/Loop_Braid_Graph.py,sha256=
|
|
32
|
-
knit_graphs/artin_wale_braids/Wale.py,sha256=
|
|
31
|
+
knit_graphs/artin_wale_braids/Loop_Braid_Graph.py,sha256=hmZVe34V6dqfV8_JzuyIUkg2SaznyIZEpr3aDayG-PQ,4876
|
|
32
|
+
knit_graphs/artin_wale_braids/Wale.py,sha256=vsM6zvTCLIBKDsX60r19KCJbl1ZGL2ZdINf7AJt0pBI,7109
|
|
33
33
|
knit_graphs/artin_wale_braids/Wale_Braid.py,sha256=Ws0QmU7s63nrBKe69acDza-r-LozDyyaKVDNxQMy10Y,2662
|
|
34
34
|
knit_graphs/artin_wale_braids/Wale_Braid_Word.py,sha256=4YBlVMUZkqp85r46ETQSbFVS_WrT4PsWthHmFv5Ny9M,4587
|
|
35
|
-
knit_graphs/artin_wale_braids/Wale_Group.py,sha256=
|
|
35
|
+
knit_graphs/artin_wale_braids/Wale_Group.py,sha256=1RTGIbO_nmyKOBmQzoFcZNsl1sHRG9p4isoUztkI0jY,6665
|
|
36
36
|
knit_graphs/artin_wale_braids/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
37
|
knit_graphs/basic_knit_graph_generators.py,sha256=doDH2MiXbMzGU9b3enEr3KdY8Fw90cssHi_lkts6VpY,11720
|
|
38
|
-
knit_graphs-0.0.
|
|
39
|
-
knit_graphs-0.0.
|
|
40
|
-
knit_graphs-0.0.
|
|
41
|
-
knit_graphs-0.0.
|
|
38
|
+
knit_graphs-0.0.10.dist-info/LICENSE,sha256=Oazk3oiRu5ZN7b-EdYNYh0vu-I3Av2uIPQ-9L_cZ6Oo,1070
|
|
39
|
+
knit_graphs-0.0.10.dist-info/METADATA,sha256=vU8ehebXJzejk8YwClxn5an0V8dhh-8yk631cvq_3Es,5194
|
|
40
|
+
knit_graphs-0.0.10.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
41
|
+
knit_graphs-0.0.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|