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.
@@ -2,20 +2,25 @@
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
- from typing import TYPE_CHECKING, cast
8
+ from typing import TYPE_CHECKING, TypeVar
8
9
 
9
10
  from networkx import DiGraph, dfs_preorder_nodes
10
11
 
11
12
  from knit_graphs.artin_wale_braids.Wale import Wale
13
+ from knit_graphs.directed_loop_graph import Directed_Loop_Graph
12
14
  from knit_graphs.Loop import Loop
15
+ from knit_graphs.Pull_Direction import Pull_Direction
13
16
 
14
17
  if TYPE_CHECKING:
15
18
  from knit_graphs.Knit_Graph import Knit_Graph
16
19
 
20
+ LoopT = TypeVar("LoopT", bound=Loop)
21
+
17
22
 
18
- class Wale_Group:
23
+ class Wale_Group(Directed_Loop_Graph[LoopT, Pull_Direction]):
19
24
  """A graph structure maintaining relationships between connected wales through decrease operations.
20
25
 
21
26
  This class represents a collection of wales that are connected through decrease stitches, where multiple wales merge into fewer wales as the knitting progresses upward.
@@ -23,48 +28,71 @@ class Wale_Group:
23
28
 
24
29
  Attributes:
25
30
  wale_graph (DiGraph): A directed graph representing the relationships between wales in this group.
26
- stitch_graph (DiGraph): A directed graph of all individual stitch connections within this wale group.
27
31
  top_loops (dict[Loop, Wale]): Mapping from the last (top) loop of each wale to the wale itself.
28
32
  bottom_loops (dict[Loop, Wale]): Mapping from the first (bottom) loop of each wale to the wale itself.
29
33
  """
30
34
 
31
- def __init__(self, terminal_loop: Loop, knit_graph: Knit_Graph):
35
+ def __init__(self, terminal_loop: LoopT, knit_graph: Knit_Graph[LoopT]):
32
36
  """Initialize a wale group starting from a terminal wale and building downward.
33
37
 
34
38
  Args:
35
39
  terminal_loop (Loop): The terminal loop of this wale-group. All the wales in the group connect up to this loop.
36
40
  knit_graph (Knit_Graph): The parent knit graph that contains this wale group.
37
41
  """
42
+ super().__init__()
38
43
  self.wale_graph: DiGraph = DiGraph()
39
- self.stitch_graph: DiGraph = DiGraph()
40
- self._knit_graph: Knit_Graph = knit_graph
41
- self._terminal_loop: Loop = terminal_loop
42
- self.top_loops: dict[Loop, Wale] = {}
43
- self.bottom_loops: dict[Loop, Wale] = {}
44
+ self._knit_graph: Knit_Graph[LoopT] = knit_graph
45
+ self._terminal_loop: LoopT = terminal_loop
46
+ self.top_loops: dict[LoopT, Wale] = {}
47
+ self.bottom_loops: dict[LoopT, Wale] = {}
44
48
  self._build_wale_group()
45
49
 
46
50
  @property
47
- def terminal_loop(self) -> Loop:
51
+ def terminal_loop(self) -> LoopT:
48
52
  """
49
53
  Returns:
50
- Loop: The loop that terminates all wales in this group.
54
+ LoopT: The loop that terminates all wales in this group.
51
55
  """
52
56
  return self._terminal_loop
53
57
 
58
+ def get_loops_over_courses(self) -> list[list[LoopT]]:
59
+ """Get loops organized by their course (horizontal row) within this wale group.
60
+
61
+ This method traces through the stitch connections starting from the terminal wale's top loop and groups loops by their vertical position (course) in the knitted structure.
62
+
63
+ Returns:
64
+ list[list[LoopT]]: 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.
65
+ """
66
+ courses: list[list[LoopT]] = []
67
+ cur_course: list[LoopT] = [self._terminal_loop]
68
+ while len(cur_course) > 0:
69
+ courses.append(cur_course)
70
+ next_course: list[LoopT] = []
71
+ for loop in cur_course:
72
+ next_course.extend(self.predecessors(loop))
73
+ cur_course = next_course
74
+ return courses
75
+
54
76
  def _build_wale_group(self) -> None:
55
77
  full_wales = self._knit_graph.get_wales_ending_with_loop(self._terminal_loop)
56
78
  # Build up the stitch graph.
