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