kececilayout 0.4.4__tar.gz → 0.4.5__tar.gz
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-0.4.4/kececilayout.egg-info → kececilayout-0.4.5}/PKG-INFO +735 -126
- {kececilayout-0.4.4 → kececilayout-0.4.5}/README.md +734 -125
- {kececilayout-0.4.4 → kececilayout-0.4.5}/kececilayout/__init__.py +3 -1
- {kececilayout-0.4.4 → kececilayout-0.4.5}/kececilayout/_version.py +1 -6
- {kececilayout-0.4.4 → kececilayout-0.4.5/kececilayout.egg-info}/PKG-INFO +735 -126
- {kececilayout-0.4.4 → kececilayout-0.4.5}/pyproject.toml +2 -1
- {kececilayout-0.4.4 → kececilayout-0.4.5}/LICENSE +0 -0
- {kececilayout-0.4.4 → kececilayout-0.4.5}/MANIFEST.in +0 -0
- {kececilayout-0.4.4 → kececilayout-0.4.5}/kececilayout/kececi_layout.py +0 -0
- {kececilayout-0.4.4 → kececilayout-0.4.5}/kececilayout.egg-info/SOURCES.txt +0 -0
- {kececilayout-0.4.4 → kececilayout-0.4.5}/kececilayout.egg-info/dependency_links.txt +0 -0
- {kececilayout-0.4.4 → kececilayout-0.4.5}/kececilayout.egg-info/requires.txt +0 -0
- {kececilayout-0.4.4 → kececilayout-0.4.5}/kececilayout.egg-info/top_level.txt +0 -0
- {kececilayout-0.4.4 → kececilayout-0.4.5}/setup.cfg +0 -0
- {kececilayout-0.4.4 → kececilayout-0.4.5}/setup.py +0 -0
- {kececilayout-0.4.4 → kececilayout-0.4.5}/tests/test_sample.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kececilayout
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.5
|
|
4
4
|
Summary: Çeşitli graf kütüphaneleri için sıralı-zigzag yerleşimleri sağlayan bir Python paketi.
|
|
5
5
|
Home-page: https://github.com/WhiteSymmetry/kececilayout
|
|
6
6
|
Author: Mehmet Keçeci
|
|
@@ -140,11 +140,11 @@ import matplotlib.pyplot as plt
|
|
|
140
140
|
import kececilayout as kl
|
|
141
141
|
|
|
142
142
|
G = nx.path_graph(10)
|
|
143
|
-
pos = kl.
|
|
143
|
+
pos = kl.kececi_layout(
|
|
144
144
|
G,
|
|
145
145
|
primary_spacing=1.0,
|
|
146
146
|
secondary_spacing=0.5,
|
|
147
|
-
primary_direction='
|
|
147
|
+
primary_direction='top_down',
|
|
148
148
|
secondary_start='right'
|
|
149
149
|
)
|
|
150
150
|
|
|
@@ -162,14 +162,51 @@ plt.show()
|
|
|
162
162
|
```python
|
|
163
163
|
import igraph as ig
|
|
164
164
|
import matplotlib.pyplot as plt
|
|
165
|
-
|
|
165
|
+
import kececilayout as kl
|
|
166
166
|
|
|
167
167
|
G = ig.Graph.Ring(10, circular=False)
|
|
168
|
-
|
|
168
|
+
|
|
169
|
+
# Get the positions using kececi_layout with proper conversion
|
|
170
|
+
try:
|
|
171
|
+
# Method 1: Direct conversion
|
|
172
|
+
pos = kl.kececi_layout(G, primary_direction='left-to-right', secondary_start='up')
|
|
173
|
+
|
|
174
|
+
# If pos is not iterable, it might be returning an error code
|
|
175
|
+
if not hasattr(pos, '__iter__'):
|
|
176
|
+
raise TypeError("kececi_layout returned non-iterable object")
|
|
177
|
+
|
|
178
|
+
except (TypeError, AttributeError) as e:
|
|
179
|
+
print(f"Direct approach failed: {e}")
|
|
180
|
+
print("Using NetworkX conversion method...")
|
|
181
|
+
|
|
182
|
+
# Method 2: Convert to NetworkX first
|
|
183
|
+
import networkx as nx
|
|
184
|
+
nx_graph = nx.Graph()
|
|
185
|
+
nx_graph.add_nodes_from(range(G.vcount()))
|
|
186
|
+
nx_graph.add_edges_from(G.get_edgelist())
|
|
187
|
+
|
|
188
|
+
pos = kl.kececi_layout(nx_graph, primary_direction='left-to-right', secondary_start='up')
|
|
189
|
+
|
|
190
|
+
# Ensure we have proper coordinates
|
|
191
|
+
if isinstance(pos, dict):
|
|
192
|
+
# Convert dictionary to list
|
|
193
|
+
pos_list = [pos[i] for i in range(G.vcount())]
|
|
194
|
+
else:
|
|
195
|
+
# Assume it's already a list of coordinates
|
|
196
|
+
pos_list = pos
|
|
197
|
+
|
|
169
198
|
layout = ig.Layout(pos_list)
|
|
170
199
|
|
|
171
200
|
fig, ax = plt.subplots(figsize=(8, 6))
|
|
172
|
-
ig.plot(G,
|
|
201
|
+
ig.plot(G,
|
|
202
|
+
target=ax,
|
|
203
|
+
layout=layout,
|
|
204
|
+
vertex_label=[f"N{i}" for i in range(G.vcount())],
|
|
205
|
+
vertex_size=30,
|
|
206
|
+
edge_width=2,
|
|
207
|
+
margin=50)
|
|
208
|
+
|
|
209
|
+
ax.set_title("Keçeci Layout with igraph")
|
|
173
210
|
ax.set_aspect('equal')
|
|
174
211
|
plt.show()
|
|
175
212
|
```
|
|
@@ -179,13 +216,94 @@ plt.show()
|
|
|
179
216
|
#### Example with RustworkX
|
|
180
217
|
|
|
181
218
|
```python
|
|
219
|
+
import matplotlib.pyplot as plt
|
|
220
|
+
from matplotlib.collections import LineCollection # Efficient edge drawing
|
|
221
|
+
import math
|
|
182
222
|
import rustworkx as rx
|
|
183
223
|
import kececilayout as kl
|
|
184
|
-
import
|
|
224
|
+
import random
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
import kececilayout as kl
|
|
229
|
+
except ImportError:
|
|
230
|
+
print("Error: 'kececi_layout.py' not found or could not be imported.")
|
|
231
|
+
print("Please ensure the file containing kececi_layout_v4 is accessible.")
|
|
232
|
+
exit()
|
|
233
|
+
|
|
234
|
+
# --- General Layout Parameters ---
|
|
235
|
+
LAYOUT_PARAMS = {
|
|
236
|
+
'primary_spacing': 1.0,
|
|
237
|
+
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
238
|
+
'primary_direction': 'top_down',
|
|
239
|
+
'secondary_start': 'right'
|
|
240
|
+
}
|
|
241
|
+
N_NODES = 10 # Number of nodes in the example graph
|
|
242
|
+
|
|
243
|
+
# === Rustworkx Example ===
|
|
244
|
+
try:
|
|
245
|
+
import rustworkx as rx
|
|
246
|
+
print("\n--- Rustworkx Example ---")
|
|
247
|
+
|
|
248
|
+
# Generate graph (Path graph)
|
|
249
|
+
G_rx = rx.generators.path_graph(N_NODES)
|
|
250
|
+
print(f"Rustworkx graph generated: {G_rx.num_nodes()} nodes, {G_rx.num_edges()} edges")
|
|
251
|
+
|
|
252
|
+
# Calculate layout
|
|
253
|
+
print("Calculating Keçeci Layout...")
|
|
254
|
+
# Call the layout function from the imported module
|
|
255
|
+
pos_rx = kl.kececi_layout_v4(G_rx, **LAYOUT_PARAMS)
|
|
256
|
+
# print("Rustworkx positions:", pos_rx) # Debug print if needed
|
|
257
|
+
|
|
258
|
+
# Plot using Matplotlib directly (Rustworkx doesn't have a built-in draw)
|
|
259
|
+
print("Plotting graph using Matplotlib...")
|
|
260
|
+
plt.figure(figsize=(6, 8))
|
|
261
|
+
ax = plt.gca() # Get current axes
|
|
262
|
+
|
|
263
|
+
node_indices_rx = G_rx.node_indices() # Get node indices [0, 1, ...]
|
|
264
|
+
|
|
265
|
+
# Check if all nodes have positions
|
|
266
|
+
if not all(idx in pos_rx for idx in node_indices_rx):
|
|
267
|
+
print("ERROR: Rustworkx positions dictionary does not cover all nodes!")
|
|
268
|
+
# Decide how to handle: exit, plot partial, etc.
|
|
269
|
+
else:
|
|
270
|
+
# Draw nodes
|
|
271
|
+
x_coords_rx = [pos_rx[i][0] for i in node_indices_rx]
|
|
272
|
+
y_coords_rx = [pos_rx[i][1] for i in node_indices_rx]
|
|
273
|
+
ax.scatter(x_coords_rx, y_coords_rx, s=700, c='#88CCEE', zorder=2, label='Nodes') # Skyblue color
|
|
274
|
+
|
|
275
|
+
# Draw labels
|
|
276
|
+
for i in node_indices_rx:
|
|
277
|
+
ax.text(pos_rx[i][0], pos_rx[i][1], str(i), ha='center', va='center', fontsize=10, zorder=3)
|
|
185
278
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
#
|
|
279
|
+
# Draw edges using LineCollection for efficiency
|
|
280
|
+
edge_lines = []
|
|
281
|
+
for u, v in G_rx.edge_list(): # Get list of edges (node index pairs)
|
|
282
|
+
if u in pos_rx and v in pos_rx:
|
|
283
|
+
# Segment format: [(x1, y1), (x2, y2)]
|
|
284
|
+
edge_lines.append([pos_rx[u], pos_rx[v]])
|
|
285
|
+
else:
|
|
286
|
+
print(f"Warning: Position not found for edge ({u},{v}) in Rustworkx graph.")
|
|
287
|
+
|
|
288
|
+
if edge_lines:
|
|
289
|
+
lc = LineCollection(edge_lines, colors='gray', linewidths=1.0, zorder=1, label='Edges')
|
|
290
|
+
ax.add_collection(lc) # Add edges to the plot axes
|
|
291
|
+
|
|
292
|
+
plt.title(f"Rustworkx ({N_NODES} Nodes) with Keçeci Layout (Matplotlib)") # Plot title
|
|
293
|
+
plt.xlabel("X Coordinate") # X-axis label
|
|
294
|
+
plt.ylabel("Y Coordinate") # Y-axis label
|
|
295
|
+
plt.axis('equal') # Ensure equal aspect ratio
|
|
296
|
+
plt.grid(False) # Ensure grid is off
|
|
297
|
+
plt.show() # Display the plot
|
|
298
|
+
|
|
299
|
+
except ImportError:
|
|
300
|
+
print("Rustworkx is not installed. Skipping this example.")
|
|
301
|
+
except Exception as e:
|
|
302
|
+
print(f"An error occurred in the Rustworkx example: {e}")
|
|
303
|
+
import traceback
|
|
304
|
+
traceback.print_exc()
|
|
305
|
+
|
|
306
|
+
print("\n--- Rustworkx Example Finished ---")
|
|
189
307
|
```
|
|
190
308
|
|
|
191
309
|

