kececilayout 0.2.1__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.
@@ -0,0 +1,723 @@
1
+ Metadata-Version: 2.4
2
+ Name: kececilayout
3
+ Version: 0.2.1
4
+ Summary: A deterministic node placement algorithm used in graph visualization. In this layout, nodes are arranged sequentially along a defined primary axis. Each subsequent node is then alternately offset along a secondary, perpendicular axis, typically moving to one side of the primary axis and then the other. Often, the magnitude of this secondary offset increases as nodes progress along the primary axis, creating a characteristic zig-zag or serpentine pattern.
5
+ Home-page: https://github.com/WhiteSymmetry/kececilayout
6
+ Author: Mehmet Keçeci
7
+ Author-email: bilginomi@yaani.com
8
+ Maintainer: Mehmet Keçeci
9
+ Maintainer-email: bilginomi@yaani.com
10
+ License: MIT
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: networkx
18
+ Requires-Dist: numpy
19
+ Requires-Dist: rustworkx
20
+ Requires-Dist: igraph
21
+ Requires-Dist: networkit
22
+ Requires-Dist: graphillion
23
+ Requires-Dist: pycairo
24
+ Requires-Dist: cairocffi
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: home-page
31
+ Dynamic: license
32
+ Dynamic: license-file
33
+ Dynamic: maintainer
34
+ Dynamic: maintainer-email
35
+ Dynamic: requires-dist
36
+ Dynamic: requires-python
37
+ Dynamic: summary
38
+
39
+ # KececiLayout
40
+
41
+ [![PyPI version](https://badge.fury.io/py/kececilayout.svg)](https://badge.fury.io/py/kececilayout)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
43
+
44
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.15313947.svg)](https://doi.org/10.5281/zenodo.15313947)
45
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.15314329.svg)](https://doi.org/10.5281/zenodo.15314329)
46
+
47
+ [![Anaconda-Server Badge](https://anaconda.org/bilgi/kececilayout/badges/version.svg)](https://anaconda.org/bilgi/kececilayout)
48
+ [![Anaconda-Server Badge](https://anaconda.org/bilgi/kececilayout/badges/latest_release_date.svg)](https://anaconda.org/bilgi/kececilayout)
49
+ [![Anaconda-Server Badge](https://anaconda.org/bilgi/kececilayout/badges/platforms.svg)](https://anaconda.org/bilgi/kececilayout)
50
+ [![Anaconda-Server Badge](https://anaconda.org/bilgi/kececilayout/badges/license.svg)](https://anaconda.org/bilgi/kececilayout)
51
+
52
+ [![Open Source](https://img.shields.io/badge/Open%20Source-Open%20Source-brightgreen.svg)](https://opensource.org/)
53
+ [![Documentation Status](https://app.readthedocs.org/projects/kececilayout/badge/?0.2.1=main)](https://kececilayout.readthedocs.io/en/main/?badge=main)
54
+
55
+ [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/10531/badge)](https://www.bestpractices.dev/projects/10531)
56
+
57
+ **Kececi Layout (Keçeci Yerleşimi)**: A deterministic graph layout algorithm designed for visualizing linear or sequential structures with a characteristic "zig-zag" or "serpentine" pattern.
58
+
59
+ *Python implementation of the Keçeci layout algorithm for graph visualization.*
60
+
61
+ ---
62
+
63
+ ## Description / Açıklama
64
+
65
+ This algorithm arranges nodes sequentially along a primary axis and offsets them alternately along a secondary axis. It's particularly useful for path graphs, chains, or showing progression.
66
+
67
+ *Bu algoritma, düğümleri birincil eksen boyunca sıralı olarak yerleştirir ve ikincil eksen boyunca dönüşümlü olarak kaydırır. Yol grafları, zincirler veya ilerlemeyi göstermek için özellikle kullanışlıdır.*
68
+
69
+ ---
70
+
71
+ ### English Description
72
+
73
+ **Keçeci Layout:**
74
+
75
+ A deterministic node placement algorithm used in graph visualization. In this layout, nodes are arranged sequentially along a defined primary axis. Each subsequent node is then alternately offset along a secondary, perpendicular axis, typically moving to one side of the primary axis and then the other. Often, the magnitude of this secondary offset increases as nodes progress along the primary axis, creating a characteristic "zig-zag" or "serpentine" pattern.
76
+
77
+ **Key Characteristics:**
78
+ * **Linear Focus:** Particularly useful for visualizing linear or sequential structures, such as paths, chains, or ordered processes.
79
+ * **Deterministic:** Produces the exact same layout for the same graph and parameters every time.
80
+ * **Overlap Reduction:** Helps prevent node collisions by spreading nodes out away from the primary axis.
81
+ * **Parametric:** Can be customized using parameters such as the primary direction (e.g., `top-down`), the starting side for the secondary offset (e.g., `start_right`), and the spacing along both axes (`primary_spacing`, `secondary_spacing`).
82
+
83
+ ---
84
+
85
+ ### Türkçe Tanımlama
86
+
87
+ **Keçeci Yerleşimi (Keçeci Layout):**
88
+
89
+ Graf görselleştirmede kullanılan deterministik bir düğüm yerleştirme algoritmasıdır. Bu yöntemde düğümler, belirlenen birincil (ana) eksen boyunca sıralı olarak yerleştirilir. Her bir sonraki düğüm, ana eksenin bir sağına bir soluna (veya bir üstüne bir altına) olmak üzere, ikincil eksen doğrultusunda dönüşümlü olarak kaydırılır. Genellikle, ana eksende ilerledikçe ikincil eksendeki kaydırma miktarı artar ve bu da karakteristik bir "zıgzag" veya "yılanvari" desen oluşturur.
90
+
91
+ **Temel Özellikleri:**
92
+ * **Doğrusal Odak:** Özellikle yollar (paths), zincirler veya sıralı süreçler gibi doğrusal veya ardışık yapıları görselleştirmek için kullanışlıdır.
93
+ * **Deterministik:** Aynı graf ve parametrelerle her zaman aynı sonucu üretir.
94
+ * **Çakışmayı Azaltma:** Düğümleri ana eksenden uzağa yayarak çakışmaları önlemeye yardımcı olur.
95
+ * **Parametrik:** Ana eksenin yönü (örn. `top-down`), ikincil kaydırmanın başlangıç yönü (örn. `start_right`) ve eksenler arası boşluklar (`primary_spacing`, `secondary_spacing`) gibi parametrelerle özelleştirilebilir.
96
+
97
+ ---
98
+
99
+ ## Installation / Kurulum
100
+
101
+ ```bash
102
+ conda install bilgi::kececilayout -y
103
+
104
+ pip install kececilayout
105
+ ```
106
+ https://anaconda.org/bilgi/kececilayout
107
+
108
+ https://pypi.org/project/KececiLayout/
109
+
110
+ https://github.com/WhiteSymmetry/kececilayout
111
+
112
+ https://zenodo.org/records/15313947
113
+
114
+ https://zenodo.org/records/15314329
115
+
116
+ ---
117
+
118
+ ## Usage / Kullanım
119
+
120
+ The layout function generally accepts a graph object and returns positions.
121
+
122
+ ### Example with NetworkX
123
+
124
+ ```python
125
+ import networkx as nx
126
+ import matplotlib.pyplot as plt
127
+ import kececilayout as kl # Assuming the main function is imported like this
128
+ import random
129
+
130
+ # Create a graph
131
+ G = nx.path_graph(10)
132
+
133
+ # Calculate layout positions using the generic function
134
+ # (Assuming kl.kececi_layout_v4 is the main/generic function)
135
+ pos = kl.kececi_layout_v4(G,
136
+ primary_spacing=1.0,
137
+ secondary_spacing=0.5,
138
+ primary_direction='top-down',
139
+ secondary_start='right')
140
+
141
+ # Draw the graph
142
+ plt.figure(figsize=(6, 8))
143
+ nx.draw(G, pos=pos, with_labels=True, node_color='skyblue', node_size=500, font_size=10)
144
+ plt.title("Keçeci Layout with NetworkX")
145
+ plt.axis('equal') # Ensure aspect ratio is equal
146
+ plt.show()
147
+ ```
148
+
149
+ ```python
150
+ import matplotlib.pyplot as plt
151
+ import math
152
+ import networkx as nx
153
+ import kececilayout as kl
154
+ import random
155
+
156
+ try:
157
+ import kececilayout as kl
158
+ except ImportError:
159
+ print("Error: 'kececi_layout.py' not found or could not be imported.")
160
+ print("Please ensure the file containing kececi_layout_v4 is accessible.")
161
+ exit()
162
+
163
+ # --- General Layout Parameters ---
164
+ LAYOUT_PARAMS = {
165
+ 'primary_spacing': 1.0,
166
+ 'secondary_spacing': 0.6, # Make the zigzag noticeable
167
+ 'primary_direction': 'top-down',
168
+ 'secondary_start': 'right'
169
+ }
170
+ N_NODES = 10 # Number of nodes in the example graph
171
+
172
+ # === NetworkX Example ===
173
+ try:
174
+ import networkx as nx
175
+ print("\n--- NetworkX Example ---")
176
+
177
+ # Generate graph (Path graph)
178
+ G_nx = nx.path_graph(N_NODES)
179
+ print(f"NetworkX graph generated: {G_nx.number_of_nodes()} nodes, {G_nx.number_of_edges()} edges")
180
+
181
+ # Calculate layout
182
+ print("Calculating Keçeci Layout...")
183
+ # Call the layout function from the imported module
184
+ pos_nx = kl.kececi_layout_v4(G_nx, **LAYOUT_PARAMS)
185
+ # print("NetworkX positions:", pos_nx) # Debug print if needed
186
+
187
+ # Plot
188
+ plt.figure(figsize=(6, 8)) # Suitable figure size for vertical layout
189
+ nx.draw(G_nx, # NetworkX graph object
190
+ pos=pos_nx, # Positions calculated by Kececi Layout
191
+ with_labels=True, # Show node labels (indices)
192
+ node_color='skyblue',# Node color
193
+ node_size=700, # Node size
194
+ font_size=10, # Label font size
195
+ edge_color='gray') # Edge color
196
+
197
+ plt.title(f"NetworkX ({N_NODES} Nodes) with Keçeci Layout") # Plot title
198
+ plt.xlabel("X Coordinate") # X-axis label
199
+ plt.ylabel("Y Coordinate") # Y-axis label
200
+ plt.axis('equal') # Ensure equal aspect ratio for correct spacing perception
201
+ # plt.grid(False) # Ensure grid is off
202
+ plt.show() # Display the plot
203
+
204
+ except ImportError:
205
+ print("NetworkX is not installed. Skipping this example.")
206
+ except Exception as e:
207
+ print(f"An error occurred in the NetworkX example: {e}")
208
+ import traceback
209
+ traceback.print_exc()
210
+
211
+ print("\n--- NetworkX Example Finished ---")
212
+ ```
213
+
214
+ ![Networkx Example](https://github.com/WhiteSymmetry/kececilayout/blob/main/examples/nx-1.png?raw=true)
215
+
216
+ ---
217
+ ### Example with iGraph
218
+
219
+ ```python
220
+ import igraph as ig
221
+ import matplotlib.pyplot as plt
222
+ # Assuming a specific function for igraph exists or the generic one handles it
223
+ from kececilayout import kececi_layout_v4_igraph # Adjust import if needed
224
+ import random
225
+
226
+ # Create a graph
227
+ G = ig.Graph.Ring(10, circular=False) # Path graph equivalent
228
+ for i in range(G.vcount()):
229
+ G.vs[i]["name"] = f"N{i}"
230
+
231
+ # Calculate layout positions (returns a list of coords)
232
+ pos_list = kececi_layout_v4_igraph(G,
233
+ primary_spacing=1.5,
234
+ secondary_spacing=1.0,
235
+ primary_direction='left-to-right',
236
+ secondary_start='up')
237
+ layout = ig.Layout(coords=pos_list)
238
+
239
+ # Draw the graph
240
+ fig, ax = plt.subplots(figsize=(8, 6))
241
+ ig.plot(
242
+ G,
243
+ target=ax,
244
+ layout=layout,
245
+ vertex_label=G.vs["name"],
246
+ vertex_color="lightblue",
247
+ vertex_size=30
248
+ )
249
+ ax.set_title("Keçeci Layout with iGraph")
250
+ ax.set_aspect('equal', adjustable='box')
251
+ plt.show()
252
+ ```
253
+
254
+ ```python
255
+ import matplotlib.pyplot as plt
256
+ import math
257
+ import igraph as ig
258
+ import kececilayout as kl
259
+
260
+
261
+ try:
262
+ import kececilayout as kl
263
+ except ImportError:
264
+ print("Error: 'kececi_layout.py' not found or could not be imported.")
265
+ print("Please ensure the file containing kececi_layout_v4 is accessible.")
266
+ exit()
267
+
268
+ # --- General Layout Parameters ---
269
+ LAYOUT_PARAMS = {
270
+ 'primary_spacing': 1.0,
271
+ 'secondary_spacing': 0.6, # Make the zigzag noticeable
272
+ 'primary_direction': 'top-down',
273
+ 'secondary_start': 'right'
274
+ }
275
+ N_NODES = 10 # Number of nodes in the example graph
276
+
277
+ # === igraph Example ===
278
+ try:
279
+ import igraph as ig
280
+ print("\n--- igraph Example ---")
281
+
282
+ # Generate graph (Path graph using Ring(circular=False))
283
+ G_ig = ig.Graph.Ring(N_NODES, directed=False, circular=False)
284
+ print(f"igraph graph generated: {G_ig.vcount()} vertices, {G_ig.ecount()} edges")
285
+
286
+ # Calculate layout
287
+ print("Calculating Keçeci Layout...")
288
+ # Call the layout function from the imported module
289
+ pos_ig = kl.kececi_layout_v4(G_ig, **LAYOUT_PARAMS)
290
+ # print("igraph positions (dict):", pos_ig) # Debug print if needed
291
+
292
+ # Convert positions dict to list ordered by vertex index for ig.plot
293
+ layout_list_ig = []
294
+ plot_possible = True
295
+ if pos_ig: # Check if dictionary is not empty
296
+ try:
297
+ # Generate list: [pos_ig[0], pos_ig[1], ..., pos_ig[N-1]]
298
+ layout_list_ig = [pos_ig[i] for i in range(G_ig.vcount())]
299
+ # print("igraph layout (list):", layout_list_ig) # Debug print if needed
300
+ except KeyError as e:
301
+ print(f"ERROR: Key {e} not found while creating position list for igraph.")
302
+ print("The layout function might not have returned positions for all vertices.")
303
+ plot_possible = False # Cannot plot if list is incomplete
304
+ else:
305
+ print("ERROR: Keçeci Layout returned empty positions for igraph.")
306
+ plot_possible = False
307
+
308
+ # Plot using igraph's plotting capabilities
309
+ print("Plotting graph using igraph.plot...")
310
+ fig, ax = plt.subplots(figsize=(6, 8)) # Generate matplotlib figure and axes
311
+
312
+ if plot_possible:
313
+ ig.plot(G_ig,
314
+ target=ax, # Draw on the matplotlib axes
315
+ layout=layout_list_ig, # Use the ORDERED LIST of coordinates
316
+ vertex_label=[str(i) for i in range(G_ig.vcount())], # Labels 0, 1,...
317
+ vertex_color='lightgreen',
318
+ vertex_size=30, # Note: igraph vertex_size scale differs
319
+ edge_color='gray')
320
+ else:
321
+ ax.text(0.5, 0.5, "Plotting failed:\nMissing or incomplete layout positions.",
322
+ ha='center', va='center', color='red', fontsize=12) # Error message on plot
323
+
324
+ ax.set_title(f"igraph ({N_NODES} Nodes) with Keçeci Layout") # Plot title
325
+ ax.set_aspect('equal', adjustable='box') # Ensure equal aspect ratio
326
+ # ax.grid(False) # Ensure grid is off
327
+ plt.show() # Display the plot
328
+
329
+ except ImportError:
330
+ print("python-igraph is not installed. Skipping this example.")
331
+ except Exception as e:
332
+ print(f"An error occurred in the igraph example: {e}")
333
+ import traceback
334
+ traceback.print_exc()
335
+
336
+ print("\n--- igraph Example Finished ---")
337
+ ```
338
+
339
+ ![iGraph Example](https://github.com/WhiteSymmetry/kececilayout/blob/main/examples/ig-1.png?raw=true)
340
+
341
+ ---
342
+
343
+ ### Example with RustworkX
344
+
345
+ ```python
346
+ import matplotlib.pyplot as plt
347
+ from matplotlib.collections import LineCollection # Efficient edge drawing
348
+ import math
349
+ import rustworkx as rx
350
+ import kececilayout as kl
351
+ import random
352
+
353
+
354
+ try:
355
+ import kececilayout as kl
356
+ except ImportError:
357
+ print("Error: 'kececi_layout.py' not found or could not be imported.")
358
+ print("Please ensure the file containing kececi_layout_v4 is accessible.")
359
+ exit()
360
+
361
+ # --- General Layout Parameters ---
362
+ LAYOUT_PARAMS = {
363
+ 'primary_spacing': 1.0,
364
+ 'secondary_spacing': 0.6, # Make the zigzag noticeable
365
+ 'primary_direction': 'top-down',
366
+ 'secondary_start': 'right'
367
+ }
368
+ N_NODES = 10 # Number of nodes in the example graph
369
+
370
+ # === Rustworkx Example ===
371
+ try:
372
+ import rustworkx as rx
373
+ print("\n--- Rustworkx Example ---")
374
+
375
+ # Generate graph (Path graph)
376
+ G_rx = rx.generators.path_graph(N_NODES)
377
+ print(f"Rustworkx graph generated: {G_rx.num_nodes()} nodes, {G_rx.num_edges()} edges")
378
+
379
+ # Calculate layout
380
+ print("Calculating Keçeci Layout...")
381
+ # Call the layout function from the imported module
382
+ pos_rx = kl.kececi_layout_v4(G_rx, **LAYOUT_PARAMS)
383
+ # print("Rustworkx positions:", pos_rx) # Debug print if needed
384
+
385
+ # Plot using Matplotlib directly (Rustworkx doesn't have a built-in draw)
386
+ print("Plotting graph using Matplotlib...")
387
+ plt.figure(figsize=(6, 8))
388
+ ax = plt.gca() # Get current axes
389
+
390
+ node_indices_rx = G_rx.node_indices() # Get node indices [0, 1, ...]
391
+
392
+ # Check if all nodes have positions
393
+ if not all(idx in pos_rx for idx in node_indices_rx):
394
+ print("ERROR: Rustworkx positions dictionary does not cover all nodes!")
395
+ # Decide how to handle: exit, plot partial, etc.
396
+ else:
397
+ # Draw nodes
398
+ x_coords_rx = [pos_rx[i][0] for i in node_indices_rx]
399
+ y_coords_rx = [pos_rx[i][1] for i in node_indices_rx]
400
+ ax.scatter(x_coords_rx, y_coords_rx, s=700, c='#88CCEE', zorder=2, label='Nodes') # Skyblue color
401
+
402
+ # Draw labels
403
+ for i in node_indices_rx:
404
+ ax.text(pos_rx[i][0], pos_rx[i][1], str(i), ha='center', va='center', fontsize=10, zorder=3)
405
+
406
+ # Draw edges using LineCollection for efficiency
407
+ edge_lines = []
408
+ for u, v in G_rx.edge_list(): # Get list of edges (node index pairs)
409
+ if u in pos_rx and v in pos_rx:
410
+ # Segment format: [(x1, y1), (x2, y2)]
411
+ edge_lines.append([pos_rx[u], pos_rx[v]])
412
+ else:
413
+ print(f"Warning: Position not found for edge ({u},{v}) in Rustworkx graph.")
414
+
415
+ if edge_lines:
416
+ lc = LineCollection(edge_lines, colors='gray', linewidths=1.0, zorder=1, label='Edges')
417
+ ax.add_collection(lc) # Add edges to the plot axes
418
+
419
+ plt.title(f"Rustworkx ({N_NODES} Nodes) with Keçeci Layout (Matplotlib)") # Plot title
420
+ plt.xlabel("X Coordinate") # X-axis label
421
+ plt.ylabel("Y Coordinate") # Y-axis label
422
+ plt.axis('equal') # Ensure equal aspect ratio
423
+ # plt.grid(False) # Ensure grid is off
424
+ plt.show() # Display the plot
425
+
426
+ except ImportError:
427
+ print("Rustworkx is not installed. Skipping this example.")
428
+ except Exception as e:
429
+ print(f"An error occurred in the Rustworkx example: {e}")
430
+ import traceback
431
+ traceback.print_exc()
432
+
433
+ print("\n--- Rustworkx Example Finished ---")
434
+ ```
435
+
436
+ ![Rustworkx Exampl](https://github.com/WhiteSymmetry/kececilayout/blob/main/examples/rx-1.png?raw=true)
437
+
438
+ ---
439
+
440
+ ### Example with Networkit
441
+
442
+ ```python
443
+ import matplotlib.pyplot as plt
444
+ from matplotlib.collections import LineCollection # Efficient edge drawing
445
+ import math
446
+ import networkit as nk
447
+ import kececilayout as kl
448
+ import random
449
+
450
+ try:
451
+ import kececilayout as kl
452
+ except ImportError:
453
+ print("Error: 'kececi_layout.py' not found or could not be imported.")
454
+ print("Please ensure the file containing kececi_layout_v4 is accessible.")
455
+ exit()
456
+
457
+ # --- General Layout Parameters ---
458
+ LAYOUT_PARAMS = {
459
+ 'primary_spacing': 1.0,
460
+ 'secondary_spacing': 0.6, # Make the zigzag noticeable
461
+ 'primary_direction': 'top-down',
462
+ 'secondary_start': 'right'
463
+ }
464
+ N_NODES = 10 # Number of nodes in the example graph
465
+
466
+ # === Networkit Example ===
467
+ try:
468
+ import networkit as nk
469
+ print("\n--- Networkit Example ---")
470
+
471
+ # Generate graph (Path graph, manually)
472
+ G_nk = nk.graph.Graph(N_NODES, weighted=False, directed=False) # Generate empty graph container
473
+ print("Empty Networkit graph generated.")
474
+ # Add nodes first (Networkit often requires this)
475
+ for i in range(N_NODES):
476
+ if not G_nk.hasNode(i): # Check if node already exists (good practice)
477
+ G_nk.addNode()
478
+ print(f"{G_nk.numberOfNodes()} nodes added.")
479
+ # Add edges
480
+ for i in range(N_NODES - 1):
481
+ G_nk.addEdge(i, i+1) # Add edges 0-1, 1-2, ...
482
+ print(f"Networkit graph constructed: {G_nk.numberOfNodes()} nodes, {G_nk.numberOfEdges()} edges")
483
+
484
+ # Calculate layout
485
+ print("Calculating Keçeci Layout...")
486
+ # Call the layout function from the imported module
487
+ pos_nk = kl.kececi_layout_v4(G_nk, **LAYOUT_PARAMS)
488
+ # print("Networkit positions:", pos_nk) # Debug print if needed
489
+
490
+ # Plot using Matplotlib directly (Networkit doesn't have a simple built-in draw)
491
+ print("Plotting graph using Matplotlib...")
492
+ plt.figure(figsize=(6, 8))
493
+ ax = plt.gca() # Get current axes
494
+
495
+ node_indices_nk = sorted(list(G_nk.iterNodes())) # Get node indices [0, 1, ...]
496
+
497
+ # Check if all nodes have positions
498
+ if not all(idx in pos_nk for idx in node_indices_nk):
499
+ print("ERROR: Networkit positions dictionary does not cover all nodes!")
500
+ else:
501
+ # Draw nodes
502
+ x_coords_nk = [pos_nk[i][0] for i in node_indices_nk]
503
+ y_coords_nk = [pos_nk[i][1] for i in node_indices_nk]
504
+ ax.scatter(x_coords_nk, y_coords_nk, s=700, c='coral', zorder=2, label='Nodes')
505
+
506
+ # Draw labels
507
+ for i in node_indices_nk:
508
+ ax.text(pos_nk[i][0], pos_nk[i][1], str(i), ha='center', va='center', fontsize=10, zorder=3)
509
+
510
+ # Draw edges using LineCollection
511
+ edge_lines_nk = []
512
+ for u, v in G_nk.iterEdges(): # Iterate through edges
513
+ if u in pos_nk and v in pos_nk:
514
+ edge_lines_nk.append([pos_nk[u], pos_nk[v]])
515
+ else:
516
+ print(f"Warning: Position not found for edge ({u},{v}) in Networkit graph.")
517
+
518
+ if edge_lines_nk:
519
+ lc_nk = LineCollection(edge_lines_nk, colors='gray', linewidths=1.0, zorder=1, label='Edges')
520
+ ax.add_collection(lc_nk)
521
+
522
+ plt.title(f"Networkit ({N_NODES} Nodes) with Keçeci Layout (Matplotlib)") # Plot title
523
+ plt.xlabel("X Coordinate") # X-axis label
524
+ plt.ylabel("Y Coordinate") # Y-axis label
525
+ plt.axis('equal') # Ensure equal aspect ratio
526
+ # plt.grid(False) # Ensure grid is off
527
+ plt.show() # Display the plot
528
+
529
+ except ImportError:
530
+ print("Networkit is not installed. Skipping this example.")
531
+ except Exception as e:
532
+ print(f"An error occurred in the Networkit example: {e}")
533
+ import traceback
534
+ traceback.print_exc()
535
+
536
+ print("\n--- Networkit Example Finished ---")
537
+ ```
538
+
539
+ ![Networkit Example](https://github.com/WhiteSymmetry/kececilayout/blob/main/examples/nk-1.png?raw=true)
540
+
541
+ ---
542
+
543
+ ### Example with Graphillion
544
+
545
+ ```python
546
+ import matplotlib.pyplot as plt
547
+ from matplotlib.collections import LineCollection # Efficient edge drawing
548
+ import math
549
+ import itertools # Graphillion might implicitly need itertools if find_max_node_id uses it internally
550
+ import graphillion as gg
551
+ import kececilayout as kl
552
+ import random
553
+
554
+
555
+ try:
556
+ import kececilayout as kl
557
+ except ImportError:
558
+ print("Error: 'kececi_layout.py' not found or could not be imported.")
559
+ print("Please ensure the file containing kececi_layout_v4 is accessible.")
560
+ exit()
561
+
562
+ # --- General Layout Parameters ---
563
+ LAYOUT_PARAMS = {
564
+ 'primary_spacing': 1.0,
565
+ 'secondary_spacing': 0.6, # Make the zigzag noticeable
566
+ 'primary_direction': 'top-down',
567
+ 'secondary_start': 'right'
568
+ }
569
+ N_NODES = 10 # Number of nodes in the example graph (will be 1 to N_NODES)
570
+
571
+ # === Graphillion Example ===
572
+ try:
573
+ import graphillion as gg
574
+ print("\n--- Graphillion Example ---")
575
+
576
+ # Define the universe of possible edges (Path graph, 1-based indexing common)
577
+ universe = []
578
+ # Edges (1,2), (2,3), ..., (N_NODES-1, N_NODES)
579
+ for i in range(1, N_NODES):
580
+ universe.append((i, i + 1))
581
+ gg.GraphSet.set_universe(universe)
582
+ max_node_gg = N_NODES # We know the max node ID for this simple case
583
+ print(f"Graphillion universe defined: {len(universe)} edges, max node ID {max_node_gg}")
584
+
585
+ # Generate a GraphSet object (can be empty, layout function uses the universe)
586
+ # The layout function provided seems to derive nodes from the universe edges.
587
+ gs = gg.GraphSet()
588
+
589
+ # Calculate layout
590
+ print("Calculating Keçeci Layout...")
591
+ # Call the layout function; it should handle the Graphillion GraphSet object
592
+ # and likely use 1-based indexing based on the universe.
593
+ pos_gg = kl.kececi_layout_v4(gs, **LAYOUT_PARAMS)
594
+ # print("Graphillion positions:", pos_gg) # Debug print if needed
595
+
596
+ # Plot using Matplotlib directly (Graphillion has no plotting)
597
+ print("Plotting graph using Matplotlib...")
598
+ plt.figure(figsize=(6, 8))
599
+ ax = plt.gca() # Get current axes
600
+
601
+ # Node indices are expected to be 1, 2, ... N_NODES from the universe
602
+ node_indices_gg = sorted(pos_gg.keys())
603
+
604
+ # Check if all expected nodes (1 to N_NODES) have positions
605
+ expected_nodes = set(range(1, N_NODES + 1))
606
+ if not expected_nodes.issubset(set(node_indices_gg)):
607
+ print(f"ERROR: Graphillion positions missing expected nodes. Found: {node_indices_gg}, Expected: {list(expected_nodes)}")
608
+ else:
609
+ # Draw nodes
610
+ x_coords_gg = [pos_gg[i][0] for i in node_indices_gg]
611
+ y_coords_gg = [pos_gg[i][1] for i in node_indices_gg]
612
+ ax.scatter(x_coords_gg, y_coords_gg, s=700, c='gold', zorder=2, label='Nodes')
613
+
614
+ # Draw labels (using the 1-based indices)
615
+ for i in node_indices_gg:
616
+ ax.text(pos_gg[i][0], pos_gg[i][1], str(i), ha='center', va='center', fontsize=10, zorder=3)
617
+
618
+ # Draw edges using LineCollection (from the defined universe)
619
+ edge_lines_gg = []
620
+ for u, v in universe: # Use the universe edges
621
+ if u in pos_gg and v in pos_gg:
622
+ edge_lines_gg.append([pos_gg[u], pos_gg[v]])
623
+ else:
624
+ print(f"Warning: Position not found for universe edge ({u},{v}) in Graphillion.")
625
+
626
+ if edge_lines_gg:
627
+ lc_gg = LineCollection(edge_lines_gg, colors='gray', linewidths=1.0, zorder=1, label='Edges')
628
+ ax.add_collection(lc_gg)
629
+
630
+ plt.title(f"Graphillion ({N_NODES} Nodes) with Keçeci Layout (Matplotlib)") # Plot title
631
+ plt.xlabel("X Coordinate") # X-axis label
632
+ plt.ylabel("Y Coordinate") # Y-axis label
633
+ plt.axis('equal') # Ensure equal aspect ratio
634
+ # plt.grid(False) # Ensure grid is off
635
+ plt.show() # Display the plot
636
+
637
+ except ImportError:
638
+ print("Graphillion is not installed. Skipping this example.")
639
+ except Exception as e:
640
+ print(f"An error occurred in the Graphillion example: {e}")
641
+ import traceback
642
+ traceback.print_exc()
643
+
644
+ print("\n--- Graphillion Example Finished ---")
645
+ ```
646
+
647
+ ![Graphillion Example](https://github.com/WhiteSymmetry/kececilayout/blob/main/examples/gg-1.png?raw=true)
648
+
649
+ ---
650
+
651
+ ## Supported Backends / Desteklenen Kütüphaneler
652
+
653
+ The layout functions are designed to work with graph objects from the following libraries:
654
+
655
+ * **NetworkX:** (`networkx.Graph`, `networkx.DiGraph`, etc.)
656
+ * **igraph:** (`igraph.Graph`)
657
+ * **Rustworkx:** (Requires appropriate conversion or adapter function)
658
+ * **Networkit:** (Requires appropriate conversion or adapter function)
659
+ * **Graphillion:** (Requires appropriate conversion or adapter function)
660
+
661
+ *Note: Direct support might vary. Check specific function documentation for compatibility details.*
662
+
663
+ ---
664
+
665
+ ## License / Lisans
666
+
667
+ This project is licensed under the MIT License. See the `LICENSE` file for details.
668
+
669
+ ```
670
+
671
+ **Ek Notlar:**
672
+
673
+ * **Rozetler (Badges):** Başlangıçta PyPI ve Lisans rozetleri ekledim (yorum satırı içinde). Eğer projeniz PyPI'da yayınlandıysa veya bir CI/CD süreci varsa, ilgili rozetleri eklemek iyi bir pratiktir.
674
+ * **LICENSE Dosyası:** `LICENSE` bölümünde bir `LICENSE` dosyasına referans verdim. Projenizin kök dizininde MIT lisans metnini içeren bir `LICENSE` dosyası oluşturduğunuzdan emin olun.
675
+ * **İçe Aktarma Yolları:** Örneklerde `import kececilayout as kl` veya `from kececilayout import kececi_layout_v4_igraph` gibi varsayımsal içe aktarma yolları kullandım. Kendi paket yapınıza göre bunları ayarlamanız gerekebilir.
676
+ * **Fonksiyon Adları:** Örneklerde `kececi_layout_v4` ve `kececi_layout_v4_igraph` gibi fonksiyon adlarını kullandım. Gerçek fonksiyon adlarınız farklıysa bunları güncelleyin.
677
+ * **Görselleştirme:** Örneklere `matplotlib.pyplot` kullanarak temel görselleştirme adımlarını ekledim, bu da kullanıcıların sonucu nasıl görebileceğini gösterir. Eksen oranlarını eşitlemek (`axis('equal')` veya `set_aspect('equal')`) layout'un doğru görünmesi için önemlidir.
678
+ ```
679
+
680
+ ## Citation
681
+
682
+ If this library was useful to you in your research, please cite us. Following the [GitHub citation standards](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-citation-files), here is the recommended citation.
683
+
684
+ ### BibTeX
685
+
686
+ ```bibtex
687
+ @misc{kececi_2025_15313947,
688
+ author = {Keçeci, Mehmet},
689
+ title = {kececilayout},
690
+ month = may,
691
+ year = 2025,
692
+ publisher = {PyPI, Anaconda, Github, Zenodo},
693
+ version = {0.2.0},
694
+ doi = {10.5281/zenodo.15313947},
695
+ url = {https://doi.org/10.5281/zenodo.15313947},
696
+ }
697
+
698
+ @misc{kececi_2025_15314329,
699
+ author = {Keçeci, Mehmet},
700
+ title = {Keçeci Layout},
701
+ month = may,
702
+ year = 2025,
703
+ publisher = {Zenodo},
704
+ version = {1.0.0},
705
+ doi = {10.5281/zenodo.15314329},
706
+ url = {https://doi.org/10.5281/zenodo.15314329},
707
+ }
708
+ ```
709
+ ### APA
710
+
711
+ ```
712
+ Keçeci, M. (2025). kececilayout (0.2.0). PyPI, Anaconda, GitHub, Zenodo. https://doi.org/10.5281/zenodo.15313947
713
+
714
+ Keçeci, M. (2025). Keçeci Layout. https://doi.org/10.5281/zenodo.15314329
715
+ ```
716
+
717
+ ### Chicago
718
+
719
+ ```
720
+ Keçeci, Mehmet. “Kececilayout”. PyPI, Anaconda, GitHub, Zenodo, 01 May 2025. https://doi.org/10.5281/zenodo.15313947.
721
+
722
+ Keçeci, Mehmet. “Keçeci Layout”, 01 May 2025. https://doi.org/10.5281/zenodo.15314329.
723
+ ```