"""Module containing the Knit_Graph_Builder class."""
from collections.abc import Sequence
from typing import Any, Generic, TypeVar, cast
from knit_graphs.artin_wale_braids.Crossing_Direction import Crossing_Direction
from knit_graphs.Knit_Graph import Knit_Graph
from knit_graphs.Loop import Loop
from knit_graphs.Pull_Direction import Pull_Direction
from knit_graphs.Yarn import Yarn, Yarn_Properties
LoopT = TypeVar("LoopT", bound=Loop)
[docs]
class Knit_Graph_Builder(Generic[LoopT]):
"""A general framework for building Knit_Graphs from standard knitting operations."""
[docs]
def __init__(
self,
loop_class: type[LoopT] | None = None,
yarn_class: type[Yarn[LoopT]] | None = None,
):
if loop_class is not None:
self._loop_class: type[LoopT] = loop_class
else:
self._loop_class: type[LoopT] = cast(type[LoopT], Loop)
if yarn_class is not None:
self._yarn_class: type[Yarn[LoopT]] = yarn_class
else:
self._yarn_class: type[Yarn[LoopT]] = cast(type[Yarn[LoopT]], Yarn)
self.knit_graph: Knit_Graph[LoopT] = Knit_Graph[LoopT]()
self._first_yarn: Yarn[LoopT] | None = None
[docs]
def add_yarn(self, yarn_properties: Yarn_Properties | None = None, **yarn_kwargs: Any) -> Yarn[LoopT]:
"""
Create and add a new yarn to the Knit_Graph with the given properties.
Args:
yarn_properties (Yarn_Properties, optional): The properties of the yarn. Defaults to standard yarn-properties.
**yarn_kwargs (Any, optional): Additional keyword arguments for forming the yarn. Defaults to no keyword arguments.
Returns:
Yarn[LoopT]: The new yarn in the Knit_Graph.
"""
yarn = self._make_yarn(yarn_properties, **yarn_kwargs)
self.knit_graph.add_yarn(yarn)
return yarn
[docs]
def cut_yarn(self, yarn: Yarn[LoopT]) -> Yarn[LoopT]:
"""Cut yarn to make it no longer active and create a new yarn instance of the same type.
Returns:
Yarn[LoopT]: New yarn of the same type after cutting this yarn.
"""
cut_yarn = yarn.cut_yarn()
self.knit_graph.add_yarn(cut_yarn)
return cut_yarn
[docs]
@staticmethod
def position_float(
first_loop: LoopT,
loops_behind_float: Sequence[LoopT] | None = None,
loops_in_front_of_float: Sequence[LoopT] | None = None,
) -> None:
"""
Position the float exiting the given loop relative to the given sets of loops.
Args:
first_loop (LoopT): First loop in the float to position.
loops_behind_float (Sequence[LoopT]): The loops in front of the float to position.
loops_in_front_of_float (Sequence[LoopT]): The loops behind the float to position.
"""
if loops_behind_float is not None:
for loop in loops_behind_float:
first_loop.add_loop_behind_started_float(loop)
if loops_in_front_of_float is not None:
for loop in loops_in_front_of_float:
first_loop.add_loop_in_front_of_started_float(loop)
[docs]
def tuck(self, yarn: Yarn[LoopT], **loop_kwargs: Any) -> LoopT:
"""
Forms a new loop at the end of the yarn with no parent loops.
Args:
yarn (Yarn[LoopT]): The yarn to form the loop from in the knitgraph.
**loop_kwargs (Any, optional): Additional keywords to support base-classes of Loop. Defaults to None.
Returns:
LoopT: The new loop formed by the tuck.
"""
return self._make_loop(yarn, **loop_kwargs)
[docs]
def knit(
self,
yarn: Yarn[LoopT],
parent_loops: Sequence[LoopT],
pull_direction: Pull_Direction | None = None,
**loop_kwargs: Any,
) -> LoopT:
"""
Args:
yarn (Yarn[LoopT]): The yarn to form the loop from in the knitgraph.
parent_loops (Sequence[LoopT]): The parent loops of stitch in their stacking order.
pull_direction (Pull_Direction, optional): Pull direction of the stitch. Defaults to the dominant pull direction of a loop or Back-to-front (i.e., knit-stitch) if the loop has no parents.
**loop_kwargs (Any, optional): Additional keywords to support base-classes of Loop. Defaults to None.
Returns:
LoopT: The new loop formed by the knit.
"""
if pull_direction is None:
pull_direction = Loop.majority_pull_direction(parent_loops)
loop = self._make_loop(yarn, **loop_kwargs)
for parent in parent_loops:
self.knit_graph.connect_loops(parent, loop, pull_direction)
return loop
[docs]
def xfer(
self,
loop: LoopT,
over_loops_to_right: Sequence[LoopT] | None = None,
over_loops_to_left: Sequence[LoopT] | None = None,
under_loops_to_right: Sequence[LoopT] | None = None,
under_loops_to_left: Sequence[LoopT] | None = None,
) -> None:
"""
Cross the given loop over neighboring loops in the braid graph.
Args:
loop (LoopT): The loop to cross over neighbors
over_loops_to_right (Sequence[LoopT], optional): The loops to cross over to the right. Defaults to no loops.
over_loops_to_left (Sequence[LoopT], optional): The loops to cross over to the left. Defaults to no loops.
under_loops_to_right (Sequence[LoopT], optional): The loops to cross under to the right. Defaults to no loops.
under_loops_to_left (Sequence[LoopT], optional): The loops to cross under to the left. Defaults to no loops.
"""
if over_loops_to_right is not None:
for right_loop in over_loops_to_right:
self.knit_graph.braid_graph.add_crossing(loop, right_loop, Crossing_Direction.Over_Right)
if under_loops_to_right is not None:
for right_loop in under_loops_to_right:
self.knit_graph.braid_graph.add_crossing(loop, right_loop, Crossing_Direction.Under_Right)
if over_loops_to_left is not None:
for left_loop in over_loops_to_left:
self.knit_graph.braid_graph.add_crossing(left_loop, loop, Crossing_Direction.Under_Right)
if under_loops_to_left is not None:
for left_loop in under_loops_to_left:
self.knit_graph.braid_graph.add_crossing(left_loop, loop, Crossing_Direction.Over_Right)
[docs]
def split(
self,
yarn: Yarn[LoopT],
parent_loops: Sequence[LoopT],
pull_direction: Pull_Direction | None = None,
over_loops_to_right: Sequence[LoopT] | None = None,
over_loops_to_left: Sequence[LoopT] | None = None,
under_loops_to_right: Sequence[LoopT] | None = None,
under_loops_to_left: Sequence[LoopT] | None = None,
**loop_kwargs: Any,
) -> LoopT:
"""
Args:
yarn (Yarn[LoopT]): The yarn to form the loop from in the knitgraph.
parent_loops (Sequence[LoopT]): The parent loops of stitch in their stacking order.
pull_direction (Pull_Direction, optional): Pull direction of the stitch. Defaults to the dominant pull direction of a loop or Back-to-front (i.e., knit-stitch) if the loop has no parents.
over_loops_to_right (Sequence[LoopT], optional): The loops to cross over to the right. Defaults to no loops.
over_loops_to_left (Sequence[LoopT], optional): The loops to cross over to the left. Defaults to no loops.
under_loops_to_right (Sequence[LoopT], optional): The loops to cross under to the right. Defaults to no loops.
under_loops_to_left (Sequence[LoopT], optional): The loops to cross under to the left. Defaults to no loops.
**loop_kwargs (Any, optional): Additional keyword arguments for forming the loop. Defaults to no keyword arguments.
Returns:
LoopT: The loop formed in the split.
"""
if pull_direction is None:
pull_direction = Loop.majority_pull_direction(parent_loops)
for loop in parent_loops:
self.xfer(loop, over_loops_to_right, over_loops_to_left, under_loops_to_right, under_loops_to_left)
return self.knit(yarn, parent_loops, pull_direction, **loop_kwargs)
def _make_yarn(self, yarn_properties: Yarn_Properties | None = None, **yarn_kwargs: Any) -> Yarn[LoopT]:
"""
Args:
yarn_properties (Yarn_Properties, optional): The properties of the yarn. Defaults to standard yarn-properties.
**yarn_kwargs (Any, optional): Additional keyword arguments for forming the yarn. Defaults to no keyword arguments.
Returns:
Yarn[LoopT]: The new yarn in the Knit_Graph.
"""
return self._yarn_class(self.knit_graph, yarn_properties, **yarn_kwargs)
def _make_loop(self, yarn: Yarn[LoopT], **loop_kwargs: Any) -> LoopT:
"""
Create a loop at the end of the given yarn and add it to the Knit_Graph.
Args:
yarn (Yarn[LoopT]): The yarn to form the loop from in the knitgraph.
**loop_kwargs (Any, optional): Additional keywords to support base-classes of Loop. Defaults to None.
Returns:
LoopT: The new loop in the Knit_Graph.
"""
loop = self._loop_class(yarn, **loop_kwargs) # type: ignore[arg-type]
yarn.add_loop_to_end(loop)
if yarn.knit_graph is not self.knit_graph:
self.knit_graph.add_loop(loop)
return loop