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.
- kececilayout/__init__.py +37 -0
- kececilayout/_version.py +9 -0
- kececilayout/kececi_layout.py +1021 -0
- kececilayout-0.2.1.dist-info/METADATA +723 -0
- kececilayout-0.2.1.dist-info/RECORD +8 -0
- kececilayout-0.2.1.dist-info/WHEEL +5 -0
- kececilayout-0.2.1.dist-info/licenses/LICENSE +21 -0
- kececilayout-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -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
|
+
[](https://badge.fury.io/py/kececilayout)
|
|
42
|
+
[](https://opensource.org/licenses/MIT)
|
|
43
|
+
|
|
44
|
+
[](https://doi.org/10.5281/zenodo.15313947)
|
|
45
|
+
[](https://doi.org/10.5281/zenodo.15314329)
|
|
46
|
+
|
|
47
|
+
[](https://anaconda.org/bilgi/kececilayout)
|
|
48
|
+
[](https://anaconda.org/bilgi/kececilayout)
|
|
49
|
+
[](https://anaconda.org/bilgi/kececilayout)
|
|
50
|
+
[](https://anaconda.org/bilgi/kececilayout)
|
|
51
|
+
|
|
52
|
+
[](https://opensource.org/)
|
|
53
|
+
[](https://kececilayout.readthedocs.io/en/main/?badge=main)
|
|
54
|
+
|
|
55
|
+
[](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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+
```
|