kececilayout 0.5.0__py3-none-any.whl → 0.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docs/conf.py +97 -0
- kececilayout/__init__.py +10 -1
- kececilayout/_version.py +3 -2
- kececilayout/kececi_layout.py +115 -3
- {kececilayout-0.5.0.dist-info → kececilayout-0.5.1.dist-info}/METADATA +749 -25
- kececilayout-0.5.1.dist-info/RECORD +10 -0
- {kececilayout-0.5.0.dist-info → kececilayout-0.5.1.dist-info}/WHEEL +1 -1
- kececilayout-0.5.1.dist-info/licenses/LICENSE +661 -0
- {kececilayout-0.5.0.dist-info → kececilayout-0.5.1.dist-info}/top_level.txt +2 -0
- tests/test_sample.py +261 -0
- kececilayout-0.5.0.dist-info/RECORD +0 -8
- kececilayout-0.5.0.dist-info/licenses/LICENSE +0 -21
docs/conf.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
# Proje kök dizinini Python yoluna ekle (kececilayout'u bulmak için)
|
|
6
|
+
sys.path.insert(0, os.path.abspath('..'))
|
|
7
|
+
|
|
8
|
+
# Proje Bilgileri
|
|
9
|
+
project = 'kececilayout'
|
|
10
|
+
copyright = '2025, Mehmet Keçeci'
|
|
11
|
+
author = 'Mehmet Keçeci'
|
|
12
|
+
|
|
13
|
+
# Sürüm Bilgisi (setuptools_scm kullanmıyorsanız sabit olarak tanımlayın)
|
|
14
|
+
# Gerçek sürümü modülden al (eğer mümkünse)
|
|
15
|
+
try:
|
|
16
|
+
from kececilayout import __version__
|
|
17
|
+
version = __version__
|
|
18
|
+
release = __version__
|
|
19
|
+
except (ImportError, AttributeError) as e:
|
|
20
|
+
print(f"Warning: Could not import __version__ from kececilayout: {e}")
|
|
21
|
+
# Varsayılan değerler korunur
|
|
22
|
+
# version = '0.2.7' # Geliştirme sürümü
|
|
23
|
+
# release = '0.2.7' # Yayın sürümü
|
|
24
|
+
|
|
25
|
+
# Ana belge
|
|
26
|
+
master_doc = 'index'
|
|
27
|
+
|
|
28
|
+
# Sphinx Uzantıları
|
|
29
|
+
extensions = [
|
|
30
|
+
'sphinx.ext.autodoc',
|
|
31
|
+
'sphinx.ext.viewcode', # "Show Source" linki
|
|
32
|
+
'sphinx.ext.napoleon', # Google/NumPy docstring desteği
|
|
33
|
+
'sphinx.ext.intersphinx', # Dış belgelere link (ör: Python, NetworkX)
|
|
34
|
+
'sphinx.ext.autosummary', # Otomatik özet tabloları
|
|
35
|
+
'sphinx_rtd_theme', # Read the Docs teması
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# Otomatik Özet (autosummary)
|
|
39
|
+
autosummary_generate = True
|
|
40
|
+
autodoc_default_options = {
|
|
41
|
+
'members': True,
|
|
42
|
+
'undoc-members': True,
|
|
43
|
+
'show-inheritance': True,
|
|
44
|
+
'special-members': '__init__',
|
|
45
|
+
'exclude-members': '__weakref__'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Mock Imports — RTD'de kurulamayan modülleri taklit et
|
|
49
|
+
autodoc_mock_imports = [
|
|
50
|
+
"igraph",
|
|
51
|
+
"networkit",
|
|
52
|
+
"rustworkx",
|
|
53
|
+
"graphillion",
|
|
54
|
+
"itertools", # Bu aslında stdlib ama bazen sorun çıkarabilir
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# Dış Belge Linkleri (intersphinx)
|
|
58
|
+
intersphinx_mapping = {
|
|
59
|
+
'python': ('https://docs.python.org/3', None),
|
|
60
|
+
'networkx': ('https://networkx.org/documentation/stable/', None),
|
|
61
|
+
'matplotlib': ('https://matplotlib.org/stable/', None),
|
|
62
|
+
'numpy': ('https://numpy.org/doc/stable/', None),
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Hariç Tutulan Dosyalar
|
|
66
|
+
exclude_patterns = [
|
|
67
|
+
'_build',
|
|
68
|
+
'Thumbs.db',
|
|
69
|
+
'.DS_Store',
|
|
70
|
+
'**/.ipynb_checkpoints',
|
|
71
|
+
'README.md' # HTML'e çevrilmemesi için
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
# HTML Ayarları
|
|
75
|
+
html_theme = 'sphinx_rtd_theme'
|
|
76
|
+
html_static_path = ['_static']
|
|
77
|
+
html_logo = '_static/logo.png' # Opsiyonel: logo
|
|
78
|
+
html_favicon = '_static/favicon.ico' # Opsiyonel: favicon
|
|
79
|
+
html_title = "KeçeciLayout Docs"
|
|
80
|
+
|
|
81
|
+
# HTML Tema Seçenekleri
|
|
82
|
+
html_theme_options = {
|
|
83
|
+
'navigation_depth': 4,
|
|
84
|
+
'collapse_navigation': False,
|
|
85
|
+
'sticky_navigation': True,
|
|
86
|
+
'logo_only': True,
|
|
87
|
+
'display_version': True,
|
|
88
|
+
'prev_next_buttons_location': 'bottom',
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# -- Extra: Eğer kececilayout içinde sürüm varsa, onu kullan (opsiyonel) --
|
|
92
|
+
# try:
|
|
93
|
+
# from kececilayout import __version__
|
|
94
|
+
# version = __version__
|
|
95
|
+
# release = __version__
|
|
96
|
+
# except (ImportError, AttributeError):
|
|
97
|
+
# pass
|
kececilayout/__init__.py
CHANGED
|
@@ -10,7 +10,7 @@ import inspect
|
|
|
10
10
|
import warnings
|
|
11
11
|
|
|
12
12
|
# Paket sürüm numarası
|
|
13
|
-
__version__ = "0.5.
|
|
13
|
+
__version__ = "0.5.1"
|
|
14
14
|
|
|
15
15
|
# =============================================================================
|
|
16
16
|
# OTOMATİK İÇE AKTARMA VE __all__ OLUŞTURMA
|
|
@@ -58,6 +58,10 @@ from .kececi_layout import ( # Veya fonksiyonların bulunduğu asıl modül
|
|
|
58
58
|
kececi_layout_toric,
|
|
59
59
|
draw_kececi_weighted,
|
|
60
60
|
draw_kececi_colored,
|
|
61
|
+
kececi_layout_edge,
|
|
62
|
+
_compute_positions,
|
|
63
|
+
_extract_graph_data,
|
|
64
|
+
_validate_directions,
|
|
61
65
|
|
|
62
66
|
# Drawing functions
|
|
63
67
|
draw_kececi,
|
|
@@ -107,6 +111,10 @@ __all__ = [
|
|
|
107
111
|
'kececi_layout_toric',
|
|
108
112
|
'draw_kececi_weighted',
|
|
109
113
|
'draw_kececi_colored',
|
|
114
|
+
'kececi_layout_edge',
|
|
115
|
+
'_compute_positions',
|
|
116
|
+
'_extract_graph_data',
|
|
117
|
+
'_validate_directions',
|
|
110
118
|
|
|
111
119
|
# Drawing functions
|
|
112
120
|
'draw_kececi',
|
|
@@ -157,3 +165,4 @@ def old_function_placeholder():
|
|
|
157
165
|
|
|
158
166
|
# Eğer bu eski fonksiyonu da dışa aktarmak istiyorsanız, __all__ listesine ekleyin
|
|
159
167
|
# __all__.append('old_function_placeholder')
|
|
168
|
+
|
kececilayout/_version.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# _version.py
|
|
2
2
|
|
|
3
3
|
__version__ = "0.5.0"
|
|
4
|
-
__license__ = "
|
|
4
|
+
__license__ = "AGPL3.0-or-later"
|
|
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"
|
|
7
7
|
__url__ = "https://github.com/WhiteSymmetry/kececilayout"
|
|
8
8
|
__docs__ = "https://github.com/WhiteSymmetry/kececilayout" # Opsiyonel: Dokümantasyon linki
|
|
9
|
-
__dependencies__ = ["python>=3.
|
|
9
|
+
__dependencies__ = ["python>=3.11"] # Diğer bağımlılıkları da ekleyebilirsiniz
|
|
10
|
+
|
kececilayout/kececi_layout.py
CHANGED
|
@@ -5,8 +5,25 @@ kececilayout.py
|
|
|
5
5
|
|
|
6
6
|
This module provides sequential-zigzag ("Keçeci Layout") and advanced visualization styles for various Python graph libraries.
|
|
7
7
|
Bu modül, çeşitli Python graf kütüphaneleri için sıralı-zigzag ("Keçeci Layout") ve gelişmiş görselleştirme stilleri sağlar.
|
|
8
|
+
|
|
9
|
+
**Key Features:**
|
|
10
|
+
* **Linear Focus:** Ideal for visualizing paths, chains, or ordered processes.
|
|
11
|
+
* **Deterministic:** Produces identical results for the same input.
|
|
12
|
+
* **Overlap Reduction:** Prevents node collisions by spreading them across axes.
|
|
13
|
+
* **Parametric:** Fully customizable with parameters like `primary_spacing`, `secondary_spacing`, `primary_direction`, and `secondary_start`.
|
|
14
|
+
|
|
15
|
+
**v0.2.7**: Curved, transparent, 3D, and `expanding=True` styles supported.
|
|
16
|
+
|
|
17
|
+
**v0.5.0:**
|
|
18
|
+
|
|
19
|
+
layouts = ['2d', 'cylindrical', 'cubic', 'spherical', 'elliptical', 'toric']
|
|
20
|
+
|
|
21
|
+
styles = ['standard', 'default', 'curved', 'helix', '3d', 'weighted', 'colored']
|
|
22
|
+
|
|
23
|
+
**v0.5.1:** edge (kececi_layout_edge)
|
|
8
24
|
"""
|
|
9
25
|
|
|
26
|
+
from collections import defaultdict
|
|
10
27
|
import graphillion as gg
|
|
11
28
|
import igraph as ig
|
|
12
29
|
import itertools # Graphillion için eklendi
|
|
@@ -245,12 +262,104 @@ def kececi_layout(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
|
245
262
|
pos[node_id] = (x, y)
|
|
246
263
|
return pos
|
|
247
264
|
|
|
265
|
+
def kececi_layout_edge(graph: Any,
|
|
266
|
+
primary_spacing: float = 1.0,
|
|
267
|
+
secondary_spacing: float = 1.0,
|
|
268
|
+
primary_direction: str = 'top_down',
|
|
269
|
+
secondary_start: str = 'right',
|
|
270
|
+
expanding: bool = True,
|
|
271
|
+
edge: bool = True) -> Dict[Any, Tuple[float, float]]:
|
|
272
|
+
"""Deterministik O(n) layout — edge farkındalıklı mod ile."""
|
|
273
|
+
nodes, edges = _extract_graph_data(graph)
|
|
274
|
+
_validate_directions(primary_direction, secondary_start)
|
|
275
|
+
|
|
276
|
+
if edge and edges:
|
|
277
|
+
degree = defaultdict(int)
|
|
278
|
+
for u, v in edges:
|
|
279
|
+
degree[u] += 1
|
|
280
|
+
degree[v] += 1
|
|
281
|
+
nodes = sorted(nodes, key=lambda n: (-degree.get(n, 0), str(n)))
|
|
282
|
+
|
|
283
|
+
return _compute_positions(
|
|
284
|
+
nodes, primary_spacing, secondary_spacing,
|
|
285
|
+
primary_direction, secondary_start, expanding
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def _validate_directions(pd: str, ss: str) -> None:
|
|
289
|
+
VERTICAL = {'top_down', 'bottom_up'}
|
|
290
|
+
HORIZONTAL = {'left-to-right', 'right-to-left'}
|
|
291
|
+
|
|
292
|
+
if pd in VERTICAL and ss not in {'left', 'right'}:
|
|
293
|
+
raise ValueError(
|
|
294
|
+
f"Invalid secondary_start '{ss}' for vertical direction '{pd}'\n"
|
|
295
|
+
f"✓ Use: 'left' or 'right' (e.g., secondary_start='right')"
|
|
296
|
+
)
|
|
297
|
+
if pd in HORIZONTAL and ss not in {'up', 'down'}:
|
|
298
|
+
raise ValueError(
|
|
299
|
+
f"Invalid secondary_start '{ss}' for horizontal direction '{pd}'\n"
|
|
300
|
+
f"✓ Use: 'up' or 'down' (e.g., secondary_start='up')"
|
|
301
|
+
)
|
|
302
|
+
if pd not in VERTICAL and pd not in HORIZONTAL:
|
|
303
|
+
raise ValueError(f"Invalid primary_direction: '{pd}'")
|
|
304
|
+
|
|
305
|
+
def _extract_graph_data(graph: Any) -> Tuple[List[Any], List[Tuple[Any, Any]]]:
|
|
306
|
+
# Rustworkx
|
|
307
|
+
try:
|
|
308
|
+
import rustworkx as rx
|
|
309
|
+
if isinstance(graph, (rx.PyGraph, rx.PyDiGraph)):
|
|
310
|
+
nodes = sorted(int(u) for u in graph.node_indices())
|
|
311
|
+
edges = [(int(u), int(v)) for u, v in graph.edge_list()]
|
|
312
|
+
return nodes, edges
|
|
313
|
+
except (ImportError, AttributeError, NameError):
|
|
314
|
+
pass
|
|
315
|
+
|
|
316
|
+
# NetworkX (fallback)
|
|
317
|
+
try:
|
|
318
|
+
import networkx as nx
|
|
319
|
+
if isinstance(graph, (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)):
|
|
320
|
+
try:
|
|
321
|
+
nodes = sorted(graph.nodes())
|
|
322
|
+
except TypeError:
|
|
323
|
+
nodes = list(graph.nodes())
|
|
324
|
+
edges = [(u, v) for u, v in graph.edges()]
|
|
325
|
+
return nodes, edges
|
|
326
|
+
except (ImportError, AttributeError, NameError):
|
|
327
|
+
pass
|
|
328
|
+
|
|
329
|
+
raise TypeError(
|
|
330
|
+
f"Unsupported graph type: {type(graph).__name__}\n"
|
|
331
|
+
"Supported: NetworkX, Rustworkx"
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
def _compute_positions(nodes: List[Any],
|
|
335
|
+
ps: float, ss: float,
|
|
336
|
+
pd: str, sc: str, exp: bool) -> Dict[Any, Tuple[float, float]]:
|
|
337
|
+
pos = {}
|
|
338
|
+
for i, node in enumerate(nodes):
|
|
339
|
+
if pd == 'top_down':
|
|
340
|
+
pc, sa = i * -ps, 'x'
|
|
341
|
+
elif pd == 'bottom_up':
|
|
342
|
+
pc, sa = i * ps, 'x'
|
|
343
|
+
elif pd == 'left-to-right':
|
|
344
|
+
pc, sa = i * ps, 'y'
|
|
345
|
+
else: # right-to-left
|
|
346
|
+
pc, sa = i * -ps, 'y'
|
|
347
|
+
|
|
348
|
+
so = 0.0
|
|
349
|
+
if i > 0:
|
|
350
|
+
sm = 1.0 if sc in {'right', 'up'} else -1.0
|
|
351
|
+
mag = math.ceil(i / 2.0) if exp else 1.0
|
|
352
|
+
side = 1 if i % 2 else -1
|
|
353
|
+
so = sm * mag * side * ss
|
|
354
|
+
|
|
355
|
+
pos[node] = (so, pc) if sa == 'x' else (pc, so)
|
|
356
|
+
return pos
|
|
357
|
+
|
|
248
358
|
# =============================================================================
|
|
249
359
|
# 1. TEMEL LAYOUT HESAPLAMA FONKSİYONU (2D)
|
|
250
360
|
# Bu fonksiyon sadece koordinatları hesaplar, çizim yapmaz.
|
|
251
361
|
# 1. LAYOUT CALCULATION FUNCTION (UNIFIED AND IMPROVED)
|
|
252
362
|
# =============================================================================
|
|
253
|
-
|
|
254
363
|
def kececi_layout_v4(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
255
364
|
primary_direction='top_down', secondary_start='right',
|
|
256
365
|
expanding=True):
|
|
@@ -343,7 +452,7 @@ def kececi_layout_nx(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
|
343
452
|
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
344
453
|
secondary_start (str): Initial direction for the zigzag offset.
|
|
345
454
|
expanding (bool): If True (default), the zigzag offset grows.
|
|
346
|
-
If False, the offset is constant (parallel lines).
|
|
455
|
+
If False, the offset is constant (parallel lines).
|
|
347
456
|
|
|
348
457
|
Returns:
|
|
349
458
|
dict: A dictionary of positions keyed by node ID.
|
|
@@ -402,7 +511,7 @@ def kececi_layout_networkx(graph, primary_spacing=1.0, secondary_spacing=1.0,
|
|
|
402
511
|
primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
|
|
403
512
|
secondary_start (str): Initial direction for the zigzag offset.
|
|
404
513
|
expanding (bool): If True (default), the zigzag offset grows.
|
|
405
|
-
If False, the offset is constant (parallel lines).
|
|
514
|
+
If False, the offset is constant (parallel lines).
|
|
406
515
|
|
|
407
516
|
Returns:
|
|
408
517
|
dict: A dictionary of positions keyed by node ID.
|
|
@@ -2075,3 +2184,6 @@ if __name__ == '__main__':
|
|
|
2075
2184
|
draw_kececi(G_test, style='3d', ax=fig_styles.add_subplot(2, 2, (3, 4), projection='3d'))
|
|
2076
2185
|
plt.tight_layout(rect=[0, 0, 1, 0.96])
|
|
2077
2186
|
plt.show()
|
|
2187
|
+
|
|
2188
|
+
|
|
2189
|
+
|