knit-graphs 0.0.9__py3-none-any.whl → 0.0.11__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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  This module provides the Wale_Group class which represents a collection of interconnected wales that are joined through decrease operations, forming a tree-like structure of vertical stitch columns.
4
4
  """
5
+
5
6
  from __future__ import annotations
6
7
 
7
8
  from typing import TYPE_CHECKING, cast
@@ -24,84 +25,68 @@ class Wale_Group:
24
25
  Attributes:
25
26
  wale_graph (DiGraph): A directed graph representing the relationships between wales in this group.
26
27
  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
28
  top_loops (dict[Loop, Wale]): Mapping from the last (top) loop of each wale to the wale itself.
29
29
  bottom_loops (dict[Loop, Wale]): Mapping from the first (bottom) loop of each wale to the wale itself.
30
30
  """
31
31
 
32
- def __init__(self, terminal_wale: Wale, knit_graph: Knit_Graph):
32
+ def __init__(self, terminal_loop: Loop, knit_graph: Knit_Graph):
33
33
  """Initialize a wale group starting from a terminal wale and building downward.
34
34
 
35
35
  Args:
36
- terminal_wale (Wale): The topmost wale in the group, used as the starting point for building the complete group structure.
36
+ terminal_loop (Loop): The terminal loop of this wale-group. All the wales in the group connect up to this loop.
37
37
  knit_graph (Knit_Graph): The parent knit graph that contains this wale group.
38
38
  """
39
39
  self.wale_graph: DiGraph = DiGraph()
40
40
  self.stitch_graph: DiGraph = DiGraph()
41
41
  self._knit_graph: Knit_Graph = knit_graph
42
- self.terminal_wale: Wale | None = terminal_wale
42
+ self._terminal_loop: Loop = terminal_loop
43
43
  self.top_loops: dict[Loop, Wale] = {}
44
44
  self.bottom_loops: dict[Loop, Wale] = {}
45
- self.build_group_from_top_wale(terminal_wale)
46
-
47
- def add_wale(self, wale: Wale) -> None:
48
- """Add a wale to the group and connect it to existing wales through shared loops.
49
-
50
- This method adds the wale to the group's graphs and establishes connections with other wales based on shared loops at their endpoints.
45
+ self._build_wale_group()
51
46
 
52
- Args:
53
- wale (Wale): The wale to add to this group. Empty wales are ignored and not added.
47
+ @property
48
+ def terminal_loop(self) -> Loop:
54
49
  """
55
- if len(wale) == 0:
56
- return # This wale is empty and therefore there is nothing to add to the wale group
50
+ Returns:
51
+ Loop: The loop that terminates all wales in this group.
52
+ """
53
+ return self._terminal_loop
54
+
55
+ def _build_wale_group(self) -> None:
56
+ full_wales = self._knit_graph.get_wales_ending_with_loop(self._terminal_loop)
57
+ # Build up the stitch graph.
58
+ for wale in full_wales:
59
+ u_loops: list[Loop] = cast(list[Loop], wale[:-1])
60
+ v_loops: list[Loop] = cast(list[Loop], wale[1:])
61
+ for u, v in zip(u_loops, v_loops, strict=False):
62
+ self.stitch_graph.add_edge(u, v, pull_direction=self._knit_graph.get_pull_direction(u, v))
63
+ wales_to_split = full_wales
64
+ while len(wales_to_split) > 0:
65
+ wale_to_split = wales_to_split.pop()
66
+ split = False
67
+ upper_loops = cast(list[Loop], wale_to_split[1:])
68
+ for (
69
+ loop
70
+ ) in upper_loops: # skip first loop in each wale as it may be already connected to a discovered decrease.
71
+ if len(loop.parent_loops) > 1: # Focal of a decrease.
72
+ clean_wale, remaining_wale = wale_to_split.split_wale(loop)
73
+ if not self.wale_graph.has_node(clean_wale):
74
+ self._add_wale(clean_wale)
75
+ if isinstance(remaining_wale, Wale):
76
+ wales_to_split.add(remaining_wale)
77
+ split = True
78
+ break
79
+ if not split:
80
+ self._add_wale(wale_to_split)
81
+ for _bot_loop, lower_wale in self.bottom_loops.items():
82
+ if lower_wale.last_loop in self.bottom_loops:
83
+ self.wale_graph.add_edge(lower_wale, self.bottom_loops[lower_wale.last_loop])
84
+
85
+ def _add_wale(self, wale: Wale) -> None:
57
86
  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
87
  self.top_loops[wale.last_loop] = wale
68
- assert isinstance(wale.first_loop, Loop)
69
88
  self.bottom_loops[wale.first_loop] = wale
70
89
 
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
90
  def get_loops_over_courses(self) -> list[list[Loop]]:
106
91
  """Get loops organized by their course (horizontal row) within this wale group.
