kececilayout 0.2.5__tar.gz → 0.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kececilayout-0.2.5/kececilayout.egg-info → kececilayout-0.2.7}/PKG-INFO +13 -5
- {kececilayout-0.2.5 → kececilayout-0.2.7}/README.md +12 -4
- kececilayout-0.2.7/kececilayout/__init__.py +60 -0
- {kececilayout-0.2.5 → kececilayout-0.2.7}/kececilayout/_version.py +1 -1
- {kececilayout-0.2.5 → kececilayout-0.2.7}/kececilayout/kececi_layout.py +333 -227
- {kececilayout-0.2.5 → kececilayout-0.2.7/kececilayout.egg-info}/PKG-INFO +13 -5
- {kececilayout-0.2.5 → kececilayout-0.2.7}/setup.py +3 -1
- kececilayout-0.2.5/kececilayout/__init__.py +0 -76
- {kececilayout-0.2.5 → kececilayout-0.2.7}/LICENSE +0 -0
- {kececilayout-0.2.5 → kececilayout-0.2.7}/MANIFEST.in +0 -0
- {kececilayout-0.2.5 → kececilayout-0.2.7}/kececilayout.egg-info/SOURCES.txt +0 -0
- {kececilayout-0.2.5 → kececilayout-0.2.7}/kececilayout.egg-info/dependency_links.txt +0 -0
- {kececilayout-0.2.5 → kececilayout-0.2.7}/kececilayout.egg-info/requires.txt +0 -0
- {kececilayout-0.2.5 → kececilayout-0.2.7}/kececilayout.egg-info/top_level.txt +0 -0
- {kececilayout-0.2.5 → kececilayout-0.2.7}/pyproject.toml +0 -0
- {kececilayout-0.2.5 → kececilayout-0.2.7}/setup.cfg +0 -0
- {kececilayout-0.2.5 → kececilayout-0.2.7}/tests/test_sample.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kececilayout
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: A deterministic node placement algorithm used in graph visualization. In this layout, nodes are arranged sequentially along a defined primary axis. Each subsequent node is then alternately offset along a secondary, perpendicular axis, typically moving to one side of the primary axis and then the other. Often, the magnitude of this secondary offset increases as nodes progress along the primary axis, creating a characteristic zig-zag or serpentine pattern.
|
|
5
5
|
Home-page: https://github.com/WhiteSymmetry/kececilayout
|
|
6
6
|
Author: Mehmet Keçeci
|
|
@@ -41,9 +41,9 @@ Dynamic: summary
|
|
|
41
41
|
[](https://badge.fury.io/py/kececilayout)
|
|
42
42
|
[](https://opensource.org/licenses/MIT)
|
|
43
43
|
|
|
44
|
-
[](https://doi.org/10.5281/zenodo.15313946)
|
|
45
|
-
[](https://doi.org/10.5281/zenodo.15353535)
|
|
44
|
+
[](https://doi.org/10.5281/zenodo.15313946)
|
|
45
|
+
[](https://doi.org/10.5281/zenodo.15314328)
|
|
46
|
+
[](https://doi.org/10.5281/zenodo.15353535)
|
|
47
47
|
|
|
48
48
|
[](https://doi.org/10.48546/workflowhub.datafile.17.1)
|
|
49
49
|
|
|
@@ -67,6 +67,10 @@ Dynamic: summary
|
|
|
67
67
|
[](https://github.com/WhiteSymmetry/kececilayout/actions/workflows/ci-cd.yml)
|
|
68
68
|
[](https://github.com/astral-sh/ruff)
|
|
69
69
|
|
|
70
|
+
| **Documentation**| **Paper**|
|
|
71
|
+
|:----------------:|:--------:|
|
|
72
|
+
|[](https://whitesymmetry.github.io/kececilayout/)|[](https://doi.org/10.5281/zenodo.15314328)|
|
|
73
|
+
|
|
70
74
|
---
|
|
71
75
|
|
|
72
76
|
<p align="left">
|
|
@@ -120,6 +124,8 @@ This algorithm arranges nodes sequentially along a primary axis and offsets them
|
|
|
120
124
|
|
|
121
125
|
*Bu algoritma, düğümleri birincil eksen boyunca sıralı olarak yerleştirir ve ikincil eksen boyunca dönüşümlü olarak kaydırır. Yol grafları, zincirler veya ilerlemeyi göstermek için özellikle kullanışlıdır.*
|
|
122
126
|
|
|
127
|
+
=> 0.2.6: Curved, transparent, 3d, expanding=True
|
|
128
|
+
|
|
123
129
|
---
|
|
124
130
|
|
|
125
131
|
### English Description
|
|
@@ -808,7 +814,9 @@ Keçeci, Mehmet. kececilayout [Data set]. WorkflowHub, 2025. https://doi.org/10.
|
|
|
808
814
|
|
|
809
815
|
Keçeci, Mehmet. "Kececilayout". Open Science Articles (OSAs), Zenodo, 2025. https://doi.org/10.5281/zenodo.15313946.
|
|
810
816
|
|
|
811
|
-
Keçeci, Mehmet. "Keçeci Layout". Open Science Articles (OSAs), Zenodo, 2025. https://doi.org/10.5281/zenodo.
|
|
817
|
+
Keçeci, Mehmet. "Keçeci Layout". Open Science Articles (OSAs), Zenodo, 2025. https://doi.org/10.5281/zenodo.15314328.
|
|
812
818
|
```
|
|
813
819
|
|
|
814
820
|
|
|
821
|
+
|
|
822
|
+
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
[](https://badge.fury.io/py/kececilayout)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
[](https://doi.org/10.5281/zenodo.15313946)
|
|
7
|
-
[](https://doi.org/10.5281/zenodo.15353535)
|
|
6
|
+
[](https://doi.org/10.5281/zenodo.15313946)
|
|
7
|
+
[](https://doi.org/10.5281/zenodo.15314328)
|
|
8
|
+
[](https://doi.org/10.5281/zenodo.15353535)
|
|
9
9
|
|
|
10
10
|
[](https://doi.org/10.48546/workflowhub.datafile.17.1)
|
|
11
11
|
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
[](https://github.com/WhiteSymmetry/kececilayout/actions/workflows/ci-cd.yml)
|
|
30
30
|
[](https://github.com/astral-sh/ruff)
|
|
31
31
|
|
|
32
|
+
| **Documentation**| **Paper**|
|
|
33
|
+
|:----------------:|:--------:|
|
|
34
|
+
|[](https://whitesymmetry.github.io/kececilayout/)|[](https://doi.org/10.5281/zenodo.15314328)|
|
|
35
|
+
|
|
32
36
|
---
|
|
33
37
|
|
|
34
38
|
<p align="left">
|
|
@@ -82,6 +86,8 @@ This algorithm arranges nodes sequentially along a primary axis and offsets them
|
|
|
82
86
|
|
|
83
87
|
*Bu algoritma, düğümleri birincil eksen boyunca sıralı olarak yerleştirir ve ikincil eksen boyunca dönüşümlü olarak kaydırır. Yol grafları, zincirler veya ilerlemeyi göstermek için özellikle kullanışlıdır.*
|
|
84
88
|
|
|
89
|
+
=> 0.2.6: Curved, transparent, 3d, expanding=True
|
|
90
|
+
|
|
85
91
|
---
|
|
86
92
|
|
|
87
93
|
### English Description
|
|
@@ -770,7 +776,9 @@ Keçeci, Mehmet. kececilayout [Data set]. WorkflowHub, 2025. https://doi.org/10.
|
|
|
770
776
|
|
|
771
777
|
Keçeci, Mehmet. "Kececilayout". Open Science Articles (OSAs), Zenodo, 2025. https://doi.org/10.5281/zenodo.15313946.
|
|
772
778
|
|
|
773
|
-
Keçeci, Mehmet. "Keçeci Layout". Open Science Articles (OSAs), Zenodo, 2025. https://doi.org/10.5281/zenodo.
|
|
779
|
+
Keçeci, Mehmet. "Keçeci Layout". Open Science Articles (OSAs), Zenodo, 2025. https://doi.org/10.5281/zenodo.15314328.
|
|
774
780
|
```
|
|
775
781
|
|
|
776
782
|
|
|
783
|
+
|
|
784
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# __init__.py
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
kececilayout - A Python package for sequential-zigzag graph layouts
|
|
5
|
+
and advanced visualizations compatible with multiple graph libraries.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
import inspect
|
|
10
|
+
import warnings
|
|
11
|
+
|
|
12
|
+
# Paket sürüm numarası
|
|
13
|
+
__version__ = "0.2.7"
|
|
14
|
+
|
|
15
|
+
# =============================================================================
|
|
16
|
+
# OTOMATİK İÇE AKTARMA VE __all__ OLUŞTURMA
|
|
17
|
+
# Bu bölüm, yeni fonksiyon eklediğinizde elle güncelleme yapma
|
|
18
|
+
# ihtiyacını ortadan kaldırır.
|
|
19
|
+
# =============================================================================
|
|
20
|
+
|
|
21
|
+
# Ana modülümüzü içe aktarıyoruz
|
|
22
|
+
from . import kececi_layout
|
|
23
|
+
|
|
24
|
+
# __all__ listesini dinamik olarak dolduracağız
|
|
25
|
+
__all__ = []
|
|
26
|
+
|
|
27
|
+
# kececi_layout modülünün içindeki tüm üyelere (fonksiyonlar, sınıflar vb.) bak
|
|
28
|
+
for name, member in inspect.getmembers(kececi_layout):
|
|
29
|
+
# Eğer üye bir fonksiyonsa VE adı '_' ile başlamıyorsa (yani public ise)
|
|
30
|
+
if inspect.isfunction(member) and not name.startswith('_'):
|
|
31
|
+
# Onu paketin ana seviyesine taşı (örn: kl.draw_kececi)
|
|
32
|
+
globals()[name] = member
|
|
33
|
+
# Ve dışa aktarılacaklar listesine ekle
|
|
34
|
+
__all__.append(name)
|
|
35
|
+
|
|
36
|
+
# Temizlik: Döngüde kullanılan geçici değişkenleri sil
|
|
37
|
+
del inspect, name, member
|
|
38
|
+
|
|
39
|
+
# =============================================================================
|
|
40
|
+
# GERİYE DÖNÜK UYUMLULUK VE UYARILAR
|
|
41
|
+
# =============================================================================
|
|
42
|
+
|
|
43
|
+
def old_function_placeholder():
|
|
44
|
+
"""
|
|
45
|
+
This is an old function scheduled for removal.
|
|
46
|
+
Please use alternative functions.
|
|
47
|
+
"""
|
|
48
|
+
warnings.warn(
|
|
49
|
+
(
|
|
50
|
+
"old_function_placeholder() is deprecated and will be removed in a future version. "
|
|
51
|
+
"Please use the new alternative functions. "
|
|
52
|
+
"Keçeci Layout should work smoothly on Python 3.7-3.14."
|
|
53
|
+
),
|
|
54
|
+
category=DeprecationWarning,
|
|
55
|
+
stacklevel=2
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Eğer bu eski fonksiyonu da dışa aktarmak istiyorsanız, __all__ listesine ekleyin
|
|
59
|
+
# __all__.append('old_function_placeholder')
|
|
60
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# _version.py
|
|
2
2
|
|
|
3
|
-
__version__ = "0.2.
|
|
3
|
+
__version__ = "0.2.7"
|
|
4
4
|
__license__ = "MIT"
|
|
5
5
|
__description__ = "A deterministic node placement algorithm used in graph visualization. In this layout, nodes are arranged sequentially along a defined primary axis. Each subsequent node is then alternately offset along a secondary, perpendicular axis, typically moving to one side of the primary axis and then the other. Often, the magnitude of this secondary offset increases as nodes progress along the primary axis, creating a characteristic zig-zag or serpentine pattern."
|
|
6
6
|
__author__ = "Mehmet Keçeci"
|
|
@@ -1,21 +1,37 @@
|
|
|
1
|
-
#
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# ruff: noqa: N806, N815
|
|
3
|
+
"""
|
|
4
|
+
kececilayout.py
|
|
2
5
|
|
|
6
|
+
Bu modül, çeşitli Python graf kütüphaneleri için sıralı-zigzag ("Keçeci Layout")
|
|
7
|
+
ve gelişmiş görselleştirme stilleri sağlar.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import graphillion as gg
|
|
11
|
+
import igraph as ig
|
|
3
12
|
import itertools # Graphillion için eklendi
|
|
4
|
-
import numpy as np # rustworkx
|
|
5
13
|
import math
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
import igraph as ig
|
|
14
|
+
import matplotlib.pyplot as plt
|
|
15
|
+
from mpl_toolkits.mplot3d import Axes3D
|
|
9
16
|
import networkit as nk
|
|
10
|
-
import
|
|
17
|
+
import networkx as nx
|
|
18
|
+
import numpy as np # rustworkx
|
|
11
19
|
import random
|
|
20
|
+
import rustworkx as rx
|
|
21
|
+
import warnings
|
|
12
22
|
|
|
13
23
|
|
|
14
|
-
#
|
|
24
|
+
# Ana bağımlılıklar (çizim için gerekli)
|
|
15
25
|
try:
|
|
16
26
|
import networkx as nx
|
|
17
|
-
|
|
18
|
-
|
|
27
|
+
#from mpl_toolkits.mplot3d import Axes3D
|
|
28
|
+
except ImportError as e:
|
|
29
|
+
raise ImportError(
|
|
30
|
+
"Bu modülün çalışması için 'networkx' ve 'matplotlib' gereklidir. "
|
|
31
|
+
"Lütfen `pip install networkx matplotlib` ile kurun."
|
|
32
|
+
) from e
|
|
33
|
+
|
|
34
|
+
# Opsiyonel graf kütüphaneleri
|
|
19
35
|
try:
|
|
20
36
|
import rustworkx as rx
|
|
21
37
|
except ImportError:
|
|
@@ -29,10 +45,11 @@ try:
|
|
|
29
45
|
except ImportError:
|
|
30
46
|
nk = None
|
|
31
47
|
try:
|
|
32
|
-
import graphillion as gg
|
|
48
|
+
import graphillion as gg
|
|
33
49
|
except ImportError:
|
|
34
50
|
gg = None
|
|
35
51
|
|
|
52
|
+
|
|
36
53
|
def find_max_node_id(edges):
|
|
37
54
|
"""Verilen kenar listesindeki en büyük düğüm ID'sini bulur."""
|
|
38
55
|
if not edges:
|
|
@@ -45,270 +62,160 @@ def find_max_node_id(edges):
|
|
|
45
62
|
print("Uyarı: Kenar formatı beklenenden farklı, max node ID 0 varsayıldı.")
|
|
46
63
|
return 0
|
|
47
64
|
|
|
48
|
-
def kececi_layout_v4(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
49
|
-
primary_direction='top-down', secondary_start='right'):
|
|
50
|
-
"""
|
|
51
|
-
Keçeci Layout v4 - Graf düğümlerine sıralı-zigzag yerleşimi sağlar.
|
|
52
|
-
NetworkX, Rustworkx, igraph, Networkit ve Graphillion grafikleriyle çalışır.
|
|
53
65
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
NetworkX, Rustworkx, igraph, Networkit veya Graphillion nesnesi.
|
|
58
|
-
Graphillion için bir GraphSet nesnesi beklenir.
|
|
59
|
-
... (diğer parametreler) ...
|
|
60
|
-
|
|
61
|
-
Dönüş:
|
|
62
|
-
------
|
|
63
|
-
dict[node_identifier, tuple[float, float]]
|
|
64
|
-
Her düğümün koordinatını içeren sözlük. Anahtarlar kütüphaneye
|
|
65
|
-
göre değişir (NX: node obj/id, RW/NK/igraph: int index, GG: 1-based int index).
|
|
66
|
+
def kececi_layout(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
67
|
+
primary_direction='top_down', secondary_start='right',
|
|
68
|
+
expanding=True):
|
|
66
69
|
"""
|
|
70
|
+
Calculates 2D sequential-zigzag coordinates for the nodes of a graph.
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# Kütüphane Tespiti ve Düğüm Listesi Çıkarımı
|
|
71
|
-
# isinstance kullanmak hasattr'dan daha güvenilirdir.
|
|
72
|
-
# Önce daha spesifik tipleri kontrol etmek iyi olabilir.
|
|
72
|
+
This function is compatible with graphs from NetworkX, Rustworkx, igraph,
|
|
73
|
+
Networkit, and Graphillion.
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
elif ig and isinstance(graph, ig.Graph): # igraph
|
|
84
|
-
nodes = sorted([v.index for v in graph.vs]) # 0-tabanlı indeksleme
|
|
85
|
-
print(f"DEBUG: igraph tespit edildi. Düğümler (0..{len(nodes)-1}): {nodes[:10]}...")
|
|
86
|
-
|
|
87
|
-
elif nk and isinstance(graph, nk.graph.Graph): # Networkit
|
|
88
|
-
try:
|
|
89
|
-
# iterNodes genellikle 0..N-1 verir ama garanti değil
|
|
90
|
-
nodes = sorted(list(graph.iterNodes()))
|
|
91
|
-
except Exception:
|
|
92
|
-
nodes = list(range(graph.numberOfNodes()))
|
|
93
|
-
print(f"DEBUG: Networkit tespit edildi. Düğümler: {nodes[:10]}...")
|
|
94
|
-
|
|
95
|
-
elif rx and isinstance(graph, (rx.PyGraph, rx.PyDiGraph)): # Rustworkx (hem yönlü hem yönsüz)
|
|
96
|
-
nodes = sorted(graph.node_indices()) # 0-tabanlı indeksleme
|
|
97
|
-
print(f"DEBUG: Rustworkx tespit edildi. Düğümler (0..{len(nodes)-1}): {nodes[:10]}...")
|
|
98
|
-
|
|
99
|
-
elif nx and isinstance(graph, (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)): # NetworkX
|
|
100
|
-
try:
|
|
101
|
-
# Düğümler sıralanabilirse sırala (genelde int/str)
|
|
102
|
-
nodes = sorted(list(graph.nodes()))
|
|
103
|
-
except TypeError:
|
|
104
|
-
# Sıralanamayan düğüm tipleri varsa (örn. tuple, nesne) sırasız al
|
|
105
|
-
nodes = list(graph.nodes())
|
|
106
|
-
print(f"DEBUG: NetworkX tespit edildi. Düğümler: {nodes[:10]}...")
|
|
107
|
-
|
|
108
|
-
else:
|
|
109
|
-
# Desteklenmeyen tip veya kütüphane kurulu değilse
|
|
110
|
-
supported_types = []
|
|
111
|
-
if nx:
|
|
112
|
-
supported_types.append("NetworkX")
|
|
113
|
-
if rx:
|
|
114
|
-
supported_types.append("Rustworkx")
|
|
115
|
-
if ig:
|
|
116
|
-
supported_types.append("igraph")
|
|
117
|
-
if nk:
|
|
118
|
-
supported_types.append("Networkit")
|
|
119
|
-
if gg:
|
|
120
|
-
supported_types.append("Graphillion.GraphSet")
|
|
121
|
-
raise TypeError(f"Unsupported graph type: {type(graph)}. Desteklenen türler: {', '.join(supported_types)}")
|
|
122
|
-
|
|
123
|
-
# ----- Buradan sonrası tüm kütüphaneler için ortak -----
|
|
75
|
+
Args:
|
|
76
|
+
graph: A graph object from a supported library.
|
|
77
|
+
primary_spacing (float): The distance between nodes along the primary axis.
|
|
78
|
+
secondary_spacing (float): The base unit for the zigzag offset.
|
|
79
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
80
|
+
secondary_start (str): Initial direction for the zigzag ('up', 'down', 'left', 'right').
|
|
81
|
+
expanding (bool): If True (default), the zigzag offset grows (the 'v4' style).
|
|
82
|
+
If False, the offset is constant (parallel lines).
|
|
124
83
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
84
|
+
Returns:
|
|
85
|
+
dict: A dictionary of positions formatted as {node_id: (x, y)}.
|
|
86
|
+
"""
|
|
87
|
+
# Bu blok, farklı kütüphanelerden düğüm listelerini doğru şekilde alır.
|
|
88
|
+
nx_graph = to_networkx(graph) # Emin olmak için en başta dönüştür
|
|
89
|
+
try:
|
|
90
|
+
nodes = sorted(list(nx_graph.nodes()))
|
|
91
|
+
except TypeError:
|
|
92
|
+
nodes = list(nx_graph.nodes())
|
|
128
93
|
|
|
129
|
-
pos = {}
|
|
94
|
+
pos = {}
|
|
95
|
+
|
|
96
|
+
# --- DOĞRULANMIŞ KONTROL BLOĞU ---
|
|
130
97
|
is_vertical = primary_direction in ['top-down', 'bottom-up']
|
|
131
98
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
132
99
|
|
|
133
|
-
# Parametre kontrolleri
|
|
134
100
|
if not (is_vertical or is_horizontal):
|
|
135
|
-
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
101
|
+
raise ValueError(f"Invalid primary_direction: '{primary_direction}'")
|
|
136
102
|
if is_vertical and secondary_start not in ['right', 'left']:
|
|
137
|
-
raise ValueError(f"Invalid secondary_start
|
|
103
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: '{secondary_start}'")
|
|
138
104
|
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
139
|
-
|
|
105
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: '{secondary_start}'")
|
|
106
|
+
# --- BİTİŞ ---
|
|
140
107
|
|
|
141
|
-
# Ana döngü - Düğümleri sıralı indekslerine göre yerleştirir,
|
|
142
|
-
# sözlüğe ise gerçek düğüm ID/indeks/nesnesini anahtar olarak kullanır.
|
|
143
108
|
for i, node_id in enumerate(nodes):
|
|
144
|
-
|
|
145
|
-
# node_id: Gerçek düğüm kimliği/indeksi - Sonuç sözlüğünün anahtarı
|
|
146
|
-
|
|
147
|
-
# 1. Ana eksen koordinatını hesapla
|
|
109
|
+
primary_coord, secondary_axis = 0.0, ''
|
|
148
110
|
if primary_direction == 'top-down':
|
|
149
|
-
primary_coord = i * -primary_spacing
|
|
150
|
-
secondary_axis = 'x'
|
|
111
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
151
112
|
elif primary_direction == 'bottom-up':
|
|
152
|
-
primary_coord = i * primary_spacing
|
|
153
|
-
secondary_axis = 'x'
|
|
113
|
+
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
154
114
|
elif primary_direction == 'left-to-right':
|
|
155
|
-
primary_coord = i * primary_spacing
|
|
156
|
-
secondary_axis = 'y'
|
|
157
|
-
else: # right-to-left
|
|
158
|
-
primary_coord = i * -primary_spacing;
|
|
159
|
-
secondary_axis = 'y'
|
|
160
|
-
|
|
161
|
-
# 2. Yan eksen ofsetini hesapla (zigzag)
|
|
162
|
-
if i == 0:
|
|
163
|
-
secondary_offset_multiplier = 0.0
|
|
115
|
+
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
164
116
|
else:
|
|
165
|
-
|
|
166
|
-
magnitude = math.ceil(i / 2.0)
|
|
167
|
-
side = 1 if i % 2 != 0 else -1
|
|
168
|
-
secondary_offset_multiplier = start_mult * magnitude * side
|
|
169
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
117
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
170
118
|
|
|
171
|
-
|
|
172
|
-
if
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
119
|
+
secondary_offset = 0.0
|
|
120
|
+
if i > 0:
|
|
121
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
122
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
123
|
+
side = 1 if i % 2 != 0 else -1
|
|
124
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
176
125
|
|
|
177
|
-
|
|
126
|
+
x, y = ((secondary_offset, primary_coord) if secondary_axis == 'x' else
|
|
127
|
+
(primary_coord, secondary_offset))
|
|
178
128
|
pos[node_id] = (x, y)
|
|
179
|
-
|
|
180
129
|
return pos
|
|
181
|
-
|
|
182
|
-
def kececi_layout(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
183
|
-
primary_direction='top-down', secondary_start='right'):
|
|
184
|
-
"""
|
|
185
|
-
Keçeci Layout v4 - Graf düğümlerine sıralı-zigzag yerleşimi sağlar.
|
|
186
|
-
NetworkX, Rustworkx, igraph, Networkit ve Graphillion grafikleriyle çalışır.
|
|
187
130
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
... (diğer parametreler) ...
|
|
131
|
+
# =============================================================================
|
|
132
|
+
# 1. TEMEL LAYOUT HESAPLAMA FONKSİYONU (2D)
|
|
133
|
+
# Bu fonksiyon sadece koordinatları hesaplar, çizim yapmaz.
|
|
134
|
+
# 1. LAYOUT CALCULATION FUNCTION (UNIFIED AND IMPROVED)
|
|
135
|
+
# =============================================================================
|
|
194
136
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Her düğümün koordinatını içeren sözlük. Anahtarlar kütüphaneye
|
|
199
|
-
göre değişir (NX: node obj/id, RW/NK/igraph: int index, GG: 1-based int index).
|
|
137
|
+
def kececi_layout_v4(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
138
|
+
primary_direction='top_down', secondary_start='right',
|
|
139
|
+
expanding=True): # v4 davranışını kontrol etmek için parametre eklendi
|
|
200
140
|
"""
|
|
141
|
+
Calculates 2D sequential-zigzag coordinates for the nodes of a graph.
|
|
201
142
|
|
|
202
|
-
|
|
143
|
+
This function is compatible with graphs from NetworkX, Rustworkx, igraph,
|
|
144
|
+
Networkit, and Graphillion.
|
|
203
145
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
146
|
+
Args:
|
|
147
|
+
graph: A graph object from a supported library.
|
|
148
|
+
primary_spacing (float): The distance between nodes along the primary axis.
|
|
149
|
+
secondary_spacing (float): The base unit for the zigzag offset.
|
|
150
|
+
primary_direction (str): 'top_down', 'bottom_up', 'left_to_right', 'right_to_left'.
|
|
151
|
+
secondary_start (str): Initial direction for the zigzag ('up', 'down', 'left', 'right').
|
|
152
|
+
expanding (bool): If True (default), the zigzag offset grows, creating the
|
|
153
|
+
triangle-like 'v4' style. If False, the offset is constant,
|
|
154
|
+
creating parallel lines.
|
|
207
155
|
|
|
208
|
-
|
|
156
|
+
Returns:
|
|
157
|
+
dict: A dictionary of positions formatted as {node_id: (x, y)}.
|
|
158
|
+
"""
|
|
159
|
+
# ==========================================================
|
|
160
|
+
# Sizin orijinal, çoklu kütüphane uyumluluk bloğunuz burada korunuyor.
|
|
161
|
+
# Bu, kodun sağlamlığını garanti eder.
|
|
162
|
+
# ==========================================================
|
|
163
|
+
nodes = None
|
|
164
|
+
if gg and isinstance(graph, gg.GraphSet):
|
|
209
165
|
edges = graph.universe()
|
|
210
|
-
max_node_id =
|
|
211
|
-
if max_node_id > 0
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
print(f"DEBUG: igraph tespit edildi. Düğümler (0..{len(nodes)-1}): {nodes[:10]}...")
|
|
220
|
-
|
|
221
|
-
elif nk and isinstance(graph, nk.graph.Graph): # Networkit
|
|
222
|
-
try:
|
|
223
|
-
# iterNodes genellikle 0..N-1 verir ama garanti değil
|
|
224
|
-
nodes = sorted(list(graph.iterNodes()))
|
|
225
|
-
except Exception:
|
|
226
|
-
nodes = list(range(graph.numberOfNodes()))
|
|
227
|
-
print(f"DEBUG: Networkit tespit edildi. Düğümler: {nodes[:10]}...")
|
|
228
|
-
|
|
229
|
-
elif rx and isinstance(graph, (rx.PyGraph, rx.PyDiGraph)): # Rustworkx (hem yönlü hem yönsüz)
|
|
230
|
-
nodes = sorted(graph.node_indices()) # 0-tabanlı indeksleme
|
|
231
|
-
print(f"DEBUG: Rustworkx tespit edildi. Düğümler (0..{len(nodes)-1}): {nodes[:10]}...")
|
|
232
|
-
|
|
233
|
-
elif nx and isinstance(graph, (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)): # NetworkX
|
|
166
|
+
max_node_id = max(set(itertools.chain.from_iterable(edges))) if edges else 0
|
|
167
|
+
nodes = list(range(1, max_node_id + 1)) if max_node_id > 0 else []
|
|
168
|
+
elif ig and isinstance(graph, ig.Graph):
|
|
169
|
+
nodes = sorted([v.index for v in graph.vs])
|
|
170
|
+
elif nk and isinstance(graph, nk.graph.Graph):
|
|
171
|
+
nodes = sorted(list(graph.iterNodes()))
|
|
172
|
+
elif rx and isinstance(graph, (rx.PyGraph, rx.PyDiGraph)):
|
|
173
|
+
nodes = sorted(graph.node_indices())
|
|
174
|
+
elif isinstance(graph, nx.Graph):
|
|
234
175
|
try:
|
|
235
|
-
# Düğümler sıralanabilirse sırala (genelde int/str)
|
|
236
176
|
nodes = sorted(list(graph.nodes()))
|
|
237
177
|
except TypeError:
|
|
238
|
-
|
|
239
|
-
nodes = list(graph.nodes())
|
|
240
|
-
print(f"DEBUG: NetworkX tespit edildi. Düğümler: {nodes[:10]}...")
|
|
241
|
-
|
|
178
|
+
nodes = list(graph.nodes())
|
|
242
179
|
else:
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
supported_types.append("NetworkX")
|
|
247
|
-
if rx:
|
|
248
|
-
supported_types.append("Rustworkx")
|
|
249
|
-
if ig:
|
|
250
|
-
supported_types.append("igraph")
|
|
251
|
-
if nk:
|
|
252
|
-
supported_types.append("Networkit")
|
|
253
|
-
if gg:
|
|
254
|
-
supported_types.append("Graphillion.GraphSet")
|
|
255
|
-
raise TypeError(f"Unsupported graph type: {type(graph)}. Desteklenen türler: {', '.join(supported_types)}")
|
|
256
|
-
|
|
257
|
-
# ----- Buradan sonrası tüm kütüphaneler için ortak -----
|
|
258
|
-
|
|
259
|
-
num_nodes = len(nodes)
|
|
260
|
-
if num_nodes == 0:
|
|
261
|
-
return {} # Boş graf için boş sözlük döndür
|
|
180
|
+
supported = ["NetworkX", "Rustworkx", "igraph", "Networkit", "Graphillion"]
|
|
181
|
+
raise TypeError(f"Unsupported graph type: {type(graph)}. Supported: {', '.join(supported)}")
|
|
182
|
+
# ==========================================================
|
|
262
183
|
|
|
263
|
-
pos = {}
|
|
264
|
-
is_vertical = primary_direction in ['
|
|
184
|
+
pos = {}
|
|
185
|
+
is_vertical = primary_direction in ['top_down', 'bottom_up']
|
|
265
186
|
is_horizontal = primary_direction in ['left-to-right', 'right-to-left']
|
|
266
187
|
|
|
267
|
-
# Parametre kontrolleri
|
|
268
188
|
if not (is_vertical or is_horizontal):
|
|
269
189
|
raise ValueError(f"Invalid primary_direction: {primary_direction}")
|
|
270
190
|
if is_vertical and secondary_start not in ['right', 'left']:
|
|
271
|
-
raise ValueError(f"Invalid secondary_start
|
|
191
|
+
raise ValueError(f"Invalid secondary_start for vertical direction: {secondary_start}")
|
|
272
192
|
if is_horizontal and secondary_start not in ['up', 'down']:
|
|
273
|
-
|
|
193
|
+
raise ValueError(f"Invalid secondary_start for horizontal direction: {secondary_start}")
|
|
274
194
|
|
|
275
|
-
# Ana döngü - Düğümleri sıralı indekslerine göre yerleştirir,
|
|
276
|
-
# sözlüğe ise gerçek düğüm ID/indeks/nesnesini anahtar olarak kullanır.
|
|
277
195
|
for i, node_id in enumerate(nodes):
|
|
278
|
-
|
|
279
|
-
# node_id: Gerçek düğüm kimliği/indeksi - Sonuç sözlüğünün anahtarı
|
|
280
|
-
|
|
281
|
-
# 1. Ana eksen koordinatını hesapla
|
|
196
|
+
primary_coord, secondary_axis = 0.0, ''
|
|
282
197
|
if primary_direction == 'top-down':
|
|
283
|
-
primary_coord = i * -primary_spacing
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
primary_coord = i * primary_spacing;
|
|
287
|
-
secondary_axis = 'x'
|
|
198
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
199
|
+
elif primary_direction == 'bottom_up':
|
|
200
|
+
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
288
201
|
elif primary_direction == 'left-to-right':
|
|
289
|
-
primary_coord = i * primary_spacing
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
primary_coord = i * -primary_spacing;
|
|
293
|
-
secondary_axis = 'y'
|
|
202
|
+
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
203
|
+
else: # 'right_to_left'
|
|
204
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
294
205
|
|
|
295
|
-
|
|
296
|
-
if i
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
206
|
+
secondary_offset = 0.0
|
|
207
|
+
if i > 0:
|
|
208
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
209
|
+
|
|
210
|
+
# --- YENİ ESNEK MANTIK BURADA ---
|
|
211
|
+
# `expanding` True ise 'v4' stili gibi genişler, değilse sabit kalır.
|
|
212
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
213
|
+
|
|
301
214
|
side = 1 if i % 2 != 0 else -1
|
|
302
|
-
|
|
303
|
-
secondary_coord = secondary_offset_multiplier * secondary_spacing
|
|
215
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
304
216
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
x, y = secondary_coord, primary_coord
|
|
308
|
-
else:
|
|
309
|
-
x, y = primary_coord, secondary_coord
|
|
310
|
-
|
|
311
|
-
# Sonuç sözlüğüne ekle
|
|
217
|
+
x, y = ((secondary_offset, primary_coord) if secondary_axis == 'x' else
|
|
218
|
+
(primary_coord, secondary_offset))
|
|
312
219
|
pos[node_id] = (x, y)
|
|
313
220
|
|
|
314
221
|
return pos
|
|
@@ -1129,5 +1036,204 @@ def generate_random_graph_ig(min_nodes=0, max_nodes=200, edge_prob_min=0.15, edg
|
|
|
1129
1036
|
g.vs["degree"] = g.degree()
|
|
1130
1037
|
return g
|
|
1131
1038
|
|
|
1039
|
+
# =============================================================================
|
|
1040
|
+
# 1. GRAPH PROCESSING AND CONVERSION HELPERS
|
|
1041
|
+
# =============================================================================
|
|
1042
|
+
|
|
1043
|
+
def _get_nodes_from_graph(graph):
|
|
1044
|
+
"""Extracts a sorted list of nodes from various graph library objects."""
|
|
1045
|
+
nodes = None
|
|
1046
|
+
if gg and isinstance(graph, gg.GraphSet):
|
|
1047
|
+
edges = graph.universe()
|
|
1048
|
+
max_node_id = max(set(itertools.chain.from_iterable(edges))) if edges else 0
|
|
1049
|
+
nodes = list(range(1, max_node_id + 1)) if max_node_id > 0 else []
|
|
1050
|
+
elif ig and isinstance(graph, ig.Graph):
|
|
1051
|
+
nodes = sorted([v.index for v in graph.vs])
|
|
1052
|
+
elif nk and isinstance(graph, nk.graph.Graph):
|
|
1053
|
+
nodes = sorted(list(graph.iterNodes()))
|
|
1054
|
+
elif rx and isinstance(graph, (rx.PyGraph, rx.PyDiGraph)):
|
|
1055
|
+
nodes = sorted(graph.node_indices())
|
|
1056
|
+
elif isinstance(graph, nx.Graph):
|
|
1057
|
+
try:
|
|
1058
|
+
nodes = sorted(list(graph.nodes()))
|
|
1059
|
+
except TypeError: # For non-sortable node types
|
|
1060
|
+
nodes = list(graph.nodes())
|
|
1061
|
+
else:
|
|
1062
|
+
supported = ["NetworkX"]
|
|
1063
|
+
if rx:
|
|
1064
|
+
supported.append("Rustworkx")
|
|
1065
|
+
if ig:
|
|
1066
|
+
supported.append("igraph")
|
|
1067
|
+
if nk:
|
|
1068
|
+
supported.append("Networkit")
|
|
1069
|
+
if gg:
|
|
1070
|
+
supported.append("Graphillion")
|
|
1071
|
+
raise TypeError(
|
|
1072
|
+
f"Unsupported graph type: {type(graph)}. Supported types: {', '.join(supported)}"
|
|
1073
|
+
)
|
|
1074
|
+
return nodes
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
def to_networkx(graph):
|
|
1078
|
+
"""Converts any supported graph type to a NetworkX graph."""
|
|
1079
|
+
if isinstance(graph, nx.Graph):
|
|
1080
|
+
return graph.copy()
|
|
1081
|
+
nx_graph = nx.Graph()
|
|
1082
|
+
if rx and isinstance(graph, (rx.PyGraph, rx.PyDiGraph)):
|
|
1083
|
+
nx_graph.add_nodes_from(graph.node_indices())
|
|
1084
|
+
nx_graph.add_edges_from(graph.edge_list())
|
|
1085
|
+
elif ig and isinstance(graph, ig.Graph):
|
|
1086
|
+
nx_graph.add_nodes_from(v.index for v in graph.vs)
|
|
1087
|
+
nx_graph.add_edges_from(graph.get_edgelist())
|
|
1088
|
+
elif nk and isinstance(graph, nk.graph.Graph):
|
|
1089
|
+
nx_graph.add_nodes_from(graph.iterNodes())
|
|
1090
|
+
nx_graph.add_edges_from(graph.iterEdges())
|
|
1091
|
+
elif gg and isinstance(graph, gg.GraphSet):
|
|
1092
|
+
edges = graph.universe()
|
|
1093
|
+
max_node_id = max(set(itertools.chain.from_iterable(edges))) if edges else 0
|
|
1094
|
+
if max_node_id > 0:
|
|
1095
|
+
nx_graph.add_nodes_from(range(1, max_node_id + 1))
|
|
1096
|
+
nx_graph.add_edges_from(edges)
|
|
1097
|
+
else:
|
|
1098
|
+
# This block is rarely reached as _get_nodes_from_graph would fail first
|
|
1099
|
+
raise TypeError(f"Unsupported graph type {type(graph)} could not be converted to NetworkX.")
|
|
1100
|
+
#raise TypeError(f"Desteklenmeyen graf tipi {type(graph)} NetworkX'e dönüştürülemedi.")
|
|
1101
|
+
return nx_graph
|
|
1102
|
+
|
|
1103
|
+
|
|
1104
|
+
def _kececi_layout_3d_helix(nx_graph):
|
|
1105
|
+
"""Internal function: Arranges nodes in a helix along the Z-axis."""
|
|
1106
|
+
pos_3d = {}
|
|
1107
|
+
nodes = sorted(list(nx_graph.nodes()))
|
|
1108
|
+
for i, node_id in enumerate(nodes):
|
|
1109
|
+
angle, radius, z_step = i * (np.pi / 2.5), 1.0, i * 0.8
|
|
1110
|
+
pos_3d[node_id] = (np.cos(angle) * radius, np.sin(angle) * radius, z_step)
|
|
1111
|
+
return pos_3d
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
# =============================================================================
|
|
1115
|
+
# 3. INTERNAL DRAWING STYLE IMPLEMENTATIONS
|
|
1116
|
+
# =============================================================================
|
|
1117
|
+
|
|
1118
|
+
def _draw_internal(nx_graph, ax, style, **kwargs):
|
|
1119
|
+
"""Internal router that handles the different drawing styles."""
|
|
1120
|
+
layout_params = {
|
|
1121
|
+
k: v for k, v in kwargs.items()
|
|
1122
|
+
if k in ['primary_spacing', 'secondary_spacing', 'primary_direction',
|
|
1123
|
+
'secondary_start', 'expanding']
|
|
1124
|
+
}
|
|
1125
|
+
draw_params = {k: v for k, v in kwargs.items() if k not in layout_params}
|
|
1126
|
+
|
|
1127
|
+
if style == 'curved':
|
|
1128
|
+
pos = kececi_layout(nx_graph, **layout_params)
|
|
1129
|
+
final_params = {'ax': ax, 'with_labels': True, 'node_color': '#1f78b4',
|
|
1130
|
+
'node_size': 700, 'font_color': 'white',
|
|
1131
|
+
'connectionstyle': 'arc3,rad=0.2', 'arrows': True}
|
|
1132
|
+
final_params.update(draw_params)
|
|
1133
|
+
with warnings.catch_warnings():
|
|
1134
|
+
warnings.simplefilter("ignore", UserWarning)
|
|
1135
|
+
nx.draw(nx_graph, pos, **final_params)
|
|
1136
|
+
ax.set_title("Keçeci Layout: Curved Edges")
|
|
1137
|
+
|
|
1138
|
+
elif style == 'transparent':
|
|
1139
|
+
pos = kececi_layout(nx_graph, **layout_params)
|
|
1140
|
+
nx.draw_networkx_nodes(nx_graph, pos, ax=ax, node_color='#2ca02c', node_size=700, **draw_params)
|
|
1141
|
+
nx.draw_networkx_labels(nx_graph, pos, ax=ax, font_color='white')
|
|
1142
|
+
edge_lengths = {e: np.linalg.norm(np.array(pos[e[0]]) - np.array(pos[e[1]])) for e in nx_graph.edges()}
|
|
1143
|
+
max_len = max(edge_lengths.values()) if edge_lengths else 1.0
|
|
1144
|
+
for edge, length in edge_lengths.items():
|
|
1145
|
+
alpha = 0.15 + 0.85 * (1 - length / max_len)
|
|
1146
|
+
nx.draw_networkx_edges(nx_graph, pos, edgelist=[edge], ax=ax, width=1.5, edge_color='black', alpha=alpha)
|
|
1147
|
+
ax.set_title("Keçeci Layout: Transparent Edges")
|
|
1148
|
+
|
|
1149
|
+
elif style == '3d':
|
|
1150
|
+
pos_3d = _kececi_layout_3d_helix(nx_graph)
|
|
1151
|
+
node_color = draw_params.get('node_color', '#d62728')
|
|
1152
|
+
edge_color = draw_params.get('edge_color', 'gray')
|
|
1153
|
+
for node, (x, y, z) in pos_3d.items():
|
|
1154
|
+
ax.scatter([x], [y], [z], s=200, c=[node_color], depthshade=True)
|
|
1155
|
+
ax.text(x, y, z, f' {node}', size=10, zorder=1, color='k')
|
|
1156
|
+
for u, v in nx_graph.edges():
|
|
1157
|
+
coords = np.array([pos_3d[u], pos_3d[v]])
|
|
1158
|
+
ax.plot(coords[:, 0], coords[:, 1], coords[:, 2], color=edge_color, alpha=0.8)
|
|
1159
|
+
ax.set_title("Keçeci Layout: 3D Helix")
|
|
1160
|
+
ax.set_axis_off()
|
|
1161
|
+
ax.view_init(elev=20, azim=-60)
|
|
1162
|
+
|
|
1163
|
+
|
|
1164
|
+
# =============================================================================
|
|
1165
|
+
# 4. MAIN USER-FACING DRAWING FUNCTION
|
|
1166
|
+
# =============================================================================
|
|
1167
|
+
|
|
1168
|
+
def draw_kececi(graph, style='curved', ax=None, **kwargs):
|
|
1169
|
+
"""
|
|
1170
|
+
Draws a graph using the Keçeci Layout with a specified style.
|
|
1171
|
+
|
|
1172
|
+
This function automatically handles graphs from different libraries
|
|
1173
|
+
(NetworkX, Rustworkx, igraph, etc.).
|
|
1174
|
+
|
|
1175
|
+
Args:
|
|
1176
|
+
graph: The graph object to be drawn.
|
|
1177
|
+
style (str): The drawing style. Options: 'curved', 'transparent', '3d'.
|
|
1178
|
+
ax (matplotlib.axis.Axis, optional): The axis to draw on. If not
|
|
1179
|
+
provided, a new figure and axis are created.
|
|
1180
|
+
**kwargs: Additional keyword arguments passed to both `kececi_layout`
|
|
1181
|
+
and the drawing functions (e.g., expanding=True, node_size=500).
|
|
1182
|
+
|
|
1183
|
+
Returns:
|
|
1184
|
+
matplotlib.axis.Axis: The axis object where the graph was drawn.
|
|
1185
|
+
"""
|
|
1186
|
+
nx_graph = to_networkx(graph)
|
|
1187
|
+
is_3d = (style.lower() == '3d')
|
|
1188
|
+
|
|
1189
|
+
if ax is None:
|
|
1190
|
+
fig = plt.figure(figsize=(10, 8))
|
|
1191
|
+
projection = '3d' if is_3d else None
|
|
1192
|
+
ax = fig.add_subplot(111, projection=projection)
|
|
1193
|
+
|
|
1194
|
+
if is_3d and getattr(ax, 'name', '') != '3d':
|
|
1195
|
+
raise ValueError("The '3d' style requires an axis with 'projection=\"3d\"'.")
|
|
1196
|
+
|
|
1197
|
+
draw_styles = ['curved', 'transparent', '3d']
|
|
1198
|
+
if style.lower() not in draw_styles:
|
|
1199
|
+
raise ValueError(f"Invalid style: '{style}'. Options are: {draw_styles}")
|
|
1200
|
+
|
|
1201
|
+
_draw_internal(nx_graph, ax, style.lower(), **kwargs)
|
|
1202
|
+
return ax
|
|
1203
|
+
|
|
1204
|
+
|
|
1205
|
+
# =============================================================================
|
|
1206
|
+
# MODULE TEST CODE
|
|
1207
|
+
# =============================================================================
|
|
1132
1208
|
|
|
1209
|
+
if __name__ == '__main__':
|
|
1210
|
+
print("Testing kececilayout.py module...")
|
|
1211
|
+
G_test = nx.gnp_random_graph(12, 0.3, seed=42)
|
|
1212
|
+
|
|
1213
|
+
# Compare expanding=False (parallel) vs. expanding=True ('v4' style)
|
|
1214
|
+
fig_v4 = plt.figure(figsize=(16, 7))
|
|
1215
|
+
fig_v4.suptitle("Effect of the `expanding` Parameter", fontsize=20)
|
|
1216
|
+
ax_v4_1 = fig_v4.add_subplot(1, 2, 1)
|
|
1217
|
+
draw_kececi(G_test, ax=ax_v4_1, style='curved',
|
|
1218
|
+
primary_direction='left_to_right', secondary_start='up',
|
|
1219
|
+
expanding=False)
|
|
1220
|
+
ax_v4_1.set_title("Parallel Style (expanding=False)", fontsize=16)
|
|
1221
|
+
|
|
1222
|
+
ax_v4_2 = fig_v4.add_subplot(1, 2, 2)
|
|
1223
|
+
draw_kececi(G_test, ax=ax_v4_2, style='curved',
|
|
1224
|
+
primary_direction='left_to_right', secondary_start='up',
|
|
1225
|
+
expanding=True)
|
|
1226
|
+
ax_v4_2.set_title("Expanding 'v4' Style (expanding=True)", fontsize=16)
|
|
1227
|
+
plt.show()
|
|
1228
|
+
|
|
1229
|
+
# Test all advanced drawing styles
|
|
1230
|
+
fig_styles = plt.figure(figsize=(18, 12))
|
|
1231
|
+
fig_styles.suptitle("Advanced Drawing Styles Test", fontsize=20)
|
|
1232
|
+
draw_kececi(G_test, style='curved', ax=fig_styles.add_subplot(2, 2, 1),
|
|
1233
|
+
primary_direction='left_to_right', secondary_start='up', expanding=True)
|
|
1234
|
+
draw_kececi(G_test, style='transparent', ax=fig_styles.add_subplot(2, 2, 2),
|
|
1235
|
+
primary_direction='top_down', secondary_start='left', expanding=True, node_color='purple')
|
|
1236
|
+
draw_kececi(G_test, style='3d', ax=fig_styles.add_subplot(2, 2, (3, 4), projection='3d'))
|
|
1237
|
+
plt.tight_layout(rect=[0, 0, 1, 0.96])
|
|
1238
|
+
plt.show()
|
|
1133
1239
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kececilayout
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: A deterministic node placement algorithm used in graph visualization. In this layout, nodes are arranged sequentially along a defined primary axis. Each subsequent node is then alternately offset along a secondary, perpendicular axis, typically moving to one side of the primary axis and then the other. Often, the magnitude of this secondary offset increases as nodes progress along the primary axis, creating a characteristic zig-zag or serpentine pattern.
|
|
5
5
|
Home-page: https://github.com/WhiteSymmetry/kececilayout
|
|
6
6
|
Author: Mehmet Keçeci
|
|
@@ -41,9 +41,9 @@ Dynamic: summary
|
|
|
41
41
|
[](https://badge.fury.io/py/kececilayout)
|
|
42
42
|
[](https://opensource.org/licenses/MIT)
|
|
43
43
|
|
|
44
|
-
[](https://doi.org/10.5281/zenodo.15313946)
|
|
45
|
-
[](https://doi.org/10.5281/zenodo.15353535)
|
|
44
|
+
[](https://doi.org/10.5281/zenodo.15313946)
|
|
45
|
+
[](https://doi.org/10.5281/zenodo.15314328)
|
|
46
|
+
[](https://doi.org/10.5281/zenodo.15353535)
|
|
47
47
|
|
|
48
48
|
[](https://doi.org/10.48546/workflowhub.datafile.17.1)
|
|
49
49
|
|
|
@@ -67,6 +67,10 @@ Dynamic: summary
|
|
|
67
67
|
[](https://github.com/WhiteSymmetry/kececilayout/actions/workflows/ci-cd.yml)
|
|
68
68
|
[](https://github.com/astral-sh/ruff)
|
|
69
69
|
|
|
70
|
+
| **Documentation**| **Paper**|
|
|
71
|
+
|:----------------:|:--------:|
|
|
72
|
+
|[](https://whitesymmetry.github.io/kececilayout/)|[](https://doi.org/10.5281/zenodo.15314328)|
|
|
73
|
+
|
|
70
74
|
---
|
|
71
75
|
|
|
72
76
|
<p align="left">
|
|
@@ -120,6 +124,8 @@ This algorithm arranges nodes sequentially along a primary axis and offsets them
|
|
|
120
124
|
|
|
121
125
|
*Bu algoritma, düğümleri birincil eksen boyunca sıralı olarak yerleştirir ve ikincil eksen boyunca dönüşümlü olarak kaydırır. Yol grafları, zincirler veya ilerlemeyi göstermek için özellikle kullanışlıdır.*
|
|
122
126
|
|
|
127
|
+
=> 0.2.6: Curved, transparent, 3d, expanding=True
|
|
128
|
+
|
|
123
129
|
---
|
|
124
130
|
|
|
125
131
|
### English Description
|
|
@@ -808,7 +814,9 @@ Keçeci, Mehmet. kececilayout [Data set]. WorkflowHub, 2025. https://doi.org/10.
|
|
|
808
814
|
|
|
809
815
|
Keçeci, Mehmet. "Kececilayout". Open Science Articles (OSAs), Zenodo, 2025. https://doi.org/10.5281/zenodo.15313946.
|
|
810
816
|
|
|
811
|
-
Keçeci, Mehmet. "Keçeci Layout". Open Science Articles (OSAs), Zenodo, 2025. https://doi.org/10.5281/zenodo.
|
|
817
|
+
Keçeci, Mehmet. "Keçeci Layout". Open Science Articles (OSAs), Zenodo, 2025. https://doi.org/10.5281/zenodo.15314328.
|
|
812
818
|
```
|
|
813
819
|
|
|
814
820
|
|
|
821
|
+
|
|
822
|
+
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="kececilayout",
|
|
5
|
-
version="0.2.
|
|
5
|
+
version="0.2.7",
|
|
6
6
|
description="A deterministic node placement algorithm used in graph visualization. In this layout, nodes are arranged sequentially along a defined primary axis. Each subsequent node is then alternately offset along a secondary, perpendicular axis, typically moving to one side of the primary axis and then the other. Often, the magnitude of this secondary offset increases as nodes progress along the primary axis, creating a characteristic zig-zag or serpentine pattern.",
|
|
7
7
|
long_description=open("README.md").read(),
|
|
8
8
|
long_description_content_type="text/markdown",
|
|
@@ -34,3 +34,5 @@ setup(
|
|
|
34
34
|
license="MIT",
|
|
35
35
|
)
|
|
36
36
|
|
|
37
|
+
|
|
38
|
+
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# __init__.py
|
|
2
|
-
# Bu dosya paketin başlangıç noktası olarak çalışır.
|
|
3
|
-
# Alt modülleri yükler, sürüm bilgileri tanımlar ve geriye dönük uyumluluk için uyarılar sağlar.
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
import importlib
|
|
7
|
-
import warnings
|
|
8
|
-
import os
|
|
9
|
-
# if os.getenv("DEVELOPMENT") == "true":
|
|
10
|
-
# importlib.reload(kececi_layout) # F821 undefined name 'kececi_layout'
|
|
11
|
-
|
|
12
|
-
# Dışa aktarılacak semboller listesi
|
|
13
|
-
__all__ = [
|
|
14
|
-
'kececi_layout_v4',
|
|
15
|
-
'kececi_layout',
|
|
16
|
-
'kececi_layout_v4_nx',
|
|
17
|
-
'kececi_layout_v4_networkx',
|
|
18
|
-
'kececi_layout_v4_ig',
|
|
19
|
-
'kececi_layout_v4_igraph',
|
|
20
|
-
'kececi_layout_v4_nk',
|
|
21
|
-
'kececi_layout_v4_networkit',
|
|
22
|
-
'kececi_layout_v4_gg',
|
|
23
|
-
'kececi_layout_v4_graphillion',
|
|
24
|
-
'kececi_layout_v4_rx',
|
|
25
|
-
'kececi_layout_v4_rustworkx',
|
|
26
|
-
'generate_random_rx_graph',
|
|
27
|
-
'kececi_layout_v4_pure',
|
|
28
|
-
'generate_random_graph',
|
|
29
|
-
'generate_random_graph_ig'
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
# Göreli modül içe aktarmaları
|
|
33
|
-
# F401 hatasını önlemek için sadece kullanacağınız şeyleri dışa aktarın
|
|
34
|
-
# Aksi halde linter'lar "imported but unused" uyarısı verir
|
|
35
|
-
try:
|
|
36
|
-
#from .kececi_layout import * # gerekirse burada belirli fonksiyonları seçmeli yapmak daha güvenlidir
|
|
37
|
-
#from . import kececi_layout # Modülün kendisine doğrudan erişim isteniyorsa
|
|
38
|
-
from .kececi_layout import (
|
|
39
|
-
kececi_layout_v4,
|
|
40
|
-
kececi_layout,
|
|
41
|
-
kececi_layout_v4_nx,
|
|
42
|
-
kececi_layout_v4_networkx,
|
|
43
|
-
kececi_layout_v4_ig,
|
|
44
|
-
kececi_layout_v4_igraph,
|
|
45
|
-
kececi_layout_v4_nk,
|
|
46
|
-
kececi_layout_v4_networkit,
|
|
47
|
-
kececi_layout_v4_gg,
|
|
48
|
-
kececi_layout_v4_graphillion,
|
|
49
|
-
kececi_layout_v4_rx,
|
|
50
|
-
kececi_layout_v4_rustworkx,
|
|
51
|
-
generate_random_rx_graph,
|
|
52
|
-
kececi_layout_v4_pure,
|
|
53
|
-
generate_random_graph,
|
|
54
|
-
generate_random_graph_ig
|
|
55
|
-
)
|
|
56
|
-
except ImportError as e:
|
|
57
|
-
warnings.warn(f"Gerekli modül yüklenemedi: {e}", ImportWarning)
|
|
58
|
-
|
|
59
|
-
# Eski bir fonksiyonun yer tutucusu - gelecekte kaldırılacak
|
|
60
|
-
def eski_fonksiyon():
|
|
61
|
-
"""
|
|
62
|
-
Kaldırılması planlanan eski bir fonksiyondur.
|
|
63
|
-
Lütfen alternatif fonksiyonları kullanın.
|
|
64
|
-
"""
|
|
65
|
-
warnings.warn(
|
|
66
|
-
"eski_fonksiyon() artık kullanılmamaktadır ve gelecekte kaldırılacaktır. "
|
|
67
|
-
"Lütfen yeni alternatif fonksiyonları kullanın. "
|
|
68
|
-
"Keçeci Layout; Python 3.7-3.14 sürümlerinde sorunsuz çalışmalıdır.",
|
|
69
|
-
category=DeprecationWarning,
|
|
70
|
-
stacklevel=2
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
# Paket sürüm numarası
|
|
74
|
-
__version__ = "0.2.5"
|
|
75
|
-
|
|
76
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|