knit-graphs 0.0.11__tar.gz → 0.0.12__tar.gz
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.11 → knit_graphs-0.0.12}/PKG-INFO +1 -1
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/pyproject.toml +1 -1
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/Course.py +63 -42
- knit_graphs-0.0.12/src/knit_graphs/Knit_Graph.py +209 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/Knit_Graph_Visualizer.py +51 -63
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/Loop.py +122 -116
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/Pull_Direction.py +3 -2
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/Yarn.py +246 -233
- knit_graphs-0.0.12/src/knit_graphs/artin_wale_braids/Loop_Braid_Graph.py +96 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/artin_wale_braids/Wale.py +61 -82
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/artin_wale_braids/Wale_Braid.py +7 -2
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/artin_wale_braids/Wale_Braid_Word.py +35 -22
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/artin_wale_braids/Wale_Group.py +46 -39
- knit_graphs-0.0.12/src/knit_graphs/basic_knit_graph_generators.py +207 -0
- knit_graphs-0.0.12/src/knit_graphs/directed_loop_graph.py +454 -0
- knit_graphs-0.0.12/src/knit_graphs/knit_graph_builder.py +187 -0
- knit_graphs-0.0.12/src/knit_graphs/knit_graph_errors/knit_graph_error.py +30 -0
- knit_graphs-0.0.12/src/knit_graphs/py.typed +0 -0
- knit_graphs-0.0.11/src/knit_graphs/Knit_Graph.py +0 -307
- knit_graphs-0.0.11/src/knit_graphs/artin_wale_braids/Loop_Braid_Graph.py +0 -116
- knit_graphs-0.0.11/src/knit_graphs/basic_knit_graph_generators.py +0 -286
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/LICENSE +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/README.md +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/Makefile +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/make.bat +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.Course.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.Knit_Graph.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.Knit_Graph_Visualizer.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.Loop.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.Pull_Direction.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.Yarn.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.artin_wale_braids.Crossing_Direction.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.artin_wale_braids.Loop_Braid_Graph.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.artin_wale_braids.Wale.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.artin_wale_braids.Wale_Braid.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.artin_wale_braids.Wale_Braid_Word.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.artin_wale_braids.Wale_Group.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.artin_wale_braids.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.basic_knit_graph_generators.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/api/knit_graphs.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/conf.py +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/index.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/docs/source/installation.rst +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/__init__.py +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/artin_wale_braids/Crossing_Direction.py +0 -0
- {knit_graphs-0.0.11 → knit_graphs-0.0.12}/src/knit_graphs/artin_wale_braids/__init__.py +0 -0
- /knit_graphs-0.0.11/src/knit_graphs/py.typed → /knit_graphs-0.0.12/src/knit_graphs/knit_graph_errors/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: knit-graphs
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.12
|
|
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
|
|
@@ -12,7 +12,7 @@ build-backend = "poetry.core.masonry.api" # Use Poetry's build system
|
|
|
12
12
|
# All the information about your project that will appear on PyPI
|
|
13
13
|
[tool.poetry]
|
|
14
14
|
name = "knit-graphs"
|
|
15
|
-
version = "0.0.
|
|
15
|
+
version = "0.0.12"
|
|
16
16
|
description = "A graph representation of knitted structures where each loop is a node and edges represent yarn and stitch relationships."
|
|
17
17
|
authors = ["Megan Hofmann <m.hofmann@northeastern.edu>"]
|
|
18
18
|
maintainers = ["Megan Hofmann <m.hofmann@northeastern.edu>"]
|
|
@@ -5,34 +5,45 @@ This module contains the Course class which represents a horizontal row of loops
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
from collections.abc import Iterator
|
|
9
|
-
from typing import TYPE_CHECKING,
|
|
8
|
+
from collections.abc import Iterator, Sequence
|
|
9
|
+
from typing import TYPE_CHECKING, Generic, TypeVar, overload
|
|
10
10
|
|
|
11
11
|
from knit_graphs.Loop import Loop
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from knit_graphs.Knit_Graph import Knit_Graph
|
|
15
15
|
|
|
16
|
+
LoopT = TypeVar("LoopT", bound=Loop)
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
|
|
19
|
+
class Course(Sequence[LoopT], Generic[LoopT]):
|
|
18
20
|
"""Course object for organizing loops into knitting rows.
|
|
19
21
|
|
|
20
22
|
A Course represents a horizontal row of loops in a knitting pattern.
|
|
21
23
|
It maintains an ordered list of loops and provides methods for analyzing the structure and relationships between courses in the knitted fabric.
|
|
22
24
|
"""
|
|
23
25
|
|
|
24
|
-
def __init__(self, knit_graph: Knit_Graph) -> None:
|
|
26
|
+
def __init__(self, course_number: int, knit_graph: Knit_Graph[LoopT]) -> None:
|
|
25
27
|
"""Initialize an empty course associated with a knit graph.
|
|
26
28
|
|
|
27
29
|
Args:
|
|
28
30
|
knit_graph (Knit_Graph): The knit graph that this course belongs to.
|
|
29
31
|
"""
|
|
30
|
-
self.
|
|
31
|
-
self.
|
|
32
|
-
self.
|
|
32
|
+
self._course_number: int = course_number
|
|
33
|
+
self._knit_graph: Knit_Graph[LoopT] = knit_graph
|
|
34
|
+
self._loops_in_order: list[LoopT] = []
|
|
35
|
+
self._loop_set: set[LoopT] = set()
|
|
33
36
|
|
|
34
37
|
@property
|
|
35
|
-
def
|
|
38
|
+
def course_number(self) -> int:
|
|
39
|
+
"""
|
|
40
|
+
Returns:
|
|
41
|
+
int: The course number of the course.
|
|
42
|
+
"""
|
|
43
|
+
return self._course_number
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def loops_in_order(self) -> list[LoopT]:
|
|
36
47
|
"""
|
|
37
48
|
Returns:
|
|
38
49
|
list[Loop]: The list of loops in this course.
|
|
@@ -40,22 +51,34 @@ class Course:
|
|
|
40
51
|
return self._loops_in_order
|
|
41
52
|
|
|
42
53
|
@property
|
|
43
|
-
def
|
|
54
|
+
def loop_ids_in_course(self) -> list[int]:
|
|
55
|
+
"""
|
|
56
|
+
Returns:
|
|
57
|
+
list[int]: The loop ids in the course in the order the occur in the course.
|
|
58
|
+
"""
|
|
59
|
+
return [l.loop_id for l in self.loops_in_order]
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def knit_graph(self) -> Knit_Graph[LoopT]:
|
|
44
63
|
"""
|
|
45
64
|
Returns:
|
|
46
65
|
Knit_Graph: The knit graph that this course belongs to.
|
|
47
66
|
"""
|
|
48
67
|
return self._knit_graph
|
|
49
68
|
|
|
50
|
-
def add_loop(self, loop:
|
|
69
|
+
def add_loop(self, loop: LoopT, index: int | None = None) -> None:
|
|
51
70
|
"""Add a loop to the course at the specified index or at the end.
|
|
52
71
|
|
|
53
72
|
Args:
|
|
54
73
|
loop (Loop): The loop to add to this course.
|
|
55
74
|
index (int | None, optional): The index position to insert the loop at. If None, appends to the end.
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
ValueError: If the loop is a parent to any loops already in the course.
|
|
56
78
|
"""
|
|
57
79
|
for parent_loop in loop.parent_loops:
|
|
58
|
-
|
|
80
|
+
if parent_loop in self:
|
|
81
|
+
raise ValueError(f"{loop} has parent {parent_loop}, cannot be added to same course")
|
|
59
82
|
self._loop_set.add(loop)
|
|
60
83
|
if index is None:
|
|
61
84
|
self.loops_in_order.append(loop)
|
|
@@ -68,7 +91,7 @@ class Course:
|
|
|
68
91
|
Returns:
|
|
69
92
|
bool: True if the course has at least one yarn over (loop with no parent loops) to start new wales.
|
|
70
93
|
"""
|
|
71
|
-
return any(not loop.has_parent_loops
|
|
94
|
+
return any(not loop.has_parent_loops for loop in self)
|
|
72
95
|
|
|
73
96
|
def has_decrease(self) -> bool:
|
|
74
97
|
"""Check if this course contains any decrease stitches that merge wales.
|
|
@@ -78,18 +101,7 @@ class Course:
|
|
|
78
101
|
"""
|
|
79
102
|
return any(len(loop.parent_loops) > 1 for loop in self)
|
|
80
103
|
|
|
81
|
-
def
|
|
82
|
-
"""Get loop(s) at the specified index or slice.
|
|
83
|
-
|
|
84
|
-
Args:
|
|
85
|
-
index (int | slice): The index or slice to retrieve from the course.
|
|
86
|
-
|
|
87
|
-
Returns:
|
|
88
|
-
Loop | list[Loop]: The loop at the specified index, or list of loops for a slice.
|
|
89
|
-
"""
|
|
90
|
-
return self.loops_in_order[index]
|
|
91
|
-
|
|
92
|
-
def in_round_with(self, next_course: Course) -> bool:
|
|
104
|
+
def in_round_with(self, next_course: Course[LoopT]) -> bool:
|
|
93
105
|
"""Check if the next course connects to this course in a circular pattern.
|
|
94
106
|
|
|
95
107
|
This method determines if the courses are connected in the round (circular knitting) by checking if the next course starts at the beginning of this course.
|
|
@@ -100,14 +112,14 @@ class Course:
|
|
|
100
112
|
Returns:
|
|
101
113
|
bool: True if the next course starts at the beginning of this course, indicating circular knitting.
|
|
102
114
|
"""
|
|
103
|
-
next_start
|
|
115
|
+
next_start = next_course[0]
|
|
104
116
|
i = 1
|
|
105
|
-
while not next_start.has_parent_loops
|
|
106
|
-
next_start =
|
|
117
|
+
while not next_start.has_parent_loops:
|
|
118
|
+
next_start = next_course[i]
|
|
107
119
|
i += 1
|
|
108
120
|
return self[0] in next_start.parent_loops
|
|
109
121
|
|
|
110
|
-
def in_row_with(self, next_course: Course) -> bool:
|
|
122
|
+
def in_row_with(self, next_course: Course[LoopT]) -> bool:
|
|
111
123
|
"""Check if the next course connects to this course in a flat/row pattern.
|
|
112
124
|
|
|
113
125
|
This method determines if the courses are connected in flat knitting (back and forth) by checking if the next course starts at the end of this course.
|
|
@@ -118,39 +130,48 @@ class Course:
|
|
|
118
130
|
Returns:
|
|
119
131
|
bool: True if the next course starts at the end of this course, indicating flat/row knitting.
|
|
120
132
|
"""
|
|
121
|
-
next_start:
|
|
133
|
+
next_start: LoopT = next_course[0]
|
|
122
134
|
i = 1
|
|
123
|
-
while not next_start.has_parent_loops
|
|
124
|
-
next_start =
|
|
135
|
+
while not next_start.has_parent_loops:
|
|
136
|
+
next_start = next_course[i]
|
|
125
137
|
i += 1
|
|
126
138
|
return self[-1] in next_start.parent_loops
|
|
127
139
|
|
|
128
|
-
def __contains__(self, loop:
|
|
140
|
+
def __contains__(self, loop: object) -> bool:
|
|
129
141
|
"""Check if a loop is contained in this course.
|
|
130
142
|
|
|
131
143
|
Args:
|
|
132
|
-
loop (
|
|
144
|
+
loop (LoopT): The loop to check for membership in this course.
|
|
133
145
|
|
|
134
146
|
Returns:
|
|
135
147
|
bool: True if the loop is in this course, False otherwise.
|
|
136
148
|
"""
|
|
137
149
|
return loop in self._loop_set
|
|
138
150
|
|
|
139
|
-
|
|
140
|
-
|
|
151
|
+
@overload
|
|
152
|
+
def __getitem__(self, index: int) -> LoopT: ...
|
|
153
|
+
|
|
154
|
+
@overload
|
|
155
|
+
def __getitem__(self, index: slice) -> list[LoopT]: ...
|
|
156
|
+
|
|
157
|
+
def __getitem__(self, index: int | slice) -> LoopT | list[LoopT]:
|
|
158
|
+
"""Get loop(s) at the specified index or slice.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
index (int | slice): The index or slice to retrieve from the course.
|
|
141
162
|
|
|
142
163
|
Returns:
|
|
143
|
-
|
|
164
|
+
Loop | list[Loop]: The loop at the specified index, or list of loops for a slice.
|
|
144
165
|
"""
|
|
145
|
-
return
|
|
166
|
+
return self.loops_in_order[index]
|
|
146
167
|
|
|
147
|
-
def
|
|
148
|
-
"""Iterate over loops in this course in
|
|
168
|
+
def __iter__(self) -> Iterator[LoopT]:
|
|
169
|
+
"""Iterate over loops in this course in order.
|
|
149
170
|
|
|
150
171
|
Returns:
|
|
151
|
-
Iterator[Loop]: An iterator over the loops in this course in
|
|
172
|
+
Iterator[Loop]: An iterator over the loops in this course in their natural order.
|
|
152
173
|
"""
|
|
153
|
-
return
|
|
174
|
+
return iter(self.loops_in_order)
|
|
154
175
|
|
|
155
176
|
def __len__(self) -> int:
|
|
156
177
|
"""Get the number of loops in this course.
|
|
@@ -166,7 +187,7 @@ class Course:
|
|
|
166
187
|
Returns:
|
|
167
188
|
str: String representation showing the ordered list of loops.
|
|
168
189
|
"""
|
|
169
|
-
return
|
|
190
|
+
return f"Course {self.course_number}: {self.loops_in_order}"
|
|
170
191
|
|
|
171
192
|
def __repr__(self) -> str:
|
|
172
193
|
"""Get string representation of this course for debugging.
|
|
@@ -0,0 +1,209 @@
|
|
|
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
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Iterator
|
|
10
|
+
from typing import TypeVar
|
|
11
|
+
|
|
12
|
+
from knit_graphs.artin_wale_braids.Crossing_Direction import Crossing_Direction
|
|
13
|
+
from knit_graphs.artin_wale_braids.Loop_Braid_Graph import Loop_Braid_Graph
|
|
14
|
+
from knit_graphs.artin_wale_braids.Wale import Wale
|
|
15
|
+
from knit_graphs.artin_wale_braids.Wale_Group import Wale_Group
|
|
16
|
+
from knit_graphs.Course import Course
|
|
17
|
+
from knit_graphs.directed_loop_graph import Directed_Loop_Graph
|
|
18
|
+
from knit_graphs.Loop import Loop
|
|
19
|
+
from knit_graphs.Pull_Direction import Pull_Direction
|
|
20
|
+
from knit_graphs.Yarn import Yarn
|
|
21
|
+
|
|
22
|
+
LoopT = TypeVar("LoopT", bound=Loop)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Knit_Graph(Directed_Loop_Graph[LoopT, Pull_Direction]):
|
|
26
|
+
"""A representation of knitted structures as connections between loops on yarns.
|
|
27
|
+
|
|
28
|
+
The Knit_Graph class is the main data structure for representing knitted fabrics.
|
|
29
|
+
It maintains a directed graph of loops connected by stitch edges, manages yarn relationships,
|
|
30
|
+
and provides methods for analyzing the structure of the knitted fabric including courses, wales, and cable crossings.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self) -> None:
|
|
34
|
+
"""Initialize an empty knit graph with no loops or yarns."""
|
|
35
|
+
super().__init__()
|
|
36
|
+
self.braid_graph: Loop_Braid_Graph[LoopT] = Loop_Braid_Graph()
|
|
37
|
+
self._last_loop: LoopT | None = None
|
|
38
|
+
self.yarns: set[Yarn[LoopT]] = set()
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def last_loop(self) -> LoopT | None:
|
|
42
|
+
"""Get the most recently added loop in the graph.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Loop | None: The last loop added to the graph, or None if no loops have been added.
|
|
46
|
+
"""
|
|
47
|
+
return self._last_loop
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def stitch_iter(self) -> Iterator[tuple[LoopT, LoopT, Pull_Direction]]:
|
|
51
|
+
"""
|
|
52
|
+
Returns:
|
|
53
|
+
Iterator[tuple[LoopT, LoopT, Pull_Direction]]: Iterator over the edges and edge-data in the graph.
|
|
54
|
+
|
|
55
|
+
Notes:
|
|
56
|
+
No guarantees about the order of the edges.
|
|
57
|
+
"""
|
|
58
|
+
return self.edge_iter
|
|
59
|
+
|
|
60
|
+
def get_pull_direction(self, parent: LoopT | int, child: LoopT | int) -> Pull_Direction:
|
|
61
|
+
"""Get the pull direction of the stitch edge between parent and child loops.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
parent (Loop | int): The parent loop of the stitch edge.
|
|
65
|
+
child (Loop | int): The child loop of the stitch edge.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Pull_Direction: The pull direction of the stitch-edge between the parent and child.
|
|
69
|
+
"""
|
|
70
|
+
return self.get_edge(parent, child)
|
|
71
|
+
|
|
72
|
+
def add_crossing(self, left_loop: LoopT, right_loop: LoopT, crossing_direction: Crossing_Direction) -> None:
|
|
73
|
+
"""Add a cable crossing between two loops with the specified crossing direction.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
left_loop (Loop): The loop on the left side of the crossing.
|
|
77
|
+
right_loop (Loop): The loop on the right side of the crossing.
|
|
78
|
+
crossing_direction (Crossing_Direction): The direction of the crossing (over, under, or none) between the loops.
|
|
79
|
+
"""
|
|
80
|
+
self.braid_graph.add_crossing(left_loop, right_loop, crossing_direction)
|
|
81
|
+
|
|
82
|
+
def add_loop(self, loop: LoopT) -> None:
|
|
83
|
+
"""Add a loop to the knit graph as a node.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
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.
|
|
87
|
+
"""
|
|
88
|
+
super().add_loop(loop)
|
|
89
|
+
if loop.yarn not in self.yarns:
|
|
90
|
+
self.add_yarn(loop.yarn)
|
|
91
|
+
if self._last_loop is None or loop > self._last_loop:
|
|
92
|
+
self._last_loop = loop
|
|
93
|
+
|
|
94
|
+
def remove_loop(self, loop: LoopT | int) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Remove the given loop from the knit graph.
|
|
97
|
+
Args:
|
|
98
|
+
loop (Loop | int): The loop or loop_id to be removed.
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
KeyError: If the loop is not in the knit graph.
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
if isinstance(loop, int):
|
|
105
|
+
loop = self.get_loop(loop)
|
|
106
|
+
if loop in self.braid_graph:
|
|
107
|
+
self.braid_graph.remove_loop(loop) # remove any crossing associated with this loop.
|
|
108
|
+
# Remove any stitch edges involving this loop.
|
|
109
|
+
if self.has_child_loop(loop):
|
|
110
|
+
child_loop = self.get_child_loop(loop)
|
|
111
|
+
if child_loop is not None:
|
|
112
|
+
child_loop.remove_parent(loop)
|
|
113
|
+
super().remove_loop(loop)
|
|
114
|
+
# Remove loop from any floating positions
|
|
115
|
+
for yarn in self.yarns:
|
|
116
|
+
yarn.remove_loop_relative_to_floats(loop)
|
|
117
|
+
# Remove loop from yarn
|
|
118
|
+
yarn = loop.yarn
|
|
119
|
+
yarn.remove_loop(loop)
|
|
120
|
+
if len(yarn) == 0: # This was the only loop on that yarn
|
|
121
|
+
self.yarns.discard(yarn)
|
|
122
|
+
# Reset last loop
|
|
123
|
+
if loop is self.last_loop:
|
|
124
|
+
if len(self.yarns) == 0: # No loops left
|
|
125
|
+
self._last_loop = None
|
|
126
|
+
else: # Set to the newest loop formed at the end of any yarns.
|
|
127
|
+
self._last_loop = max(y.last_loop for y in self.yarns if y.last_loop is not None)
|
|
128
|
+
|
|
129
|
+
def add_yarn(self, yarn: Yarn[LoopT]) -> None:
|
|
130
|
+
"""Add a yarn to the graph without adding its loops.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
yarn (Yarn[LoopT]): The yarn to be added to the graph structure. This method assumes that loops do not need to be added separately.
|
|
134
|
+
"""
|
|
135
|
+
self.yarns.add(yarn)
|
|
136
|
+
|
|
137
|
+
def connect_loops(
|
|
138
|
+
self,
|
|
139
|
+
parent_loop: LoopT,
|
|
140
|
+
child_loop: LoopT,
|
|
141
|
+
pull_direction: Pull_Direction = Pull_Direction.BtF,
|
|
142
|
+
stack_position: int | None = None,
|
|
143
|
+
) -> None:
|
|
144
|
+
"""Create a stitch edge by connecting a parent and child loop.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
parent_loop (Loop): The parent loop to connect to the child loop.
|
|
148
|
+
child_loop (Loop): The child loop to connect to the parent loop.
|
|
149
|
+
pull_direction (Pull_Direction): The direction the child is pulled through the parent. Defaults to Pull_Direction.BtF (knit stitch).
|
|
150
|
+
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.
|
|
151
|
+
|
|
152
|
+
Raises:
|
|
153
|
+
KeyError: If either the parent_loop or child_loop is not already in the knit graph.
|
|
154
|
+
"""
|
|
155
|
+
super().add_edge(parent_loop, child_loop, pull_direction)
|
|
156
|
+
child_loop.add_parent_loop(parent_loop, stack_position)
|
|
157
|
+
|
|
158
|
+
def get_wales_ending_with_loop(self, last_loop: LoopT) -> set[Wale[LoopT]]:
|
|
159
|
+
"""Get all wales (vertical columns of stitches) that end at the specified loop.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
last_loop (Loop): The last loop of the joined set of wales.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
set[Wale[LoopT]]: The set of wales that end at this loop.
|
|
166
|
+
"""
|
|
167
|
+
if len(last_loop.parent_loops) == 0:
|
|
168
|
+
return {Wale[LoopT](last_loop, self)}
|
|
169
|
+
sources = self.source_loops(last_loop)
|
|
170
|
+
return {Wale[LoopT](source, self) for source in sources}
|
|
171
|
+
|
|
172
|
+
def get_terminal_wales(self) -> dict[LoopT, list[Wale]]:
|
|
173
|
+
"""
|
|
174
|
+
Get wale groups organized by their terminal loops.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
dict[Loop, list[Wale]]: Dictionary mapping terminal loops to list of wales that terminate that wale.
|
|
178
|
+
"""
|
|
179
|
+
wale_groups = {}
|
|
180
|
+
for loop in self.terminal_loops:
|
|
181
|
+
wale_groups[loop] = list(self.get_wales_ending_with_loop(loop))
|
|
182
|
+
return wale_groups
|
|
183
|
+
|
|
184
|
+
def get_courses(self) -> list[Course[LoopT]]:
|
|
185
|
+
"""Get all courses (horizontal rows) in the knit graph in chronological order.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
list[Course[LoopT]: A list of courses representing horizontal rows of loops.
|
|
189
|
+
The first course contains the initial set of loops. A course change occurs when a loop has a parent loop in the previous course.
|
|
190
|
+
"""
|
|
191
|
+
courses = []
|
|
192
|
+
course = Course(0, self)
|
|
193
|
+
for loop in self.sorted_loops:
|
|
194
|
+
for parent in loop.parent_loops:
|
|
195
|
+
if parent in course: # start a new course
|
|
196
|
+
courses.append(course)
|
|
197
|
+
course = Course(course.course_number + 1, self)
|
|
198
|
+
break
|
|
199
|
+
course.add_loop(loop)
|
|
200
|
+
courses.append(course)
|
|
201
|
+
return courses
|
|
202
|
+
|
|
203
|
+
def get_wale_groups(self) -> set[Wale_Group]:
|
|
204
|
+
"""Get wale groups organized by their terminal loops.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
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.
|
|
208
|
+
"""
|
|
209
|
+
return {Wale_Group(terminal_loop, self) for terminal_loop in self.terminal_loops}
|