|
|
@@ -193,15 +311,100 @@ pos = kl.kececi_layout_v4(G, primary_direction='bottom-up')
|
|
|
193
311
|
#### Example with Networkit
|
|
194
312
|
|
|
195
313
|
```python
|
|
314
|
+
import matplotlib.pyplot as plt
|
|
315
|
+
from matplotlib.collections import LineCollection # Efficient edge drawing
|
|
316
|
+
import math
|
|
196
317
|
import networkit as nk
|
|
197
318
|
import kececilayout as kl
|
|
198
|
-
import
|
|
319
|
+
import random
|
|
320
|
+
|
|
321
|
+
try:
|
|
322
|
+
import kececilayout as kl
|
|
323
|
+
except ImportError:
|
|
324
|
+
print("Error: 'kececi_layout.py' not found or could not be imported.")
|
|
325
|
+
print("Please ensure the file containing kececi_layout_v4 is accessible.")
|
|
326
|
+
exit()
|
|
327
|
+
|
|
328
|
+
# --- General Layout Parameters ---
|
|
329
|
+
LAYOUT_PARAMS = {
|
|
330
|
+
'primary_spacing': 1.0,
|
|
331
|
+
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
332
|
+
'primary_direction': 'top_down',
|
|
333
|
+
'secondary_start': 'right'
|
|
334
|
+
}
|
|
335
|
+
N_NODES = 10 # Number of nodes in the example graph
|
|
336
|
+
|
|
337
|
+
# === Networkit Example ===
|
|
338
|
+
try:
|
|
339
|
+
import networkit as nk
|
|
340
|
+
print("\n--- Networkit Example ---")
|
|
341
|
+
|
|
342
|
+
# Generate graph (Path graph, manually)
|
|
343
|
+
G_nk = nk.graph.Graph(N_NODES, weighted=False, directed=False) # Generate empty graph container
|
|
344
|
+
print("Empty Networkit graph generated.")
|
|
345
|
+
# Add nodes first (Networkit often requires this)
|
|
346
|
+
for i in range(N_NODES):
|
|
347
|
+
if not G_nk.hasNode(i): # Check if node already exists (good practice)
|
|
348
|
+
G_nk.addNode()
|
|
349
|
+
print(f"{G_nk.numberOfNodes()} nodes added.")
|
|
350
|
+
# Add edges
|
|
351
|
+
for i in range(N_NODES - 1):
|
|
352
|
+
G_nk.addEdge(i, i+1) # Add edges 0-1, 1-2, ...
|
|
353
|
+
print(f"Networkit graph constructed: {G_nk.numberOfNodes()} nodes, {G_nk.numberOfEdges()} edges")
|
|
354
|
+
|
|
355
|
+
# Calculate layout
|
|
356
|
+
print("Calculating Keçeci Layout...")
|
|
357
|
+
# Call the layout function from the imported module
|
|
358
|
+
pos_nk = kl.kececi_layout_v4(G_nk, **LAYOUT_PARAMS)
|
|
359
|
+
# print("Networkit positions:", pos_nk) # Debug print if needed
|
|
360
|
+
|
|
361
|
+
# Plot using Matplotlib directly (Networkit doesn't have a simple built-in draw)
|
|
362
|
+
print("Plotting graph using Matplotlib...")
|
|
363
|
+
plt.figure(figsize=(6, 8))
|
|
364
|
+
ax = plt.gca() # Get current axes
|
|
365
|
+
|
|
366
|
+
node_indices_nk = sorted(list(G_nk.iterNodes())) # Get node indices [0, 1, ...]
|
|
199
367
|
|
|
200
|
-
|
|
201
|
-
for
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
# Draw
|
|
368
|
+
# Check if all nodes have positions
|
|
369
|
+
if not all(idx in pos_nk for idx in node_indices_nk):
|
|
370
|
+
print("ERROR: Networkit positions dictionary does not cover all nodes!")
|
|
371
|
+
else:
|
|
372
|
+
# Draw nodes
|
|
373
|
+
x_coords_nk = [pos_nk[i][0] for i in node_indices_nk]
|
|
374
|
+
y_coords_nk = [pos_nk[i][1] for i in node_indices_nk]
|
|
375
|
+
ax.scatter(x_coords_nk, y_coords_nk, s=700, c='coral', zorder=2, label='Nodes')
|
|
376
|
+
|
|
377
|
+
# Draw labels
|
|
378
|
+
for i in node_indices_nk:
|
|
379
|
+
ax.text(pos_nk[i][0], pos_nk[i][1], str(i), ha='center', va='center', fontsize=10, zorder=3)
|
|
380
|
+
|
|
381
|
+
# Draw edges using LineCollection
|
|
382
|
+
edge_lines_nk = []
|
|
383
|
+
for u, v in G_nk.iterEdges(): # Iterate through edges
|
|
384
|
+
if u in pos_nk and v in pos_nk:
|
|
385
|
+
edge_lines_nk.append([pos_nk[u], pos_nk[v]])
|
|
386
|
+
else:
|
|
387
|
+
print(f"Warning: Position not found for edge ({u},{v}) in Networkit graph.")
|
|
388
|
+
|
|
389
|
+
if edge_lines_nk:
|
|
390
|
+
lc_nk = LineCollection(edge_lines_nk, colors='gray', linewidths=1.0, zorder=1, label='Edges')
|
|
391
|
+
ax.add_collection(lc_nk)
|
|
392
|
+
|
|
393
|
+
plt.title(f"Networkit ({N_NODES} Nodes) with Keçeci Layout (Matplotlib)") # Plot title
|
|
394
|
+
plt.xlabel("X Coordinate") # X-axis label
|
|
395
|
+
plt.ylabel("Y Coordinate") # Y-axis label
|
|
396
|
+
plt.axis('equal') # Ensure equal aspect ratio
|
|
397
|
+
plt.grid(False) # Ensure grid is off
|
|
398
|
+
plt.show() # Display the plot
|
|
399
|
+
|
|
400
|
+
except ImportError:
|
|
401
|
+
print("Networkit is not installed. Skipping this example.")
|
|
402
|
+
except Exception as e:
|
|
403
|
+
print(f"An error occurred in the Networkit example: {e}")
|
|
404
|
+
import traceback
|
|
405
|
+
traceback.print_exc()
|
|
406
|
+
|
|
407
|
+
print("\n--- Networkit Example Finished ---")
|
|
205
408
|
```
|
|
206
409
|
|
|
207
410
|

