"""Course representation of a section of knitting with no parent loops.
This module contains the Course class which represents a horizontal row of loops in a knitting pattern.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Iterator, cast
from knit_graphs.Loop import Loop
if TYPE_CHECKING:
from knit_graphs.Knit_Graph import Knit_Graph
[docs]
class Course:
"""Course object for organizing loops into knitting rows.
A Course represents a horizontal row of loops in a knitting pattern.
It maintains an ordered list of loops and provides methods for analyzing the structure and relationships between courses in the knitted fabric.
"""
[docs]
def __init__(self, knit_graph: Knit_Graph) -> None:
"""Initialize an empty course associated with a knit graph.
Args:
knit_graph (Knit_Graph): The knit graph that this course belongs to.
"""
self._knit_graph: Knit_Graph = knit_graph
self._loops_in_order: list[Loop] = []
self._loop_set: set[Loop] = set()
@property
def loops_in_order(self) -> list[Loop]:
"""
Returns:
list[Loop]: The list of loops in this course.
"""
return self._loops_in_order
@property
def knit_graph(self) -> Knit_Graph:
"""
Returns:
Knit_Graph: The knit graph that this course belongs to.
"""
return self._knit_graph
[docs]
def add_loop(self, loop: Loop, index: int | None = None) -> None:
"""Add a loop to the course at the specified index or at the end.
Args:
loop (Loop): The loop to add to this course.
index (int | None, optional): The index position to insert the loop at. If None, appends to the end.
"""
for parent_loop in loop.parent_loops:
assert parent_loop not in self, f"{loop} has parent {parent_loop}, cannot be added to same course"
self._loop_set.add(loop)
if index is None:
self.loops_in_order.append(loop)
else:
self.loops_in_order.insert(index, loop)
[docs]
def has_increase(self) -> bool:
"""Check if this course contains any yarn overs that start new wales.
Returns:
bool: True if the course has at least one yarn over (loop with no parent loops) to start new wales.
"""
return any(not loop.has_parent_loops() for loop in self)
[docs]
def has_decrease(self) -> bool:
"""Check if this course contains any decrease stitches that merge wales.
Returns:
bool: True if the course has at least one decrease stitch (loop with multiple parent loops) merging two or more wales.
"""
return any(len(loop.parent_loops) > 1 for loop in self)
[docs]
def __getitem__(self, index: int | slice) -> Loop | list[Loop]:
"""Get loop(s) at the specified index or slice.
Args:
index (int | slice): The index or slice to retrieve from the course.
Returns:
Loop | list[Loop]: The loop at the specified index, or list of loops for a slice.
"""
return self.loops_in_order[index]
[docs]
def in_round_with(self, next_course: Course) -> bool:
"""Check if the next course connects to this course in a circular pattern.
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.
Args:
next_course (Course): The course that should follow this course in circular knitting.
Returns:
bool: True if the next course starts at the beginning of this course, indicating circular knitting.
"""
next_start: Loop = cast(Loop, next_course[0])
i = 1
while not next_start.has_parent_loops():
next_start = cast(Loop, next_course[i])
i += 1
return self[0] in next_start.parent_loops
[docs]
def in_row_with(self, next_course: Course) -> bool:
"""Check if the next course connects to this course in a flat/row pattern.
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.
Args:
next_course (Course): The course that should follow this course in flat knitting.
Returns:
bool: True if the next course starts at the end of this course, indicating flat/row knitting.
"""
next_start: Loop = cast(Loop, next_course[0])
i = 1
while not next_start.has_parent_loops():
next_start = cast(Loop, next_course[i])
i += 1
return self[-1] in next_start.parent_loops
[docs]
def __contains__(self, loop: Loop) -> bool:
"""Check if a loop is contained in this course.
Args:
loop (Loop): The loop to check for membership in this course.
Returns:
bool: True if the loop is in this course, False otherwise.
"""
return loop in self._loop_set
[docs]
def __iter__(self) -> Iterator[Loop]:
"""Iterate over loops in this course in order.
Returns:
Iterator[Loop]: An iterator over the loops in this course in their natural order.
"""
return iter(self.loops_in_order)
[docs]
def __reversed__(self) -> Iterator[Loop]:
"""Iterate over loops in this course in reverse order.
Returns:
Iterator[Loop]: An iterator over the loops in this course in reverse order.
"""
return reversed(self.loops_in_order)
[docs]
def __len__(self) -> int:
"""Get the number of loops in this course.
Returns:
int: The total number of loops in this course.
"""
return len(self.loops_in_order)
[docs]
def __str__(self) -> str:
"""Get string representation of this course.
Returns:
str: String representation showing the ordered list of loops.
"""
return str(self.loops_in_order)
[docs]
def __repr__(self) -> str:
"""Get string representation of this course for debugging.
Returns:
str: String representation showing the ordered list of loops.
"""
return str(self)