107
92
 
@@ -110,11 +95,8 @@ class Wale_Group:
110
95
  Returns:
111
96
  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
97
  """
113
- if self.terminal_wale is None:
114
- return []
115
- top_loop: Loop = cast(Loop, self.terminal_wale.last_loop)
116
98
  courses: list[list[Loop]] = []
117
- cur_course: list[Loop] = [top_loop]
99
+ cur_course: list[Loop] = [self._terminal_loop]
118
100
  while len(cur_course) > 0:
119
101
  courses.append(cur_course)
120
102
  next_course = []
@@ -132,7 +114,41 @@ class Wale_Group:
132
114
  int: The height of the wale group from the base loops to the tallest terminal, measured in total number of loops.
133
115
  """
134
116
  max_len = 0
135
- for bot_loop, wale in self.bottom_loops.items():
117
+ for _bot_loop, wale in self.bottom_loops.items():
136
118
  path_len = sum(len(successor) for successor in dfs_preorder_nodes(self.wale_graph, wale))
137
119
  max_len = max(max_len, path_len)
138
120
  return max_len
121
+
122
+ def __hash__(self) -> int:
123
+ """
124
+ Returns:
125
+ int: Hash value of the terminal loop of this group.
126
+ """
127
+ return hash(self._terminal_loop)
128
+
129
+ def __contains__(self, item: Loop | int | Wale) -> bool:
130
+ """
131
+ Args:
132
+ item (Loop | int | Wale): The item to check for in the wale group.
133
+
134
+ Returns:
135
+ bool: True if the given loop, loop_id (int), or wale is in this group.
136
+ """
137
+ if isinstance(item, (Loop, int)):
138
+ return item in self.stitch_graph.nodes
139
+ else: # isinstance(item, Wale):
140
+ return item in self.wale_graph
141
+
142
+ def __str__(self) -> str:
143
+ """
144
+ Returns:
145
+ str: The string representation of this wale group.
146
+ """
147
+ return f"WG({self.terminal_loop})"
148
+
149
+ def __repr__(self) -> str:
150
+ """
151
+ Returns:
152
+ str: The string representation of this wale group.
153
+ """
154
+ return str(self)
@@ -3,6 +3,7 @@
3
3
  This module provides utility functions for creating common knitting patterns and structures as knit graphs.
4
4
  These functions serve as building blocks for testing and demonstration purposes.