|
|
@@ -209,15 +412,105 @@ pos = kl.kececi_layout_v4(G)
|
|
|
209
412
|
#### Example with Graphillion
|
|
210
413
|
|
|
211
414
|
```python
|
|
415
|
+
import matplotlib.pyplot as plt
|
|
416
|
+
from matplotlib.collections import LineCollection # Efficient edge drawing
|
|
417
|
+
import math
|
|
418
|
+
import itertools # Graphillion might implicitly need itertools if find_max_node_id uses it internally
|
|
212
419
|
import graphillion as gg
|
|
213
420
|
import kececilayout as kl
|
|
214
|
-
import
|
|
421
|
+
import random
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
try:
|
|
425
|
+
import kececilayout as kl
|
|
426
|
+
except ImportError:
|
|
427
|
+
print("Error: 'kececi_layout.py' not found or could not be imported.")
|
|
428
|
+
print("Please ensure the file containing kececi_layout_v4 is accessible.")
|
|
429
|
+
exit()
|
|
430
|
+
|
|
431
|
+
# --- General Layout Parameters ---
|
|
432
|
+
LAYOUT_PARAMS = {
|
|
433
|
+
'primary_spacing': 1.0,
|
|
434
|
+
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
435
|
+
'primary_direction': 'top_down',
|
|
436
|
+
'secondary_start': 'right'
|
|
437
|
+
}
|
|
438
|
+
N_NODES = 10 # Number of nodes in the example graph (will be 1 to N_NODES)
|
|
439
|
+
|
|
440
|
+
# === Graphillion Example ===
|
|
441
|
+
try:
|
|
442
|
+
import graphillion as gg
|
|
443
|
+
print("\n--- Graphillion Example ---")
|
|
444
|
+
|
|
445
|
+
# Define the universe of possible edges (Path graph, 1-based indexing common)
|
|
446
|
+
universe = []
|
|
447
|
+
# Edges (1,2), (2,3), ..., (N_NODES-1, N_NODES)
|
|
448
|
+
for i in range(1, N_NODES):
|
|
449
|
+
universe.append((i, i + 1))
|
|
450
|
+
gg.GraphSet.set_universe(universe)
|
|
451
|
+
max_node_gg = N_NODES # We know the max node ID for this simple case
|
|
452
|
+
print(f"Graphillion universe defined: {len(universe)} edges, max node ID {max_node_gg}")
|
|
453
|
+
|
|
454
|
+
# Generate a GraphSet object (can be empty, layout function uses the universe)
|
|
455
|
+
# The layout function provided seems to derive nodes from the universe edges.
|
|
456
|
+
gs = gg.GraphSet()
|
|
457
|
+
|
|
458
|
+
# Calculate layout
|
|
459
|
+
print("Calculating Keçeci Layout...")
|
|
460
|
+
# Call the layout function; it should handle the Graphillion GraphSet object
|
|
461
|
+
# and likely use 1-based indexing based on the universe.
|
|
462
|
+
pos_gg = kl.kececi_layout_v4(gs, **LAYOUT_PARAMS)
|
|
463
|
+
# print("Graphillion positions:", pos_gg) # Debug print if needed
|
|
464
|
+
|
|
465
|
+
# Plot using Matplotlib directly (Graphillion has no plotting)
|
|
466
|
+
print("Plotting graph using Matplotlib...")
|
|
467
|
+
plt.figure(figsize=(6, 8))
|
|
468
|
+
ax = plt.gca() # Get current axes
|
|
469
|
+
|
|
470
|
+
# Node indices are expected to be 1, 2, ... N_NODES from the universe
|
|
471
|
+
node_indices_gg = sorted(pos_gg.keys())
|
|
472
|
+
|
|
473
|
+
# Check if all expected nodes (1 to N_NODES) have positions
|
|
474
|
+
expected_nodes = set(range(1, N_NODES + 1))
|
|
475
|
+
if not expected_nodes.issubset(set(node_indices_gg)):
|
|
476
|
+
print(f"ERROR: Graphillion positions missing expected nodes. Found: {node_indices_gg}, Expected: {list(expected_nodes)}")
|
|
477
|
+
else:
|
|
478
|
+
# Draw nodes
|
|
479
|
+
x_coords_gg = [pos_gg[i][0] for i in node_indices_gg]
|
|
480
|
+
y_coords_gg = [pos_gg[i][1] for i in node_indices_gg]
|
|
481
|
+
ax.scatter(x_coords_gg, y_coords_gg, s=700, c='gold', zorder=2, label='Nodes')
|
|
482
|
+
|
|
483
|
+
# Draw labels (using the 1-based indices)
|
|
484
|
+
for i in node_indices_gg:
|
|
485
|
+
ax.text(pos_gg[i][0], pos_gg[i][1], str(i), ha='center', va='center', fontsize=10, zorder=3)
|
|
486
|
+
|
|
487
|
+
# Draw edges using LineCollection (from the defined universe)
|
|
488
|
+
edge_lines_gg = []
|
|
489
|
+
for u, v in universe: # Use the universe edges
|
|
490
|
+
if u in pos_gg and v in pos_gg:
|
|
491
|
+
edge_lines_gg.append([pos_gg[u], pos_gg[v]])
|
|
492
|
+
else:
|
|
493
|
+
print(f"Warning: Position not found for universe edge ({u},{v}) in Graphillion.")
|
|
494
|
+
|
|
495
|
+
if edge_lines_gg:
|
|
496
|
+
lc_gg = LineCollection(edge_lines_gg, colors='gray', linewidths=1.0, zorder=1, label='Edges')
|
|
497
|
+
ax.add_collection(lc_gg)
|
|
498
|
+
|
|
499
|
+
plt.title(f"Graphillion ({N_NODES} Nodes) with Keçeci Layout (Matplotlib)") # Plot title
|
|
500
|
+
plt.xlabel("X Coordinate") # X-axis label
|
|
501
|
+
plt.ylabel("Y Coordinate") # Y-axis label
|
|
502
|
+
plt.axis('equal') # Ensure equal aspect ratio
|
|
503
|
+
plt.grid(False) # Ensure grid is off
|
|
504
|
+
plt.show() # Display the plot
|
|
505
|
+
|
|
506
|
+
except ImportError:
|
|
507
|
+
print("Graphillion is not installed. Skipping this example.")
|
|
508
|
+
except Exception as e:
|
|
509
|
+
print(f"An error occurred in the Graphillion example: {e}")
|
|
510
|
+
import traceback
|
|
511
|
+
traceback.print_exc()
|
|
215
512
|
|
|
216
|
-
|
|
217
|
-
gg.GraphSet.set_universe(universe)
|
|
218
|
-
gs = gg.GraphSet()
|
|
219
|
-
pos = kl.kececi_layout_v4(gs)
|
|
220
|
-
# Draw with matplotlib
|
|
513
|
+
print("\n--- Graphillion Example Finished ---")
|
|
221
514
|
```
|
|
222
515
|
|
|
223
516
|