57
79
  for wale in full_wales:
58
- u_loops: list[Loop] = cast(list[Loop], wale[:-1])
59
- v_loops: list[Loop] = cast(list[Loop], wale[1:])
60
- for u, v in zip(u_loops, v_loops):
61
- self.stitch_graph.add_edge(u, v, pull_direction=self._knit_graph.get_pull_direction(u, v))
80
+ u_loops = wale[:-1]
81
+ v_loops = wale[1:]
82
+ for u, v in zip(u_loops, v_loops, strict=False):
83
+ if u not in self:
84
+ self.add_loop(u)
85
+ if v not in self:
86
+ self.add_loop(v)
87
+ self.add_edge(u, v, self._knit_graph.get_pull_direction(u, v))
62
88
  wales_to_split = full_wales
63
89
  while len(wales_to_split) > 0:
64
90
  wale_to_split = wales_to_split.pop()
65
91
  split = False
66
- upper_loops = cast(list[Loop], wale_to_split[1:])
67
- for loop in upper_loops: # skip first loop in each wale as it may be already connected to a discovered decrease.
92
+ upper_loops = wale_to_split[1:]
93
+ for (
94
+ loop
95
+ ) in upper_loops: # skip first loop in each wale as it may be already connected to a discovered decrease.
68
96
  if len(loop.parent_loops) > 1: # Focal of a decrease.
69
97
  clean_wale, remaining_wale = wale_to_split.split_wale(loop)
70
98
  if not self.wale_graph.has_node(clean_wale):
@@ -75,7 +103,7 @@ class Wale_Group:
75
103
  break
76
104
  if not split:
77
105
  self._add_wale(wale_to_split)
78
- for bot_loop, lower_wale in self.bottom_loops.items():
106
+ for _bot_loop, lower_wale in self.bottom_loops.items():
79
107
  if lower_wale.last_loop in self.bottom_loops:
80
108
  self.wale_graph.add_edge(lower_wale, self.bottom_loops[lower_wale.last_loop])
81
109
 
@@ -84,24 +112,6 @@ class Wale_Group:
84
112
  self.top_loops[wale.last_loop] = wale
85
113
  self.bottom_loops[wale.first_loop] = wale
86
114
 
87
- def get_loops_over_courses(self) -> list[list[Loop]]:
88
- """Get loops organized by their course (horizontal row) within this wale group.
89
-
90
- This method traces through the stitch connections starting from the terminal wale's top loop and groups loops by their vertical position (course) in the knitted structure.
91
-
92
- Returns:
93
- 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.
94
- """
95
- courses: list[list[Loop]] = []
96
- cur_course: list[Loop] = [self._terminal_loop]
97
- while len(cur_course) > 0:
98
- courses.append(cur_course)
99
- next_course = []
100
- for loop in cur_course:
101
- next_course.extend(self.stitch_graph.predecessors(loop))
102
- cur_course = next_course
103
- return courses
104
-
105
115
  def __len__(self) -> int:
106
116
  """Get the height of the wale group measured as the maximum number of loops from base to terminal.
107
117
 
@@ -111,7 +121,7 @@ class Wale_Group:
111
121
  int: The height of the wale group from the base loops to the tallest terminal, measured in total number of loops.
112
122
  """
113
123
  max_len = 0
114
- for bot_loop, wale in self.bottom_loops.items():
124
+ for _bot_loop, wale in self.bottom_loops.items():
115
125
  path_len = sum(len(successor) for successor in dfs_preorder_nodes(self.wale_graph, wale))
116
126
  max_len = max(max_len, path_len)
117
127
  return max_len
@@ -123,18 +133,18 @@ class Wale_Group:
123
133
  """
124
134
  return hash(self._terminal_loop)
125
135
 
126
- def __contains__(self, item: Loop | int | Wale) -> bool:
136
+ def __contains__(self, item: LoopT | int | tuple[LoopT | int, LoopT | int] | Wale) -> bool:
127
137
  """
128
138
  Args:
129
- item (Loop | int | Wale): The item to check for in the wale group.
139
+ item (LoopT | int | tuple[LoopT | int, LoopT | int] | Wale): The item to check for in the wale group.
130
140
 
131
141
  Returns:
