kececilayout 0.2.9__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.
@@ -51,16 +51,37 @@ except ImportError:
51
51
 
52
52
 
53
53
  def find_max_node_id(edges):
54
- """Verilen kenar listesindeki en büyük düğüm ID'sini bulur."""
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
- # Tüm düğüm ID'lerini tek bir kümede topla ve en büyüğünü bul
59
- all_nodes = set(itertools.chain.from_iterable(edges))
60
- return max(all_nodes) if all_nodes else 0
61
- except TypeError: # Eğer kenarlar (node, node) formatında değilse
62
- print("Uyarı: Kenar formatı beklenenden farklı, max node ID 0 varsayıldı.")
63
- return 0
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 ['top-down', 'bottom-up']
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 == 'top-down':
131
+ if primary_direction == 'top_down':
111
132
  primary_coord, secondary_axis = i * -primary_spacing, 'x'
112
- elif primary_direction == 'bottom-up':
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 == 'top-down':
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 kececi_layout_v4_nx(graph, primary_spacing=1.0, secondary_spacing=1.0,
224
- primary_direction='top-down', secondary_start='right'):
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
- Genişletilmiş Keçeci Düzeni: Ana eksen boyunca ilerler, ikincil eksende artan şekilde sapar.
227
- Düğümler ikincil eksende daha geniş bir alana yayılır.
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
- num_nodes = len(nodes)
233
- if num_nodes == 0:
265
+ if not nodes:
234
266
  return {}
235
267
 
236
- is_vertical = primary_direction in ['top-down', 'bottom-up']
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. Ana Eksen Koordinatını Hesapla
247
- if primary_direction == 'top-down':
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 == 'bottom-up':
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: # right-to-left
286
+ else:
254
287
  primary_coord, secondary_axis = i * -primary_spacing, 'y'
255
288
 
256
- # 2. İkincil Eksen Koordinatını Hesapla (Genişletilmiş Sapma)
257
- if i == 0:
258
- secondary_offset_multiplier = 0.0
259
- else:
260
- # Sapma yönünü belirle (sağ/yukarı +1, sol/aşağı -1)
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
- secondary_offset_multiplier = start_mult * magnitude * side
267
-
268
- secondary_coord = secondary_offset_multiplier * secondary_spacing
295
+ secondary_offset = start_multiplier * magnitude * side * secondary_spacing
269
296
 
270
- # 3. (x, y) Koordinatlarını Ata
271
- x, y = (secondary_coord, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_coord)
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 kececi_layout_v4_networkx(graph, primary_spacing=1.0, secondary_spacing=1.0,
277
- primary_direction='top-down', secondary_start='right'):
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
- Genişletilmiş Keçeci Düzeni: Ana eksen boyunca ilerler, ikincil eksende artan şekilde sapar.
280
- Düğümler ikincil eksende daha geniş bir alana yayılır.
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
- num_nodes = len(nodes)
286
- if num_nodes == 0:
324
+ if not nodes:
287
325
  return {}
288
326
 
289
- is_vertical = primary_direction in ['top-down', 'bottom-up']
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. Ana Eksen Koordinatını Hesapla
300
- if primary_direction == 'top-down':
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 == 'bottom-up':
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: # right-to-left
345
+ else:
307
346
  primary_coord, secondary_axis = i * -primary_spacing, 'y'
308
347
 
309
- # 2. İkincil Eksen Koordinatını Hesapla (Genişletilmiş Sapma)
310
- if i == 0:
311
- secondary_offset_multiplier = 0.0
312
- else:
313
- # Sapma yönünü belirle (sağ/yukarı +1, sol/aşağı -1)
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
- secondary_offset_multiplier = start_mult * magnitude * side
320
-
321
- secondary_coord = secondary_offset_multiplier * secondary_spacing
354
+ secondary_offset = start_multiplier * magnitude * side * secondary_spacing
322
355
 