|
|
@@ -271,131 +564,457 @@ If this library was useful in your research, please cite:
|
|
|
271
564
|
}
|
|
272
565
|
```
|
|
273
566
|
|
|
274
|
-
---
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## 🇹🇷 Türkçe
|
|
570
|
+
|
|
571
|
+
### Keçeci Yerleşimi (Kececi Layout)
|
|
572
|
+
|
|
573
|
+
**KececiLayout**, doğrusal veya ardışık yapıları görselleştirmek için tasarlanmış, karakteristik bir "zıgzag" veya "yılanvari" desen oluşturan deterministik bir graf yerleşim algoritmasıdır.
|
|
574
|
+
|
|
575
|
+
*Graf görselleştirme için Keçeci yerleşim algoritmasının Python uygulaması.*
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
### Açıklama
|
|
580
|
+
|
|
581
|
+
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.
|
|
582
|
+
|
|
583
|
+
**Temel Özellikler:**
|
|
584
|
+
* **Doğrusal Odak:** Yollar, zincirler veya sıralı süreçler için idealdir.
|
|
585
|
+
* **Deterministik:** Aynı giriş için her zaman aynı çıktıyı üretir.
|
|
586
|
+
* **Çakışmayı Azaltma:** Düğümleri eksenler boyunca yayarak çakışmaları önler.
|
|
587
|
+
* **Parametrik:** `primary_spacing`, `secondary_spacing`, `primary_direction`, `secondary_start` gibi parametrelerle özelleştirilebilir.
|
|
588
|
+
|
|
589
|
+
=> **v0.2.7**: Eğri, şeffaf, 3B ve `expanding=True` stilleri desteklenir.
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
### Kurulum
|
|
594
|
+
|
|
595
|
+
```bash
|
|
596
|
+
conda install bilgi::kececilayout -y
|
|
597
|
+
pip install kececilayout
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
🔗 [PyPI](https://pypi.org/project/kececilayout/) | [Conda](https://anaconda.org/bilgi/kececilayout) | [GitHub](https://github.com/WhiteSymmetry/kececilayout)
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
### Kullanım
|
|
605
|
+
|
|
606
|
+
#### NetworkX ile Örnek
|
|
607
|
+
|
|
608
|
+
```python
|
|
609
|
+
import networkx as nx
|
|
610
|
+
import matplotlib.pyplot as plt
|
|
611
|
+
import kececilayout as kl
|
|
612
|
+
|
|
613
|
+
G = nx.path_graph(10)
|
|
614
|
+
pos = kl.kececi_layout(
|
|
615
|
+
G,
|
|
616
|
+
primary_spacing=1.0,
|
|
617
|
+
secondary_spacing=0.5,
|
|
618
|
+
primary_direction='top_down',
|
|
619
|
+
secondary_start='right'
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
plt.figure(figsize=(6, 8))
|
|
623
|
+
nx.draw(G, pos=pos, with_labels=True, node_color='skyblue', node_size=500)
|
|
624
|
+
plt.title("Kececi Layout with NetworkX")
|
|
625
|
+
plt.axis('equal')
|
|
626
|
+
plt.show()
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+

|
|
630
|
+
|
|
631
|
+
#### iGraph ile Örnek
|
|
632
|
+
|
|
633
|
+
```python
|
|
634
|
+
import matplotlib.pyplot as plt
|
|
635
|
+
import math
|
|
636
|
+
import igraph as ig
|
|
637
|
+
import kececilayout as kl
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
try:
|
|
641
|
+
import kececilayout as kl
|
|
642
|
+
except ImportError:
|
|
643
|
+
print("Error: 'kececi_layout.py' not found or could not be imported.")
|
|
644
|
+
print("Please ensure the file containing kececi_layout_v4 is accessible.")
|
|
645
|
+
exit()
|
|
646
|
+
|
|
647
|
+
# --- General Layout Parameters ---
|
|
648
|
+
LAYOUT_PARAMS = {
|
|
649
|
+
'primary_spacing': 1.0,
|
|
650
|
+
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
651
|
+
'primary_direction': 'top_down',
|
|
652
|
+
'secondary_start': 'right'
|
|
653
|
+
}
|
|
654
|
+
N_NODES = 10 # Number of nodes in the example graph
|
|
655
|
+
|
|
656
|
+
# === igraph Example ===
|
|
657
|
+
try:
|
|
658
|
+
import igraph as ig
|
|
659
|
+
print("\n--- igraph Example ---")
|
|
660
|
+
|
|
661
|
+
# Generate graph (Path graph using Ring(circular=False))
|
|
662
|
+
G_ig = ig.Graph.Ring(N_NODES, directed=False, circular=False)
|
|
663
|
+
print(f"igraph graph generated: {G_ig.vcount()} vertices, {G_ig.ecount()} edges")
|
|
664
|
+
|
|
665
|
+
# Calculate layout
|
|
666
|
+
print("Calculating Keçeci Layout...")
|
|
667
|
+
# Call the layout function from the imported module
|
|
668
|
+
pos_ig = kl.kececi_layout_v4(G_ig, **LAYOUT_PARAMS)
|
|
669
|
+
# print("igraph positions (dict):", pos_ig) # Debug print if needed
|
|
670
|
+
|
|
671
|
+
# Convert positions dict to list ordered by vertex index for ig.plot
|
|
672
|
+
layout_list_ig = []
|
|
673
|
+
plot_possible = True
|
|
674
|
+
if pos_ig: # Check if dictionary is not empty
|
|
675
|
+
try:
|
|
676
|
+
# Generate list: [pos_ig[0], pos_ig[1], ..., pos_ig[N-1]]
|
|
677
|
+
layout_list_ig = [pos_ig[i] for i in range(G_ig.vcount())]
|
|
678
|
+
# print("igraph layout (list):", layout_list_ig) # Debug print if needed
|
|
679
|
+
except KeyError as e:
|
|
680
|
+
print(f"ERROR: Key {e} not found while creating position list for igraph.")
|
|
681
|
+
print("The layout function might not have returned positions for all vertices.")
|
|
682
|
+
plot_possible = False # Cannot plot if list is incomplete
|
|
683
|
+
else:
|
|
684
|
+
print("ERROR: Keçeci Layout returned empty positions for igraph.")
|
|
685
|
+
plot_possible = False
|
|
686
|
+
|
|
687
|
+
# Plot using igraph's plotting capabilities
|
|
688
|
+
print("Plotting graph using igraph.plot...")
|
|
689
|
+
fig, ax = plt.subplots(figsize=(6, 8)) # Generate matplotlib figure and axes
|
|
690
|
+
|
|
691
|
+
if plot_possible:
|
|
692
|
+
ig.plot(G_ig,
|
|
693
|
+
target=ax, # Draw on the matplotlib axes
|
|
694
|
+
layout=layout_list_ig, # Use the ORDERED LIST of coordinates
|
|
695
|
+
vertex_label=[str(i) for i in range(G_ig.vcount())], # Labels 0, 1,...
|
|
696
|
+
vertex_color='lightgreen',
|
|
697
|
+
vertex_size=30, # Note: igraph vertex_size scale differs
|
|
698
|
+
edge_color='gray')
|
|
699
|
+
else:
|
|
700
|
+
ax.text(0.5, 0.5, "Plotting failed:\nMissing or incomplete layout positions.",
|
|
701
|
+
ha='center', va='center', color='red', fontsize=12) # Error message on plot
|
|
702
|
+
|
|
703
|
+
ax.set_title(f"igraph ({N_NODES} Nodes) with Keçeci Layout") # Plot title
|
|
704
|
+
ax.set_aspect('equal', adjustable='box') # Ensure equal aspect ratio
|
|
705
|
+
# ax.grid(False) # Ensure grid is off
|
|
706
|
+
plt.show() # Display the plot
|
|
707
|
+
|
|
708
|
+
except ImportError:
|
|
709
|
+
print("python-igraph is not installed. Skipping this example.")
|
|
710
|
+
except Exception as e:
|
|
711
|
+
print(f"An error occurred in the igraph example: {e}")
|
|
712
|
+
import traceback
|
|
713
|
+
traceback.print_exc()
|
|
714
|
+
|
|
715
|
+
print("\n--- igraph Example Finished ---")
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+

|
|
719
|
+
|
|
720
|
+
#### RustworkX ile Örnek
|
|
721
|
+
|
|
722
|
+
```python
|
|
723
|
+
import matplotlib.pyplot as plt
|
|
724
|
+
from matplotlib.collections import LineCollection # Efficient edge drawing
|
|
725
|
+
import math
|
|
726
|
+
import rustworkx as rx
|
|
727
|
+
import kececilayout as kl
|
|
728
|
+
import random
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
try:
|
|
732
|
+
import kececilayout as kl
|
|
733
|
+
except ImportError:
|
|
734
|
+
print("Error: 'kececi_layout.py' not found or could not be imported.")
|
|
735
|
+
print("Please ensure the file containing kececi_layout_v4 is accessible.")
|
|
736
|
+
exit()
|
|
737
|
+
|
|
738
|
+
# --- General Layout Parameters ---
|
|
739
|
+
LAYOUT_PARAMS = {
|
|
740
|
+
'primary_spacing': 1.0,
|
|
741
|
+
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
742
|
+
'primary_direction': 'top_down',
|
|
743
|
+
'secondary_start': 'right'
|
|
744
|
+
}
|
|
745
|
+
N_NODES = 10 # Number of nodes in the example graph
|
|
746
|
+
|
|
747
|
+
# === Rustworkx Example ===
|
|
748
|
+
try:
|
|
749
|
+
import rustworkx as rx
|
|
750
|
+
print("\n--- Rustworkx Example ---")
|
|
751
|
+
|
|
752
|
+
# Generate graph (Path graph)
|
|
753
|
+
G_rx = rx.generators.path_graph(N_NODES)
|
|
754
|
+
print(f"Rustworkx graph generated: {G_rx.num_nodes()} nodes, {G_rx.num_edges()} edges")
|
|
755
|
+
|
|
756
|
+
# Calculate layout
|
|
757
|
+
print("Calculating Keçeci Layout...")
|
|
758
|
+
# Call the layout function from the imported module
|
|
759
|
+
pos_rx = kl.kececi_layout_v4(G_rx, **LAYOUT_PARAMS)
|
|
760
|
+
# print("Rustworkx positions:", pos_rx) # Debug print if needed
|
|
761
|
+
|
|
762
|
+
# Plot using Matplotlib directly (Rustworkx doesn't have a built-in draw)
|
|
763
|
+
print("Plotting graph using Matplotlib...")
|
|
764
|
+
plt.figure(figsize=(6, 8))
|
|
765
|
+
ax = plt.gca() # Get current axes
|
|
766
|
+
|
|
767
|
+
node_indices_rx = G_rx.node_indices() # Get node indices [0, 1, ...]
|
|
768
|
+
|
|
769
|
+
# Check if all nodes have positions
|
|
770
|
+
if not all(idx in pos_rx for idx in node_indices_rx):
|
|
771
|
+
print("ERROR: Rustworkx positions dictionary does not cover all nodes!")
|
|
772
|
+
# Decide how to handle: exit, plot partial, etc.
|
|
773
|
+
else:
|
|
774
|
+
# Draw nodes
|
|
775
|
+
x_coords_rx = [pos_rx[i][0] for i in node_indices_rx]
|
|
776
|
+
y_coords_rx = [pos_rx[i][1] for i in node_indices_rx]
|
|
777
|
+
ax.scatter(x_coords_rx, y_coords_rx, s=700, c='#88CCEE', zorder=2, label='Nodes') # Skyblue color
|
|
778
|
+
|
|
779
|
+
# Draw labels
|
|
780
|
+
for i in node_indices_rx:
|
|
781
|
+
ax.text(pos_rx[i][0], pos_rx[i][1], str(i), ha='center', va='center', fontsize=10, zorder=3)
|
|
782
|
+
|
|
783
|
+
# Draw edges using LineCollection for efficiency
|
|
784
|
+
edge_lines = []
|
|
785
|
+
for u, v in G_rx.edge_list(): # Get list of edges (node index pairs)
|
|
786
|
+
if u in pos_rx and v in pos_rx:
|
|
787
|
+
# Segment format: [(x1, y1), (x2, y2)]
|
|
788
|
+
edge_lines.append([pos_rx[u], pos_rx[v]])
|
|
789
|
+
else:
|
|
790
|
+
print(f"Warning: Position not found for edge ({u},{v}) in Rustworkx graph.")
|
|
791
|
+
|
|
792
|
+
if edge_lines:
|
|
793
|
+
lc = LineCollection(edge_lines, colors='gray', linewidths=1.0, zorder=1, label='Edges')
|
|
794
|
+
ax.add_collection(lc) # Add edges to the plot axes
|
|
795
|
+
|
|
796
|
+
plt.title(f"Rustworkx ({N_NODES} Nodes) with Keçeci Layout (Matplotlib)") # Plot title
|
|
797
|
+
plt.xlabel("X Coordinate") # X-axis label
|
|
798
|
+
plt.ylabel("Y Coordinate") # Y-axis label
|
|
799
|
+
plt.axis('equal') # Ensure equal aspect ratio
|
|
800
|
+
plt.grid(False) # Ensure grid is off
|
|
801
|
+
plt.show() # Display the plot
|
|
275
802
|
|
|
276
|
-
|
|
803
|
+
except ImportError:
|
|
804
|
+
print("Rustworkx is not installed. Skipping this example.")
|
|
805
|
+
except Exception as e:
|
|
806
|
+
print(f"An error occurred in the Rustworkx example: {e}")
|
|
807
|
+
import traceback
|
|
808
|
+
traceback.print_exc()
|
|
277
809
|
|
|
278
|
-
|
|
810
|
+
print("\n--- Rustworkx Example Finished ---")
|
|
811
|
+
```
|
|
279
812
|
|
|
280
|
-
|
|
813
|
+

|
|
281
814
|
|
|
282
|
-
|
|
815
|
+
#### Networkit ile Örnek
|
|
283
816
|
|
|
284
|
-
|
|
817
|
+
```python
|
|
818
|
+
import matplotlib.pyplot as plt
|
|
819
|
+
from matplotlib.collections import LineCollection # Efficient edge drawing
|
|
820
|
+
import math
|
|
821
|
+
import networkit as nk
|
|
822
|
+
import kececilayout as kl
|
|
823
|
+
import random
|
|
285
824
|
|
|
286
|
-
|
|
825
|
+
try:
|
|
826
|
+
import kececilayout as kl
|
|
827
|
+
except ImportError:
|
|
828
|
+
print("Error: 'kececi_layout.py' not found or could not be imported.")
|
|
829
|
+
print("Please ensure the file containing kececi_layout_v4 is accessible.")
|
|
830
|
+
exit()
|
|
287
831
|
|
|
288
|
-
|
|
832
|
+
# --- General Layout Parameters ---
|
|
833
|
+
LAYOUT_PARAMS = {
|
|
834
|
+
'primary_spacing': 1.0,
|
|
835
|
+
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
836
|
+
'primary_direction': 'top_down',
|
|
837
|
+
'secondary_start': 'right'
|
|
838
|
+
}
|
|
839
|
+
N_NODES = 10 # Number of nodes in the example graph
|
|
289
840
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
* **Parametrik:** `primary_spacing`, `secondary_spacing`, `primary_direction`, `secondary_start` gibi parametrelerle özelleştirilebilir.
|
|
841
|
+
# === Networkit Example ===
|
|
842
|
+
try:
|
|
843
|
+
import networkit as nk
|
|
844
|
+
print("\n--- Networkit Example ---")
|
|
295
845
|
|
|
296
|
-
|
|
846
|
+
# Generate graph (Path graph, manually)
|
|
847
|
+
G_nk = nk.graph.Graph(N_NODES, weighted=False, directed=False) # Generate empty graph container
|
|
848
|
+
print("Empty Networkit graph generated.")
|
|
849
|
+
# Add nodes first (Networkit often requires this)
|
|
850
|
+
for i in range(N_NODES):
|
|
851
|
+
if not G_nk.hasNode(i): # Check if node already exists (good practice)
|
|
852
|
+
G_nk.addNode()
|
|
853
|
+
print(f"{G_nk.numberOfNodes()} nodes added.")
|
|
854
|
+
# Add edges
|
|
855
|
+
for i in range(N_NODES - 1):
|
|
856
|
+
G_nk.addEdge(i, i+1) # Add edges 0-1, 1-2, ...
|
|
857
|
+
print(f"Networkit graph constructed: {G_nk.numberOfNodes()} nodes, {G_nk.numberOfEdges()} edges")
|
|
297
858
|
|
|
298
|
-
|
|
859
|
+
# Calculate layout
|
|
860
|
+
print("Calculating Keçeci Layout...")
|
|
861
|
+
# Call the layout function from the imported module
|
|
862
|
+
pos_nk = kl.kececi_layout_v4(G_nk, **LAYOUT_PARAMS)
|
|
863
|
+
# print("Networkit positions:", pos_nk) # Debug print if needed
|
|
299
864
|
|
|
300
|
-
|
|
865
|
+
# Plot using Matplotlib directly (Networkit doesn't have a simple built-in draw)
|
|
866
|
+
print("Plotting graph using Matplotlib...")
|
|
867
|
+
plt.figure(figsize=(6, 8))
|
|
868
|
+
ax = plt.gca() # Get current axes
|
|
301
869
|
|
|
302
|
-
|
|
303
|
-
conda install bilgi::kececilayout -y
|
|
304
|
-
pip install kececilayout
|
|
305
|
-
```
|
|
870
|
+
node_indices_nk = sorted(list(G_nk.iterNodes())) # Get node indices [0, 1, ...]
|
|
306
871
|
|
|
307
|
-
|
|
872
|
+
# Check if all nodes have positions
|
|
873
|
+
if not all(idx in pos_nk for idx in node_indices_nk):
|
|
874
|
+
print("ERROR: Networkit positions dictionary does not cover all nodes!")
|
|
875
|
+
else:
|
|
876
|
+
# Draw nodes
|
|
877
|
+
x_coords_nk = [pos_nk[i][0] for i in node_indices_nk]
|
|
878
|
+
y_coords_nk = [pos_nk[i][1] for i in node_indices_nk]
|
|
879
|
+
ax.scatter(x_coords_nk, y_coords_nk, s=700, c='coral', zorder=2, label='Nodes')
|
|
308
880
|
|
|
309
|
-
|
|
881
|
+
# Draw labels
|
|
882
|
+
for i in node_indices_nk:
|
|
883
|
+
ax.text(pos_nk[i][0], pos_nk[i][1], str(i), ha='center', va='center', fontsize=10, zorder=3)
|
|
310
884
|
|
|
311
|
-
|
|
885
|
+
# Draw edges using LineCollection
|
|
886
|
+
edge_lines_nk = []
|
|
887
|
+
for u, v in G_nk.iterEdges(): # Iterate through edges
|
|
888
|
+
if u in pos_nk and v in pos_nk:
|
|
889
|
+
edge_lines_nk.append([pos_nk[u], pos_nk[v]])
|
|
890
|
+
else:
|
|
891
|
+
print(f"Warning: Position not found for edge ({u},{v}) in Networkit graph.")
|
|
312
892
|
|
|
313
|
-
|
|
893
|
+
if edge_lines_nk:
|
|
894
|
+
lc_nk = LineCollection(edge_lines_nk, colors='gray', linewidths=1.0, zorder=1, label='Edges')
|
|
895
|
+
ax.add_collection(lc_nk)
|
|
314
896
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
897
|
+
plt.title(f"Networkit ({N_NODES} Nodes) with Keçeci Layout (Matplotlib)") # Plot title
|
|
898
|
+
plt.xlabel("X Coordinate") # X-axis label
|
|
899
|
+
plt.ylabel("Y Coordinate") # Y-axis label
|
|
900
|
+
plt.axis('equal') # Ensure equal aspect ratio
|
|
901
|
+
plt.grid(False) # Ensure grid is off
|
|
902
|
+
plt.show() # Display the plot
|
|
319
903
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
secondary_start='right'
|
|
327
|
-
)
|
|
904
|
+
except ImportError:
|
|
905
|
+
print("Networkit is not installed. Skipping this example.")
|
|
906
|
+
except Exception as e:
|
|
907
|
+
print(f"An error occurred in the Networkit example: {e}")
|
|
908
|
+
import traceback
|
|
909
|
+
traceback.print_exc()
|
|
328
910
|
|
|
329
|
-
|
|
330
|
-
nx.draw(G, pos=pos, with_labels=True, node_color='skyblue', node_size=500)
|
|
331
|
-
plt.title("Kececi Layout with NetworkX")
|
|
332
|
-
plt.axis('equal')
|
|
333
|
-
plt.show()
|
|
911
|
+
print("\n--- Networkit Example Finished ---")
|
|
334
912
|
```
|
|
335
913
|
|
|
336
|
-

|
|
337
915
|
|
|
338
|
-
####
|
|
916
|
+
#### Graphillion ile Örnek
|
|
339
917
|
|
|
340
918
|
```python
|
|
341
|
-
import igraph as ig
|
|
342
919
|
import matplotlib.pyplot as plt
|
|
343
|
-
from
|
|
920
|
+
from matplotlib.collections import LineCollection # Efficient edge drawing
|
|
921
|
+
import math
|
|
922
|
+
import itertools # Graphillion might implicitly need itertools if find_max_node_id uses it internally
|
|
923
|
+
import graphillion as gg
|
|
924
|
+
import kececilayout as kl
|
|
925
|
+
import random
|
|
344
926
|
|
|
345
|
-
G = ig.Graph.Ring(10, circular=False)
|
|
346
|
-
pos_list = kececi_layout_v4_igraph(G, primary_direction='left-to-right', secondary_start='up')
|
|
347
|
-
layout = ig.Layout(pos_list)
|
|
348
927
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
928
|
+
try:
|
|
929
|
+
import kececilayout as kl
|
|
930
|
+
except ImportError:
|
|
931
|
+
print("Error: 'kececi_layout.py' not found or could not be imported.")
|
|
932
|
+
print("Please ensure the file containing kececi_layout_v4 is accessible.")
|
|
933
|
+
exit()
|
|
354
934
|
|
|
355
|
-
|
|
935
|
+
# --- General Layout Parameters ---
|
|
936
|
+
LAYOUT_PARAMS = {
|
|
937
|
+
'primary_spacing': 1.0,
|
|
938
|
+
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
939
|
+
'primary_direction': 'top_down',
|
|
940
|
+
'secondary_start': 'right'
|
|
941
|
+
}
|
|
942
|
+
N_NODES = 10 # Number of nodes in the example graph (will be 1 to N_NODES)
|
|
356
943
|
|
|
357
|
-
|
|
944
|
+
# === Graphillion Example ===
|
|
945
|
+
try:
|
|
946
|
+
import graphillion as gg
|
|
947
|
+
print("\n--- Graphillion Example ---")
|
|
358
948
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
949
|
+
# Define the universe of possible edges (Path graph, 1-based indexing common)
|
|
950
|
+
universe = []
|
|
951
|
+
# Edges (1,2), (2,3), ..., (N_NODES-1, N_NODES)
|
|
952
|
+
for i in range(1, N_NODES):
|
|
953
|
+
universe.append((i, i + 1))
|
|
954
|
+
gg.GraphSet.set_universe(universe)
|
|
955
|
+
max_node_gg = N_NODES # We know the max node ID for this simple case
|
|
956
|
+
print(f"Graphillion universe defined: {len(universe)} edges, max node ID {max_node_gg}")
|
|
363
957
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
```
|
|
958
|
+
# Generate a GraphSet object (can be empty, layout function uses the universe)
|
|
959
|
+
# The layout function provided seems to derive nodes from the universe edges.
|
|
960
|
+
gs = gg.GraphSet()
|
|
368
961
|
|
|
369
|
-
|
|
962
|
+
# Calculate layout
|
|
963
|
+
print("Calculating Keçeci Layout...")
|
|
964
|
+
# Call the layout function; it should handle the Graphillion GraphSet object
|
|
965
|
+
# and likely use 1-based indexing based on the universe.
|
|
966
|
+
pos_gg = kl.kececi_layout_v4(gs, **LAYOUT_PARAMS)
|
|
967
|
+
# print("Graphillion positions:", pos_gg) # Debug print if needed
|
|
370
968
|
|
|
371
|
-
|
|
969
|
+
# Plot using Matplotlib directly (Graphillion has no plotting)
|
|
970
|
+
print("Plotting graph using Matplotlib...")
|
|
971
|
+
plt.figure(figsize=(6, 8))
|
|
972
|
+
ax = plt.gca() # Get current axes
|
|
372
973
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
import kececilayout as kl
|
|
376
|
-
import matplotlib.pyplot as plt
|
|
974
|
+
# Node indices are expected to be 1, 2, ... N_NODES from the universe
|
|
975
|
+
node_indices_gg = sorted(pos_gg.keys())
|
|
377
976
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
977
|
+
# Check if all expected nodes (1 to N_NODES) have positions
|
|
978
|
+
expected_nodes = set(range(1, N_NODES + 1))
|
|
979
|
+
if not expected_nodes.issubset(set(node_indices_gg)):
|
|
980
|
+
print(f"ERROR: Graphillion positions missing expected nodes. Found: {node_indices_gg}, Expected: {list(expected_nodes)}")
|
|
981
|
+
else:
|
|
982
|
+
# Draw nodes
|
|
983
|
+
x_coords_gg = [pos_gg[i][0] for i in node_indices_gg]
|
|
984
|
+
y_coords_gg = [pos_gg[i][1] for i in node_indices_gg]
|
|
985
|
+
ax.scatter(x_coords_gg, y_coords_gg, s=700, c='gold', zorder=2, label='Nodes')
|
|
384
986
|
|
|
385
|
-
|
|
987
|
+
# Draw labels (using the 1-based indices)
|
|
988
|
+
for i in node_indices_gg:
|
|
989
|
+
ax.text(pos_gg[i][0], pos_gg[i][1], str(i), ha='center', va='center', fontsize=10, zorder=3)
|
|
386
990
|
|
|
387
|
-
|
|
991
|
+
# Draw edges using LineCollection (from the defined universe)
|
|
992
|
+
edge_lines_gg = []
|
|
993
|
+
for u, v in universe: # Use the universe edges
|
|
994
|
+
if u in pos_gg and v in pos_gg:
|
|
995
|
+
edge_lines_gg.append([pos_gg[u], pos_gg[v]])
|
|
996
|
+
else:
|
|
997
|
+
print(f"Warning: Position not found for universe edge ({u},{v}) in Graphillion.")
|
|
388
998
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
999
|
+
if edge_lines_gg:
|
|
1000
|
+
lc_gg = LineCollection(edge_lines_gg, colors='gray', linewidths=1.0, zorder=1, label='Edges')
|
|
1001
|
+
ax.add_collection(lc_gg)
|
|
1002
|
+
|
|
1003
|
+
plt.title(f"Graphillion ({N_NODES} Nodes) with Keçeci Layout (Matplotlib)") # Plot title
|
|
1004
|
+
plt.xlabel("X Coordinate") # X-axis label
|
|
1005
|
+
plt.ylabel("Y Coordinate") # Y-axis label
|
|
1006
|
+
plt.axis('equal') # Ensure equal aspect ratio
|
|
1007
|
+
plt.grid(False) # Ensure grid is off
|
|
1008
|
+
plt.show() # Display the plot
|
|
1009
|
+
|
|
1010
|
+
except ImportError:
|
|
1011
|
+
print("Graphillion is not installed. Skipping this example.")
|
|
1012
|
+
except Exception as e:
|
|
1013
|
+
print(f"An error occurred in the Graphillion example: {e}")
|
|
1014
|
+
import traceback
|
|
1015
|
+
traceback.print_exc()
|
|
393
1016
|
|
|
394
|
-
|
|
395
|
-
gg.GraphSet.set_universe(universe)
|
|
396
|
-
gs = gg.GraphSet()
|
|
397
|
-
pos = kl.kececi_layout_v4(gs)
|
|
398
|
-
# Matplotlib ile çizim
|
|
1017
|
+
print("\n--- Graphillion Example Finished ---")
|
|
399
1018
|
```
|
|
400
1019
|
|
|
401
1020
|