132
- bool: True if the given loop, loop_id (int), or wale is in this group.
142
+ bool: True if the given loop, loop_id (int), pair of loops, or wale is in this group.
133
143
  """
134
- if isinstance(item, Loop) or isinstance(item, int):
135
- return item in self.stitch_graph.nodes
136
- else: # isinstance(item, Wale):
144
+ if isinstance(item, Wale):
137
145
  return item in self.wale_graph
146
+ else:
147
+ return super().__contains__(item)
138
148
 
139
149
  def __str__(self) -> str:
140
150
  """
@@ -3,30 +3,35 @@
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
- from knit_graphs.artin_wale_braids.Crossing_Direction import Crossing_Direction
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Sequence
10
+
7
11
  from knit_graphs.Knit_Graph import Knit_Graph
12
+ from knit_graphs.knit_graph_builder import Knit_Graph_Builder
8
13
  from knit_graphs.Loop import Loop
9
14
  from knit_graphs.Pull_Direction import Pull_Direction
10
15
  from knit_graphs.Yarn import Yarn
11
16
 
12
17
 
13
- def co_loops(width: int) -> tuple[Knit_Graph, Yarn]:
18
+ def co_loops(width: int) -> tuple[Knit_Graph_Builder[Loop], Knit_Graph[Loop], Yarn[Loop]]:
14
19
  """Create a cast-on row of loops forming the foundation for knitting patterns.
15
20
 
16
21
  Args:
17
22
  width (int): The number of loops to create in the cast-on row.
18
23
 
19
24
  Returns:
20
- tuple[Knit_Graph, Yarn]: A tuple containing the knit graph with one course of the specified width and the yarn used to create it.
25
+ tuple[Knit_Graph_Builder[Loop], Knit_Graph[Loop], Yarn[Loop]: A tuple containing the knit graph builder and its knitgraph with one course of the specified width and the yarn used to create it.
21
26
  """
22
- knit_graph = Knit_Graph()
23
- yarn = Yarn(knit_graph=knit_graph)
27
+ builder = Knit_Graph_Builder[Loop]()
28
+ yarn = builder.add_yarn()
24
29
  for _ in range(0, width):
25
- _loop = yarn.make_loop_on_end()
26
- return knit_graph, yarn
30
+ _loop = builder.tuck(yarn)
31
+ return builder, builder.knit_graph, yarn
27
32
 
28
33
 
29
- def jersey_swatch(width: int, height: int) -> Knit_Graph:
34
+ def jersey_swatch(width: int, height: int) -> Knit_Graph[Loop]:
30
35
  """Generate a rectangular knit swatch with all knit stitches in a flat sheet structure.
31
36
 
32
37
  This creates a basic stockinette/jersey pattern where all stitches are worked as knit stitches from back to front.
@@ -38,19 +43,14 @@ def jersey_swatch(width: int, height: int) -> Knit_Graph:
38
43
  Returns:
39
44
  Knit_Graph: A knit graph representing a flat rectangular swatch with all knit stitches.
40
45
  """
41
- knit_graph, yarn = co_loops(width)
42
- last_course = list(knit_graph.get_courses()[0])
46
+ builder, knit_graph, yarn = co_loops(width)
47
+ last_course: Sequence[Loop] = knit_graph.get_courses()[0]
43
48
  for _ in range(0, height):
44
- next_course = []
45
- for parent_loop in reversed(last_course):
46
- child_loop = yarn.make_loop_on_end()
47
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF)
48
- next_course.append(child_loop)
49
- last_course = next_course
49
+ last_course = [builder.knit(yarn, [parent_loop]) for parent_loop in reversed(last_course)]
50
50
  return knit_graph
51
51
 
52
52
 
53
- def jersey_tube(tube_width: int, height: int) -> Knit_Graph:
53
+ def jersey_tube(tube_width: int, height: int) -> Knit_Graph[Loop]:
54
54
  """Generate a tubular knit structure with all knit stitches worked in the round.
55
55
 
56
56
  This creates a seamless tube by knitting in the round, where the front and back sections are connected by floats to maintain the circular structure.
@@ -62,29 +62,22 @@ def jersey_tube(tube_width: int, height: int) -> Knit_Graph:
62
62
  Returns:
63
63
  Knit_Graph: A knit graph representing a seamless tube with all knit stitches.
64
64
  """