323
- # 3. (x, y) Koordinatlarını Ata
324
- x, y = (secondary_coord, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_coord)
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 kececi_layout_v4_ig(graph: ig.Graph, primary_spacing=1.0, secondary_spacing=1.0,
331
- primary_direction='top-down', secondary_start='right'):
332
- """igraph.Graph nesnesi için Keçeci layout.
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 nesnesi.
336
- primary_spacing: Ana eksendeki düğümler arasındaki boşluk.
337
- secondary_spacing: İkincil eksendeki ofset boşluğu.
338
- primary_direction: Ana eksenin yönü ('top-down', 'bottom-up', 'left-to-right', 'right-to-left').
339
- secondary_start: İkincil eksendeki ilk ofsetin yönü ('right', 'left', 'up', 'down').
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
- Vertex ID'lerine göre sıralanmış koordinatların listesi (ör: [[x0,y0], [x1,y1], ...]).
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
- # Koordinat listesi oluştur (vertex ID'leri 0'dan N-1'e sıralı olacak şekilde)
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
- # Vertex ID'leri zaten 0'dan N-1'e olduğu için doğrudan range kullanabiliriz
351
- nodes = range(num_nodes) # Vertex ID'leri
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 ['top-down', 'bottom-up']
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: # i burada vertex index'i (0, 1, 2...)
364
- if primary_direction == 'top-down':
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 == 'bottom-up':
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: # right-to-left
407
+ else: # right-to-left
371
408
  primary_coord, secondary_axis = i * -primary_spacing, 'y'
372
409
 
373
- if i == 0:
374
- secondary_offset_multiplier = 0.0
375
- else:
376
- start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
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
- secondary_offset_multiplier = start_mult * magnitude * side
416
+ secondary_offset = start_multiplier * magnitude * side * secondary_spacing
380
417
 
381
- secondary_coord = secondary_offset_multiplier * secondary_spacing
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
- x, y = (secondary_coord, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_coord)
384
- pos_list[i] = [x, y] # Listeye doğru index'e [x, y] olarak ekle
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 kececi_layout_v4_igraph(graph: ig.Graph, primary_spacing=1.0, secondary_spacing=1.0,
392
- primary_direction='top-down', secondary_start='right'):
393
- """igraph.Graph nesnesi için Keçeci layout.
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 nesnesi.
397
- primary_spacing: Ana eksendeki düğümler arasındaki boşluk.
398
- secondary_spacing: İkincil eksendeki ofset boşluğu.
399
- primary_direction: Ana eksenin yönü ('top-down', 'bottom-up', 'left-to-right', 'right-to-left').
400
- secondary_start: İkincil eksendeki ilk ofsetin yönü ('right', 'left', 'up', 'down').
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
- Vertex ID'lerine göre sıralanmış koordinatların listesi (ör: [[x0,y0], [x1,y1], ...]).
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
- # Koordinat listesi oluştur (vertex ID'leri 0'dan N-1'e sıralı olacak şekilde)
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
- # Vertex ID'leri zaten 0'dan N-1'e olduğu için doğrudan range kullanabiliriz
412
- nodes = range(num_nodes) # Vertex ID'leri
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 ['top-down', 'bottom-up']
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: # i burada vertex index'i (0, 1, 2...)
425
- if primary_direction == 'top-down':
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 == 'bottom-up':
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: # right-to-left
471
+ else: # right-to-left
432
472
  primary_coord, secondary_axis = i * -primary_spacing, 'y'
433
473
 
434
- if i == 0:
435
- secondary_offset_multiplier = 0.0
436
- else:
437
- start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
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
- secondary_offset_multiplier = start_mult * magnitude * side
480
+ secondary_offset = start_multiplier * magnitude * side * secondary_spacing
441
481
 
442
- secondary_coord = secondary_offset_multiplier * secondary_spacing
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
- x, y = (secondary_coord, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_coord)
445
- pos_list[i] = [x, y] # Listeye doğru index'e [x, y] olarak ekle
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 kececi_layout_v4_nk(graph: nk.graph.Graph,
453
- primary_spacing=1.0,
454
- secondary_spacing=1.0,
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
- # Networkit'te düğüm ID'leri genellikle 0'dan N-1'e sıralıdır,
481
- # ancak garantiye almak için sıralı bir liste alalım.
482
- # iterNodes() düğüm ID'lerini döndürür.
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"Networkit düğüm listesi alınırken hata: {e}")
489
- # Alternatif olarak, eğer ID'lerin 0'dan başladığı varsayılıyorsa:
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) # Veya graph.numberOfNodes()
518
+ num_nodes = len(nodes)
495
519
  if num_nodes == 0:
496
- return {} # Boş graf için boş sözlük döndür
520
+ return {}
497
521
 