|
|
@@ -559,7 +1178,7 @@ A deterministic node placement algorithm used in graph visualization. In this la
|
|
|
559
1178
|
* **Linear Focus:** Particularly useful for visualizing linear or sequential structures, such as paths, chains, or ordered processes.
|
|
560
1179
|
* **Deterministic:** Produces the exact same layout for the same graph and parameters every time.
|
|
561
1180
|
* **Overlap Reduction:** Helps prevent node collisions by spreading nodes out away from the primary axis.
|
|
562
|
-
* **Parametric:** Can be customized using parameters such as the primary direction (e.g., `
|
|
1181
|
+
* **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`).
|
|
563
1182
|
|
|
564
1183
|
---
|
|
565
1184
|
|
|
@@ -573,7 +1192,7 @@ Graf görselleştirmede kullanılan deterministik bir düğüm yerleştirme algo
|
|
|
573
1192
|
* **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.
|
|
574
1193
|
* **Deterministik:** Aynı graf ve parametrelerle her zaman aynı sonucu üretir.
|
|
575
1194
|
* **Çakışmayı Azaltma:** Düğümleri ana eksenden uzağa yayarak çakışmaları önlemeye yardımcı olur.
|
|
576
|
-
* **Parametrik:** Ana eksenin yönü (örn. `
|
|
1195
|
+
* **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.
|
|
577
1196
|
|
|
578
1197
|
---
|
|
579
1198
|
|
|
@@ -616,7 +1235,7 @@ G = nx.path_graph(10)
|
|
|
616
1235
|
pos = kl.kececi_layout_v4(G,
|
|
617
1236
|
primary_spacing=1.0,
|
|
618
1237
|
secondary_spacing=0.5,
|
|
619
|
-
primary_direction='
|
|
1238
|
+
primary_direction='top_down',
|
|
620
1239
|
secondary_start='right')
|
|
621
1240
|
|
|
622
1241
|
# Draw the graph
|
|
@@ -645,7 +1264,7 @@ except ImportError:
|
|
|
645
1264
|
LAYOUT_PARAMS = {
|
|
646
1265
|
'primary_spacing': 1.0,
|
|
647
1266
|
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
648
|
-
'primary_direction': '
|
|
1267
|
+
'primary_direction': 'top_down',
|
|
649
1268
|
'secondary_start': 'right'
|
|
650
1269
|
}
|
|
651
1270
|
N_NODES = 10 # Number of nodes in the example graph
|
|
@@ -750,7 +1369,7 @@ except ImportError:
|
|
|
750
1369
|
LAYOUT_PARAMS = {
|
|
751
1370
|
'primary_spacing': 1.0,
|
|
752
1371
|
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
753
|
-
'primary_direction': '
|
|
1372
|
+
'primary_direction': 'top_down',
|
|
754
1373
|
'secondary_start': 'right'
|
|
755
1374
|
}
|
|
756
1375
|
N_NODES = 10 # Number of nodes in the example graph
|
|
@@ -843,7 +1462,7 @@ except ImportError:
|
|
|
843
1462
|
LAYOUT_PARAMS = {
|
|
844
1463
|
'primary_spacing': 1.0,
|
|
845
1464
|
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
846
|
-
'primary_direction': '
|
|
1465
|
+
'primary_direction': 'top_down',
|
|
847
1466
|
'secondary_start': 'right'
|
|
848
1467
|
}
|
|
849
1468
|
N_NODES = 10 # Number of nodes in the example graph
|
|
@@ -939,7 +1558,7 @@ except ImportError:
|
|
|
939
1558
|
LAYOUT_PARAMS = {
|
|
940
1559
|
'primary_spacing': 1.0,
|
|
941
1560
|
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
942
|
-
'primary_direction': '
|
|
1561
|
+
'primary_direction': 'top_down',
|
|
943
1562
|
'secondary_start': 'right'
|
|
944
1563
|
}
|
|
945
1564
|
N_NODES = 10 # Number of nodes in the example graph
|
|
@@ -1044,7 +1663,7 @@ except ImportError:
|
|
|
1044
1663
|
LAYOUT_PARAMS = {
|
|
1045
1664
|
'primary_spacing': 1.0,
|
|
1046
1665
|
'secondary_spacing': 0.6, # Make the zigzag noticeable
|
|
1047
|
-
'primary_direction': '
|
|
1666
|
+
'primary_direction': 'top_down',
|
|
1048
1667
|
'secondary_start': 'right'
|
|
1049
1668
|
}
|
|
1050
1669
|
N_NODES = 10 # Number of nodes in the example graph (will be 1 to N_NODES)
|
|
@@ -1241,13 +1860,3 @@ Keçeci, Mehmet. "Kececilayout". Open Science Articles (OSAs), Zenodo, 2025. htt
|
|
|
1241
1860
|
|
|
1242
1861
|
Keçeci, Mehmet. "Keçeci Layout". Open Science Articles (OSAs), Zenodo, 2025. https://doi.org/10.5281/zenodo.15314328.
|
|
1243
1862
|
```
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|