65
- knit_graph, yarn = co_loops(tube_width * 2)
66
- last_course = [*knit_graph.get_courses()[0]]
67
-
68
- def _set_tube_floats() -> None:
69
- """Internal helper function to set up float connections between front and back of tube."""
70
- front_loops = last_course[0:tube_width]
71
- 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
- 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)):
75
- yarn.add_loop_in_front_of_float(front, first_back, second_back)
76
-
77
- _set_tube_floats()
65
+ builder = Knit_Graph_Builder[Loop]()
66
+ yarn = builder.add_yarn()
67
+ front_of_tube = [builder.tuck(yarn) for _ in range(0, tube_width)]
68
+ back_of_tube = [builder.tuck(yarn) for _ in reversed(front_of_tube)]
78
69
  for _ in range(0, height):
79
- next_course = [yarn.make_loop_on_end() for _p in last_course]
80
- for parent_loop, child_loop in zip(last_course, next_course):
81
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF)
82
- last_course = next_course
83
- _set_tube_floats()
84
- return knit_graph
70
+ for fl, bl in zip(front_of_tube[:-1], reversed(back_of_tube[1:]), strict=False):
71
+ builder.position_float(fl, loops_behind_float=[bl])
72
+ for fl, bl in zip(reversed(front_of_tube[1:]), back_of_tube[:-1], strict=False):
73
+ builder.position_float(bl, loops_in_front_of_float=[fl])
74
+ front_of_tube = [builder.knit(yarn, [fl], Pull_Direction.BtF) for fl in front_of_tube]
75
+ back_of_tube = [builder.knit(yarn, [bl], Pull_Direction.FtB) for bl in back_of_tube]
76
+
77
+ return builder.knit_graph
85
78
 
86
79
 
87
- def kp_rib_swatch(width: int, height: int) -> Knit_Graph:
80
+ def kp_rib_swatch(width: int, height: int) -> Knit_Graph[Loop]:
88
81
  """Generate a knit-purl ribbing swatch with alternating wales of knit and purl stitches.
89
82
 
90
83
  This creates a 1x1 ribbing pattern where knit and purl wales alternate, maintaining their stitch type throughout the height of the swatch for a stretchy, textured fabric.
@@ -96,30 +89,19 @@ def kp_rib_swatch(width: int, height: int) -> Knit_Graph:
96
89
  Returns:
97
90
  Knit_Graph: A knit graph representing a ribbed swatch with alternating knit and purl wales.
98
91
  """
99
- knit_graph, yarn = co_loops(width)
100
- last_course = knit_graph.get_courses()[0]
101
- next_course = []
102
- next_pull = Pull_Direction.BtF
103
- for parent_loop in reversed(last_course):
104
- child_loop = yarn.make_loop_on_end()
105
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=next_pull)
106
- next_pull = next_pull.opposite()
107
- next_course.append(child_loop)
108
- last_course = next_course
109
- for _ in range(1, height):
110
- next_course = []
111
- for parent_loop in reversed(last_course):
112
- grand_parent = parent_loop.parent_loops[0]
113
- parent_pull = knit_graph.get_pull_direction(grand_parent, parent_loop)
114
- assert isinstance(parent_pull, Pull_Direction)
115
- child_loop = yarn.make_loop_on_end()
116
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=parent_pull)
117
- next_course.append(child_loop)
118
- last_course = next_course
92
+ builder, knit_graph, yarn = co_loops(width)
93
+ last_course: Sequence[Loop] = knit_graph.get_courses()[0]
94
+ for _ in range(0, height):
95
+ assert yarn.last_loop is not None
96
+ pull_direction = yarn.last_loop.pull_directions[0] if yarn.last_loop.has_parent_loops else Pull_Direction.BtF
97
+ last_course = [
98
+ builder.knit(yarn, [parent_loop], pull_direction=pull_direction if i % 2 == 0 else pull_direction.opposite)
99
+ for i, parent_loop in enumerate(reversed(last_course))
100
+ ]
119
101
  return knit_graph
120
102
 
121
103
 
122
- def seed_swatch(width: int, height: int) -> Knit_Graph:
104
+ def seed_swatch(width: int, height: int) -> Knit_Graph[Loop]:
123
105
  """Generate a seed stitch swatch with a checkerboard pattern of knit and purl stitches.
124
106
 
125
107
  This creates a textured fabric where each stitch alternates between knit and purl both horizontally and vertically, creating a bumpy, non-curling fabric texture.
@@ -131,30 +113,19 @@ def seed_swatch(width: int, height: int) -> Knit_Graph:
131
113
  Returns:
132
114
  Knit_Graph: A knit graph representing a seed stitch swatch with checkerboard knit-purl pattern.
133
115
  """
