knit-graphs 0.0.7__py3-none-any.whl → 0.0.8__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.
- docs/source/api/knit_graphs.Course.rst +7 -0
- docs/source/api/knit_graphs.Knit_Graph.rst +7 -0
- docs/source/api/knit_graphs.Knit_Graph_Visualizer.rst +7 -0
- docs/source/api/knit_graphs.Loop.rst +7 -0
- docs/source/api/knit_graphs.Pull_Direction.rst +7 -0
- docs/source/api/knit_graphs.Yarn.rst +7 -0
- docs/source/api/knit_graphs.artin_wale_braids.Crossing_Direction.rst +7 -0
- docs/source/api/knit_graphs.artin_wale_braids.Loop_Braid_Graph.rst +7 -0
- docs/source/api/knit_graphs.artin_wale_braids.Wale.rst +7 -0
- docs/source/api/knit_graphs.artin_wale_braids.Wale_Braid.rst +7 -0
- docs/source/api/knit_graphs.artin_wale_braids.Wale_Braid_Word.rst +7 -0
- docs/source/api/knit_graphs.artin_wale_braids.Wale_Group.rst +7 -0
- docs/source/api/knit_graphs.artin_wale_braids.rst +11 -46
- docs/source/api/knit_graphs.basic_knit_graph_generators.rst +7 -0
- docs/source/api/knit_graphs.rst +12 -54
- knit_graphs/Knit_Graph_Visualizer.py +188 -193
- knit_graphs/basic_knit_graph_generators.py +24 -75
- {knit_graphs-0.0.7.dist-info → knit_graphs-0.0.8.dist-info}/METADATA +2 -2
- knit_graphs-0.0.8.dist-info/RECORD +42 -0
- knit_graphs-0.0.7.dist-info/RECORD +0 -29
- {knit_graphs-0.0.7.dist-info → knit_graphs-0.0.8.dist-info}/LICENSE +0 -0
- {knit_graphs-0.0.7.dist-info → knit_graphs-0.0.8.dist-info}/WHEEL +0 -0
|
@@ -1,58 +1,23 @@
|
|
|
1
1
|
knit\_graphs.artin\_wale\_braids package
|
|
2
2
|
========================================
|
|
3
3
|
|
|
4
|
-
.. automodule:: knit_graphs.artin_wale_braids
|
|
5
|
-
:members:
|
|
6
|
-
:undoc-members:
|
|
7
|
-
:show-inheritance:
|
|
8
|
-
|
|
9
4
|
Submodules
|
|
10
5
|
----------
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
.. toctree::
|
|
8
|
+
:maxdepth: 4
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
knit_graphs.artin_wale_braids.Crossing_Direction
|
|
11
|
+
knit_graphs.artin_wale_braids.Loop_Braid_Graph
|
|
12
|
+
knit_graphs.artin_wale_braids.Wale
|
|
13
|
+
knit_graphs.artin_wale_braids.Wale_Braid
|
|
14
|
+
knit_graphs.artin_wale_braids.Wale_Braid_Word
|
|
15
|
+
knit_graphs.artin_wale_braids.Wale_Group
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
Module contents
|
|
18
|
+
---------------
|
|
22
19
|
|
|
23
|
-
.. automodule:: knit_graphs.artin_wale_braids
|
|
24
|
-
:members:
|
|
25
|
-
:undoc-members:
|
|
26
|
-
:show-inheritance:
|
|
27
|
-
|
|
28
|
-
knit\_graphs.artin\_wale\_braids.Wale module
|
|
29
|
-
--------------------------------------------
|
|
30
|
-
|
|
31
|
-
.. automodule:: knit_graphs.artin_wale_braids.Wale
|
|
32
|
-
:members:
|
|
33
|
-
:undoc-members:
|
|
34
|
-
:show-inheritance:
|
|
35
|
-
|
|
36
|
-
knit\_graphs.artin\_wale\_braids.Wale\_Braid module
|
|
37
|
-
---------------------------------------------------
|
|
38
|
-
|
|
39
|
-
.. automodule:: knit_graphs.artin_wale_braids.Wale_Braid
|
|
40
|
-
:members:
|
|
41
|
-
:undoc-members:
|
|
42
|
-
:show-inheritance:
|
|
43
|
-
|
|
44
|
-
knit\_graphs.artin\_wale\_braids.Wale\_Braid\_Word module
|
|
45
|
-
---------------------------------------------------------
|
|
46
|
-
|
|
47
|
-
.. automodule:: knit_graphs.artin_wale_braids.Wale_Braid_Word
|
|
48
|
-
:members:
|
|
49
|
-
:undoc-members:
|
|
50
|
-
:show-inheritance:
|
|
51
|
-
|
|
52
|
-
knit\_graphs.artin\_wale\_braids.Wale\_Group module
|
|
53
|
-
---------------------------------------------------
|
|
54
|
-
|
|
55
|
-
.. automodule:: knit_graphs.artin_wale_braids.Wale_Group
|
|
20
|
+
.. automodule:: knit_graphs.artin_wale_braids
|
|
56
21
|
:members:
|
|
57
22
|
:undoc-members:
|
|
58
23
|
:show-inheritance:
|
docs/source/api/knit_graphs.rst
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
knit\_graphs package
|
|
2
2
|
====================
|
|
3
3
|
|
|
4
|
-
.. automodule:: knit_graphs
|
|
5
|
-
:members:
|
|
6
|
-
:undoc-members:
|
|
7
|
-
:show-inheritance:
|
|
8
|
-
|
|
9
4
|
Subpackages
|
|
10
5
|
-----------
|
|
11
6
|
|
|
@@ -17,58 +12,21 @@ Subpackages
|
|
|
17
12
|
Submodules
|
|
18
13
|
----------
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.. automodule:: knit_graphs.Course
|
|
24
|
-
:members:
|
|
25
|
-
:undoc-members:
|
|
26
|
-
:show-inheritance:
|
|
27
|
-
|
|
28
|
-
knit\_graphs.Knit\_Graph module
|
|
29
|
-
-------------------------------
|
|
30
|
-
|
|
31
|
-
.. automodule:: knit_graphs.Knit_Graph
|
|
32
|
-
:members:
|
|
33
|
-
:undoc-members:
|
|
34
|
-
:show-inheritance:
|
|
35
|
-
|
|
36
|
-
knit\_graphs.Knit\_Graph\_Visualizer module
|
|
37
|
-
-------------------------------------------
|
|
38
|
-
|
|
39
|
-
.. automodule:: knit_graphs.Knit_Graph_Visualizer
|
|
40
|
-
:members:
|
|
41
|
-
:undoc-members:
|
|
42
|
-
:show-inheritance:
|
|
43
|
-
|
|
44
|
-
knit\_graphs.Loop module
|
|
45
|
-
------------------------
|
|
46
|
-
|
|
47
|
-
.. automodule:: knit_graphs.Loop
|
|
48
|
-
:members:
|
|
49
|
-
:undoc-members:
|
|
50
|
-
:show-inheritance:
|
|
51
|
-
|
|
52
|
-
knit\_graphs.Pull\_Direction module
|
|
53
|
-
-----------------------------------
|
|
54
|
-
|
|
55
|
-
.. automodule:: knit_graphs.Pull_Direction
|
|
56
|
-
:members:
|
|
57
|
-
:undoc-members:
|
|
58
|
-
:show-inheritance:
|
|
59
|
-
|
|
60
|
-
knit\_graphs.Yarn module
|
|
61
|
-
------------------------
|
|
15
|
+
.. toctree::
|
|
16
|
+
:maxdepth: 4
|
|
62
17
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
18
|
+
knit_graphs.Course
|
|
19
|
+
knit_graphs.Knit_Graph
|
|
20
|
+
knit_graphs.Knit_Graph_Visualizer
|
|
21
|
+
knit_graphs.Loop
|
|
22
|
+
knit_graphs.Pull_Direction
|
|
23
|
+
knit_graphs.Yarn
|
|
24
|
+
knit_graphs.basic_knit_graph_generators
|
|
67
25
|
|
|
68
|
-
|
|
69
|
-
|
|
26
|
+
Module contents
|
|
27
|
+
---------------
|
|
70
28
|
|
|
71
|
-
.. automodule:: knit_graphs
|
|
29
|
+
.. automodule:: knit_graphs
|
|
72
30
|
:members:
|
|
73
31
|
:undoc-members:
|
|
74
32
|
:show-inheritance:
|
|
@@ -3,8 +3,13 @@
|
|
|
3
3
|
This module provides comprehensive visualization capabilities for knit graphs using Plotly.
|
|
4
4
|
It handles the positioning of loops, rendering of yarn paths, stitch edges, and cable crossings to create interactive 2D visualizations of knitted structures.
|
|
5
5
|
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
6
10
|
from typing import Iterable, cast
|
|
7
11
|
|
|
12
|
+
import plotly.io as pio
|
|
8
13
|
from networkx import DiGraph
|
|
9
14
|
from plotly.graph_objs import Figure, Layout, Scatter
|
|
10
15
|
|
|
@@ -15,6 +20,25 @@ from knit_graphs.Loop import Loop
|
|
|
15
20
|
from knit_graphs.Pull_Direction import Pull_Direction
|
|
16
21
|
|
|
17
22
|
|
|
23
|
+
def configure_plotly_environment() -> None:
|
|
24
|
+
"""Auto-configure Plotly based on environment detection to avoid socket issues."""
|
|
25
|
+
# Check if we're in a testing environment
|
|
26
|
+
if ('pytest' in sys.modules or 'unittest' in sys.modules or
|
|
27
|
+
os.environ.get('TESTING') or os.environ.get('CI')):
|
|
28
|
+
# For testing, don't set a default renderer - we'll handle this with show_figure=False
|
|
29
|
+
pass
|
|
30
|
+
elif 'ipykernel' in sys.modules or 'jupyter' in sys.modules:
|
|
31
|
+
# Jupyter environment - use notebook renderer
|
|
32
|
+
pio.renderers.default = "notebook"
|
|
33
|
+
else:
|
|
34
|
+
# Development environment - use browser but with timeout to prevent hanging
|
|
35
|
+
pio.renderers.default = "browser"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Configure environment on import
|
|
39
|
+
configure_plotly_environment()
|
|
40
|
+
|
|
41
|
+
|
|
18
42
|
class Knit_Graph_Visualizer:
|
|
19
43
|
"""A class used to visualize a knit graph using the plotly graph objects library.
|
|
20
44
|
|
|
@@ -79,11 +103,14 @@ class Knit_Graph_Visualizer:
|
|
|
79
103
|
self._set_yarn_traces()
|
|
80
104
|
self._add_stitch_edges()
|
|
81
105
|
|
|
82
|
-
def make_figure(self, graph_title: str = "Knit Graph") ->
|
|
83
|
-
"""Generate
|
|
106
|
+
def make_figure(self, graph_title: str = "Knit Graph") -> Figure:
|
|
107
|
+
"""Generate the interactive figure to visualize this knit graph.
|
|
84
108
|
|
|
85
109
|
Args:
|
|
86
110
|
graph_title (str, optional): The title to display on the figure. Defaults to "Knit Graph".
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Figure: The plotly figure object.
|
|
87
114
|
"""
|
|
88
115
|
go_layout = Layout(title=graph_title,
|
|
89
116
|
showlegend=True,
|
|
@@ -97,7 +124,34 @@ class Knit_Graph_Visualizer:
|
|
|
97
124
|
figure_data.extend(self._loop_markers)
|
|
98
125
|
fig = Figure(data=figure_data,
|
|
99
126
|
layout=go_layout)
|
|
100
|
-
fig
|
|
127
|
+
return fig
|
|
128
|
+
|
|
129
|
+
def show_figure(self, graph_title: str = "Knit Graph", renderer: str | None = None) -> None:
|
|
130
|
+
"""Generate and display the interactive figure to visualize this knit graph.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
graph_title (str, optional): The title to display on the figure. Defaults to "Knit Graph".
|
|
134
|
+
renderer (str, optional): Plotly renderer to use. If None, uses the default configured renderer.
|
|
135
|
+
"""
|
|
136
|
+
fig = self.make_figure(graph_title)
|
|
137
|
+
|
|
138
|
+
# Configure display to minimize resource usage
|
|
139
|
+
config = {
|
|
140
|
+
'displayModeBar': False, # Hide toolbar to reduce resource usage
|
|
141
|
+
'displaylogo': False, # Hide plotly logo
|
|
142
|
+
'staticPlot': False, # Keep interactive
|
|
143
|
+
'scrollZoom': False, # Allow zoom
|
|
144
|
+
'doubleClick': 'reset+autosize' # Double-click behavior
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
if renderer:
|
|
149
|
+
fig.show(renderer=renderer, config=config)
|
|
150
|
+
else:
|
|
151
|
+
fig.show(config=config)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
print(f"Warning: Could not display figure: {e}")
|
|
154
|
+
print("Figure created successfully but display failed. Consider using show_figure=False and accessing the returned Figure object directly.")
|
|
101
155
|
|
|
102
156
|
def _no_cross_knit_trace(self, line_width: float = 4.0, knit_color: str = 'blue') -> Scatter:
|
|
103
157
|
"""Create a scatter trace for knit stitches not involved in cable crossings.
|
|
@@ -159,12 +213,12 @@ class Knit_Graph_Visualizer:
|
|
|
159
213
|
"""
|
|
160
214
|
return self._stitch_trace(self._top_purl_trace_data, "Purl Stitches on Top of Cable", purl_color, line_width, opacity=1.0)
|
|
161
215
|
|
|
162
|
-
def _bot_purl_trace(self, line_width: float = 3.0, purl_color: str = '
|
|
216
|
+
def _bot_purl_trace(self, line_width: float = 3.0, purl_color: str = 'red') -> Scatter:
|
|
163
217
|
"""Create a scatter trace for purl stitches that cross under other stitches in cables.
|
|
164
218
|
|
|
165
219
|
Args:
|
|
166
220
|
line_width (float, optional): The width of the lines representing the stitch edges. Defaults to 3.0.
|
|
167
|
-
purl_color (str, optional): The color of purl stitches in the visualization. Defaults to '
|
|
221
|
+
purl_color (str, optional): The color of purl stitches in the visualization. Defaults to 'red'.
|
|
168
222
|
|
|
169
223
|
Returns:
|
|
170
224
|
Scatter: The plotly scatter object used to visualize purl stitches below cable crossings.
|
|
@@ -192,10 +246,7 @@ class Knit_Graph_Visualizer:
|
|
|
192
246
|
mode='lines')
|
|
193
247
|
|
|
194
248
|
def _add_cable_edges(self) -> None:
|
|
195
|
-
"""Add all stitch edges that are involved in cable crossings to the appropriate trace data.
|
|
196
|
-
|
|
197
|
-
This method processes all cable crossings in the knit graph and adds the associated stitch edges to the correct trace data based on their crossing direction (over/under).
|
|
198
|
-
"""
|
|
249
|
+
"""Add all stitch edges that are involved in cable crossings to the appropriate trace data."""
|
|
199
250
|
for left_loop, right_loop in self.knit_graph.braid_graph.loop_crossing_graph.edges:
|
|
200
251
|
crossing_direction = self.knit_graph.braid_graph.get_crossing(left_loop, right_loop)
|
|
201
252
|
for left_parent in left_loop.parent_loops:
|
|
@@ -204,10 +255,7 @@ class Knit_Graph_Visualizer:
|
|
|
204
255
|
self._add_stitch_edge(right_parent, right_loop, ~crossing_direction)
|
|
205
256
|
|
|
206
257
|
def _add_stitch_edges(self) -> None:
|
|
207
|
-
"""Add all stitch edges to the visualization trace data based on their type and cable position.
|
|
208
|
-
|
|
209
|
-
This method first adds cable-involved edges, then adds all remaining stitch edges as non-cable stitches. It ensures all visible stitch connections are properly categorized for rendering.
|
|
210
|
-
"""
|
|
258
|
+
"""Add all stitch edges to the visualization trace data based on their type and cable position."""
|
|
211
259
|
self._add_cable_edges()
|
|
212
260
|
# Add remaining stitches as though they have no cable crossing.
|
|
213
261
|
for u, v in self.knit_graph.stitch_graph.edges:
|
|
@@ -216,13 +264,7 @@ class Knit_Graph_Visualizer:
|
|
|
216
264
|
self._add_stitch_edge(u, v, Crossing_Direction.No_Cross)
|
|
217
265
|
|
|
218
266
|
def _add_stitch_edge(self, u: Loop, v: Loop, crossing_direction: Crossing_Direction) -> None:
|
|
219
|
-
"""Add a single stitch edge to the appropriate trace data based on stitch type and cable crossing.
|
|
220
|
-
|
|
221
|
-
Args:
|
|
222
|
-
u (Loop): The parent loop in the stitch connection.
|
|
223
|
-
v (Loop): The child loop in the stitch connection.
|
|
224
|
-
crossing_direction (Crossing_Direction): The cable crossing direction of this stitch edge.
|
|
225
|
-
"""
|
|
267
|
+
"""Add a single stitch edge to the appropriate trace data based on stitch type and cable crossing."""
|
|
226
268
|
pull_direction = self.knit_graph.get_pull_direction(u, v)
|
|
227
269
|
if pull_direction is None:
|
|
228
270
|
return # No edge between these loops
|
|
@@ -253,12 +295,7 @@ class Knit_Graph_Visualizer:
|
|
|
253
295
|
trace_data['y'].append(None)
|
|
254
296
|
|
|
255
297
|
def _set_loop_markers(self, loop_size: float = 30.0, loop_border_width: float = 2.0) -> None:
|
|
256
|
-
"""Create plotly scatter objects to mark the position of each loop in the visualization.
|
|
257
|
-
|
|
258
|
-
Args:
|
|
259
|
-
loop_size (float, optional): The diameter of the circle marking each loop. Defaults to 30.0.
|
|
260
|
-
loop_border_width (float, optional): The width of the border around each loop marker. Defaults to 2.0.
|
|
261
|
-
"""
|
|
298
|
+
"""Create plotly scatter objects to mark the position of each loop in the visualization."""
|
|
262
299
|
yarns_to_loop_data = {yarn: {'x': [self._get_x_of_loop(loop) for loop in yarn],
|
|
263
300
|
'y': [self._get_y_of_loop(loop) for loop in yarn],
|
|
264
301
|
'loop_id': [loop.loop_id for loop in yarn]
|
|
@@ -276,12 +313,7 @@ class Knit_Graph_Visualizer:
|
|
|
276
313
|
for yarn, yarn_data in yarns_to_loop_data.items()]
|
|
277
314
|
|
|
278
315
|
def _set_yarn_traces(self, line_width: float = 1.0, smoothing: float = 1.3) -> None:
|
|
279
|
-
"""Create plotly traces representing the path of each yarn through the knitted structure.
|
|
280
|
-
|
|
281
|
-
Args:
|
|
282
|
-
line_width (float, optional): The width of the lines representing yarn paths. Defaults to 1.0.
|
|
283
|
-
smoothing (float, optional): The smoothing factor for spline interpolation of yarn paths. Defaults to 1.3.
|
|
284
|
-
"""
|
|
316
|
+
"""Create plotly traces representing the path of each yarn through the knitted structure."""
|
|
285
317
|
yarns_to_float_data = {}
|
|
286
318
|
for yarn in self.knit_graph.yarns:
|
|
287
319
|
float_data: dict[str, list[float]] = {'x': [], 'y': []}
|
|
@@ -300,23 +332,14 @@ class Knit_Graph_Visualizer:
|
|
|
300
332
|
for yarn, float_data in yarns_to_float_data.items()]
|
|
301
333
|
|
|
302
334
|
def _position_loops(self) -> None:
|
|
303
|
-
"""Calculate and set the x,y coordinate positions of all loops to be visualized.
|
|
304
|
-
|
|
305
|
-
This method orchestrates the complete positioning process including base course positioning, subsequent course positioning, knit/purl shifting, and float alignment adjustments.
|
|
306
|
-
"""
|
|
335
|
+
"""Calculate and set the x,y coordinate positions of all loops to be visualized."""
|
|
307
336
|
self._position_base_course()
|
|
308
337
|
self._place_loops_in_courses()
|
|
309
338
|
self._shift_knit_purl()
|
|
310
339
|
self._shift_loops_by_float_alignment()
|
|
311
340
|
|
|
312
341
|
def _shift_knit_purl(self, shift: float = 0.1) -> None:
|
|
313
|
-
"""Adjust the horizontal position of loops to visually distinguish knit from purl stitches.
|
|
314
|
-
|
|
315
|
-
This method shifts knit stitches slightly left and purl stitches slightly right to create visual separation that makes knit-purl patterns more distinct in the visualization.
|
|
316
|
-
|
|
317
|
-
Args:
|
|
318
|
-
shift (float, optional): The amount to shift stitches horizontally. Knit stitches are shifted left and purl stitches are shifted right. Defaults to 0.1.
|
|
319
|
-
"""
|
|
342
|
+
"""Adjust the horizontal position of loops to visually distinguish knit from purl stitches."""
|
|
320
343
|
has_knits = any(self.knit_graph.get_pull_direction(u, v) is Pull_Direction.BtF for u, v in self.knit_graph.stitch_graph.edges)
|
|
321
344
|
has_purls = any(self.knit_graph.get_pull_direction(u, v) is Pull_Direction.FtB for u, v in self.knit_graph.stitch_graph.edges)
|
|
322
345
|
if not (has_knits and has_purls):
|
|
@@ -340,14 +363,7 @@ class Knit_Graph_Visualizer:
|
|
|
340
363
|
self._set_x_of_loop(loop, self._get_x_of_loop(child_loop))
|
|
341
364
|
|
|
342
365
|
def _shift_loops_by_float_alignment(self, float_increment: float = 0.25) -> None:
|
|
343
|
-
"""Adjust the vertical position of loops based on their float relationships.
|
|
344
|
-
|
|
345
|
-
Loops that pass in front of floats are shifted down while loops that pass behind floats are shifted up.
|
|
346
|
-
This creates a visual layering effect that represents the three-dimensional structure of the knitted fabric.
|
|
347
|
-
|
|
348
|
-
Args:
|
|
349
|
-
float_increment (float, optional): The vertical spacing adjustment for loops relative to floats they cross. Defaults to 0.25.
|
|
350
|
-
"""
|
|
366
|
+
"""Adjust the vertical position of loops based on their float relationships."""
|
|
351
367
|
for yarn in self.knit_graph.yarns:
|
|
352
368
|
for u, v, front_loops in yarn.loops_in_front_of_floats():
|
|
353
369
|
for front_loop in front_loops:
|
|
@@ -359,26 +375,11 @@ class Knit_Graph_Visualizer:
|
|
|
359
375
|
self._set_y_of_loop(back_loop, self._get_y_of_loop(back_loop) + float_increment) # shift loop up to show it is behind the float.
|
|
360
376
|
|
|
361
377
|
def _get_course_of_loop(self, loop: Loop) -> Course:
|
|
362
|
-
"""Get the course (horizontal row) that contains the specified loop.
|
|
363
|
-
|
|
364
|
-
Args:
|
|
365
|
-
loop (Loop): The loop to find the course for.
|
|
366
|
-
|
|
367
|
-
Returns:
|
|
368
|
-
Course: The course that contains this loop.
|
|
369
|
-
"""
|
|
378
|
+
"""Get the course (horizontal row) that contains the specified loop."""
|
|
370
379
|
return self.loops_to_course[loop]
|
|
371
380
|
|
|
372
381
|
def _place_loop(self, loop: Loop, x: float, y: float) -> None:
|
|
373
|
-
"""Add a loop to the visualization data graph at the specified coordinates.
|
|
374
|
-
|
|
375
|
-
If the loop already exists in the data graph, its coordinates will be updated to the new position.
|
|
376
|
-
|
|
377
|
-
Args:
|
|
378
|
-
loop (Loop): The loop to position in the visualization.
|
|
379
|
-
x (float): The x coordinate for the loop.
|
|
380
|
-
y (float): The y coordinate for the loop.
|
|
381
|
-
"""
|
|
382
|
+
"""Add a loop to the visualization data graph at the specified coordinates."""
|
|
382
383
|
if self._loop_has_position(loop):
|
|
383
384
|
self._set_x_of_loop(loop, x)
|
|
384
385
|
self._set_y_of_loop(loop, y)
|
|
@@ -386,98 +387,43 @@ class Knit_Graph_Visualizer:
|
|
|
386
387
|
self.data_graph.add_node(loop, x=x, y=y)
|
|
387
388
|
|
|
388
389
|
def _set_x_of_loop(self, loop: Loop, x: float) -> None:
|
|
389
|
-
"""Update the x coordinate of a loop that already exists in the visualization data graph.
|
|
390
|
-
|
|
391
|
-
Args:
|
|
392
|
-
loop (Loop): The loop to update the x coordinate for.
|
|
393
|
-
x (float): The new x coordinate for the loop.
|
|
394
|
-
|
|
395
|
-
Raises:
|
|
396
|
-
KeyError: If the loop does not exist in the visualization data graph.
|
|
397
|
-
"""
|
|
390
|
+
"""Update the x coordinate of a loop that already exists in the visualization data graph."""
|
|
398
391
|
if self._loop_has_position(loop):
|
|
399
392
|
self.data_graph.nodes[loop]['x'] = x
|
|
400
393
|
else:
|
|
401
394
|
raise KeyError(f"Loop {loop} is not in the data graph")
|
|
402
395
|
|
|
403
396
|
def _set_y_of_loop(self, loop: Loop, y: float) -> None:
|
|
404
|
-
"""Update the y coordinate of a loop that already exists in the visualization data graph.
|
|
405
|
-
|
|
406
|
-
Args:
|
|
407
|
-
loop (Loop): The loop to update the y coordinate for.
|
|
408
|
-
y (float): The new y coordinate for the loop.
|
|
409
|
-
|
|
410
|
-
Raises:
|
|
411
|
-
KeyError: If the loop does not exist in the visualization data graph.
|
|
412
|
-
"""
|
|
397
|
+
"""Update the y coordinate of a loop that already exists in the visualization data graph."""
|
|
413
398
|
if self._loop_has_position(loop):
|
|
414
399
|
self.data_graph.nodes[loop]['y'] = y
|
|
415
400
|
else:
|
|
416
401
|
raise KeyError(f"Loop {loop} is not in the data graph")
|
|
417
402
|
|
|
418
403
|
def _get_x_of_loop(self, loop: Loop) -> float:
|
|
419
|
-
"""Get the x coordinate of a loop from the visualization data graph.
|
|
420
|
-
|
|
421
|
-
Args:
|
|
422
|
-
loop (Loop): The loop to get the x coordinate for.
|
|
423
|
-
|
|
424
|
-
Returns:
|
|
425
|
-
float: The x coordinate of the loop.
|
|
426
|
-
|
|
427
|
-
Raises:
|
|
428
|
-
KeyError: If the loop does not exist in the visualization data graph.
|
|
429
|
-
"""
|
|
404
|
+
"""Get the x coordinate of a loop from the visualization data graph."""
|
|
430
405
|
if self._loop_has_position(loop):
|
|
431
406
|
return float(self.data_graph.nodes[loop]['x'])
|
|
432
407
|
else:
|
|
433
408
|
raise KeyError(f"Loop {loop} is not in the data graph")
|
|
434
409
|
|
|
435
410
|
def _get_y_of_loop(self, loop: Loop) -> float:
|
|
436
|
-
"""Get the y coordinate of a loop from the visualization data graph.
|
|
437
|
-
|
|
438
|
-
Args:
|
|
439
|
-
loop (Loop): The loop to get the y coordinate for.
|
|
440
|
-
|
|
441
|
-
Returns:
|
|
442
|
-
float: The y coordinate of the loop.
|
|
443
|
-
|
|
444
|
-
Raises:
|
|
445
|
-
KeyError: If the loop does not exist in the visualization data graph.
|
|
446
|
-
"""
|
|
411
|
+
"""Get the y coordinate of a loop from the visualization data graph."""
|
|
447
412
|
if self._loop_has_position(loop):
|
|
448
413
|
return float(self.data_graph.nodes[loop]['y'])
|
|
449
414
|
else:
|
|
450
415
|
raise KeyError(f"Loop {loop} is not in the data graph")
|
|
451
416
|
|
|
452
417
|
def _loop_has_position(self, loop: Loop) -> bool:
|
|
453
|
-
"""Check if a loop has been positioned in the visualization data graph.
|
|
454
|
-
|
|
455
|
-
Args:
|
|
456
|
-
loop (Loop): The loop to check for positioning.
|
|
457
|
-
|
|
458
|
-
Returns:
|
|
459
|
-
bool: True if the loop has been positioned in the visualization, False otherwise.
|
|
460
|
-
"""
|
|
418
|
+
"""Check if a loop has been positioned in the visualization data graph."""
|
|
461
419
|
return bool(self.data_graph.has_node(loop))
|
|
462
420
|
|
|
463
421
|
def _stitch_has_position(self, u: Loop, v: Loop) -> bool:
|
|
464
|
-
"""Check if a stitch edge between two loops has been added to the visualization data graph.
|
|
465
|
-
|
|
466
|
-
Args:
|
|
467
|
-
u (Loop): The parent loop in the stitch edge.
|
|
468
|
-
v (Loop): The child loop in the stitch edge.
|
|
469
|
-
|
|
470
|
-
Returns:
|
|
471
|
-
bool: True if a stitch edge from u to v exists in the visualization data graph, False otherwise.
|
|
472
|
-
"""
|
|
422
|
+
"""Check if a stitch edge between two loops has been added to the visualization data graph."""
|
|
473
423
|
return bool(self.data_graph.has_edge(u, v))
|
|
474
424
|
|
|
475
425
|
def _place_loops_in_courses(self, course_spacing: float = 1.0) -> None:
|
|
476
|
-
"""Position loops in all courses above the base course using parent relationships and yarn connections.
|
|
477
|
-
|
|
478
|
-
Args:
|
|
479
|
-
course_spacing (float, optional): The vertical distance between consecutive courses. Defaults to 1.0.
|
|
480
|
-
"""
|
|
426
|
+
"""Position loops in all courses above the base course using parent relationships and yarn connections."""
|
|
481
427
|
y = course_spacing
|
|
482
428
|
for course in self.courses[self.first_course_index + 1:self.top_course_index]:
|
|
483
429
|
self._place_loops_by_parents(course, y)
|
|
@@ -487,11 +433,7 @@ class Knit_Graph_Visualizer:
|
|
|
487
433
|
y += course_spacing # Shift y coordinate up with each course
|
|
488
434
|
|
|
489
435
|
def _swap_loops_in_cables(self, course: Course) -> None:
|
|
490
|
-
"""Swap the horizontal positions of loops involved in cable crossings within a course.
|
|
491
|
-
|
|
492
|
-
Args:
|
|
493
|
-
course (Course): The course containing loops that may need position swapping due to cable crossings.
|
|
494
|
-
"""
|
|
436
|
+
"""Swap the horizontal positions of loops involved in cable crossings within a course."""
|
|
495
437
|
for left_loop in course:
|
|
496
438
|
for right_loop in self.knit_graph.braid_graph.left_crossing_loops(left_loop):
|
|
497
439
|
crossing_direction = self.knit_graph.braid_graph.get_crossing(left_loop, right_loop)
|
|
@@ -501,12 +443,7 @@ class Knit_Graph_Visualizer:
|
|
|
501
443
|
self._set_x_of_loop(right_loop, left_x)
|
|
502
444
|
|
|
503
445
|
def _place_loops_by_parents(self, course: Course, y: float) -> None:
|
|
504
|
-
"""Position loops in a course based on the average position of their parent loops.
|
|
505
|
-
|
|
506
|
-
Args:
|
|
507
|
-
course (Course): The course containing loops to be positioned.
|
|
508
|
-
y (float): The y coordinate for all loops in this course.
|
|
509
|
-
"""
|
|
446
|
+
"""Position loops in a course based on the average position of their parent loops."""
|
|
510
447
|
for x, loop in enumerate(course):
|
|
511
448
|
self._set_loop_x_by_parent_average(loop, y)
|
|
512
449
|
placed_loops = set()
|
|
@@ -519,14 +456,7 @@ class Knit_Graph_Visualizer:
|
|
|
519
456
|
# A loops past the first course should have at least one yarn neighbor to place them.
|
|
520
457
|
|
|
521
458
|
def _set_loop_x_by_parent_average(self, loop: Loop, y: float) -> None:
|
|
522
|
-
"""Set the x coordinate of a loop based on the weighted average position of its parent loops.
|
|
523
|
-
|
|
524
|
-
If the loop has no parent loops, it is added to the set of loops that need positioning through other methods.
|
|
525
|
-
|
|
526
|
-
Args:
|
|
527
|
-
loop (Loop): The loop to position based on its parents.
|
|
528
|
-
y (float): The y coordinate to assign to the loop.
|
|
529
|
-
"""
|
|
459
|
+
"""Set the x coordinate of a loop based on the weighted average position of its parent loops."""
|
|
530
460
|
if len(loop.parent_loops) == 0:
|
|
531
461
|
self._loops_need_placement.add(loop)
|
|
532
462
|
return
|
|
@@ -542,16 +472,7 @@ class Knit_Graph_Visualizer:
|
|
|
542
472
|
self._place_loop(loop, x=x, y=y)
|
|
543
473
|
|
|
544
474
|
def _set_loop_between_yarn_neighbors(self, loop: Loop, y: float, spacing: float = 1.0) -> bool:
|
|
545
|
-
"""Position a loop based on the average position of its neighboring loops along the yarn.
|
|
546
|
-
|
|
547
|
-
Args:
|
|
548
|
-
loop (Loop): The loop to position based on its yarn neighbors.
|
|
549
|
-
y (float): The y coordinate to assign to the loop.
|
|
550
|
-
spacing (float, optional): The minimum spacing to maintain between adjacent loops on the same course. Defaults to 1.0.
|
|
551
|
-
|
|
552
|
-
Returns:
|
|
553
|
-
bool: True if the loop was successfully positioned, False if no yarn neighbors were available for positioning.
|
|
554
|
-
"""
|
|
475
|
+
"""Position a loop based on the average position of its neighboring loops along the yarn."""
|
|
555
476
|
spacing = abs(spacing) # Ensure spacing is positive.
|
|
556
477
|
x_neighbors = []
|
|
557
478
|
prior_loop = loop.prior_loop_on_yarn()
|
|
@@ -573,11 +494,7 @@ class Knit_Graph_Visualizer:
|
|
|
573
494
|
return True
|
|
574
495
|
|
|
575
496
|
def _position_base_course(self) -> None:
|
|
576
|
-
"""Position the loops in the bottom course of the visualization and establish base metrics.
|
|
577
|
-
|
|
578
|
-
This method determines whether the base course is knit in the round (tube) or in rows (flat) and positions loops accordingly.
|
|
579
|
-
It also establishes the base width and left position for scaling other courses.
|
|
580
|
-
"""
|
|
497
|
+
"""Position the loops in the bottom course of the visualization and establish base metrics."""
|
|
581
498
|
base_course = self.courses[self.first_course_index]
|
|
582
499
|
if (len(self.courses) > self.first_course_index + 1 # There are more courses to show after the base course
|
|
583
500
|
and base_course.in_round_with(self.courses[self.first_course_index + 1])): # The first course is knit in the round to form a tube structure.
|
|
@@ -590,13 +507,7 @@ class Knit_Graph_Visualizer:
|
|
|
590
507
|
self.base_width = max_x - self.base_left
|
|
591
508
|
|
|
592
509
|
def _get_base_round_course_positions(self, base_course: Course, loop_space: float = 1.0, back_shift: float = 0.5) -> None:
|
|
593
|
-
"""Position loops in the base course for circular/tube knitting structure.
|
|
594
|
-
|
|
595
|
-
Args:
|
|
596
|
-
base_course (Course): The base course to position for circular knitting.
|
|
597
|
-
loop_space (float, optional): The horizontal spacing between loops in the course. Defaults to 1.0.
|
|
598
|
-
back_shift (float, optional): The vertical offset for loops on the back of the tube. Defaults to 0.5.
|
|
599
|
-
"""
|
|
510
|
+
"""Position loops in the base course for circular/tube knitting structure."""
|
|
600
511
|
split_index = len(base_course) // 2 # Split the course in half to form a tube.
|
|
601
512
|
front_loops: list[Loop] = cast(list[Loop], base_course[:split_index])
|
|
602
513
|
front_set: set[Loop] = set(front_loops)
|
|
@@ -617,12 +528,7 @@ class Knit_Graph_Visualizer:
|
|
|
617
528
|
self._place_loop(back_loop, x=(x * loop_space) - back_shift, y=0)
|
|
618
529
|
|
|
619
530
|
def _get_base_row_course_positions(self, base_course: Course, loop_space: float = 1.0) -> None:
|
|
620
|
-
"""Position loops in the base course for flat/row knitting structure.
|
|
621
|
-
|
|
622
|
-
Args:
|
|
623
|
-
base_course (Course): The base course to position for flat knitting.
|
|
624
|
-
loop_space (float, optional): The horizontal spacing between loops. Defaults to 1.0.
|
|
625
|
-
"""
|
|
531
|
+
"""Position loops in the base course for flat/row knitting structure."""
|
|
626
532
|
loops: Iterable[Loop] = list(base_course)
|
|
627
533
|
if not self.start_on_left:
|
|
628
534
|
loops = reversed(base_course)
|
|
@@ -630,13 +536,7 @@ class Knit_Graph_Visualizer:
|
|
|
630
536
|
self._place_loop(loop, x=x * loop_space, y=0)
|
|
631
537
|
|
|
632
538
|
def _left_align_course(self, course: Course) -> None:
|
|
633
|
-
"""Align the leftmost loop of a course to x=0 if left alignment is enabled.
|
|
634
|
-
|
|
635
|
-
The relative positions of all loops in the course are preserved while shifting the entire course horizontally.
|
|
636
|
-
|
|
637
|
-
Args:
|
|
638
|
-
course (Course): The course to potentially left-align.
|
|
639
|
-
"""
|
|
539
|
+
"""Align the leftmost loop of a course to x=0 if left alignment is enabled."""
|
|
640
540
|
if self.left_zero_align:
|
|
641
541
|
current_left = min(self._get_x_of_loop(loop) for loop in course)
|
|
642
542
|
if current_left != 0.0:
|
|
@@ -644,11 +544,7 @@ class Knit_Graph_Visualizer:
|
|
|
644
544
|
self._set_x_of_loop(loop, self._get_x_of_loop(loop) - current_left)
|
|
645
545
|
|
|
646
546
|
def _balance_course(self, course: Course) -> None:
|
|
647
|
-
"""Scale the width of a course to match the base course width if balancing is enabled.
|
|
648
|
-
|
|
649
|
-
Args:
|
|
650
|
-
course (Course): The course to potentially balance to match the base width.
|
|
651
|
-
"""
|
|
547
|
+
"""Scale the width of a course to match the base course width if balancing is enabled."""
|
|
652
548
|
current_left = min(self._get_x_of_loop(loop) for loop in course)
|
|
653
549
|
max_x = max(self._get_x_of_loop(loop) for loop in course)
|
|
654
550
|
course_width = max_x - current_left
|
|
@@ -660,10 +556,60 @@ class Knit_Graph_Visualizer:
|
|
|
660
556
|
for loop in course:
|
|
661
557
|
self._set_x_of_loop(loop, _target_distance_from_left(loop) + current_left)
|
|
662
558
|
|
|
559
|
+
def x_coordinate_differences(self, other: Knit_Graph_Visualizer) -> dict[Loop: tuple[float | None, float | None]]:
|
|
560
|
+
"""Find the differences in x-coordinates between two knitgraph visualizations. Used for testing and comparing visualization results.
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
other (Knit_Graph_Visualizer): The knitgraph visualization to compare to.
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
dict[Loop: tuple[float | None, float | None]]: The differences in x-coordinates.
|
|
567
|
+
* The keys of the dictionary are loops with differences between the two visualizations.
|
|
568
|
+
* The values of the dictionary are tuples of floats or None.
|
|
569
|
+
** The first value in each tuple is the x-coordinate of the loop in this knitgraph visualization or None if the loop is not in this visualization.
|
|
570
|
+
** The second value of each tuple is the x-coordinate of the loop in the other visualization or NOne if the loop is not in that visualization.
|
|
571
|
+
|
|
572
|
+
"""
|
|
573
|
+
differences = {l: (self._get_x_of_loop(l), None) for l in self.data_graph.nodes if not other.data_graph.has_node(l)}
|
|
574
|
+
differences.update({l: (None, other._get_x_of_loop(l)) for l in other.data_graph.nodes if not self.data_graph.has_node(l)})
|
|
575
|
+
differences.update({l: (self._get_x_of_loop(l), other._get_x_of_loop(l)) for l in self.data_graph.nodes if other.data_graph.has_node(l) and self._get_x_of_loop(l) != other._get_x_of_loop(l)})
|
|
576
|
+
return differences
|
|
577
|
+
|
|
578
|
+
def y_coordinate_differences(self, other: Knit_Graph_Visualizer) -> dict[Loop: tuple[float | None, float | None]]:
|
|
579
|
+
"""Find the differences in y-coordinates between two knitgraph visualizations. Used for testing and comparing visualization results.
|
|
580
|
+
|
|
581
|
+
Args:
|
|
582
|
+
other (Knit_Graph_Visualizer): The knitgraph visualization to compare to.
|
|
583
|
+
|
|
584
|
+
Returns:
|
|
585
|
+
dict[Loop: tuple[float | None, float | None]]: The differences in y-coordinates.
|
|
586
|
+
* The keys of the dictionary are loops with differences between the two visualizations.
|
|
587
|
+
* The values of the dictionary are tuples of floats or None.
|
|
588
|
+
** The first value in each tuple is the y-coordinate of the loop in this knitgraph visualization or None if the loop is not in this visualization.
|
|
589
|
+
** The second value of each tuple is the y-coordinate of the loop in the other visualization or NOne if the loop is not in that visualization.
|
|
590
|
+
|
|
591
|
+
"""
|
|
592
|
+
differences = {l: (self._get_y_of_loop(l), None) for l in self.data_graph.nodes if not other.data_graph.has_node(l)}
|
|
593
|
+
differences.update({l: (None, other._get_y_of_loop(l)) for l in other.data_graph.nodes if not self.data_graph.has_node(l)})
|
|
594
|
+
differences.update({l: (self._get_y_of_loop(l), other._get_y_of_loop(l)) for l in self.data_graph.nodes if other.data_graph.has_node(l) and self._get_y_of_loop(l) != other._get_y_of_loop(l)})
|
|
595
|
+
return differences
|
|
596
|
+
|
|
597
|
+
def __eq__(self, other: Knit_Graph_Visualizer) -> bool:
|
|
598
|
+
"""Two visualizations are equal if share the same x,y coordinates for all loops in the visualization and both contain the same set of loop nodes.
|
|
599
|
+
Args:
|
|
600
|
+
other (Knit_Graph_Visualizer): The knitgraph visualization to compare to.
|
|
601
|
+
|
|
602
|
+
Returns:
|
|
603
|
+
bool: True if the knitgraph visualizations are equal, False otherwise.
|
|
604
|
+
"""
|
|
605
|
+
return len(self.data_graph.nodes) == len(other.data_graph.nodes) and len(self.x_coordinate_differences(other)) == 0 and len(self.y_coordinate_differences(other)) == 0
|
|
663
606
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
607
|
+
|
|
608
|
+
def visualize_knit_graph(knit_graph: Knit_Graph, first_course_index: int = 0, top_course_index: int | None = None,
|
|
609
|
+
start_on_left: bool = True, balance_by_base_width: bool = False,
|
|
610
|
+
left_zero_align: bool = True, graph_title: str = "knit_graph",
|
|
611
|
+
show_figure: bool = True, renderer: str | None = None) -> Figure:
|
|
612
|
+
"""Generate and optionally display a plotly visualization of the given knit graph with specified configuration.
|
|
667
613
|
|
|
668
614
|
Args:
|
|
669
615
|
knit_graph (Knit_Graph): The knit graph to visualize.
|
|
@@ -674,7 +620,56 @@ def visualize_knit_graph(knit_graph: Knit_Graph, first_course_index: int = 0, to
|
|
|
674
620
|
left_zero_align (bool): Whether to align the leftmost position of each course with x=0 in the figure. Defaults to True.
|
|
675
621
|
graph_title (str): The title to display on the generated figure. Defaults to "knit_graph".
|
|
676
622
|
show_figure (bool, optional): If True, the visualization will be shown. Defaults to True.
|
|
623
|
+
renderer (str, optional): Plotly renderer to use. Common options: 'browser', 'notebook'. Defaults to None (uses configured default).
|
|
624
|
+
|
|
625
|
+
Returns:
|
|
626
|
+
Figure: The plotly figure object.
|
|
677
627
|
"""
|
|
678
628
|
visualizer = Knit_Graph_Visualizer(knit_graph, first_course_index, top_course_index, start_on_left, balance_by_base_width, left_zero_align)
|
|
629
|
+
fig = visualizer.make_figure(graph_title)
|
|
630
|
+
|
|
679
631
|
if show_figure:
|
|
680
|
-
|
|
632
|
+
try:
|
|
633
|
+
# Configure display to minimize resource usage
|
|
634
|
+
config = {
|
|
635
|
+
'displayModeBar': False, # Hide toolbar to reduce resource usage
|
|
636
|
+
'displaylogo': False, # Hide plotly logo
|
|
637
|
+
'staticPlot': False, # Keep interactive
|
|
638
|
+
'scrollZoom': True, # Allow zoom
|
|
639
|
+
'doubleClick': 'reset+autosize' # Double-click behavior
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if renderer:
|
|
643
|
+
fig.show(renderer=renderer, config=config)
|
|
644
|
+
else:
|
|
645
|
+
fig.show(config=config)
|
|
646
|
+
except Exception as e:
|
|
647
|
+
print(f"Warning: Could not display figure '{graph_title}': {e}")
|
|
648
|
+
print("Figure created successfully but display failed.")
|
|
649
|
+
|
|
650
|
+
return fig
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def visualize_knit_graph_safe(knit_graph: Knit_Graph, first_course_index: int = 0, top_course_index: int | None = None,
|
|
654
|
+
start_on_left: bool = True, balance_by_base_width: bool = False,
|
|
655
|
+
left_zero_align: bool = True, graph_title: str = "knit_graph") -> Figure:
|
|
656
|
+
"""Generate a plotly visualization of the given knit graph with specified configuration.
|
|
657
|
+
This function is safe for UnitTest and other headless environments because it does not attempt to show the visualization.
|
|
658
|
+
|
|
659
|
+
Args:
|
|
660
|
+
knit_graph (Knit_Graph): The knit graph to visualize.
|
|
661
|
+
first_course_index (int): Index of the first (bottom) course to include in the visualization. Defaults to 0.
|
|
662
|
+
top_course_index (int | None): Index of the last (top) course to include in the visualization. If None, visualizes up to the top of the knit graph.
|
|
663
|
+
start_on_left (bool): Whether the first loop knit is presumed to be positioned on the left of the pattern. Defaults to True.
|
|
664
|
+
balance_by_base_width (bool): Whether to scale all course widths to match the first course width. Defaults to False.
|
|
665
|
+
left_zero_align (bool): Whether to align the leftmost position of each course with x=0 in the figure. Defaults to True.
|
|
666
|
+
graph_title (str): The title to display on the generated figure. Defaults to "knit_graph".
|
|
667
|
+
|
|
668
|
+
Returns:
|
|
669
|
+
Figure: The plotly figure object.
|
|
670
|
+
"""
|
|
671
|
+
return visualize_knit_graph(knit_graph, first_course_index=first_course_index, top_course_index=top_course_index,
|
|
672
|
+
start_on_left=start_on_left,
|
|
673
|
+
balance_by_base_width=balance_by_base_width,
|
|
674
|
+
left_zero_align=left_zero_align, graph_title=graph_title,
|
|
675
|
+
show_figure=False)
|
|
@@ -5,6 +5,7 @@ These functions serve as building blocks for testing and demonstration purposes.
|
|
|
5
5
|
"""
|
|
6
6
|
from knit_graphs.artin_wale_braids.Crossing_Direction import Crossing_Direction
|
|
7
7
|
from knit_graphs.Knit_Graph import Knit_Graph
|
|
8
|
+
from knit_graphs.Loop import Loop
|
|
8
9
|
from knit_graphs.Pull_Direction import Pull_Direction
|
|
9
10
|
from knit_graphs.Yarn import Yarn
|
|
10
11
|
|
|
@@ -153,105 +154,53 @@ def seed_swatch(width: int, height: int) -> Knit_Graph:
|
|
|
153
154
|
return knit_graph
|
|
154
155
|
|
|
155
156
|
|
|
156
|
-
def
|
|
157
|
-
"""Generate a mesh pattern with left
|
|
158
|
-
|
|
159
|
-
This creates an openwork mesh pattern where purl stitches are decreased leftward on even courses and replaced with yarn overs,
|
|
160
|
-
creating decorative holes in the fabric while maintaining the overall stitch count.
|
|
157
|
+
def lace_mesh(width: int, height: int) -> Knit_Graph:
|
|
158
|
+
"""Generate a mesh pattern with alternating left and right leaning decrease paired to yarn-overs.
|
|
159
|
+
These pairings create a basic lace pattern with eyelets formed around the increases.
|
|
161
160
|
|
|
162
161
|
Args:
|
|
163
162
|
width (int): The number of stitches per course (horizontal row).
|
|
164
163
|
height (int): The number of courses (vertical rows) in the swatch.
|
|
165
164
|
|
|
166
165
|
Returns:
|
|
167
|
-
Knit_Graph: A knit graph representing a mesh swatch
|
|
166
|
+
Knit_Graph: A knit graph representing a mesh swatch.
|
|
168
167
|
"""
|
|
169
|
-
# k<o k<o k <-: 1->2
|
|
170
|
-
# |\ |\
|
|
171
|
-
# k p k p k ->: 0->1
|
|
172
|
-
# 0 1 2 3 4
|
|
173
168
|
knit_graph, yarn = co_loops(width)
|
|
174
169
|
last_course = knit_graph.get_courses()[0]
|
|
175
170
|
next_course = []
|
|
176
|
-
next_pull = Pull_Direction.BtF
|
|
177
171
|
for parent_loop in reversed(last_course):
|
|
178
172
|
child_loop = yarn.make_loop_on_end()
|
|
179
|
-
knit_graph.connect_loops(parent_loop, child_loop, pull_direction=
|
|
180
|
-
next_pull = next_pull.opposite()
|
|
173
|
+
knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF)
|
|
181
174
|
next_course.append(child_loop)
|
|
182
175
|
last_course = next_course
|
|
183
176
|
for _ in range(1, height):
|
|
177
|
+
# Make the lace course
|
|
184
178
|
next_course = []
|
|
185
|
-
|
|
179
|
+
yo_parent = None
|
|
180
|
+
prior_child = None
|
|
181
|
+
for i, parent_loop in enumerate(reversed(last_course)):
|
|
186
182
|
child_loop = yarn.make_loop_on_end()
|
|
187
|
-
|
|
188
|
-
parent_pull = knit_graph.get_pull_direction(grand_parent, parent_loop)
|
|
189
|
-
if parent_pull is Pull_Direction.BtF: # knits stay in decrease at bottom of stack
|
|
183
|
+
if i % 3 == 0: # Just knit every third stitch
|
|
190
184
|
knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF, stack_position=0)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
child_loop = yarn.make_loop_on_end()
|
|
199
|
-
if len(parent_loop.parent_loops) == 0:
|
|
200
|
-
knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.FtB)
|
|
201
|
-
else:
|
|
202
|
-
knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF)
|
|
203
|
-
next_course.append(child_loop)
|
|
204
|
-
last_course = next_course
|
|
205
|
-
return knit_graph
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
def kp_mesh_decrease_right_swatch(width: int, height: int) -> Knit_Graph:
|
|
209
|
-
"""Generate a mesh pattern with right-leaning decreases and yarn overs in knit-purl ribbing.
|
|
210
|
-
|
|
211
|
-
This creates an openwork mesh pattern where purl stitches are decreased rightward on even courses and replaced with yarn overs,
|
|
212
|
-
creating decorative holes in the fabric while maintaining the overall stitch count.
|
|
213
|
-
|
|
214
|
-
Args:
|
|
215
|
-
width (int): The number of stitches per course (horizontal row).
|
|
216
|
-
height (int): The number of courses (vertical rows) in the swatch.
|
|
217
|
-
|
|
218
|
-
Returns:
|
|
219
|
-
Knit_Graph: A knit graph representing a mesh swatch with right-leaning decreases and yarn overs.
|
|
220
|
-
"""
|
|
221
|
-
# k o>k o>k <-: 1->2
|
|
222
|
-
# /| /|
|
|
223
|
-
# k p k p k ->: 0->1
|
|
224
|
-
# 0 1 2 3 4
|
|
225
|
-
knit_graph, yarn = co_loops(width)
|
|
226
|
-
last_course = knit_graph.get_courses()[0]
|
|
227
|
-
next_course = []
|
|
228
|
-
next_pull = Pull_Direction.BtF
|
|
229
|
-
for parent_loop in reversed(last_course):
|
|
230
|
-
child_loop = yarn.make_loop_on_end()
|
|
231
|
-
knit_graph.connect_loops(parent_loop, child_loop, pull_direction=next_pull)
|
|
232
|
-
next_pull = next_pull.opposite()
|
|
233
|
-
next_course.append(child_loop)
|
|
234
|
-
last_course = next_course
|
|
235
|
-
for _ in range(1, height):
|
|
236
|
-
next_course = []
|
|
237
|
-
for parent_loop in reversed(last_course):
|
|
238
|
-
child_loop = yarn.make_loop_on_end()
|
|
239
|
-
grand_parent = parent_loop.parent_loops[0]
|
|
240
|
-
parent_pull = knit_graph.get_pull_direction(grand_parent, parent_loop)
|
|
241
|
-
if parent_pull is Pull_Direction.BtF: # knits stay in decrease at bottom of stack
|
|
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
|
|
242
192
|
knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF, stack_position=0)
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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)
|
|
246
197
|
next_course.append(child_loop)
|
|
247
198
|
last_course = next_course
|
|
199
|
+
# Make a basic jersey course
|
|
248
200
|
next_course = []
|
|
249
201
|
for parent_loop in reversed(last_course):
|
|
250
202
|
child_loop = yarn.make_loop_on_end()
|
|
251
|
-
|
|
252
|
-
knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.FtB)
|
|
253
|
-
else:
|
|
254
|
-
knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF)
|
|
203
|
+
knit_graph.connect_loops(parent_loop, child_loop, pull_direction=Pull_Direction.BtF)
|
|
255
204
|
next_course.append(child_loop)
|
|
256
205
|
last_course = next_course
|
|
257
206
|
return knit_graph
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: knit-graphs
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
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-graphs/
|
|
6
6
|
License: MIT
|
|
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Classifier: Topic :: Scientific/Engineering
|
|
23
23
|
Requires-Dist: networkx (>=3.5)
|
|
24
|
-
Requires-Dist: plotly (>=6.3.0)
|
|
24
|
+
Requires-Dist: plotly (>=6.3.0,<7.0.0)
|
|
25
25
|
Project-URL: Documentation, https://mhofmann-Khoury.github.io/knit-graphs/
|
|
26
26
|
Project-URL: Repository, https://github.com/mhofmann-Khoury/knit-graphs
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
LICENSE,sha256=Oazk3oiRu5ZN7b-EdYNYh0vu-I3Av2uIPQ-9L_cZ6Oo,1070
|
|
2
|
+
README.md,sha256=Hrs3kN8A7hxyel-WhEkzZYY89456vAh3KWbupcOAHsI,3963
|
|
3
|
+
docs/Makefile,sha256=4zv3TVkTACm6JBaKgTES3ZI9cETXgM6ULbZkXZP1as8,638
|
|
4
|
+
docs/make.bat,sha256=0qjrzODegavKd7LLwmr9ADhaYftiTnBgqOVAL9kApyo,769
|
|
5
|
+
docs/source/api/knit_graphs.Course.rst,sha256=k3RxvR2pSYgf_nyLjLgT3J56hjzk4fUU6kIdmp85pEc,144
|
|
6
|
+
docs/source/api/knit_graphs.Knit_Graph.rst,sha256=_VZEniw4cvMz6uUrXrTK1hBuYEZSaDUk1138AbIEZ2s,158
|
|
7
|
+
docs/source/api/knit_graphs.Knit_Graph_Visualizer.rst,sha256=vo7RSxjhWhIAFlKJMlwq48x1j3DszzZmpF-8-09uDaE,193
|
|
8
|
+
docs/source/api/knit_graphs.Loop.rst,sha256=Rgrs-yFP_0Qt6Mxjjt9s_PE4Vzk6gRpmZ_Qo8zHWNVQ,138
|
|
9
|
+
docs/source/api/knit_graphs.Pull_Direction.rst,sha256=Y6z1SBcsBb1Gdr_UnSgjvIawzfjDndBbciQhoNYTKhE,170
|
|
10
|
+
docs/source/api/knit_graphs.Yarn.rst,sha256=S2Vcm0xxVGx_2EvXLex0QusiTRpJEIras5XPOjL3ONQ,138
|
|
11
|
+
docs/source/api/knit_graphs.artin_wale_braids.Crossing_Direction.rst,sha256=C_apeGs2QvSG06jfcimuXGJDMAAZW4L_9fZT3vSnhts,240
|
|
12
|
+
docs/source/api/knit_graphs.artin_wale_braids.Loop_Braid_Graph.rst,sha256=5R0PtnLiAYFtsejQq-KJQYSMLeoGOs4jAAkVTbKtvy0,236
|
|
13
|
+
docs/source/api/knit_graphs.artin_wale_braids.Wale.rst,sha256=L3-9V-_5txjPa-XrBbOCAO4Le5ewSnGrJX9ZyUgWUDQ,196
|
|
14
|
+
docs/source/api/knit_graphs.artin_wale_braids.Wale_Braid.rst,sha256=tHXetyWrArkVBM89fIjCauGfOuVhb0jkZRDIZs5ZAWA,216
|
|
15
|
+
docs/source/api/knit_graphs.artin_wale_braids.Wale_Braid_Word.rst,sha256=NkXO_cCev1XdUwcUVsdb2CivEHm-xqJyFg3ZgHOfmZ0,233
|
|
16
|
+
docs/source/api/knit_graphs.artin_wale_braids.Wale_Group.rst,sha256=bAGHs3s8dW6lbkctyh8VvVNzlYkjwZXhrbXx-VvSKYQ,216
|
|
17
|
+
docs/source/api/knit_graphs.artin_wale_braids.rst,sha256=ESKgE9gg830yJ6w5mKuv3MHgH3tMZ-Ee2y3b-bSnU4Y,547
|
|
18
|
+
docs/source/api/knit_graphs.basic_knit_graph_generators.rst,sha256=OgV2jkEMY0LfRq8v0cNhJUk3fbUOS9VcP8zRv4VBvC4,213
|
|
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
|
|
22
|
+
docs/source/installation.rst,sha256=GyNRk_oXKVgOZ4KFLAgkXLwjHYzDYsx8gcokLRrS0ZI,1247
|
|
23
|
+
knit_graphs/Course.py,sha256=oT-YghPdAg7A51GI_StdfF8oxYGWGkoad72woEkRKeY,5818
|
|
24
|
+
knit_graphs/Knit_Graph.py,sha256=KgcWQPzZJ-Iif7g3zWq6hBy4fdJXkJXnFxGOSMYOn-g,10795
|
|
25
|
+
knit_graphs/Knit_Graph_Visualizer.py,sha256=s2zp6Z7-ZF_52x3zTx8oQr__tQScpkGthIoWck57ykE,40227
|
|
26
|
+
knit_graphs/Loop.py,sha256=zp1ZiVRFSCPL2CnXNxgwNPv7WrcuqFRe3S_ZATxEBAo,5781
|
|
27
|
+
knit_graphs/Pull_Direction.py,sha256=8xRLN-j7V1mExgmt3YjcJcTDa5rLedA2bF4wI8gd-DE,2334
|
|
28
|
+
knit_graphs/Yarn.py,sha256=wNOmj15dnSA56Qw2D5ksgbmQqug-u5kPPxKGuitQ_qI,15901
|
|
29
|
+
knit_graphs/__init__.py,sha256=qzZAOxTqjgLkhoGnKNZwOkuYbKEyzfWKkKpKkV6lVfk,111
|
|
30
|
+
knit_graphs/_base_classes.py,sha256=ZEv_jkUglHzkfg-0TD6oonBq7QQmA4G7jrC_A6OubpY,6401
|
|
31
|
+
knit_graphs/artin_wale_braids/Crossing_Direction.py,sha256=71ckmEFfL0s25NJb8lfYwH2zku0uM1C5X9gGM7PYC5A,2820
|
|
32
|
+
knit_graphs/artin_wale_braids/Loop_Braid_Graph.py,sha256=2tsE16IzcvgsIKij1JNHWah6hLOgrpXCcjb6eocbfFs,4575
|
|
33
|
+
knit_graphs/artin_wale_braids/Wale.py,sha256=PK8IQDnTFXk5924QZYiGCqfckS62laG9_XhK7xH2zAo,7171
|
|
34
|
+
knit_graphs/artin_wale_braids/Wale_Braid.py,sha256=Ws0QmU7s63nrBKe69acDza-r-LozDyyaKVDNxQMy10Y,2662
|
|
35
|
+
knit_graphs/artin_wale_braids/Wale_Braid_Word.py,sha256=4YBlVMUZkqp85r46ETQSbFVS_WrT4PsWthHmFv5Ny9M,4587
|
|
36
|
+
knit_graphs/artin_wale_braids/Wale_Group.py,sha256=HTl01PHzFhInhFJuB30OScWx7pIejmo3-HwHN4RB4V4,6868
|
|
37
|
+
knit_graphs/artin_wale_braids/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
knit_graphs/basic_knit_graph_generators.py,sha256=doDH2MiXbMzGU9b3enEr3KdY8Fw90cssHi_lkts6VpY,11720
|
|
39
|
+
knit_graphs-0.0.8.dist-info/LICENSE,sha256=Oazk3oiRu5ZN7b-EdYNYh0vu-I3Av2uIPQ-9L_cZ6Oo,1070
|
|
40
|
+
knit_graphs-0.0.8.dist-info/METADATA,sha256=H8xfTEPGsLFE-_j4ZfleigF2RlmbKJU9SycIFhkDuX4,5195
|
|
41
|
+
knit_graphs-0.0.8.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
42
|
+
knit_graphs-0.0.8.dist-info/RECORD,,
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
LICENSE,sha256=Oazk3oiRu5ZN7b-EdYNYh0vu-I3Av2uIPQ-9L_cZ6Oo,1070
|
|
2
|
-
README.md,sha256=Hrs3kN8A7hxyel-WhEkzZYY89456vAh3KWbupcOAHsI,3963
|
|
3
|
-
docs/Makefile,sha256=4zv3TVkTACm6JBaKgTES3ZI9cETXgM6ULbZkXZP1as8,638
|
|
4
|
-
docs/make.bat,sha256=0qjrzODegavKd7LLwmr9ADhaYftiTnBgqOVAL9kApyo,769
|
|
5
|
-
docs/source/api/knit_graphs.artin_wale_braids.rst,sha256=_KUBZld6fAS5OP0k4gd5yKamNGXEqH12XjakmWeJIII,1549
|
|
6
|
-
docs/source/api/knit_graphs.rst,sha256=zdmnXipviweW0ZGQsQCZtfOOc_6Fj0yWdg_TwOMafLI,1398
|
|
7
|
-
docs/source/conf.py,sha256=TZK8Rz0JNgQs2J_cUhVX5_4tmMBvRdGtILJsnAnAFWo,13350
|
|
8
|
-
docs/source/index.rst,sha256=D4KmCX1_d2-WrPgxSeggHHsc9-TeQwSUGYOkehRQWrw,1462
|
|
9
|
-
docs/source/installation.rst,sha256=GyNRk_oXKVgOZ4KFLAgkXLwjHYzDYsx8gcokLRrS0ZI,1247
|
|
10
|
-
knit_graphs/Course.py,sha256=oT-YghPdAg7A51GI_StdfF8oxYGWGkoad72woEkRKeY,5818
|
|
11
|
-
knit_graphs/Knit_Graph.py,sha256=KgcWQPzZJ-Iif7g3zWq6hBy4fdJXkJXnFxGOSMYOn-g,10795
|
|
12
|
-
knit_graphs/Knit_Graph_Visualizer.py,sha256=bbWl9iPQR93MtpWMPji4RTFR3Rgon0vzBlS11e2R8Zg,37925
|
|
13
|
-
knit_graphs/Loop.py,sha256=zp1ZiVRFSCPL2CnXNxgwNPv7WrcuqFRe3S_ZATxEBAo,5781
|
|
14
|
-
knit_graphs/Pull_Direction.py,sha256=8xRLN-j7V1mExgmt3YjcJcTDa5rLedA2bF4wI8gd-DE,2334
|
|
15
|
-
knit_graphs/Yarn.py,sha256=wNOmj15dnSA56Qw2D5ksgbmQqug-u5kPPxKGuitQ_qI,15901
|
|
16
|
-
knit_graphs/__init__.py,sha256=qzZAOxTqjgLkhoGnKNZwOkuYbKEyzfWKkKpKkV6lVfk,111
|
|
17
|
-
knit_graphs/_base_classes.py,sha256=ZEv_jkUglHzkfg-0TD6oonBq7QQmA4G7jrC_A6OubpY,6401
|
|
18
|
-
knit_graphs/artin_wale_braids/Crossing_Direction.py,sha256=71ckmEFfL0s25NJb8lfYwH2zku0uM1C5X9gGM7PYC5A,2820
|
|
19
|
-
knit_graphs/artin_wale_braids/Loop_Braid_Graph.py,sha256=2tsE16IzcvgsIKij1JNHWah6hLOgrpXCcjb6eocbfFs,4575
|
|
20
|
-
knit_graphs/artin_wale_braids/Wale.py,sha256=PK8IQDnTFXk5924QZYiGCqfckS62laG9_XhK7xH2zAo,7171
|
|
21
|
-
knit_graphs/artin_wale_braids/Wale_Braid.py,sha256=Ws0QmU7s63nrBKe69acDza-r-LozDyyaKVDNxQMy10Y,2662
|
|
22
|
-
knit_graphs/artin_wale_braids/Wale_Braid_Word.py,sha256=4YBlVMUZkqp85r46ETQSbFVS_WrT4PsWthHmFv5Ny9M,4587
|
|
23
|
-
knit_graphs/artin_wale_braids/Wale_Group.py,sha256=HTl01PHzFhInhFJuB30OScWx7pIejmo3-HwHN4RB4V4,6868
|
|
24
|
-
knit_graphs/artin_wale_braids/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
-
knit_graphs/basic_knit_graph_generators.py,sha256=_8CbtZ4M5NRp0mXXNdCNDPtIaCKEzmpAJQVNlmV5l8c,13893
|
|
26
|
-
knit_graphs-0.0.7.dist-info/LICENSE,sha256=Oazk3oiRu5ZN7b-EdYNYh0vu-I3Av2uIPQ-9L_cZ6Oo,1070
|
|
27
|
-
knit_graphs-0.0.7.dist-info/METADATA,sha256=YuT2TGLdupJYXAKbbC-pXi-z0fKbOelNz31STuy7tks,5188
|
|
28
|
-
knit_graphs-0.0.7.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
29
|
-
knit_graphs-0.0.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|