kececilayout 0.2.8__py3-none-any.whl → 0.3.0__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 +2 -1
- kececilayout/_version.py +3 -1
- kececilayout/kececi_layout.py +508 -473
- {kececilayout-0.2.8.dist-info → kececilayout-0.3.0.dist-info}/METADATA +41 -19
- kececilayout-0.3.0.dist-info/RECORD +8 -0
- kececilayout-0.2.8.dist-info/RECORD +0 -8
- {kececilayout-0.2.8.dist-info → kececilayout-0.3.0.dist-info}/WHEEL +0 -0
- {kececilayout-0.2.8.dist-info → kececilayout-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {kececilayout-0.2.8.dist-info → kececilayout-0.3.0.dist-info}/top_level.txt +0 -0
kececilayout/kececi_layout.py
CHANGED
|
@@ -51,16 +51,37 @@ except ImportError:
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
def find_max_node_id(edges):
|
|
54
|
-
"""
|
|
54
|
+
"""
|
|
55
|
+
Finds the highest node ID from a list of edges.
|
|
56
|
+
|
|
57
|
+
This function is robust and handles empty lists or malformed edge data
|
|
58
|
+
gracefully by returning 0.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
edges (iterable): An iterable of edge tuples, e.g., [(1, 2), (3, 2)].
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
int: The highest node ID found, or 0 if the list is empty.
|
|
65
|
+
"""
|
|
66
|
+
# 1. Handle the most common case first: an empty list of edges.
|
|
55
67
|
if not edges:
|
|
56
68
|
return 0
|
|
69
|
+
|
|
57
70
|
try:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
# 2. Efficiently flatten the list of tuples into a single sequence
|
|
72
|
+
# and use a set to get unique node IDs.
|
|
73
|
+
# e.g., [(1, 2), (3, 2)] -> {1, 2, 3}
|
|
74
|
+
all_nodes = set(itertools.chain.from_iterable(edges))
|
|
75
|
+
|
|
76
|
+
# 3. Return the maximum ID from the set. If the set is somehow empty
|
|
77
|
+
# after processing, return 0 as a fallback.
|
|
78
|
+
return max(all_nodes) if all_nodes else 0
|
|
79
|
+
|
|
80
|
+
except TypeError:
|
|
81
|
+
# 4. If the edge data is not in the expected format (e.g., not a list
|
|
82
|
+
# of tuples), catch the error and return 0 safely.
|
|
83
|
+
print("Warning: Edge format was unexpected. Assuming max node ID is 0.")
|
|
84
|
+
return 0
|
|
64
85
|
|
|
65
86
|
|
|
66
87
|
def kececi_layout(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
@@ -94,7 +115,7 @@ def kececi_layout(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
|
94
115
|
pos = {}
|
|
95
116
|
|
|
96
117
|
# --- DOĞRULANMIŞ KONTROL BLOĞU ---
|
|
97
|
-
is_vertical = primary_direction in ['
|
|
118
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
98
119
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
99
120
|
|
|
100
121
|
if not (is_vertical or is_horizontal):
|
|
@@ -107,9 +128,9 @@ def kececi_layout(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
|
107
128
|
|
|
108
129
|
for i, node_id in enumerate(nodes):
|
|
109
130
|
primary_coord, secondary_axis = 0.0, ''
|
|
110
|
-
if primary_direction == '
|
|
131
|
+
if primary_direction == 'top_down':
|
|
111
132
|
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
112
|
-
elif primary_direction == '
|
|
133
|
+
elif primary_direction == 'bottom_up':
|
|
113
134
|
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
114
135
|
elif primary_direction == 'left-to-right':
|
|
115
136
|
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
@@ -194,7 +215,7 @@ def kececi_layout_v4(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
|
194
215
|
|
|
195
216
|
for i, node_id in enumerate(nodes):
|
|
196
217
|
primary_coord, secondary_axis = 0.0, ''
|
|
197
|
-
if primary_direction == '
|
|
218
|
+
if primary_direction == 'top_down':
|
|
198
219
|
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
199
220
|
elif primary_direction == 'bottom_up':
|
|
200
221
|
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
@@ -220,649 +241,714 @@ def kececi_layout_v4(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
|
220
241
|
|
|
221
242
|
return pos
|
|
222
243
|
|
|
223
|
-
def
|
|
224
|
-
|
|
244
|
+
def kececi_layout_nx(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
245
|
+
primary_direction='top_down', secondary_start='right',
|
|
246
|
+
expanding=True):
|
|
225
247
|
"""
|
|
226
|
-
|
|
227
|
-
|
|
248
|
+
Expanding Kececi Layout: Progresses along the primary axis, with an offset
|
|
249
|
+
on the secondary axis.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
graph (networkx.Graph): A NetworkX graph object.
|
|
253
|
+
primary_spacing (float): The distance between nodes along the primary axis.
|
|
254
|
+
secondary_spacing (float): The base unit for the zigzag offset.
|
|
255
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
256
|
+
secondary_start (str): Initial direction for the zigzag offset.
|
|
257
|
+
expanding (bool): If True (default), the zigzag offset grows.
|
|
258
|
+
If False, the offset is constant (parallel lines). # <-- 2. DOKÜMANTASYON GÜNCELLENDİ
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
dict: A dictionary of positions keyed by node ID.
|
|
228
262
|
"""
|
|
229
263
|
pos = {}
|
|
230
|
-
# NetworkX 2.x ve 3.x uyumluluğu için listeye çevirme
|
|
231
264
|
nodes = sorted(list(graph.nodes()))
|
|
232
|
-
|
|
233
|
-
if num_nodes == 0:
|
|
265
|
+
if not nodes:
|
|
234
266
|
return {}
|
|
235
267
|
|
|
236
|
-
is_vertical = primary_direction in ['
|
|
268
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
237
269
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
238
|
-
if not (is_vertical or is_horizontal):
|
|
270
|
+
if not (is_vertical or is_horizontal):
|
|
239
271
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
240
|
-
if is_vertical and secondary_start not in ['right', 'left']:
|
|
241
|
-
raise ValueError(f"Invalid secondary_start for vertical: {secondary_start}")
|
|
242
|
-
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
243
|
-
raise ValueError(f"Invalid secondary_start for horizontal: {secondary_start}")
|
|
272
|
+
if is_vertical and secondary_start not in ['right', 'left']:
|
|
273
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: {secondary_start}")
|
|
274
|
+
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
275
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: {secondary_start}")
|
|
276
|
+
|
|
244
277
|
|
|
245
278
|
for i, node_id in enumerate(nodes):
|
|
246
|
-
# 1.
|
|
247
|
-
if primary_direction == '
|
|
279
|
+
# 1. Calculate Primary Axis Coordinate
|
|
280
|
+
if primary_direction == 'top_down':
|
|
248
281
|
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
249
|
-
elif primary_direction == '
|
|
282
|
+
elif primary_direction == 'bottom_up':
|
|
250
283
|
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
251
|
-
elif primary_direction == 'left-to-right':
|
|
284
|
+
elif primary_direction == 'left-to-right':
|
|
252
285
|
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
253
|
-
else:
|
|
286
|
+
else:
|
|
254
287
|
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
255
288
|
|
|
256
|
-
# 2.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
262
|
-
# Sapma büyüklüğünü belirle (i arttıkça artar: 1, 1, 2, 2, 3, 3, ...)
|
|
263
|
-
magnitude = math.ceil(i / 2.0)
|
|
264
|
-
# Sapma tarafını belirle (tek i için pozitif, çift i için negatif)
|
|
289
|
+
# 2. Calculate Secondary Axis Offset
|
|
290
|
+
secondary_offset = 0.0
|
|
291
|
+
if i > 0:
|
|
292
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
293
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
265
294
|
side = 1 if i % 2 != 0 else -1
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
295
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
269
296
|
|
|
270
|
-
# 3.
|
|
271
|
-
x, y = (
|
|
297
|
+
# 3. Assign Coordinates
|
|
298
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
272
299
|
pos[node_id] = (x, y)
|
|
273
300
|
|
|
274
301
|
return pos
|
|
275
302
|
|
|
276
|
-
def
|
|
277
|
-
|
|
303
|
+
def kececi_layout_networkx(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
304
|
+
primary_direction='top_down', secondary_start='right',
|
|
305
|
+
expanding=True):
|
|
278
306
|
"""
|
|
279
|
-
|
|
280
|
-
|
|
307
|
+
Expanding Kececi Layout: Progresses along the primary axis, with an offset
|
|
308
|
+
on the secondary axis.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
graph (networkx.Graph): A NetworkX graph object.
|
|
312
|
+
primary_spacing (float): The distance between nodes along the primary axis.
|
|
313
|
+
secondary_spacing (float): The base unit for the zigzag offset.
|
|
314
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
315
|
+
secondary_start (str): Initial direction for the zigzag offset.
|
|
316
|
+
expanding (bool): If True (default), the zigzag offset grows.
|
|
317
|
+
If False, the offset is constant (parallel lines). # <-- 2. DOKÜMANTASYON GÜNCELLENDİ
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
dict: A dictionary of positions keyed by node ID.
|
|
281
321
|
"""
|
|
282
322
|
pos = {}
|
|
283
|
-
# NetworkX 2.x ve 3.x uyumluluğu için listeye çevirme
|
|
284
323
|
nodes = sorted(list(graph.nodes()))
|
|
285
|
-
|
|
286
|
-
if num_nodes == 0:
|
|
324
|
+
if not nodes:
|
|
287
325
|
return {}
|
|
288
326
|
|
|
289
|
-
is_vertical = primary_direction in ['
|
|
327
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
290
328
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
291
|
-
if not (is_vertical or is_horizontal):
|
|
329
|
+
if not (is_vertical or is_horizontal):
|
|
292
330
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
293
|
-
if is_vertical and secondary_start not in ['right', 'left']:
|
|
294
|
-
raise ValueError(f"Invalid secondary_start for vertical: {secondary_start}")
|
|
295
|
-
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
296
|
-
raise ValueError(f"Invalid secondary_start for horizontal: {secondary_start}")
|
|
331
|
+
if is_vertical and secondary_start not in ['right', 'left']:
|
|
332
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: {secondary_start}")
|
|
333
|
+
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
334
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: {secondary_start}")
|
|
335
|
+
|
|
297
336
|
|
|
298
337
|
for i, node_id in enumerate(nodes):
|
|
299
|
-
# 1.
|
|
300
|
-
if primary_direction == '
|
|
338
|
+
# 1. Calculate Primary Axis Coordinate
|
|
339
|
+
if primary_direction == 'top_down':
|
|
301
340
|
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
302
|
-
elif primary_direction == '
|
|
341
|
+
elif primary_direction == 'bottom_up':
|
|
303
342
|
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
304
|
-
elif primary_direction == 'left-to-right':
|
|
343
|
+
elif primary_direction == 'left-to-right':
|
|
305
344
|
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
306
|
-
else:
|
|
345
|
+
else:
|
|
307
346
|
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
308
347
|
|
|
309
|
-
# 2.
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
315
|
-
# Sapma büyüklüğünü belirle (i arttıkça artar: 1, 1, 2, 2, 3, 3, ...)
|
|
316
|
-
magnitude = math.ceil(i / 2.0)
|
|
317
|
-
# Sapma tarafını belirle (tek i için pozitif, çift i için negatif)
|
|
348
|
+
# 2. Calculate Secondary Axis Offset
|
|
349
|
+
secondary_offset = 0.0
|
|
350
|
+
if i > 0:
|
|
351
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
352
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
318
353
|
side = 1 if i % 2 != 0 else -1
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
354
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
322
355
|
|
|
323
|
-
# 3.
|
|
324
|
-
x, y = (
|
|
356
|
+
# 3. Assign Coordinates
|
|
357
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
325
358
|
pos[node_id] = (x, y)
|
|
326
359
|
|
|
327
360
|
return pos
|
|
328
361
|
|
|
329
362
|
|
|
330
|
-
def
|
|
331
|
-
|
|
332
|
-
|
|
363
|
+
def kececi_layout_ig(graph: "ig.Graph", primary_spacing=1.0, secondary_spacing=1.0,
|
|
364
|
+
primary_direction='top_down', secondary_start='right',
|
|
365
|
+
expanding=True):
|
|
366
|
+
"""
|
|
367
|
+
Expanding Kececi Layout: Progresses along the primary axis, with an offset
|
|
368
|
+
on the secondary axis.
|
|
369
|
+
Kececi layout for an igraph.Graph object.
|
|
333
370
|
|
|
334
371
|
Args:
|
|
335
|
-
graph: igraph.Graph
|
|
336
|
-
primary_spacing:
|
|
337
|
-
secondary_spacing:
|
|
338
|
-
primary_direction:
|
|
339
|
-
secondary_start:
|
|
372
|
+
graph (igraph.Graph): An igraph.Graph object.
|
|
373
|
+
primary_spacing (float): The spacing between nodes on the primary axis.
|
|
374
|
+
secondary_spacing (float): The offset spacing on the secondary axis.
|
|
375
|
+
primary_direction (str): Direction of the primary axis ('top_down', 'bottom_up', 'left-to-right', 'right-to-left').
|
|
376
|
+
secondary_start (str): Direction of the initial offset on the secondary axis ('right', 'left', 'up', 'down').
|
|
340
377
|
|
|
341
378
|
Returns:
|
|
342
|
-
|
|
379
|
+
list: A list of coordinates sorted by vertex ID (e.g., [[x0,y0], [x1,y1], ...]).
|
|
343
380
|
"""
|
|
344
381
|
num_nodes = graph.vcount()
|
|
345
382
|
if num_nodes == 0:
|
|
346
383
|
return []
|
|
347
384
|
|
|
348
|
-
#
|
|
385
|
+
# Create coordinate list (will be ordered by vertex IDs 0 to N-1)
|
|
349
386
|
pos_list = [[0.0, 0.0]] * num_nodes
|
|
350
|
-
#
|
|
351
|
-
nodes = range(num_nodes)
|
|
387
|
+
# Since vertex IDs are already 0 to N-1, we can use range directly
|
|
388
|
+
nodes = range(num_nodes) # Vertex IDs
|
|
352
389
|
|
|
353
|
-
is_vertical = primary_direction in ['
|
|
390
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
354
391
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
355
392
|
|
|
356
393
|
if not (is_vertical or is_horizontal):
|
|
357
394
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
358
395
|
if is_vertical and secondary_start not in ['right', 'left']:
|
|
359
|
-
raise ValueError(f"Invalid secondary_start for vertical: {secondary_start}")
|
|
396
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: {secondary_start}")
|
|
360
397
|
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
361
|
-
raise ValueError(f"Invalid secondary_start for horizontal: {secondary_start}")
|
|
398
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: {secondary_start}")
|
|
362
399
|
|
|
363
|
-
for i in nodes:
|
|
364
|
-
if primary_direction == '
|
|
400
|
+
for i in nodes: # Here, i is the vertex index (0, 1, 2...)
|
|
401
|
+
if primary_direction == 'top_down':
|
|
365
402
|
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
366
|
-
elif primary_direction == '
|
|
403
|
+
elif primary_direction == 'bottom_up':
|
|
367
404
|
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
368
405
|
elif primary_direction == 'left-to-right':
|
|
369
406
|
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
370
|
-
else:
|
|
407
|
+
else: # right-to-left
|
|
371
408
|
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
372
409
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
magnitude = math.ceil(i / 2.0)
|
|
410
|
+
# 2. Calculate Secondary Axis Offset
|
|
411
|
+
secondary_offset = 0.0
|
|
412
|
+
if i > 0:
|
|
413
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
414
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
378
415
|
side = 1 if i % 2 != 0 else -1
|
|
379
|
-
|
|
416
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
380
417
|
|
|
381
|
-
|
|
418
|
+
# 3. Assign Coordinates
|
|
419
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
420
|
+
pos_list[i] = [x, y] # Add [x, y] to the list at the correct index
|
|
382
421
|
|
|
383
|
-
|
|
384
|
-
|
|
422
|
+
# Returning a direct list is the most common and flexible approach.
|
|
423
|
+
# The plot function accepts a list of coordinates directly.
|
|
424
|
+
return pos_list
|
|
385
425
|
|
|
386
|
-
# igraph Layout nesnesi gibi davranması için basit bir nesne döndürelim
|
|
387
|
-
# veya doğrudan koordinat listesini kullanalım. Çizim fonksiyonu listeyi kabul eder.
|
|
388
|
-
# return ig.Layout(pos_list) # İsterseniz Layout nesnesi de döndürebilirsiniz
|
|
389
|
-
return pos_list # Doğrudan liste döndürmek en yaygın ve esnek yoldur
|
|
390
426
|
|
|
391
|
-
def
|
|
392
|
-
|
|
393
|
-
|
|
427
|
+
def kececi_layout_igraph(graph: "ig.Graph", primary_spacing=1.0, secondary_spacing=1.0,
|
|
428
|
+
primary_direction='top_down', secondary_start='right',
|
|
429
|
+
expanding=True):
|
|
430
|
+
"""
|
|
431
|
+
Expanding Kececi Layout: Progresses along the primary axis, with an offset
|
|
432
|
+
on the secondary axis.
|
|
433
|
+
Kececi layout for an igraph.Graph object.
|
|
394
434
|
|
|
395
435
|
Args:
|
|
396
|
-
graph: igraph.Graph
|
|
397
|
-
primary_spacing:
|
|
398
|
-
secondary_spacing:
|
|
399
|
-
primary_direction:
|
|
400
|
-
secondary_start:
|
|
436
|
+
graph (igraph.Graph): An igraph.Graph object.
|
|
437
|
+
primary_spacing (float): The spacing between nodes on the primary axis.
|
|
438
|
+
secondary_spacing (float): The offset spacing on the secondary axis.
|
|
439
|
+
primary_direction (str): Direction of the primary axis ('top_down', 'bottom_up', 'left-to-right', 'right-to-left').
|
|
440
|
+
secondary_start (str): Direction of the initial offset on the secondary axis ('right', 'left', 'up', 'down').
|
|
401
441
|
|
|
402
442
|
Returns:
|
|
403
|
-
|
|
443
|
+
list: A list of coordinates sorted by vertex ID (e.g., [[x0,y0], [x1,y1], ...]).
|
|
404
444
|
"""
|
|
405
445
|
num_nodes = graph.vcount()
|
|
406
446
|
if num_nodes == 0:
|
|
407
447
|
return []
|
|
408
448
|
|
|
409
|
-
#
|
|
449
|
+
# Create coordinate list (will be ordered by vertex IDs 0 to N-1)
|
|
410
450
|
pos_list = [[0.0, 0.0]] * num_nodes
|
|
411
|
-
#
|
|
412
|
-
nodes = range(num_nodes)
|
|
451
|
+
# Since vertex IDs are already 0 to N-1, we can use range directly
|
|
452
|
+
nodes = range(num_nodes) # Vertex IDs
|
|
413
453
|
|
|
414
|
-
is_vertical = primary_direction in ['
|
|
454
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
415
455
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
416
456
|
|
|
417
457
|
if not (is_vertical or is_horizontal):
|
|
418
458
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
419
459
|
if is_vertical and secondary_start not in ['right', 'left']:
|
|
420
|
-
raise ValueError(f"Invalid secondary_start for vertical: {secondary_start}")
|
|
460
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: {secondary_start}")
|
|
421
461
|
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
422
|
-
raise ValueError(f"Invalid secondary_start for horizontal: {secondary_start}")
|
|
462
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: {secondary_start}")
|
|
423
463
|
|
|
424
|
-
for i in nodes:
|
|
425
|
-
if primary_direction == '
|
|
464
|
+
for i in nodes: # Here, i is the vertex index (0, 1, 2...)
|
|
465
|
+
if primary_direction == 'top_down':
|
|
426
466
|
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
427
|
-
elif primary_direction == '
|
|
467
|
+
elif primary_direction == 'bottom_up':
|
|
428
468
|
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
429
469
|
elif primary_direction == 'left-to-right':
|
|
430
470
|
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
431
|
-
else:
|
|
471
|
+
else: # right-to-left
|
|
432
472
|
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
433
473
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
magnitude = math.ceil(i / 2.0)
|
|
474
|
+
# 2. Calculate Secondary Axis Offset
|
|
475
|
+
secondary_offset = 0.0
|
|
476
|
+
if i > 0:
|
|
477
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
478
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
439
479
|
side = 1 if i % 2 != 0 else -1
|
|
440
|
-
|
|
480
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
441
481
|
|
|
442
|
-
|
|
482
|
+
# 3. Assign Coordinates
|
|
483
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
484
|
+
pos_list[i] = [x, y] # Add [x, y] to the list at the correct index
|
|
443
485
|
|
|
444
|
-
|
|
445
|
-
|
|
486
|
+
# Returning a direct list is the most common and flexible approach.
|
|
487
|
+
# The plot function accepts a list of coordinates directly.
|
|
488
|
+
return pos_list
|
|
446
489
|
|
|
447
|
-
# igraph Layout nesnesi gibi davranması için basit bir nesne döndürelim
|
|
448
|
-
# veya doğrudan koordinat listesini kullanalım. Çizim fonksiyonu listeyi kabul eder.
|
|
449
|
-
# return ig.Layout(pos_list) # İsterseniz Layout nesnesi de döndürebilirsiniz
|
|
450
|
-
return pos_list # Doğrudan liste döndürmek en yaygın ve esnek yoldur
|
|
451
490
|
|
|
452
|
-
def
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
primary_direction='top-down',
|
|
456
|
-
secondary_start='right'):
|
|
457
|
-
"""
|
|
458
|
-
Keçeci Layout v4 - Networkit graf düğümlerine sıralı-zigzag yerleşimi sağlar.
|
|
459
|
-
|
|
460
|
-
Parametreler:
|
|
461
|
-
-------------
|
|
462
|
-
graph : networkit.graph.Graph
|
|
463
|
-
Kenar ve düğüm bilgisi içeren Networkit graf nesnesi.
|
|
464
|
-
primary_spacing : float
|
|
465
|
-
Ana yön mesafesi.
|
|
466
|
-
secondary_spacing : float
|
|
467
|
-
Yan yön mesafesi.
|
|
468
|
-
primary_direction : str
|
|
469
|
-
'top-down', 'bottom-up', 'left-to-right', 'right-to-left'.
|
|
470
|
-
secondary_start : str
|
|
471
|
-
Başlangıç yönü ('right', 'left', 'up', 'down').
|
|
472
|
-
|
|
473
|
-
Dönüş:
|
|
474
|
-
------
|
|
475
|
-
dict[int, tuple[float, float]]
|
|
476
|
-
Her düğüm ID'sinin (Networkit'te genelde integer olur)
|
|
477
|
-
koordinatını içeren sözlük.
|
|
491
|
+
def kececi_layout_nk(graph: "nk.graph.Graph", primary_spacing=1.0, secondary_spacing=1.0,
|
|
492
|
+
primary_direction='top_down', secondary_start='right',
|
|
493
|
+
expanding=True):
|
|
478
494
|
"""
|
|
495
|
+
Expanding Kececi Layout: Progresses along the primary axis, with an offset
|
|
496
|
+
on the secondary axis.
|
|
497
|
+
Kececi Layout - Provides a sequential-zigzag layout for nodes in a NetworKit graph.
|
|
479
498
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
499
|
+
Args:
|
|
500
|
+
graph (networkit.graph.Graph): A NetworKit graph object.
|
|
501
|
+
primary_spacing (float): The distance on the primary axis.
|
|
502
|
+
secondary_spacing (float): The distance on the secondary axis.
|
|
503
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
504
|
+
secondary_start (str): The starting direction for the offset ('right', 'left', 'up', 'down').
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
dict[int, tuple[float, float]]: A dictionary containing the coordinate
|
|
508
|
+
for each node ID (typically an integer in NetworKit).
|
|
509
|
+
"""
|
|
510
|
+
# In NetworKit, node IDs are generally sequential, but let's get a sorted
|
|
511
|
+
# list to be safe. iterNodes() returns the node IDs.
|
|
483
512
|
try:
|
|
484
|
-
# Networkit'te node ID'lerinin contiguous (0..n-1) olup olmadığını kontrol edebiliriz
|
|
485
|
-
# ama her zaman böyle olmayabilir. iterNodes en genel yöntem.
|
|
486
513
|
nodes = sorted(list(graph.iterNodes()))
|
|
487
514
|
except Exception as e:
|
|
488
|
-
print(f"
|
|
489
|
-
#
|
|
490
|
-
# nodes = list(range(graph.numberOfNodes()))
|
|
491
|
-
# Ancak iterNodes daha güvenlidir.
|
|
492
|
-
return {} # Hata durumunda boş dön
|
|
515
|
+
print(f"Error getting NetworKit node list: {e}")
|
|
516
|
+
return {} # Return empty on error
|
|
493
517
|
|
|
494
|
-
num_nodes = len(nodes)
|
|
518
|
+
num_nodes = len(nodes)
|
|
495
519
|
if num_nodes == 0:
|
|
496
|
-
return {}
|
|
520
|
+
return {}
|
|
497
521
|
|
|
498
|
-
pos = {}
|
|
499
|
-
is_vertical = primary_direction in ['
|
|
522
|
+
pos = {}
|
|
523
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
500
524
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
501
525
|
|
|
502
|
-
# Parametre kontrolleri
|
|
503
526
|
if not (is_vertical or is_horizontal):
|
|
504
527
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
505
528
|
if is_vertical and secondary_start not in ['right', 'left']:
|
|
506
|
-
raise ValueError(f"Invalid secondary_start ('{secondary_start}') for vertical primary_direction
|
|
529
|
+
raise ValueError(f"Invalid secondary_start ('{secondary_start}') for vertical primary_direction. Use 'right' or 'left'.")
|
|
507
530
|
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
508
|
-
|
|
531
|
+
raise ValueError(f"Invalid secondary_start ('{secondary_start}') for horizontal primary_direction. Use 'up' or 'down'.")
|
|
509
532
|
|
|
510
|
-
#
|
|
533
|
+
# Main loop
|
|
511
534
|
for i, node_id in enumerate(nodes):
|
|
512
|
-
# i:
|
|
513
|
-
# node_id:
|
|
514
|
-
|
|
515
|
-
# 1.
|
|
516
|
-
if primary_direction == '
|
|
517
|
-
primary_coord = i * -primary_spacing
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
primary_coord = i * primary_spacing
|
|
521
|
-
secondary_axis = 'x'
|
|
535
|
+
# i: The index in the sorted list (0, 1, 2, ...), used for positioning.
|
|
536
|
+
# node_id: The actual NetworKit node ID, used as the key in the result dictionary.
|
|
537
|
+
|
|
538
|
+
# 1. Calculate Primary Axis Coordinate
|
|
539
|
+
if primary_direction == 'top_down':
|
|
540
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
541
|
+
elif primary_direction == 'bottom_up':
|
|
542
|
+
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
522
543
|
elif primary_direction == 'left-to-right':
|
|
523
|
-
primary_coord = i * primary_spacing
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
primary_coord = i * -primary_spacing
|
|
527
|
-
secondary_axis = 'y'
|
|
528
|
-
|
|
529
|
-
# 2. Yan eksen ofsetini hesapla (zigzag)
|
|
530
|
-
if i == 0:
|
|
531
|
-
secondary_offset_multiplier = 0.0
|
|
532
|
-
else:
|
|
533
|
-
start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
534
|
-
magnitude = math.ceil(i / 2.0)
|
|
535
|
-
side = 1 if i % 2 != 0 else -1
|
|
536
|
-
secondary_offset_multiplier = start_mult * magnitude * side
|
|
537
|
-
|
|
538
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
544
|
+
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
545
|
+
else: # 'right-to-left'
|
|
546
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
539
547
|
|
|
540
|
-
#
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
548
|
+
# 2. Calculate Secondary Axis Offset
|
|
549
|
+
secondary_offset = 0.0
|
|
550
|
+
if i > 0:
|
|
551
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
552
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
553
|
+
side = 1 if i % 2 != 0 else -1
|
|
554
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
545
555
|
|
|
546
|
-
#
|
|
556
|
+
# 3. Assign Coordinates
|
|
557
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
547
558
|
pos[node_id] = (x, y)
|
|
548
559
|
|
|
549
560
|
return pos
|
|
550
561
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
secondary_start='right'):
|
|
556
|
-
"""
|
|
557
|
-
Keçeci Layout v4 - Networkit graf düğümlerine sıralı-zigzag yerleşimi sağlar.
|
|
558
|
-
|
|
559
|
-
Parametreler:
|
|
560
|
-
-------------
|
|
561
|
-
graph : networkit.graph.Graph
|
|
562
|
-
Kenar ve düğüm bilgisi içeren Networkit graf nesnesi.
|
|
563
|
-
primary_spacing : float
|
|
564
|
-
Ana yön mesafesi.
|
|
565
|
-
secondary_spacing : float
|
|
566
|
-
Yan yön mesafesi.
|
|
567
|
-
primary_direction : str
|
|
568
|
-
'top-down', 'bottom-up', 'left-to-right', 'right-to-left'.
|
|
569
|
-
secondary_start : str
|
|
570
|
-
Başlangıç yönü ('right', 'left', 'up', 'down').
|
|
571
|
-
|
|
572
|
-
Dönüş:
|
|
573
|
-
------
|
|
574
|
-
dict[int, tuple[float, float]]
|
|
575
|
-
Her düğüm ID'sinin (Networkit'te genelde integer olur)
|
|
576
|
-
koordinatını içeren sözlük.
|
|
562
|
+
|
|
563
|
+
def kececi_layout_networkit(graph: "nk.graph.Graph", primary_spacing=1.0, secondary_spacing=1.0,
|
|
564
|
+
primary_direction='top_down', secondary_start='right',
|
|
565
|
+
expanding=True):
|
|
577
566
|
"""
|
|
567
|
+
Expanding Kececi Layout: Progresses along the primary axis, with an offset
|
|
568
|
+
on the secondary axis.
|
|
569
|
+
Kececi Layout - Provides a sequential-zigzag layout for nodes in a NetworKit graph.
|
|
578
570
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
571
|
+
Args:
|
|
572
|
+
graph (networkit.graph.Graph): A NetworKit graph object.
|
|
573
|
+
primary_spacing (float): The distance on the primary axis.
|
|
574
|
+
secondary_spacing (float): The distance on the secondary axis.
|
|
575
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
576
|
+
secondary_start (str): The starting direction for the offset ('right', 'left', 'up', 'down').
|
|
577
|
+
|
|
578
|
+
Returns:
|
|
579
|
+
dict[int, tuple[float, float]]: A dictionary containing the coordinate
|
|
580
|
+
for each node ID (typically an integer in NetworKit).
|
|
581
|
+
"""
|
|
582
|
+
# In NetworKit, node IDs are generally sequential, but let's get a sorted
|
|
583
|
+
# list to be safe. iterNodes() returns the node IDs.
|
|
582
584
|
try:
|
|
583
|
-
# Networkit'te node ID'lerinin contiguous (0..n-1) olup olmadığını kontrol edebiliriz
|
|
584
|
-
# ama her zaman böyle olmayabilir. iterNodes en genel yöntem.
|
|
585
585
|
nodes = sorted(list(graph.iterNodes()))
|
|
586
586
|
except Exception as e:
|
|
587
|
-
print(f"
|
|
588
|
-
#
|
|
589
|
-
# nodes = list(range(graph.numberOfNodes()))
|
|
590
|
-
# Ancak iterNodes daha güvenlidir.
|
|
591
|
-
return {} # Hata durumunda boş dön
|
|
587
|
+
print(f"Error getting NetworKit node list: {e}")
|
|
588
|
+
return {} # Return empty on error
|
|
592
589
|
|
|
593
|
-
num_nodes = len(nodes)
|
|
590
|
+
num_nodes = len(nodes)
|
|
594
591
|
if num_nodes == 0:
|
|
595
|
-
return {}
|
|
592
|
+
return {}
|
|
596
593
|
|
|
597
|
-
pos = {}
|
|
598
|
-
is_vertical = primary_direction in ['
|
|
594
|
+
pos = {}
|
|
595
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
599
596
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
600
597
|
|
|
601
|
-
# Parametre kontrolleri
|
|
602
598
|
if not (is_vertical or is_horizontal):
|
|
603
599
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
604
600
|
if is_vertical and secondary_start not in ['right', 'left']:
|
|
605
|
-
raise ValueError(f"Invalid secondary_start ('{secondary_start}') for vertical primary_direction
|
|
601
|
+
raise ValueError(f"Invalid secondary_start ('{secondary_start}') for vertical primary_direction. Use 'right' or 'left'.")
|
|
606
602
|
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
607
|
-
|
|
603
|
+
raise ValueError(f"Invalid secondary_start ('{secondary_start}') for horizontal primary_direction. Use 'up' or 'down'.")
|
|
608
604
|
|
|
609
|
-
#
|
|
605
|
+
# Main loop
|
|
610
606
|
for i, node_id in enumerate(nodes):
|
|
611
|
-
# i:
|
|
612
|
-
# node_id:
|
|
613
|
-
|
|
614
|
-
# 1.
|
|
615
|
-
if primary_direction == '
|
|
616
|
-
primary_coord = i * -primary_spacing
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
primary_coord = i * primary_spacing
|
|
620
|
-
secondary_axis = 'x'
|
|
607
|
+
# i: The index in the sorted list (0, 1, 2, ...), used for positioning.
|
|
608
|
+
# node_id: The actual NetworKit node ID, used as the key in the result dictionary.
|
|
609
|
+
|
|
610
|
+
# 1. Calculate Primary Axis Coordinate
|
|
611
|
+
if primary_direction == 'top_down':
|
|
612
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
613
|
+
elif primary_direction == 'bottom_up':
|
|
614
|
+
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
621
615
|
elif primary_direction == 'left-to-right':
|
|
622
|
-
primary_coord = i * primary_spacing
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
primary_coord = i * -primary_spacing
|
|
626
|
-
secondary_axis = 'y'
|
|
627
|
-
|
|
628
|
-
# 2. Yan eksen ofsetini hesapla (zigzag)
|
|
629
|
-
if i == 0:
|
|
630
|
-
secondary_offset_multiplier = 0.0
|
|
631
|
-
else:
|
|
632
|
-
start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
633
|
-
magnitude = math.ceil(i / 2.0)
|
|
634
|
-
side = 1 if i % 2 != 0 else -1
|
|
635
|
-
secondary_offset_multiplier = start_mult * magnitude * side
|
|
636
|
-
|
|
637
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
616
|
+
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
617
|
+
else: # 'right-to-left'
|
|
618
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
638
619
|
|
|
639
|
-
#
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
620
|
+
# 2. Calculate Secondary Axis Offset
|
|
621
|
+
secondary_offset = 0.0
|
|
622
|
+
if i > 0:
|
|
623
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
624
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
625
|
+
side = 1 if i % 2 != 0 else -1
|
|
626
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
644
627
|
|
|
645
|
-
#
|
|
628
|
+
# 3. Assign Coordinates
|
|
629
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
646
630
|
pos[node_id] = (x, y)
|
|
647
631
|
|
|
648
632
|
return pos
|
|
649
633
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
secondary_start='right'):
|
|
655
|
-
"""
|
|
656
|
-
Keçeci Layout v4 - Graphillion evren grafının düğümlerine
|
|
657
|
-
sıralı-zigzag yerleşimi sağlar.
|
|
634
|
+
|
|
635
|
+
def kececi_layout_gg(graph_set: "gg.GraphSet", primary_spacing=1.0, secondary_spacing=1.0,
|
|
636
|
+
primary_direction='top_down', secondary_start='right',
|
|
637
|
+
expanding=True):
|
|
658
638
|
"""
|
|
639
|
+
Expanding Kececi Layout: Progresses along the primary axis, with an offset
|
|
640
|
+
on the secondary axis.
|
|
641
|
+
Kececi Layout - Provides a sequential-zigzag layout for nodes in a Graphillion universe.
|
|
659
642
|
|
|
660
|
-
|
|
643
|
+
Args:
|
|
644
|
+
graph_set (graphillion.GraphSet): A Graphillion GraphSet object.
|
|
645
|
+
primary_spacing (float): The distance on the primary axis.
|
|
646
|
+
secondary_spacing (float): The distance on the secondary axis.
|
|
647
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
648
|
+
secondary_start (str): The starting direction for the offset ('right', 'left', 'up', 'down').
|
|
649
|
+
Returns:
|
|
650
|
+
dict: A dictionary of positions keyed by node ID.
|
|
651
|
+
"""
|
|
652
|
+
# CORRECTION: Get the edge list from the universe.
|
|
661
653
|
edges_in_universe = graph_set.universe()
|
|
662
|
-
|
|
663
|
-
# DÜZELTME: Düğüm sayısını kenarlardan türet
|
|
654
|
+
# CORRECTION: Derive the number of nodes from the edges.
|
|
664
655
|
num_vertices = find_max_node_id(edges_in_universe)
|
|
665
656
|
|
|
666
657
|
if num_vertices == 0:
|
|
667
658
|
return {}
|
|
668
659
|
|
|
669
|
-
# Graphillion
|
|
670
|
-
#
|
|
671
|
-
nodes = list(range(1, num_vertices + 1))
|
|
660
|
+
# Graphillion often uses 1-based node indexing.
|
|
661
|
+
# Create the node ID list: 1, 2, ..., num_vertices
|
|
662
|
+
nodes = list(range(1, num_vertices + 1))
|
|
672
663
|
|
|
673
|
-
pos = {}
|
|
674
|
-
is_vertical = primary_direction in ['
|
|
664
|
+
pos = {}
|
|
665
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
675
666
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
676
667
|
|
|
677
|
-
# Parametre kontrolleri (değişiklik yok)
|
|
678
668
|
if not (is_vertical or is_horizontal):
|
|
679
669
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
680
670
|
if is_vertical and secondary_start not in ['right', 'left']:
|
|
681
|
-
raise ValueError(f"Invalid secondary_start
|
|
671
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: {secondary_start}")
|
|
682
672
|
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
683
|
-
|
|
673
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: {secondary_start}")
|
|
684
674
|
|
|
685
|
-
# Ana döngü (değişiklik yok)
|
|
686
675
|
for i, node_id in enumerate(nodes):
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
secondary_axis = 'x'
|
|
691
|
-
elif primary_direction == 'bottom-up':
|
|
692
|
-
primary_coord = i * primary_spacing;
|
|
693
|
-
secondary_axis = 'x'
|
|
676
|
+
if primary_direction == 'top_down':
|
|
677
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
678
|
+
elif primary_direction == 'bottom_up':
|
|
679
|
+
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
694
680
|
elif primary_direction == 'left-to-right':
|
|
695
|
-
primary_coord = i * primary_spacing
|
|
696
|
-
secondary_axis = 'y'
|
|
681
|
+
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
697
682
|
else: # right-to-left
|
|
698
|
-
primary_coord = i * -primary_spacing
|
|
699
|
-
secondary_axis = 'y'
|
|
683
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
700
684
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
magnitude = math.ceil(i / 2.0)
|
|
685
|
+
# 2. Calculate Secondary Axis Offset
|
|
686
|
+
secondary_offset = 0.0
|
|
687
|
+
if i > 0:
|
|
688
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
689
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
706
690
|
side = 1 if i % 2 != 0 else -1
|
|
707
|
-
|
|
708
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
691
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
709
692
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
else:
|
|
713
|
-
x, y = primary_coord, secondary_coord
|
|
693
|
+
# 3. Assign Coordinates
|
|
694
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
714
695
|
pos[node_id] = (x, y)
|
|
715
696
|
|
|
716
697
|
return pos
|
|
717
698
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
secondary_start='right'):
|
|
723
|
-
"""
|
|
724
|
-
Keçeci Layout v4 - Graphillion evren grafının düğümlerine
|
|
725
|
-
sıralı-zigzag yerleşimi sağlar.
|
|
699
|
+
|
|
700
|
+
def kececi_layout_graphillion(graph_set: "gg.GraphSet", primary_spacing=1.0, secondary_spacing=1.0,
|
|
701
|
+
primary_direction='top_down', secondary_start='right',
|
|
702
|
+
expanding=True):
|
|
726
703
|
"""
|
|
704
|
+
Expanding Kececi Layout: Progresses along the primary axis, with an offset
|
|
705
|
+
on the secondary axis.
|
|
706
|
+
Kececi Layout - Provides a sequential-zigzag layout for nodes in a Graphillion universe.
|
|
727
707
|
|
|
728
|
-
|
|
708
|
+
Args:
|
|
709
|
+
graph_set (graphillion.GraphSet): A Graphillion GraphSet object.
|
|
710
|
+
primary_spacing (float): The distance on the primary axis.
|
|
711
|
+
secondary_spacing (float): The distance on the secondary axis.
|
|
712
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
713
|
+
secondary_start (str): The starting direction for the offset ('right', 'left', 'up', 'down').
|
|
714
|
+
Returns:
|
|
715
|
+
dict: A dictionary of positions keyed by node ID.
|
|
716
|
+
"""
|
|
717
|
+
# CORRECTION: Get the edge list from the universe.
|
|
729
718
|
edges_in_universe = graph_set.universe()
|
|
730
|
-
|
|
731
|
-
# DÜZELTME: Düğüm sayısını kenarlardan türet
|
|
719
|
+
# CORRECTION: Derive the number of nodes from the edges.
|
|
732
720
|
num_vertices = find_max_node_id(edges_in_universe)
|
|
733
721
|
|
|
734
722
|
if num_vertices == 0:
|
|
735
723
|
return {}
|
|
736
724
|
|
|
737
|
-
# Graphillion
|
|
738
|
-
#
|
|
739
|
-
nodes = list(range(1, num_vertices + 1))
|
|
725
|
+
# Graphillion often uses 1-based node indexing.
|
|
726
|
+
# Create the node ID list: 1, 2, ..., num_vertices
|
|
727
|
+
nodes = list(range(1, num_vertices + 1))
|
|
740
728
|
|
|
741
|
-
pos = {}
|
|
742
|
-
is_vertical = primary_direction in ['
|
|
729
|
+
pos = {}
|
|
730
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
743
731
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
744
732
|
|
|
745
|
-
# Parametre kontrolleri (değişiklik yok)
|
|
746
733
|
if not (is_vertical or is_horizontal):
|
|
747
734
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
748
735
|
if is_vertical and secondary_start not in ['right', 'left']:
|
|
749
|
-
raise ValueError(f"Invalid secondary_start
|
|
736
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: {secondary_start}")
|
|
750
737
|
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
751
|
-
|
|
738
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: {secondary_start}")
|
|
752
739
|
|
|
753
|
-
# Ana döngü (değişiklik yok)
|
|
754
740
|
for i, node_id in enumerate(nodes):
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
secondary_axis = 'x'
|
|
759
|
-
elif primary_direction == 'bottom-up':
|
|
760
|
-
primary_coord = i * primary_spacing;
|
|
761
|
-
secondary_axis = 'x'
|
|
741
|
+
if primary_direction == 'top_down':
|
|
742
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
743
|
+
elif primary_direction == 'bottom_up':
|
|
744
|
+
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
762
745
|
elif primary_direction == 'left-to-right':
|
|
763
|
-
primary_coord = i * primary_spacing
|
|
764
|
-
secondary_axis = 'y'
|
|
746
|
+
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
765
747
|
else: # right-to-left
|
|
766
|
-
primary_coord = i * -primary_spacing
|
|
767
|
-
secondary_axis = 'y'
|
|
748
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
768
749
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
magnitude = math.ceil(i / 2.0)
|
|
750
|
+
# 2. Calculate Secondary Axis Offset
|
|
751
|
+
secondary_offset = 0.0
|
|
752
|
+
if i > 0:
|
|
753
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
754
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
774
755
|
side = 1 if i % 2 != 0 else -1
|
|
775
|
-
|
|
776
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
756
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
777
757
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
else:
|
|
781
|
-
x, y = primary_coord, secondary_coord
|
|
758
|
+
# 3. Assign Coordinates
|
|
759
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
782
760
|
pos[node_id] = (x, y)
|
|
783
761
|
|
|
784
762
|
return pos
|
|
785
763
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
764
|
+
|
|
765
|
+
def kececi_layout_rx(graph: "rx.PyGraph", primary_spacing=1.0, secondary_spacing=1.0,
|
|
766
|
+
primary_direction='top_down', secondary_start='right',
|
|
767
|
+
expanding=True):
|
|
768
|
+
"""
|
|
769
|
+
Expanding Kececi Layout: Progresses along the primary axis, with an offset
|
|
770
|
+
on the secondary axis.
|
|
771
|
+
Kececi layout for a Rustworkx PyGraph object.
|
|
772
|
+
|
|
773
|
+
Args:
|
|
774
|
+
graph (rustworkx.PyGraph): A Rustworkx graph object.
|
|
775
|
+
primary_spacing (float): The spacing between nodes on the primary axis.
|
|
776
|
+
secondary_spacing (float): The offset spacing on the secondary axis.
|
|
777
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
778
|
+
secondary_start (str): Initial direction for the offset ('right', 'left', 'up', 'down').
|
|
779
|
+
|
|
780
|
+
Returns:
|
|
781
|
+
dict: A dictionary of positions keyed by node index, where values are numpy arrays.
|
|
782
|
+
"""
|
|
789
783
|
pos = {}
|
|
790
784
|
nodes = sorted(graph.node_indices())
|
|
791
785
|
num_nodes = len(nodes)
|
|
792
|
-
if num_nodes == 0:
|
|
786
|
+
if num_nodes == 0:
|
|
793
787
|
return {}
|
|
794
788
|
|
|
795
|
-
is_vertical = primary_direction in ['
|
|
789
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
796
790
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
797
|
-
if not (is_vertical or is_horizontal):
|
|
791
|
+
if not (is_vertical or is_horizontal):
|
|
798
792
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
799
|
-
if is_vertical and secondary_start not in ['right', 'left']:
|
|
800
|
-
raise ValueError(f"Invalid secondary_start for vertical: {secondary_start}")
|
|
801
|
-
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
802
|
-
raise ValueError(f"Invalid secondary_start for horizontal: {secondary_start}")
|
|
793
|
+
if is_vertical and secondary_start not in ['right', 'left']:
|
|
794
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: {secondary_start}")
|
|
795
|
+
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
796
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: {secondary_start}")
|
|
803
797
|
|
|
804
798
|
for i, node_index in enumerate(nodes):
|
|
805
|
-
if primary_direction == '
|
|
799
|
+
if primary_direction == 'top_down':
|
|
806
800
|
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
807
|
-
elif primary_direction == '
|
|
801
|
+
elif primary_direction == 'bottom_up':
|
|
808
802
|
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
809
|
-
elif primary_direction == 'left-to-right':
|
|
803
|
+
elif primary_direction == 'left-to-right':
|
|
810
804
|
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
811
|
-
else:
|
|
805
|
+
else:
|
|
812
806
|
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
813
807
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
magnitude = math.ceil(i / 2.0)
|
|
808
|
+
# 2. Calculate Secondary Axis Offset
|
|
809
|
+
secondary_offset = 0.0
|
|
810
|
+
if i > 0:
|
|
811
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
812
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
819
813
|
side = 1 if i % 2 != 0 else -1
|
|
820
|
-
|
|
821
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
814
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
822
815
|
|
|
823
|
-
|
|
816
|
+
# 3. Assign Coordinates
|
|
817
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
824
818
|
pos[node_index] = np.array([x, y])
|
|
819
|
+
|
|
825
820
|
return pos
|
|
826
821
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
822
|
+
|
|
823
|
+
def kececi_layout_rustworkx(graph: "rx.PyGraph", primary_spacing=1.0, secondary_spacing=1.0,
|
|
824
|
+
primary_direction='top_down', secondary_start='right',
|
|
825
|
+
expanding=True):
|
|
826
|
+
"""
|
|
827
|
+
Expanding Kececi Layout: Progresses along the primary axis, with an offset
|
|
828
|
+
on the secondary axis.
|
|
829
|
+
Kececi layout for a Rustworkx PyGraph object.
|
|
830
|
+
|
|
831
|
+
Args:
|
|
832
|
+
graph (rustworkx.PyGraph): A Rustworkx graph object.
|
|
833
|
+
primary_spacing (float): The spacing between nodes on the primary axis.
|
|
834
|
+
secondary_spacing (float): The offset spacing on the secondary axis.
|
|
835
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
836
|
+
secondary_start (str): Initial direction for the offset ('right', 'left', 'up', 'down').
|
|
837
|
+
|
|
838
|
+
Returns:
|
|
839
|
+
dict: A dictionary of positions keyed by node index, where values are numpy arrays.
|
|
840
|
+
"""
|
|
830
841
|
pos = {}
|
|
831
842
|
nodes = sorted(graph.node_indices())
|
|
832
843
|
num_nodes = len(nodes)
|
|
833
|
-
if num_nodes == 0:
|
|
844
|
+
if num_nodes == 0:
|
|
834
845
|
return {}
|
|
835
846
|
|
|
836
|
-
is_vertical = primary_direction in ['
|
|
847
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
837
848
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
838
|
-
if not (is_vertical or is_horizontal):
|
|
849
|
+
if not (is_vertical or is_horizontal):
|
|
839
850
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
840
|
-
if is_vertical and secondary_start not in ['right', 'left']:
|
|
841
|
-
raise ValueError(f"Invalid secondary_start for vertical: {secondary_start}")
|
|
842
|
-
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
843
|
-
raise ValueError(f"Invalid secondary_start for horizontal: {secondary_start}")
|
|
851
|
+
if is_vertical and secondary_start not in ['right', 'left']:
|
|
852
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: {secondary_start}")
|
|
853
|
+
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
854
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: {secondary_start}")
|
|
844
855
|
|
|
845
856
|
for i, node_index in enumerate(nodes):
|
|
846
|
-
if primary_direction == '
|
|
857
|
+
if primary_direction == 'top_down':
|
|
847
858
|
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
848
|
-
elif primary_direction == '
|
|
859
|
+
elif primary_direction == 'bottom_up':
|
|
849
860
|
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
850
|
-
elif primary_direction == 'left-to-right':
|
|
861
|
+
elif primary_direction == 'left-to-right':
|
|
851
862
|
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
852
|
-
else:
|
|
863
|
+
else:
|
|
853
864
|
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
854
865
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
magnitude = math.ceil(i / 2.0)
|
|
866
|
+
# 2. Calculate Secondary Axis Offset
|
|
867
|
+
secondary_offset = 0.0
|
|
868
|
+
if i > 0:
|
|
869
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
870
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
860
871
|
side = 1 if i % 2 != 0 else -1
|
|
861
|
-
|
|
862
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
872
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
863
873
|
|
|
864
|
-
|
|
874
|
+
# 3. Assign Coordinates
|
|
875
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
865
876
|
pos[node_index] = np.array([x, y])
|
|
877
|
+
|
|
878
|
+
return pos
|
|
879
|
+
|
|
880
|
+
def kececi_layout_pure(nodes, primary_spacing=1.0, secondary_spacing=1.0,
|
|
881
|
+
primary_direction='top_down', secondary_start='right',
|
|
882
|
+
expanding=True):
|
|
883
|
+
"""
|
|
884
|
+
Calculates 2D sequential-zigzag coordinates for a given list of nodes.
|
|
885
|
+
This function does not require any external graph library.
|
|
886
|
+
|
|
887
|
+
Args:
|
|
888
|
+
nodes (iterable): A list or other iterable containing the node IDs to be positioned.
|
|
889
|
+
primary_spacing (float): The distance between nodes along the primary axis.
|
|
890
|
+
secondary_spacing (float): The base unit for the zigzag offset.
|
|
891
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', or 'right-to-left'.
|
|
892
|
+
secondary_start (str): The initial direction for the zigzag ('up', 'down', 'left', 'right').
|
|
893
|
+
expanding (bool): If True (default), the zigzag offset grows.
|
|
894
|
+
If False, the offset is constant (resulting in parallel lines).
|
|
895
|
+
|
|
896
|
+
Returns:
|
|
897
|
+
dict: A dictionary of positions formatted as {node_id: (x, y)}.
|
|
898
|
+
"""
|
|
899
|
+
try:
|
|
900
|
+
# Try to sort the nodes for a consistent output.
|
|
901
|
+
sorted_nodes = sorted(list(nodes))
|
|
902
|
+
except TypeError:
|
|
903
|
+
# For unsortable nodes (e.g., mixed types), keep the original order.
|
|
904
|
+
sorted_nodes = list(nodes)
|
|
905
|
+
|
|
906
|
+
pos = {}
|
|
907
|
+
|
|
908
|
+
# --- Direction Validation Block ---
|
|
909
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
910
|
+
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
911
|
+
|
|
912
|
+
if not (is_vertical or is_horizontal):
|
|
913
|
+
raise ValueError(f"Invalid primary_direction: '{primary_direction}'")
|
|
914
|
+
if is_vertical and secondary_start not in ['right', 'left']:
|
|
915
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: '{secondary_start}'")
|
|
916
|
+
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
917
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: '{secondary_start}'")
|
|
918
|
+
# --- End of Block ---
|
|
919
|
+
|
|
920
|
+
for i, node_id in enumerate(sorted_nodes):
|
|
921
|
+
# 1. Calculate the Primary Axis Coordinate
|
|
922
|
+
primary_coord = 0.0
|
|
923
|
+
secondary_axis = ''
|
|
924
|
+
if primary_direction == 'top_down':
|
|
925
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
926
|
+
elif primary_direction == 'bottom_up':
|
|
927
|
+
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
928
|
+
elif primary_direction == 'left-to-right':
|
|
929
|
+
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
930
|
+
else: # 'right-to-left'
|
|
931
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
932
|
+
|
|
933
|
+
# 2. Calculate the Secondary Axis Offset
|
|
934
|
+
secondary_offset = 0.0
|
|
935
|
+
if i > 0:
|
|
936
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
937
|
+
|
|
938
|
+
# Determine the offset magnitude based on the 'expanding' flag.
|
|
939
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
940
|
+
|
|
941
|
+
# Determine the zigzag side (e.g., left vs. right).
|
|
942
|
+
side = 1 if i % 2 != 0 else -1
|
|
943
|
+
|
|
944
|
+
# Calculate the final offset.
|
|
945
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
946
|
+
|
|
947
|
+
# 3. Assign the (x, y) Coordinates
|
|
948
|
+
x, y = ((secondary_offset, primary_coord) if secondary_axis == 'x' else
|
|
949
|
+
(primary_coord, secondary_offset))
|
|
950
|
+
pos[node_id] = (x, y)
|
|
951
|
+
|
|
866
952
|
return pos
|
|
867
953
|
|
|
868
954
|
# =============================================================================
|
|
@@ -909,61 +995,8 @@ def generate_random_rx_graph(min_nodes=5, max_nodes=15, edge_prob_min=0.15, edge
|
|
|
909
995
|
print(f"Oluşturulan Rustworkx Graf: {G.num_nodes()} Düğüm, {G.num_edges()} Kenar (Başlangıç p={edge_probability:.3f})")
|
|
910
996
|
return G
|
|
911
997
|
|
|
912
|
-
|
|
913
|
-
def kececi_layout_v4_pure(nodes, primary_spacing=1.0, secondary_spacing=1.0,
|
|
914
|
-
primary_direction='top-down', secondary_start='right'):
|
|
915
|
-
"""
|
|
916
|
-
Keçeci layout mantığını kullanarak düğüm pozisyonlarını hesaplar.
|
|
917
|
-
Sadece standart Python ve math modülünü kullanır.
|
|
918
|
-
"""
|
|
919
|
-
pos = {}
|
|
920
|
-
# Tutarlı sıra garantisi için düğümleri sırala
|
|
921
|
-
# Girdi zaten liste/tuple olsa bile kopyasını oluşturup sırala
|
|
922
|
-
# ... (Bir önceki cevaptaki fonksiyonun TAMAMI buraya yapıştırılacak) ...
|
|
923
|
-
try:
|
|
924
|
-
sorted_nodes = sorted(list(nodes))
|
|
925
|
-
except TypeError:
|
|
926
|
-
print("Uyarı: Düğümler sıralanamadı...")
|
|
927
|
-
sorted_nodes = list(nodes)
|
|
928
|
-
|
|
929
|
-
num_nodes = len(sorted_nodes)
|
|
930
|
-
if num_nodes == 0:
|
|
931
|
-
return {}
|
|
932
|
-
is_vertical = primary_direction in ['top-down', 'bottom-up']
|
|
933
|
-
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
934
|
-
if not (is_vertical or is_horizontal):
|
|
935
|
-
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
936
|
-
if is_vertical and secondary_start not in ['right', 'left']:
|
|
937
|
-
raise ValueError(f"Dikey yön için geçersiz secondary_start: {secondary_start}")
|
|
938
|
-
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
939
|
-
raise ValueError(f"Yatay yön için geçersiz secondary_start: {secondary_start}")
|
|
940
|
-
|
|
941
|
-
for i, node_id in enumerate(sorted_nodes):
|
|
942
|
-
primary_coord = 0.0
|
|
943
|
-
secondary_axis = ''
|
|
944
|
-
if primary_direction == 'top-down':
|
|
945
|
-
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
946
|
-
elif primary_direction == 'bottom-up':
|
|
947
|
-
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
948
|
-
elif primary_direction == 'left-to-right':
|
|
949
|
-
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
950
|
-
else:
|
|
951
|
-
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
952
|
-
|
|
953
|
-
secondary_offset_multiplier = 0.0
|
|
954
|
-
if i > 0:
|
|
955
|
-
start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
956
|
-
magnitude = math.ceil(i / 2.0)
|
|
957
|
-
side = 1 if i % 2 != 0 else -1
|
|
958
|
-
secondary_offset_multiplier = start_mult * magnitude * side
|
|
959
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
960
|
-
|
|
961
|
-
x, y = (secondary_coord, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_coord)
|
|
962
|
-
pos[node_id] = (x, y)
|
|
963
|
-
return pos
|
|
964
|
-
|
|
965
998
|
# =============================================================================
|
|
966
|
-
# Rastgele Graf Oluşturma Fonksiyonu (NetworkX)
|
|
999
|
+
# Rastgele Graf Oluşturma Fonksiyonu (NetworkX)
|
|
967
1000
|
# =============================================================================
|
|
968
1001
|
def generate_random_graph(min_nodes=0, max_nodes=200, edge_prob_min=0.15, edge_prob_max=0.4):
|
|
969
1002
|
|
|
@@ -1237,3 +1270,5 @@ if __name__ == '__main__':
|
|
|
1237
1270
|
plt.tight_layout(rect=[0, 0, 1, 0.96])
|
|
1238
1271
|
plt.show()
|
|
1239
1272
|
|
|
1273
|
+
|
|
1274
|
+
|