134
- knit_graph, yarn = co_loops(width)
135
- last_course = knit_graph.get_courses()[0]
136
- next_course = []
137
- next_pull = Pull_Direction.BtF
138
- for parent_loop in reversed(last_course):
139
- child_loop = yarn.make_loop_on_end()
140
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=next_pull)
141
- next_pull = next_pull.opposite()
142
- next_course.append(child_loop)
143
- last_course = next_course
144
- for _ in range(1, height):
145
- next_course = []
146
- for parent_loop in reversed(last_course):
147
- grand_parent = parent_loop.parent_loops[0]
148
- parent_pull = knit_graph.get_pull_direction(grand_parent, parent_loop)
149
- assert isinstance(parent_pull, Pull_Direction)
150
- child_loop = yarn.make_loop_on_end()
151
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=parent_pull.opposite())
152
- next_course.append(child_loop)
153
- last_course = next_course
116
+ builder, knit_graph, yarn = co_loops(width)
117
+ last_course: Sequence[Loop] = knit_graph.get_courses()[0]
118
+ for _ in range(0, height):
119
+ assert yarn.last_loop is not None
120
+ pull_direction = yarn.last_loop.pull_directions[0] if yarn.last_loop.has_parent_loops else Pull_Direction.BtF
121
+ last_course = [
122
+ builder.knit(yarn, [parent_loop], pull_direction=pull_direction.opposite if i % 2 == 0 else pull_direction)
123
+ for i, parent_loop in enumerate(reversed(last_course))
124
+ ]
154
125
  return knit_graph
155
126
 
156
127
 
157
- def lace_mesh(width: int, height: int) -> Knit_Graph:
128
+ def lace_mesh(width: int, height: int) -> Knit_Graph[Loop]:
158
129
  """Generate a mesh pattern with alternating left and right leaning decrease paired to yarn-overs.
159
130
  These pairings create a basic lace pattern with eyelets formed around the increases.
160
131
 
@@ -165,48 +136,31 @@ def lace_mesh(width: int, height: int) -> Knit_Graph:
165
136
  Returns:
166
137
  Knit_Graph: A knit graph representing a mesh swatch.
167
138
  """
168
- knit_graph, yarn = co_loops(width)
169
- last_course = knit_graph.get_courses()[0]
170
- next_course = []
171
- for parent_loop in reversed(last_course):
172
- child_loop = yarn.make_loop_on_end()
173
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF)
174
- next_course.append(child_loop)
175
- last_course = next_course
139
+ builder, knit_graph, yarn = co_loops(width)
140
+ last_course = [builder.knit(yarn, [p]) for p in reversed(knit_graph.get_courses()[0])]
176
141
  for _ in range(1, height):
177
- # Make the lace course
142
+ dec1 = last_course[1::3]
143
+ dec2 = last_course[2::3]
144
+ decs = [
145
+ dec_parents if i % 2 == 0 else (dec_parents[1], dec_parents[0])
146
+ for i, dec_parents in enumerate(zip(reversed(dec1), reversed(dec2), strict=False))
147
+ ]
148
+ knits = last_course[0::3]
178
149
  next_course = []
179
- yo_parent = None
180
- prior_child = None
181
- for i, parent_loop in enumerate(reversed(last_course)):
182
- child_loop = yarn.make_loop_on_end()
183
- 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)
185
- elif i % 6 == 1: # Second of every 6 stitches (0 indexed) is yarn over before a decrease.
186
- yo_parent = parent_loop
187
- 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)
189
- assert isinstance(yo_parent, Loop)
190
- knit_graph.connect_loops(yo_parent, child_loop, pull_direction=Pull_Direction.BtF, stack_position=1)
191
- 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)
193
- prior_child = child_loop
194
- elif i % 6 == 5: # The last of six stitches is the top of the prior decrease and new yarn-over
195
- assert isinstance(prior_child, Loop)
196
- knit_graph.connect_loops(parent_loop, prior_child, pull_direction=Pull_Direction.BtF, stack_position=1)
197
- next_course.append(child_loop)
198
- last_course = next_course
199
- # Make a basic jersey course
200
- next_course = []
201
- for parent_loop in reversed(last_course):
202
- child_loop = yarn.make_loop_on_end()
203
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF)
204
- next_course.append(child_loop)
205
- last_course = next_course
150
+ for i, parent_loop in enumerate(reversed(knits)):
151
+ next_course.append(builder.knit(yarn, [parent_loop]))
152
+ if len(decs) > 0:
153
+ if i % 2 == 0:
154
+ next_course.append(builder.tuck(yarn))
155
+ next_course.append(builder.knit(yarn, decs.pop(0)))
156
+ else:
157
+ next_course.append(builder.knit(yarn, decs.pop(0)))
158
+ next_course.append(builder.tuck(yarn))
159
+ last_course = [builder.knit(yarn, [p]) for p in reversed(next_course)]
206
160
  return knit_graph
