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.
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 typing import TYPE_CHECKING
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]]): A dictionary tracking loops that this loop floats in front of.
24
- back_floats (dict[Loop, set[Loop]]): A dictionary tracking loops that this loop floats behind.
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 (Yarn): The yarn that creates and holds this loop.
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.parent_loops: list[Loop] = []
39
- self.front_floats: dict[Loop, set[Loop]] = {}
40
- self.back_floats: dict[Loop, set[Loop]] = {}
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
- def add_loop_in_front_of_float(self, u: Loop, v: Loop) -> None:
43
- """Set this loop to be in front of the float between loops u and v.
48
+ @property
49
+ def loop_id(self) -> int:
50
+ """Get the unique identifier of this loop.
44
51
 
45
- This method establishes that this loop passes in front of a floating yarn segment between two other loops.
52
+ Returns:
53
+ int: The id of the loop.
54
+ """
55
+ return self._loop_id
46
56
 
47
- Args:
48
- u (Loop): The first loop in the float pair.
49
- v (Loop): The second loop in the float pair.
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
- Raises:
52
- ValueError: If u and v are not on the same yarn.
61
+ Returns:
62
+ bool: True if the loop has stitch-edge parents, False otherwise.
53
63
  """
54
- if u.yarn != v.yarn:
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
- def remove_loop_from_front_floats(self) -> None:
66
+ @property
67
+ def parent_loop_ids(self) -> list[int]:
64
68
  """
65
- Removes this loop from being in front of all marked floats. Mutates the yarns that own edges of those floats.
69
+ Returns:
70
+ list[int]: The ids of the parent loops of this loop in their stacking order.
66
71
  """
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
-
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
- This method establishes that this loop passes behind a floating yarn segment between two other loops.
79
-
80
- Args:
81
- u (Loop): The first loop in the float pair.
82
- v (Loop): The second loop in the float pair.
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
- Raises:
85
- ValueError: If u and v are not on the same yarn.
82
+ @property
83
+ def pull_direction(self) -> Pull_Direction:
86
84
  """
87
- if u.yarn != v.yarn:
88
- raise ValueError("Loops of a float must share a yarn.")
89
- if u not in self.front_floats:
90
- self.front_floats[u] = set()
91
- if v not in self.front_floats:
92
- self.front_floats[v] = set()
93
- self.front_floats[u].add(v)
94
- self.front_floats[v].add(u)
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
- def remove_loop_from_back_floats(self) -> None:
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
- Removes this loop from being behind of all marked floats. Mutates the yarns that own edges of those floats.
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
- 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
-
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: Loop, v: Loop) -> bool:
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
- def prior_loop_on_yarn(self) -> Loop | None:
133
- """Get the loop that precedes this loop on the same yarn.
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
- Loop | None: The prior loop on the yarn, or None if this is the first loop on the yarn.
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
- return self.yarn.prior_loop(self)
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 next_loop_on_yarn(self) -> Loop | None:
141
- """Get the loop that follows this loop on the same yarn.
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
- Returns:
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
- 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.
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
- parents = self.parent_loops
155
- self.parent_loops = []
156
- return parents
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 has_parent_loops(self) -> bool:
159
- """Check if this loop has any parent loops connected through stitch edges.
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
- Returns:
162
- bool: True if the loop has stitch-edge parents, False otherwise.
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
- return len(self.parent_loops) > 0
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: Loop, stack_position: int | None = None) -> None:
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: Loop) -> None:
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: Loop) -> bool:
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 (Loop): The other loop to compare with.
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, other.__class__) and self.loop_id == other.loop_id
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.
@@ -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.