498
- pos = {} # Sonuç sözlüğü
499
- is_vertical = primary_direction in ['top-down', 'bottom-up']
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 ('{primary_direction}'). Use 'right' or 'left'.")
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
- raise ValueError(f"Invalid secondary_start ('{secondary_start}') for horizontal primary_direction ('{primary_direction}'). Use 'up' or 'down'.")
531
+ raise ValueError(f"Invalid secondary_start ('{secondary_start}') for horizontal primary_direction. Use 'up' or 'down'.")
509
532
 
510
- # Ana döngü
533
+ # Main loop
511
534
  for i, node_id in enumerate(nodes):
512
- # i: Düğümün sıralı listedeki indeksi (0, 1, 2, ...) - Yerleşim için kullanılır
513
- # node_id: Gerçek Networkit düğüm ID'si - Sonuç sözlüğünün anahtarı
514
-
515
- # 1. Ana eksen koordinatını hesapla
516
- if primary_direction == 'top-down':
517
- primary_coord = i * -primary_spacing
518
- secondary_axis = 'x'
519
- elif primary_direction == 'bottom-up':
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
- secondary_axis = 'y'
525
- else: # primary_direction == 'right-to-left'
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
- # 3. (x, y) koordinatlarını ata
541
- if secondary_axis == 'x':
542
- x, y = secondary_coord, primary_coord
543
- else: # secondary_axis == 'y'
544
- x, y = primary_coord, secondary_coord
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
- # Sonuç sözlüğüne ekle: anahtar=düğüm ID, değer=(x, y) tuple'ı
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
- def kececi_layout_v4_networkit(graph: nk.graph.Graph,
552
- primary_spacing=1.0,
553
- secondary_spacing=1.0,
554
- primary_direction='top-down',
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
- # Networkit'te düğüm ID'leri genellikle 0'dan N-1'e sıralıdır,
580
- # ancak garantiye almak için sıralı bir liste alalım.
581
- # iterNodes() düğüm ID'lerini döndürür.
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"Networkit düğüm listesi alınırken hata: {e}")
588
- # Alternatif olarak, eğer ID'lerin 0'dan başladığı varsayılıyorsa:
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) # Veya graph.numberOfNodes()
590
+ num_nodes = len(nodes)
594
591
  if num_nodes == 0:
595
- return {} # Boş graf için boş sözlük döndür
592
+ return {}
596
593
 
597
- pos = {} # Sonuç sözlüğü
598
- is_vertical = primary_direction in ['top-down', 'bottom-up']
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 ('{primary_direction}'). Use 'right' or 'left'.")
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
- raise ValueError(f"Invalid secondary_start ('{secondary_start}') for horizontal primary_direction ('{primary_direction}'). Use 'up' or 'down'.")
603
+ raise ValueError(f"Invalid secondary_start ('{secondary_start}') for horizontal primary_direction. Use 'up' or 'down'.")
608
604
 
609
- # Ana döngü
605
+ # Main loop
610
606
  for i, node_id in enumerate(nodes):
