knit-graphs 0.0.5__py3-none-any.whl → 0.0.7__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-0.0.5.dist-info/licenses/LICENSE → LICENSE +21 -21
- README.md +75 -0
- docs/Makefile +20 -0
- docs/make.bat +35 -0
- docs/source/api/knit_graphs.artin_wale_braids.rst +58 -0
- docs/source/api/knit_graphs.rst +74 -0
- docs/source/conf.py +335 -0
- docs/source/index.rst +71 -0
- docs/source/installation.rst +67 -0
- knit_graphs/Course.py +156 -104
- knit_graphs/Knit_Graph.py +249 -186
- knit_graphs/Knit_Graph_Visualizer.py +680 -0
- knit_graphs/Loop.py +141 -155
- knit_graphs/Pull_Direction.py +68 -23
- knit_graphs/Yarn.py +424 -267
- knit_graphs/__init__.py +3 -3
- knit_graphs/_base_classes.py +173 -0
- knit_graphs/artin_wale_braids/Crossing_Direction.py +74 -15
- knit_graphs/artin_wale_braids/Loop_Braid_Graph.py +95 -62
- knit_graphs/artin_wale_braids/Wale.py +169 -93
- knit_graphs/artin_wale_braids/Wale_Braid.py +50 -30
- knit_graphs/artin_wale_braids/Wale_Braid_Word.py +99 -54
- knit_graphs/artin_wale_braids/Wale_Group.py +136 -88
- knit_graphs/{knit_graph_generators/basic_knit_graph_generators.py → basic_knit_graph_generators.py} +302 -248
- knit_graphs-0.0.7.dist-info/LICENSE +21 -0
- {knit_graphs-0.0.5.dist-info → knit_graphs-0.0.7.dist-info}/METADATA +33 -25
- knit_graphs-0.0.7.dist-info/RECORD +29 -0
- {knit_graphs-0.0.5.dist-info → knit_graphs-0.0.7.dist-info}/WHEEL +1 -1
- knit_graphs/__about__.py +0 -4
- knit_graphs/knit_graph_generators/__init__.py +0 -0
- knit_graphs/knit_graph_visualizer/Stitch_Visualizer.py +0 -427
- knit_graphs/knit_graph_visualizer/__init__.py +0 -0
- knit_graphs-0.0.5.dist-info/RECORD +0 -22
knit_graphs/Knit_Graph.py
CHANGED
|
@@ -1,186 +1,249 @@
|
|
|
1
|
-
"""The graph structure used to represent knitted objects
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
from
|
|
9
|
-
|
|
10
|
-
from knit_graphs.
|
|
11
|
-
from knit_graphs.artin_wale_braids.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"""
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
"""
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
"""
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"""
|
|
186
|
-
|
|
1
|
+
"""The graph structure used to represent knitted objects.
|
|
2
|
+
|
|
3
|
+
This module contains the main Knit_Graph class which serves as the central data structure for representing knitted fabrics.
|
|
4
|
+
It manages the relationships between loops, yarns, and structural elements like courses and wales.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Any, cast
|
|
9
|
+
|
|
10
|
+
from knit_graphs._base_classes import _Base_Knit_Graph
|
|
11
|
+
from knit_graphs.artin_wale_braids.Crossing_Direction import Crossing_Direction
|
|
12
|
+
from knit_graphs.artin_wale_braids.Loop_Braid_Graph import Loop_Braid_Graph
|
|
13
|
+
from knit_graphs.artin_wale_braids.Wale import Wale
|
|
14
|
+
from knit_graphs.artin_wale_braids.Wale_Group import Wale_Group
|
|
15
|
+
from knit_graphs.Course import Course
|
|
16
|
+
from knit_graphs.Loop import Loop
|
|
17
|
+
from knit_graphs.Pull_Direction import Pull_Direction
|
|
18
|
+
from knit_graphs.Yarn import Yarn
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Knit_Graph(_Base_Knit_Graph):
|
|
22
|
+
"""A representation of knitted structures as connections between loops on yarns.
|
|
23
|
+
|
|
24
|
+
The Knit_Graph class is the main data structure for representing knitted fabrics.
|
|
25
|
+
It maintains a directed graph of loops connected by stitch edges, manages yarn relationships,
|
|
26
|
+
and provides methods for analyzing the structure of the knitted fabric including courses, wales, and cable crossings.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
"""Initialize an empty knit graph with no loops or yarns."""
|
|
31
|
+
super().__init__()
|
|
32
|
+
self.braid_graph: Loop_Braid_Graph = Loop_Braid_Graph()
|
|
33
|
+
self._last_loop: None | Loop = None
|
|
34
|
+
self.yarns: set[Yarn] = set()
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def last_loop(self) -> None | Loop:
|
|
38
|
+
"""Get the most recently added loop in the graph.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
None | Loop: The last loop added to the graph, or None if no loops have been added.
|
|
42
|
+
"""
|
|
43
|
+
return self._last_loop
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def has_loop(self) -> bool:
|
|
47
|
+
"""Check if the graph contains any loops.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
bool: True if the graph has at least one loop, False otherwise.
|
|
51
|
+
"""
|
|
52
|
+
return self.last_loop is not None
|
|
53
|
+
|
|
54
|
+
def add_crossing(self, left_loop: Loop, right_loop: Loop, crossing_direction: Crossing_Direction) -> None:
|
|
55
|
+
"""Add a cable crossing between two loops with the specified crossing direction.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
left_loop (Loop): The loop on the left side of the crossing.
|
|
59
|
+
right_loop (Loop): The loop on the right side of the crossing.
|
|
60
|
+
crossing_direction (Crossing_Direction): The direction of the crossing (over, under, or none) between the loops.
|
|
61
|
+
"""
|
|
62
|
+
self.braid_graph.add_crossing(left_loop, right_loop, crossing_direction)
|
|
63
|
+
|
|
64
|
+
def add_loop(self, loop: Loop) -> None:
|
|
65
|
+
"""Add a loop to the knit graph as a node.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
loop (Loop): The loop to be added as a node in the graph. If the loop's yarn is not already in the graph, it will be added automatically.
|
|
69
|
+
"""
|
|
70
|
+
self.stitch_graph.add_node(loop)
|
|
71
|
+
if loop.yarn not in self.yarns:
|
|
72
|
+
self.add_yarn(cast(Yarn, loop.yarn))
|
|
73
|
+
if self._last_loop is None or loop > self._last_loop:
|
|
74
|
+
self._last_loop = loop
|
|
75
|
+
|
|
76
|
+
def add_yarn(self, yarn: Yarn) -> None:
|
|
77
|
+
"""Add a yarn to the graph without adding its loops.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
yarn (Yarn): The yarn to be added to the graph structure. This method assumes that loops do not need to be added separately.
|
|
81
|
+
"""
|
|
82
|
+
self.yarns.add(yarn)
|
|
83
|
+
|
|
84
|
+
def connect_loops(self, parent_loop: Loop, child_loop: Loop,
|
|
85
|
+
pull_direction: Pull_Direction = Pull_Direction.BtF,
|
|
86
|
+
stack_position: int | None = None) -> None:
|
|
87
|
+
"""Create a stitch edge by connecting a parent and child loop.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
parent_loop (Loop): The parent loop to connect to the child loop.
|
|
91
|
+
child_loop (Loop): The child loop to connect to the parent loop.
|
|
92
|
+
pull_direction (Pull_Direction): The direction the child is pulled through the parent. Defaults to Pull_Direction.BtF (knit stitch).
|
|
93
|
+
stack_position (int | None, optional): The position to insert the parent into the child's parent stack. If None, adds on top of the stack. Defaults to None.
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
KeyError: If either the parent_loop or child_loop is not already in the knit graph.
|
|
97
|
+
"""
|
|
98
|
+
if parent_loop not in self:
|
|
99
|
+
raise KeyError(f"parent loop {parent_loop} not in Knit Graph")
|
|
100
|
+
if child_loop not in self:
|
|
101
|
+
raise KeyError(f"child loop {parent_loop} not in Knit Graph")
|
|
102
|
+
self.stitch_graph.add_edge(parent_loop, child_loop, pull_direction=pull_direction)
|
|
103
|
+
child_loop.add_parent_loop(parent_loop, stack_position)
|
|
104
|
+
|
|
105
|
+
def get_wale_starting_with_loop(self, first_loop: Loop) -> Wale:
|
|
106
|
+
"""Get a wale (vertical column of stitches) starting from the specified loop.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
first_loop (Loop): The loop at the start of the wale to be constructed.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Wale: A wale object representing the vertical column of stitches starting from the given loop.
|
|
113
|
+
"""
|
|
114
|
+
wale = Wale(first_loop)
|
|
115
|
+
cur_loop = first_loop
|
|
116
|
+
while len(self.stitch_graph.successors(cur_loop)) == 1:
|
|
117
|
+
cur_loop = [*self.stitch_graph.successors(cur_loop)][0]
|
|
118
|
+
assert isinstance(wale.last_loop, Loop)
|
|
119
|
+
wale.add_loop_to_end(cur_loop, self.get_pull_direction(wale.last_loop, cur_loop))
|
|
120
|
+
return wale
|
|
121
|
+
|
|
122
|
+
def get_wales_ending_with_loop(self, last_loop: Loop) -> list[Wale]:
|
|
123
|
+
"""Get all wales (vertical columns of stitches) that end at the specified loop.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
last_loop (Loop): The last loop of the joined set of wales.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
list[Wale]: The set of wales that end at this loop. Only returns multiple wales if this loop is a child of a decrease stitch.
|
|
130
|
+
"""
|
|
131
|
+
wales = []
|
|
132
|
+
for top_stitch_parent in self.stitch_graph.predecessors(last_loop):
|
|
133
|
+
wale = Wale(last_loop)
|
|
134
|
+
wale.add_loop_to_beginning(top_stitch_parent, cast(Pull_Direction, self.get_pull_direction(top_stitch_parent, last_loop)))
|
|
135
|
+
cur_loop = top_stitch_parent
|
|
136
|
+
while len(cur_loop.parent_loops) == 1: # stop at split for decrease or start of wale
|
|
137
|
+
cur_loop = [*self.stitch_graph.predecessors(cur_loop)][0]
|
|
138
|
+
wale.add_loop_to_beginning(cur_loop, cast(Pull_Direction, self.get_pull_direction(cur_loop, cast(Loop, wale.first_loop))))
|
|
139
|
+
wales.append(wale)
|
|
140
|
+
return wales
|
|
141
|
+
|
|
142
|
+
def get_courses(self) -> list[Course]:
|
|
143
|
+
"""Get all courses (horizontal rows) in the knit graph in chronological order.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
list[Course]: A list of courses representing horizontal rows of loops.
|
|
147
|
+
The first course contains the initial set of loops. A course change occurs when a loop has a parent loop in the previous course.
|
|
148
|
+
"""
|
|
149
|
+
courses = []
|
|
150
|
+
course = Course(self)
|
|
151
|
+
for loop in sorted([*self.stitch_graph.nodes]):
|
|
152
|
+
for parent in self.stitch_graph.predecessors(loop):
|
|
153
|
+
if parent in course: # start a new course
|
|
154
|
+
courses.append(course)
|
|
155
|
+
course = Course(self)
|
|
156
|
+
break
|
|
157
|
+
course.add_loop(loop)
|
|
158
|
+
courses.append(course)
|
|
159
|
+
return courses
|
|
160
|
+
|
|
161
|
+
def get_wale_groups(self) -> dict[Loop, Wale_Group]:
|
|
162
|
+
"""Get wale groups organized by their terminal loops.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
dict[Loop, Wale_Group]: Dictionary mapping terminal loops to the wale groups they terminate. Each wale group represents a collection of wales that end at the same terminal loop.
|
|
166
|
+
"""
|
|
167
|
+
wale_groups = {}
|
|
168
|
+
for loop in self.stitch_graph.nodes:
|
|
169
|
+
if self.is_terminal_loop(loop):
|
|
170
|
+
wale_groups.update({loop: Wale_Group(wale, self) for wale in self.get_wales_ending_with_loop(loop)})
|
|
171
|
+
return wale_groups
|
|
172
|
+
|
|
173
|
+
def __contains__(self, item: Loop) -> bool:
|
|
174
|
+
"""Check if a loop is contained in the knit graph.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
item (Loop): The loop being checked for in the graph.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
bool: True if the loop is in the graph, False otherwise.
|
|
181
|
+
"""
|
|
182
|
+
return bool(self.stitch_graph.has_node(item))
|
|
183
|
+
|
|
184
|
+
def get_pull_direction(self, parent: Loop, child: Loop) -> Pull_Direction | None:
|
|
185
|
+
"""Get the pull direction of the stitch edge between parent and child loops.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
parent (Loop): The parent loop of the stitch edge.
|
|
189
|
+
child (Loop): The child loop of the stitch edge.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Pull_Direction | None: The pull direction of the stitch-edge between the parent and child, or None if there is no edge between these loops.
|
|
193
|
+
"""
|
|
194
|
+
edge = self.get_stitch_edge(parent, child)
|
|
195
|
+
if edge is None:
|
|
196
|
+
return None
|
|
197
|
+
else:
|
|
198
|
+
return cast(Pull_Direction, edge['pull_direction'])
|
|
199
|
+
|
|
200
|
+
def get_stitch_edge(self, parent: Loop, child: Loop) -> dict[str, Any] | None:
|
|
201
|
+
"""Get the stitch edge data between two loops.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
parent (Loop): The parent loop of the stitch edge.
|
|
205
|
+
child (Loop): The child loop of the stitch edge.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
dict[str, Any] | None: The edge data dictionary for this stitch edge, or None if no edge exists between these loops.
|
|
209
|
+
"""
|
|
210
|
+
if self.stitch_graph.has_edge(parent, child):
|
|
211
|
+
return cast(dict[str, Any], self.stitch_graph.get_edge_data(parent, child))
|
|
212
|
+
else:
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
def get_child_loop(self, loop: Loop) -> Loop | None:
|
|
216
|
+
"""Get the child loop of the specified parent loop.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
loop (Loop): The loop to look for a child loop from.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Loop | None: The child loop if one exists, or None if no child loop is found.
|
|
223
|
+
"""
|
|
224
|
+
successors = [*self.stitch_graph.successors(loop)]
|
|
225
|
+
if len(successors) == 0:
|
|
226
|
+
return None
|
|
227
|
+
return cast(Loop, successors[0])
|
|
228
|
+
|
|
229
|
+
def has_child_loop(self, loop: Loop) -> bool:
|
|
230
|
+
"""Check if a loop has a child loop connected to it.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
loop (Loop): The loop to check for child connections.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
bool: True if the loop has a child loop, False otherwise.
|
|
237
|
+
"""
|
|
238
|
+
return self.get_child_loop(loop) is not None
|
|
239
|
+
|
|
240
|
+
def is_terminal_loop(self, loop: Loop) -> bool:
|
|
241
|
+
"""Check if a loop is terminal (has no child loops and terminates a wale).
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
loop (Loop): The loop to check for terminal status.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
bool: True if the loop has no child loops and terminates a wale, False otherwise.
|
|
248
|
+
"""
|
|
249
|
+
return not self.has_child_loop(loop)
|