5
5
  """
6
+
6
7
  from knit_graphs.artin_wale_braids.Crossing_Direction import Crossing_Direction
7
8
  from knit_graphs.Knit_Graph import Knit_Graph
8
9
  from knit_graphs.Loop import Loop
@@ -69,15 +70,19 @@ def jersey_tube(tube_width: int, height: int) -> Knit_Graph:
69
70
  """Internal helper function to set up float connections between front and back of tube."""
70
71
  front_loops = last_course[0:tube_width]
71
72
  back_loops = last_course[tube_width:]
72
- for first_front, second_front, back in zip(front_loops[0:-1], front_loops[1:], reversed(back_loops)):
73
+ for first_front, second_front, back in zip(
74
+ front_loops[0:-1], front_loops[1:], reversed(back_loops), strict=False
75
+ ):
73
76
  yarn.add_loop_behind_float(back, first_front, second_front)
74
- for (first_back, second_back, front) in zip(back_loops[0:-1], back_loops[1:], reversed(front_loops)):
77
+ for first_back, second_back, front in zip(
78
+ back_loops[0:-1], back_loops[1:], reversed(front_loops), strict=False
79
+ ):
75
80
  yarn.add_loop_in_front_of_float(front, first_back, second_back)
76
81
 
77
82
  _set_tube_floats()
78
83
  for _ in range(0, height):
79
84
  next_course = [yarn.make_loop_on_end() for _p in last_course]
80
- for parent_loop, child_loop in zip(last_course, next_course):
85
+ for parent_loop, child_loop in zip(last_course, next_course, strict=False):
81
86
  knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF)
82
87
  last_course = next_course
83
88
  _set_tube_floats()
@@ -181,19 +186,44 @@ def lace_mesh(width: int, height: int) -> Knit_Graph:
181
186
  for i, parent_loop in enumerate(reversed(last_course)):
182
187
  child_loop = yarn.make_loop_on_end()
183
188
  if i % 3 == 0: # Just knit every third stitch
184
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF, stack_position=0)
189
+ knit_graph.connect_loops(
190
+ parent_loop,
191
+ child_loop,
192
+ pull_direction=Pull_Direction.BtF,
193
+ stack_position=0,
194
+ )
185
195
  elif i % 6 == 1: # Second of every 6 stitches (0 indexed) is yarn over before a decrease.
186
196
  yo_parent = parent_loop
187
197
  elif i % 6 == 2: # Third of every 6 stitches is bottom of decrease with prior yarn-over's parent
188
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF, stack_position=0)
198
+ knit_graph.connect_loops(
199
+ parent_loop,
200
+ child_loop,
201
+ pull_direction=Pull_Direction.BtF,
202
+ stack_position=0,
203
+ )
189
204
  assert isinstance(yo_parent, Loop)
190
- knit_graph.connect_loops(yo_parent, child_loop, pull_direction=Pull_Direction.BtF, stack_position=1)
205
+ knit_graph.connect_loops(
206
+ yo_parent,
207
+ child_loop,
208
+ pull_direction=Pull_Direction.BtF,
209
+ stack_position=1,
210
+ )
191
211
  elif i % 6 == 4: # Fifth of every six stitches is bottom of decrease with next yarn-over's parent
192
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF, stack_position=0)
212
+ knit_graph.connect_loops(
213
+ parent_loop,
214
+ child_loop,
215
+ pull_direction=Pull_Direction.BtF,
216
+ stack_position=0,
217
+ )
193
218
  prior_child = child_loop
194
219
  elif i % 6 == 5: # The last of six stitches is the top of the prior decrease and new yarn-over
195
220
  assert isinstance(prior_child, Loop)
196
- knit_graph.connect_loops(parent_loop, prior_child, pull_direction=Pull_Direction.BtF, stack_position=1)
221
+ knit_graph.connect_loops(
222
+ parent_loop,
223
+ prior_child,
224
+ pull_direction=Pull_Direction.BtF,
225
+ stack_position=1,
226
+ )
197
227
  next_course.append(child_loop)
198
228
  last_course = next_course
199
229
  # Make a basic jersey course
@@ -226,7 +256,12 @@ def twist_cable(width: int, height: int) -> Knit_Graph:
226
256
  knit_graph, yarn = co_loops(width)
227
257
  last_course = knit_graph.get_courses()[0]
228
258
  next_course = []
229
- pull_directions = [Pull_Direction.FtB, Pull_Direction.BtF, Pull_Direction.BtF, Pull_Direction.FtB]
259
+ pull_directions = [
260
+ Pull_Direction.FtB,
261
+ Pull_Direction.BtF,
262
+ Pull_Direction.BtF,
263
+ Pull_Direction.FtB,
264
+ ]
230
265
  for i, parent_loop in enumerate(reversed(last_course)):
231
266
  child_loop = yarn.make_loop_on_end()
232
267
  knit_graph.connect_loops(parent_loop, child_loop, pull_direction=pull_directions[i % 4])
@@ -244,7 +279,7 @@ def twist_cable(width: int, height: int) -> Knit_Graph:
244
279
  child_loop = next_course[i - 1]
245
280
  knit_graph.connect_loops(parent_loop, child_loop, pull_direction=pull_directions[i % 4])
246
281
  if r % 2 == 1: # cable row
247
- for left_loop, right_loop in zip(next_course[1::4], next_course[2::4]):
282
+ for left_loop, right_loop in zip(next_course[1::4], next_course[2::4], strict=False):
248
283
  knit_graph.add_crossing(left_loop, right_loop, crossing)
249
284
  crossing = ~crossing
250
285
  last_course = next_course
knit_graphs/py.typed ADDED
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: knit-graphs
3
- Version: 0.0.9
3
+ Version: 0.0.11
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
@@ -20,8 +20,9 @@ Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Topic :: Scientific/Engineering
23
+ Classifier: Typing :: Typed
23
24
  Requires-Dist: networkx (>=3.5)
24
- Requires-Dist: plotly (>=6.3.0,<7.0.0)
25
+ Requires-Dist: plotly (>=6.3.0)
25
26
  Project-URL: Documentation, https://mhofmann-khoury.github.io/knit_graph/
26
27
  Project-URL: Repository, https://github.com/mhofmann-Khoury/knit_graph/
27
28
  Description-Content-Type: text/markdown
@@ -17,25 +17,26 @@ docs/source/api/knit_graphs.artin_wale_braids.Wale_Group.rst,sha256=bAGHs3s8dW6l
17
17
  docs/source/api/knit_graphs.artin_wale_braids.rst,sha256=ESKgE9gg830yJ6w5mKuv3MHgH3tMZ-Ee2y3b-bSnU4Y,547
18
18
  docs/source/api/knit_graphs.basic_knit_graph_generators.rst,sha256=OgV2jkEMY0LfRq8v0cNhJUk3fbUOS9VcP8zRv4VBvC4,213
19
19
  docs/source/api/knit_graphs.rst,sha256=w9HIhWI8P0_gG0H_efRUMHYBtWbgCprcS9moclgM_3I,499
20
- docs/source/conf.py,sha256=TZK8Rz0JNgQs2J_cUhVX5_4tmMBvRdGtILJsnAnAFWo,13350
21
- docs/source/index.rst,sha256=D4KmCX1_d2-WrPgxSeggHHsc9-TeQwSUGYOkehRQWrw,1462
20
+ docs/source/conf.py,sha256=qfKxtgms3gNDSoXGpOzWEDMzKJH20ovRMqTC57LkYF4,13282
21
+ docs/source/index.rst,sha256=E82Y_wpDU2hR8w6bSt2k6ZstslCe7u63Ld6ZFgT70RY,1284
22
22
  docs/source/installation.rst,sha256=GyNRk_oXKVgOZ4KFLAgkXLwjHYzDYsx8gcokLRrS0ZI,1247
23
- knit_graphs/Course.py,sha256=hO768vPM0KFRsjCYsBTUgasqMzlvl0gsuSrsBX0UUSs,6280
24
- knit_graphs/Knit_Graph.py,sha256=3NUeznjxAJPkvmwl_qxGpwxeN1LbsQBruAzLu551AZQ,11614
25
- knit_graphs/Knit_Graph_Visualizer.py,sha256=s2zp6Z7-ZF_52x3zTx8oQr__tQScpkGthIoWck57ykE,40227
26
- knit_graphs/Loop.py,sha256=WQh9ag1lv7_cJndZLGhhoGx1msdLEGixpahdXd2PhGk,7189
27
- knit_graphs/Pull_Direction.py,sha256=8xRLN-j7V1mExgmt3YjcJcTDa5rLedA2bF4wI8gd-DE,2334
28
- knit_graphs/Yarn.py,sha256=Stm-_1SRJTdN1hFPjtuBPyY0Ln55zC15FKGu5vyqerA,16000
23
+ knit_graphs/Course.py,sha256=dcJvWtTYc66iiVpxtYzULDD2ttWuvuGnlahWpWs1wAY,6308
24
+ knit_graphs/Knit_Graph.py,sha256=ys4-oeu23_8yZ8yjun-QngeiErUK0l3F8lnNlg7cB70,12442
25
+ knit_graphs/Knit_Graph_Visualizer.py,sha256=R-iSEEq5_hDi3NLDjP0hVrArrynKUASHs7vA7MAccJg,42108
26
+ knit_graphs/Loop.py,sha256=B1YU6bjTsGYZ8QrBZN52vg1btf7jvzeviCAH-Exrmw8,9747
27
+ knit_graphs/Pull_Direction.py,sha256=3QW_TKEF3fb-Y7I8xDOHHTe6vCYK-ADi3_SDtqr5uSU,2336
28
+ knit_graphs/Yarn.py,sha256=RaU0_At-V6Yk0TGgrRbsKeWJsI4WMoHkZHzUfs7txAE,18391
29
29
  knit_graphs/__init__.py,sha256=qzZAOxTqjgLkhoGnKNZwOkuYbKEyzfWKkKpKkV6lVfk,111
30
- knit_graphs/artin_wale_braids/Crossing_Direction.py,sha256=71ckmEFfL0s25NJb8lfYwH2zku0uM1C5X9gGM7PYC5A,2820
31
- knit_graphs/artin_wale_braids/Loop_Braid_Graph.py,sha256=2tsE16IzcvgsIKij1JNHWah6hLOgrpXCcjb6eocbfFs,4575
32
- knit_graphs/artin_wale_braids/Wale.py,sha256=pdzGYdpy88UWPa1ePvu9YUsDkRAB2wGrqCnMA_vIwAA,7135
33
- knit_graphs/artin_wale_braids/Wale_Braid.py,sha256=Ws0QmU7s63nrBKe69acDza-r-LozDyyaKVDNxQMy10Y,2662
34
- knit_graphs/artin_wale_braids/Wale_Braid_Word.py,sha256=4YBlVMUZkqp85r46ETQSbFVS_WrT4PsWthHmFv5Ny9M,4587
35
- knit_graphs/artin_wale_braids/Wale_Group.py,sha256=JJXR5TvLuoC2LXvNfmwbHhvBObn0MONUVE9ZsBcSmUc,6867
30
+ knit_graphs/artin_wale_braids/Crossing_Direction.py,sha256=qQCxgsI5L_PMY16bX7_W_C0YG2SKTVcq05RO5eNcZDA,2822
31
+ knit_graphs/artin_wale_braids/Loop_Braid_Graph.py,sha256=A4KoqjBBw8mqvUyzXwCZcwSDDIePtUqgnVVKAWXTa-w,4997
32
+ knit_graphs/artin_wale_braids/Wale.py,sha256=G7Uci5wkE5y7gHGK2bbTpRyswGRSWhVXFxyIUYOprlo,7259
33
+ knit_graphs/artin_wale_braids/Wale_Braid.py,sha256=p2SsFQW6nQZTO89TtCnFSWTWkYAFDm6kykvhb9G7chg,2663
34
+ knit_graphs/artin_wale_braids/Wale_Braid_Word.py,sha256=rY1cB_PpAfz4vFGPh2qUaLf0yNDczk2l6pgxtYA6Sqg,4593
35
+ knit_graphs/artin_wale_braids/Wale_Group.py,sha256=Px-3yxKAUCeqI52IE6t6L2aSCFwgTTsVbN0bWdhQ518,6696
36
36
  knit_graphs/artin_wale_braids/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- knit_graphs/basic_knit_graph_generators.py,sha256=doDH2MiXbMzGU9b3enEr3KdY8Fw90cssHi_lkts6VpY,11720
38
- knit_graphs-0.0.9.dist-info/LICENSE,sha256=Oazk3oiRu5ZN7b-EdYNYh0vu-I3Av2uIPQ-9L_cZ6Oo,1070
39
- knit_graphs-0.0.9.dist-info/METADATA,sha256=XzWrqXe9vJDLKHiA7a3wTNY4foVu6MemvqqWRyuDQ8s,5193
40
- knit_graphs-0.0.9.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
41
- knit_graphs-0.0.9.dist-info/RECORD,,
37
+ knit_graphs/basic_knit_graph_generators.py,sha256=x1zHC2PYbzkmjE40xe5_3k2vDsT749L9iufUIfC2QUw,12353
38
+ knit_graphs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
+ knit_graphs-0.0.11.dist-info/LICENSE,sha256=Oazk3oiRu5ZN7b-EdYNYh0vu-I3Av2uIPQ-9L_cZ6Oo,1070
40
+ knit_graphs-0.0.11.dist-info/METADATA,sha256=I8Kkhsuu02YB0UX9W7Gzwzrq5Y9PrXmWvqo1j8JwUJA,5215
41
+ knit_graphs-0.0.11.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
42
+ knit_graphs-0.0.11.dist-info/RECORD,,