611
- # i: Düğümün sıralı listedeki indeksi (0, 1, 2, ...) - Yerleşim için kullanılır
612
- # node_id: Gerçek Networkit düğüm ID'si - Sonuç sözlüğünün anahtarı
613
-
614
- # 1. Ana eksen koordinatını hesapla
615
- if primary_direction == 'top-down':
616
- primary_coord = i * -primary_spacing
617
- secondary_axis = 'x'
618
- elif primary_direction == 'bottom-up':
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
- secondary_axis = 'y'
624
- else: # primary_direction == 'right-to-left'
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
- # 3. (x, y) koordinatlarını ata
640
- if secondary_axis == 'x':
641
- x, y = secondary_coord, primary_coord
642
- else: # secondary_axis == 'y'
643
- x, y = primary_coord, secondary_coord
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
- # Sonuç sözlüğüne ekle: anahtar=düğüm ID, değer=(x, y) tuple'ı
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
- def kececi_layout_v4_gg(graph_set: gg.GraphSet,
651
- primary_spacing=1.0,
652
- secondary_spacing=1.0,
653
- primary_direction='top-down',
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
- # DÜZELTME: Evrenden kenar listesini al
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 genellikle 1-tabanlı düğüm indekslemesi kullanır.
670
- # Düğüm ID listesini oluştur: 1, 2, ..., num_vertices
671
- nodes = list(range(1, num_vertices + 1)) # En yüksek ID'ye kadar tüm nodları varsay
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 = {} # Sonuç sözlüğü
674
- is_vertical = primary_direction in ['top-down', 'bottom-up']
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 ('{secondary_start}') for vertical primary_direction ('{primary_direction}'). Use 'right' or 'left'.")
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
- raise ValueError(f"Invalid secondary_start ('{secondary_start}') for horizontal primary_direction ('{primary_direction}'). Use 'up' or 'down'.")
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
- # ... (Koordinat hesaplama kısmı aynı kalır) ...
688
- if primary_direction == 'top-down':
689
- primary_coord = i * -primary_spacing;
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
- if i == 0:
702
- secondary_offset_multiplier = 0.0
703
- else:
704
- start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
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
- secondary_offset_multiplier = start_mult * magnitude * side
708
- secondary_coord = secondary_offset_multiplier * secondary_spacing
691
+ secondary_offset = start_multiplier * magnitude * side * secondary_spacing
709
692
 
710
- if secondary_axis == 'x':
711
- x, y = secondary_coord, primary_coord
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
- def kececi_layout_v4_graphillion(graph_set: gg.GraphSet,
719
- primary_spacing=1.0,
720
- secondary_spacing=1.0,
721
- primary_direction='top-down',
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
- # DÜZELTME: Evrenden kenar listesini al
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 genellikle 1-tabanlı düğüm indekslemesi kullanır.
738
- # Düğüm ID listesini oluştur: 1, 2, ..., num_vertices
739
- nodes = list(range(1, num_vertices + 1)) # En yüksek ID'ye kadar tüm nodları varsay
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 = {} # Sonuç sözlüğü
742
- is_vertical = primary_direction in ['top-down', 'bottom-up']
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 ('{secondary_start}') for vertical primary_direction ('{primary_direction}'). Use 'right' or 'left'.")
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
- raise ValueError(f"Invalid secondary_start ('{secondary_start}') for horizontal primary_direction ('{primary_direction}'). Use 'up' or 'down'.")
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
- # ... (Koordinat hesaplama kısmı aynı kalır) ...
756
- if primary_direction == 'top-down':
757
- primary_coord = i * -primary_spacing;
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
- if i == 0:
770
- secondary_offset_multiplier = 0.0
771
- else:
772
- start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
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
- secondary_offset_multiplier = start_mult * magnitude * side
776
- secondary_coord = secondary_offset_multiplier * secondary_spacing
756
+ secondary_offset = start_multiplier * magnitude * side * secondary_spacing
777
757
 
778
- if secondary_axis == 'x':
779
- x, y = secondary_coord, primary_coord
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
- def kececi_layout_v4_rx(graph:
787
- rx.PyGraph, primary_spacing=1.0, secondary_spacing=1.0,
788
- primary_direction='top-down', secondary_start='right'):
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 ['top-down', 'bottom-up']
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 == 'top-down':
799
+ if primary_direction == 'top_down':
806
800
  primary_coord, secondary_axis = i * -primary_spacing, 'x'
807
- elif primary_direction == 'bottom-up':
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
- if i == 0:
815
- secondary_offset_multiplier = 0.0
816
- else:
817
- start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
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
- secondary_offset_multiplier = start_mult * magnitude * side
821
- secondary_coord = secondary_offset_multiplier * secondary_spacing
814
+ secondary_offset = start_multiplier * magnitude * side * secondary_spacing
822
815
 
823
- x, y = (secondary_coord, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_coord)
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
- def kececi_layout_v4_rustworkx(graph:
828
- rx.PyGraph, primary_spacing=1.0, secondary_spacing=1.0,
829
- primary_direction='top-down', secondary_start='right'):
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 ['top-down', 'bottom-up']
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 == 'top-down':
857
+ if primary_direction == 'top_down':
847
858
  primary_coord, secondary_axis = i * -primary_spacing, 'x'
848
- elif primary_direction == 'bottom-up':
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
- if i == 0:
856
- secondary_offset_multiplier = 0.0
857
- else:
858
- start_mult = 1.0 if secondary_start in ['right', 'up'] else -1.0
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
- secondary_offset_multiplier = start_mult * magnitude * side
862
- secondary_coord = secondary_offset_multiplier * secondary_spacing
872
+ secondary_offset = start_multiplier * magnitude * side * secondary_spacing
863
873
 
864
- x, y = (secondary_coord, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_coord)
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) - Değişiklik yok
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
+