207
161
 
208
162
 
209
- def twist_cable(width: int, height: int) -> Knit_Graph:
163
+ def twist_cable(width: int, height: int) -> Knit_Graph[Loop]:
210
164
  """Generate a twisted cable pattern with alternating crossing directions and purl separators.
211
165
 
212
166
  This creates a cable pattern with 1x1 twists that alternate direction every two rows, separated by purl wales to make the cable structure more prominent.
@@ -223,29 +177,31 @@ def twist_cable(width: int, height: int) -> Knit_Graph:
223
177
  # p k/k p ->: 1-2
224
178
  # p k k p <-: 0-1
225
179
  # 0 1 2 3
226
- knit_graph, yarn = co_loops(width)
227
- last_course = knit_graph.get_courses()[0]
228
- next_course = []
229
- pull_directions = [Pull_Direction.FtB, Pull_Direction.BtF, Pull_Direction.BtF, Pull_Direction.FtB]
230
- for i, parent_loop in enumerate(reversed(last_course)):
231
- child_loop = yarn.make_loop_on_end()
232
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=pull_directions[i % 4])
233
- next_course.append(child_loop)
234
- last_course = next_course
235
- crossing = Crossing_Direction.Over_Right
236
- for r in range(1, height):
237
- next_course = [yarn.make_loop_on_end() for _ in last_course]
238
- for i, parent_loop in enumerate(reversed(last_course)):
239
- if r % 2 == 0 or i % 4 == 0 or i % 4 == 3: # not cable row (even) or in purl wale
240
- child_loop = next_course[i]
241
- elif i % 4 == 1:
242
- child_loop = next_course[i + 1]
180
+ builder, knit_graph, yarn = co_loops(width)
181
+ pull_directions = [
182
+ Pull_Direction.FtB,
183
+ Pull_Direction.BtF,
184
+ Pull_Direction.BtF,
185
+ Pull_Direction.FtB,
186
+ ]
187
+ last_course = [
188
+ builder.knit(yarn, [p], pull_directions[i % 4]) for i, p in enumerate(reversed(knit_graph.get_courses()[0]))
189
+ ]
190
+ for r in range(1, height, 2):
191
+ crossed_course: list[Loop] = []
192
+ for i, parent_loop in enumerate(last_course):
193
+ if i % 4 == 2:
194
+ crossed_course.insert(-1, parent_loop)
195
+ else:
196
+ crossed_course.append(parent_loop)
197
+ last_course = [builder.knit(yarn, [p]) for p in reversed(crossed_course)]
198
+ left_cable_loops = last_course[-2:0:-4]
199
+ right_cable_loops = last_course[-3:0:-4]
200
+ for left_loop, right_loop in zip(left_cable_loops, right_cable_loops, strict=False):
201
+ if r % 4 == 1:
202
+ builder.xfer(left_loop, over_loops_to_right=[right_loop])
243
203
  else:
244
- child_loop = next_course[i - 1]
245
- knit_graph.connect_loops(parent_loop, child_loop, pull_direction=pull_directions[i % 4])
246
- if r % 2 == 1: # cable row
247
- for left_loop, right_loop in zip(next_course[1::4], next_course[2::4]):
248
- knit_graph.add_crossing(left_loop, right_loop, crossing)
249
- crossing = ~crossing
250
- last_course = next_course
204
+ builder.xfer(left_loop, under_loops_to_right=[right_loop])
205
+ last_course = [builder.knit(yarn, [p]) for p in reversed(last_course)]
206
+
251
207
  return knit_graph