kececilayout 0.4.8__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kececilayout-0.4.8/kececilayout.egg-info → kececilayout-0.5.0}/PKG-INFO +10 -1
- {kececilayout-0.4.8 → kececilayout-0.5.0}/README.md +8 -0
- {kececilayout-0.4.8 → kececilayout-0.5.0}/kececilayout/__init__.py +21 -3
- {kececilayout-0.4.8 → kececilayout-0.5.0}/kececilayout/_version.py +1 -3
- {kececilayout-0.4.8 → kececilayout-0.5.0}/kececilayout/kececi_layout.py +472 -3
- {kececilayout-0.4.8 → kececilayout-0.5.0/kececilayout.egg-info}/PKG-INFO +10 -1
- {kececilayout-0.4.8 → kececilayout-0.5.0}/kececilayout.egg-info/requires.txt +1 -0
- {kececilayout-0.4.8 → kececilayout-0.5.0}/pyproject.toml +2 -4
- {kececilayout-0.4.8 → kececilayout-0.5.0}/setup.py +1 -3
- {kececilayout-0.4.8 → kececilayout-0.5.0}/LICENSE +0 -0
- {kececilayout-0.4.8 → kececilayout-0.5.0}/MANIFEST.in +0 -0
- {kececilayout-0.4.8 → kececilayout-0.5.0}/kececilayout.egg-info/SOURCES.txt +0 -0
- {kececilayout-0.4.8 → kececilayout-0.5.0}/kececilayout.egg-info/dependency_links.txt +0 -0
- {kececilayout-0.4.8 → kececilayout-0.5.0}/kececilayout.egg-info/top_level.txt +0 -0
- {kececilayout-0.4.8 → kececilayout-0.5.0}/setup.cfg +0 -0
- {kececilayout-0.4.8 → kececilayout-0.5.0}/tests/test_sample.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kececilayout
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Çeşitli graf kütüphaneleri için sıralı-zigzag yerleşimleri sağlayan bir Python paketi.
|
|
5
5
|
Home-page: https://github.com/WhiteSymmetry/kececilayout
|
|
6
6
|
Author: Mehmet Keçeci
|
|
@@ -54,6 +54,7 @@ Requires-Dist: rustworkx; extra == "test"
|
|
|
54
54
|
Requires-Dist: networkit; extra == "test"
|
|
55
55
|
Requires-Dist: graphillion; extra == "test"
|
|
56
56
|
Requires-Dist: graph-tool; extra == "test"
|
|
57
|
+
Requires-Dist: numba; extra == "test"
|
|
57
58
|
Dynamic: author
|
|
58
59
|
Dynamic: home-page
|
|
59
60
|
Dynamic: license-file
|
|
@@ -118,6 +119,12 @@ This algorithm arranges nodes sequentially along a primary axis and offsets them
|
|
|
118
119
|
|
|
119
120
|
=> **v0.2.7**: Curved, transparent, 3D, and `expanding=True` styles supported.
|
|
120
121
|
|
|
122
|
+
**v0.5.0:**
|
|
123
|
+
|
|
124
|
+
layouts = ['2d', 'cylindrical', 'cubic', 'spherical', 'elliptical', 'toric']
|
|
125
|
+
|
|
126
|
+
styles = ['standard', 'default', 'curved', 'helix', '3d', 'weighted', 'colored']
|
|
127
|
+
|
|
121
128
|
---
|
|
122
129
|
|
|
123
130
|
### Installation
|
|
@@ -1958,3 +1965,5 @@ Keçeci, Mehmet. "Keçeci Layout". Open Science Articles (OSAs), Zenodo, 2025. h
|
|
|
1958
1965
|
|
|
1959
1966
|
|
|
1960
1967
|
|
|
1968
|
+
|
|
1969
|
+
|
|
@@ -55,6 +55,12 @@ This algorithm arranges nodes sequentially along a primary axis and offsets them
|
|
|
55
55
|
|
|
56
56
|
=> **v0.2.7**: Curved, transparent, 3D, and `expanding=True` styles supported.
|
|
57
57
|
|
|
58
|
+
**v0.5.0:**
|
|
59
|
+
|
|
60
|
+
layouts = ['2d', 'cylindrical', 'cubic', 'spherical', 'elliptical', 'toric']
|
|
61
|
+
|
|
62
|
+
styles = ['standard', 'default', 'curved', 'helix', '3d', 'weighted', 'colored']
|
|
63
|
+
|
|
58
64
|
---
|
|
59
65
|
|
|
60
66
|
### Installation
|
|
@@ -1895,3 +1901,5 @@ Keçeci, Mehmet. "Keçeci Layout". Open Science Articles (OSAs), Zenodo, 2025. h
|
|
|
1895
1901
|
|
|
1896
1902
|
|
|
1897
1903
|
|
|
1904
|
+
|
|
1905
|
+
|
|
@@ -10,7 +10,7 @@ import inspect
|
|
|
10
10
|
import warnings
|
|
11
11
|
|
|
12
12
|
# Paket sürüm numarası
|
|
13
|
-
__version__ = "0.
|
|
13
|
+
__version__ = "0.5.0"
|
|
14
14
|
|
|
15
15
|
# =============================================================================
|
|
16
16
|
# OTOMATİK İÇE AKTARMA VE __all__ OLUŞTURMA
|
|
@@ -48,6 +48,16 @@ from .kececi_layout import ( # Veya fonksiyonların bulunduğu asıl modül
|
|
|
48
48
|
get_text_color_for_bg,
|
|
49
49
|
generate_soft_random_colors,
|
|
50
50
|
generate_distinct_colors,
|
|
51
|
+
calculate_coordinates,
|
|
52
|
+
calculate_coordinates,
|
|
53
|
+
kececi_layout_2d,
|
|
54
|
+
kececi_layout_cylindrical,
|
|
55
|
+
kececi_layout_cubic,
|
|
56
|
+
kececi_layout_spherical,
|
|
57
|
+
kececi_layout_elliptical,
|
|
58
|
+
kececi_layout_toric,
|
|
59
|
+
draw_kececi_weighted,
|
|
60
|
+
draw_kececi_colored,
|
|
51
61
|
|
|
52
62
|
# Drawing functions
|
|
53
63
|
draw_kececi,
|
|
@@ -88,7 +98,16 @@ __all__ = [
|
|
|
88
98
|
'get_text_color_for_bg',
|
|
89
99
|
'generate_soft_random_colors',
|
|
90
100
|
'generate_distinct_colors',
|
|
91
|
-
|
|
101
|
+
'calculate_coordinates',
|
|
102
|
+
'kececi_layout_2d',
|
|
103
|
+
'kececi_layout_cylindrical',
|
|
104
|
+
'kececi_layout_cubic',
|
|
105
|
+
'kececi_layout_spherical',
|
|
106
|
+
'kececi_layout_elliptical',
|
|
107
|
+
'kececi_layout_toric',
|
|
108
|
+
'draw_kececi_weighted',
|
|
109
|
+
'draw_kececi_colored',
|
|
110
|
+
|
|
92
111
|
# Drawing functions
|
|
93
112
|
'draw_kececi',
|
|
94
113
|
'_draw_internal', # <- TESTLER İÇİN GEREKLİ
|
|
@@ -138,4 +157,3 @@ def old_function_placeholder():
|
|
|
138
157
|
|
|
139
158
|
# Eğer bu eski fonksiyonu da dışa aktarmak istiyorsanız, __all__ listesine ekleyin
|
|
140
159
|
# __all__.append('old_function_placeholder')
|
|
141
|
-
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# _version.py
|
|
2
2
|
|
|
3
|
-
__version__ = "0.
|
|
3
|
+
__version__ = "0.5.0"
|
|
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"
|
|
7
7
|
__url__ = "https://github.com/WhiteSymmetry/kececilayout"
|
|
8
8
|
__docs__ = "https://github.com/WhiteSymmetry/kececilayout" # Opsiyonel: Dokümantasyon linki
|
|
9
9
|
__dependencies__ = ["python>=3.10"] # Diğer bağımlılıkları da ekleyebilirsiniz
|
|
10
|
-
|
|
11
|
-
|
|
@@ -16,9 +16,11 @@ from mpl_toolkits.mplot3d import Axes3D
|
|
|
16
16
|
import networkit as nk
|
|
17
17
|
import networkx as nx
|
|
18
18
|
import numpy as np # rustworkx
|
|
19
|
+
from numba import jit
|
|
19
20
|
import platform # graph_tool için
|
|
20
21
|
import random
|
|
21
22
|
import rustworkx as rx
|
|
23
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
22
24
|
import warnings
|
|
23
25
|
|
|
24
26
|
|
|
@@ -58,6 +60,79 @@ if platform.system() == "Linux":
|
|
|
58
60
|
else:
|
|
59
61
|
gt = None
|
|
60
62
|
|
|
63
|
+
"""
|
|
64
|
+
@jit(nopython=True)
|
|
65
|
+
def calculate_coordinates(nodes, primary_spacing, secondary_spacing, primary_direction, secondary_start, expanding):
|
|
66
|
+
#Numba ile hızlandırılmış koordinat hesaplama.
|
|
67
|
+
pos = {}
|
|
68
|
+
for i, node_id in enumerate(nodes):
|
|
69
|
+
# Koordinat hesaplama mantığı...
|
|
70
|
+
pos[node_id] = (x, y)
|
|
71
|
+
return pos
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
@jit(nopython=True)
|
|
75
|
+
def calculate_coordinates(
|
|
76
|
+
nodes: list,
|
|
77
|
+
primary_spacing: float,
|
|
78
|
+
secondary_spacing: float,
|
|
79
|
+
primary_direction: str,
|
|
80
|
+
secondary_start: str,
|
|
81
|
+
expanding: bool
|
|
82
|
+
) -> dict:
|
|
83
|
+
"""
|
|
84
|
+
Numba ile hızlandırılmış koordinat hesaplama fonksiyonu.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
nodes: Düğümlerin listesi.
|
|
88
|
+
primary_spacing: Birincil eksendeki düğümler arası mesafe.
|
|
89
|
+
secondary_spacing: İkincil eksendeki zigzag ofseti.
|
|
90
|
+
primary_direction: Birincil yön ('left-to-right', 'right-to-left', 'top_down', 'bottom_up').
|
|
91
|
+
secondary_start: Zigzag'ın başlangıç yönü ('up', 'down', 'left', 'right').
|
|
92
|
+
expanding: Zigzag ofsetinin büyümesi gerekip gerekmediği (True/False).
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
dict: Düğümlerin koordinatlarını içeren sözlük. Örneğin: {0: (x, y), 1: (x, y), ...}.
|
|
96
|
+
"""
|
|
97
|
+
pos = {}
|
|
98
|
+
n = len(nodes)
|
|
99
|
+
|
|
100
|
+
for i in range(n):
|
|
101
|
+
node_id = nodes[i]
|
|
102
|
+
primary_coord = 0.0
|
|
103
|
+
secondary_axis = ''
|
|
104
|
+
|
|
105
|
+
# Birincil eksen koordinatını hesapla
|
|
106
|
+
if primary_direction == 'left-to-right':
|
|
107
|
+
primary_coord = i * primary_spacing
|
|
108
|
+
secondary_axis = 'y'
|
|
109
|
+
elif primary_direction == 'right-to-left':
|
|
110
|
+
primary_coord = -i * primary_spacing
|
|
111
|
+
secondary_axis = 'y'
|
|
112
|
+
elif primary_direction == 'top_down':
|
|
113
|
+
primary_coord = -i * primary_spacing
|
|
114
|
+
secondary_axis = 'x'
|
|
115
|
+
elif primary_direction == 'bottom_up':
|
|
116
|
+
primary_coord = i * primary_spacing
|
|
117
|
+
secondary_axis = 'x'
|
|
118
|
+
|
|
119
|
+
# İkincil eksen ofsetini hesapla
|
|
120
|
+
secondary_offset = 0.0
|
|
121
|
+
if i > 0:
|
|
122
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
123
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
124
|
+
side = 1 if i % 2 != 0 else -1
|
|
125
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
126
|
+
|
|
127
|
+
# Koordinatları ata
|
|
128
|
+
if secondary_axis == 'x':
|
|
129
|
+
x, y = (secondary_offset, primary_coord)
|
|
130
|
+
else:
|
|
131
|
+
x, y = (primary_coord, secondary_offset)
|
|
132
|
+
|
|
133
|
+
pos[node_id] = (x, y)
|
|
134
|
+
|
|
135
|
+
return pos
|
|
61
136
|
|
|
62
137
|
def find_max_node_id(edges):
|
|
63
138
|
"""
|
|
@@ -1420,6 +1495,228 @@ def generate_distinct_colors(n):
|
|
|
1420
1495
|
colors.append(adjusted_rgb)
|
|
1421
1496
|
return colors
|
|
1422
1497
|
|
|
1498
|
+
# 2D Layout
|
|
1499
|
+
def kececi_layout_2d(
|
|
1500
|
+
nx_graph: nx.Graph,
|
|
1501
|
+
primary_spacing: float = 1.0,
|
|
1502
|
+
secondary_spacing: float = 1.0,
|
|
1503
|
+
primary_direction: str = 'left-to-right',
|
|
1504
|
+
secondary_start: str = 'up',
|
|
1505
|
+
expanding: bool = True
|
|
1506
|
+
) -> Dict[int, Tuple[float, float]]:
|
|
1507
|
+
pos = {}
|
|
1508
|
+
nodes = sorted(list(nx_graph.nodes()))
|
|
1509
|
+
|
|
1510
|
+
for i, node_id in enumerate(nodes):
|
|
1511
|
+
if primary_direction == 'left-to-right':
|
|
1512
|
+
primary_coord, secondary_axis = i * primary_spacing, 'y'
|
|
1513
|
+
elif primary_direction == 'right-to-left':
|
|
1514
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'y'
|
|
1515
|
+
elif primary_direction == 'top_down':
|
|
1516
|
+
primary_coord, secondary_axis = i * -primary_spacing, 'x'
|
|
1517
|
+
else: # 'bottom_up'
|
|
1518
|
+
primary_coord, secondary_axis = i * primary_spacing, 'x'
|
|
1519
|
+
|
|
1520
|
+
secondary_offset = 0.0
|
|
1521
|
+
if i > 0:
|
|
1522
|
+
start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
|
|
1523
|
+
magnitude = math.ceil(i / 2.0) if expanding else 1.0
|
|
1524
|
+
side = 1 if i % 2 != 0 else -1
|
|
1525
|
+
secondary_offset = start_multiplier * magnitude * side * secondary_spacing
|
|
1526
|
+
|
|
1527
|
+
x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
|
|
1528
|
+
pos[node_id] = (x, y)
|
|
1529
|
+
|
|
1530
|
+
return pos
|
|
1531
|
+
|
|
1532
|
+
# Silindirik Layout
|
|
1533
|
+
def kececi_layout_cylindrical(
|
|
1534
|
+
nx_graph: nx.Graph,
|
|
1535
|
+
radius: float = 5.0,
|
|
1536
|
+
height: float = 10.0
|
|
1537
|
+
) -> Dict[int, Tuple[float, float, float]]:
|
|
1538
|
+
pos_3d = {}
|
|
1539
|
+
nodes = sorted(list(nx_graph.nodes()))
|
|
1540
|
+
num_nodes = len(nodes)
|
|
1541
|
+
|
|
1542
|
+
for i, node_id in enumerate(nodes):
|
|
1543
|
+
theta = 2 * np.pi * i / num_nodes
|
|
1544
|
+
x = radius * np.cos(theta)
|
|
1545
|
+
y = radius * np.sin(theta)
|
|
1546
|
+
z = height * i / num_nodes
|
|
1547
|
+
pos_3d[node_id] = (x, y, z)
|
|
1548
|
+
|
|
1549
|
+
return pos_3d
|
|
1550
|
+
|
|
1551
|
+
# Kübik Layout
|
|
1552
|
+
def kececi_layout_cubic(
|
|
1553
|
+
nx_graph: nx.Graph,
|
|
1554
|
+
size: float = 5.0
|
|
1555
|
+
) -> Dict[int, Tuple[float, float, float]]:
|
|
1556
|
+
pos_3d = {}
|
|
1557
|
+
nodes = sorted(list(nx_graph.nodes()))
|
|
1558
|
+
num_nodes = len(nodes)
|
|
1559
|
+
cube_size = int(np.cbrt(num_nodes)) + 1
|
|
1560
|
+
|
|
1561
|
+
for i, node_id in enumerate(nodes):
|
|
1562
|
+
x = size * (i % cube_size)
|
|
1563
|
+
y = size * ((i // cube_size) % cube_size)
|
|
1564
|
+
z = size * ((i // (cube_size ** 2)) % cube_size)
|
|
1565
|
+
pos_3d[node_id] = (x, y, z)
|
|
1566
|
+
|
|
1567
|
+
return pos_3d
|
|
1568
|
+
|
|
1569
|
+
# Küresel Layout
|
|
1570
|
+
def kececi_layout_spherical(
|
|
1571
|
+
nx_graph: nx.Graph,
|
|
1572
|
+
radius: float = 5.0
|
|
1573
|
+
) -> Dict[int, Tuple[float, float, float]]:
|
|
1574
|
+
pos_3d = {}
|
|
1575
|
+
nodes = sorted(list(nx_graph.nodes()))
|
|
1576
|
+
num_nodes = len(nodes)
|
|
1577
|
+
|
|
1578
|
+
for i, node_id in enumerate(nodes):
|
|
1579
|
+
theta = 2 * np.pi * i / num_nodes
|
|
1580
|
+
phi = np.arccos(1 - 2 * (i + 0.5) / num_nodes)
|
|
1581
|
+
x = radius * np.sin(phi) * np.cos(theta)
|
|
1582
|
+
y = radius * np.sin(phi) * np.sin(theta)
|
|
1583
|
+
z = radius * np.cos(phi)
|
|
1584
|
+
pos_3d[node_id] = (x, y, z)
|
|
1585
|
+
|
|
1586
|
+
return pos_3d
|
|
1587
|
+
|
|
1588
|
+
# Eliptik Layout
|
|
1589
|
+
def kececi_layout_elliptical(
|
|
1590
|
+
nx_graph: nx.Graph,
|
|
1591
|
+
a: float = 5.0,
|
|
1592
|
+
b: float = 3.0
|
|
1593
|
+
) -> Dict[int, Tuple[float, float]]:
|
|
1594
|
+
pos = {}
|
|
1595
|
+
nodes = sorted(list(nx_graph.nodes()))
|
|
1596
|
+
num_nodes = len(nodes)
|
|
1597
|
+
|
|
1598
|
+
for i, node_id in enumerate(nodes):
|
|
1599
|
+
theta = 2 * np.pi * i / num_nodes
|
|
1600
|
+
x = a * np.cos(theta)
|
|
1601
|
+
y = b * np.sin(theta)
|
|
1602
|
+
pos[node_id] = (x, y)
|
|
1603
|
+
|
|
1604
|
+
return pos
|
|
1605
|
+
|
|
1606
|
+
# Torik (Halkasal) Layout
|
|
1607
|
+
def kececi_layout_toric(
|
|
1608
|
+
nx_graph: nx.Graph,
|
|
1609
|
+
major_radius: float = 5.0,
|
|
1610
|
+
minor_radius: float = 2.0
|
|
1611
|
+
) -> Dict[int, Tuple[float, float, float]]:
|
|
1612
|
+
pos_3d = {}
|
|
1613
|
+
nodes = sorted(list(nx_graph.nodes()))
|
|
1614
|
+
num_nodes = len(nodes)
|
|
1615
|
+
|
|
1616
|
+
for i, node_id in enumerate(nodes):
|
|
1617
|
+
theta = 2 * np.pi * i / num_nodes
|
|
1618
|
+
phi = 2 * np.pi * i / num_nodes
|
|
1619
|
+
x = (major_radius + minor_radius * np.cos(phi)) * np.cos(theta)
|
|
1620
|
+
y = (major_radius + minor_radius * np.cos(phi)) * np.sin(theta)
|
|
1621
|
+
z = minor_radius * np.sin(phi)
|
|
1622
|
+
pos_3d[node_id] = (x, y, z)
|
|
1623
|
+
|
|
1624
|
+
return pos_3d
|
|
1625
|
+
|
|
1626
|
+
# Ağırlıklı Çizim (draw_kececi_weighted)
|
|
1627
|
+
def draw_kececi_weighted(
|
|
1628
|
+
nx_graph: nx.Graph,
|
|
1629
|
+
pos: Dict[int, Tuple[float, ...]],
|
|
1630
|
+
ax: Optional[plt.Axes] = None,
|
|
1631
|
+
node_size: int = 300,
|
|
1632
|
+
edge_width_scale: float = 2.0,
|
|
1633
|
+
**kwargs
|
|
1634
|
+
) -> plt.Axes:
|
|
1635
|
+
if ax is None:
|
|
1636
|
+
fig = plt.figure(figsize=(10, 8))
|
|
1637
|
+
ax = fig.add_subplot(111)
|
|
1638
|
+
|
|
1639
|
+
weights = nx.get_edge_attributes(nx_graph, 'weight')
|
|
1640
|
+
if not weights:
|
|
1641
|
+
weights = {edge: 1.0 for edge in nx_graph.edges()}
|
|
1642
|
+
|
|
1643
|
+
nx.draw_networkx_nodes(nx_graph, pos, ax=ax, node_size=node_size, **kwargs)
|
|
1644
|
+
|
|
1645
|
+
is_3d = len(pos[next(iter(pos))]) == 3
|
|
1646
|
+
if is_3d:
|
|
1647
|
+
for node, coord in pos.items():
|
|
1648
|
+
ax.text(coord[0], coord[1], coord[2], f' {node}', size=10, zorder=1, color='black')
|
|
1649
|
+
else:
|
|
1650
|
+
nx.draw_networkx_labels(nx_graph, pos, ax=ax)
|
|
1651
|
+
|
|
1652
|
+
for (u, v), weight in weights.items():
|
|
1653
|
+
width = weight * edge_width_scale
|
|
1654
|
+
if is_3d:
|
|
1655
|
+
ax.plot(
|
|
1656
|
+
[pos[u][0], pos[v][0]],
|
|
1657
|
+
[pos[u][1], pos[v][1]],
|
|
1658
|
+
[pos[u][2], pos[v][2]],
|
|
1659
|
+
linewidth=width,
|
|
1660
|
+
color='gray',
|
|
1661
|
+
alpha=0.7
|
|
1662
|
+
)
|
|
1663
|
+
else:
|
|
1664
|
+
ax.plot(
|
|
1665
|
+
[pos[u][0], pos[v][0]],
|
|
1666
|
+
[pos[u][1], pos[v][1]],
|
|
1667
|
+
linewidth=width,
|
|
1668
|
+
color='gray',
|
|
1669
|
+
alpha=0.7
|
|
1670
|
+
)
|
|
1671
|
+
|
|
1672
|
+
ax.set_title("Keçeci Layout: Weighted Edges")
|
|
1673
|
+
return ax
|
|
1674
|
+
|
|
1675
|
+
# Renkli Çizim (draw_kececi_colored)
|
|
1676
|
+
def draw_kececi_colored(
|
|
1677
|
+
nx_graph: nx.Graph,
|
|
1678
|
+
pos: Dict[int, Tuple[float, ...]],
|
|
1679
|
+
ax: Optional[plt.Axes] = None,
|
|
1680
|
+
node_size: int = 300,
|
|
1681
|
+
**kwargs
|
|
1682
|
+
) -> plt.Axes:
|
|
1683
|
+
if ax is None:
|
|
1684
|
+
fig = plt.figure(figsize=(10, 8))
|
|
1685
|
+
ax = fig.add_subplot(111)
|
|
1686
|
+
|
|
1687
|
+
degrees = dict(nx_graph.degree())
|
|
1688
|
+
max_degree = max(degrees.values()) if degrees else 1
|
|
1689
|
+
node_colors = [plt.cm.viridis(deg / max_degree) for deg in degrees.values()]
|
|
1690
|
+
|
|
1691
|
+
nx.draw_networkx_nodes(
|
|
1692
|
+
nx_graph, pos, ax=ax,
|
|
1693
|
+
node_color=node_colors,
|
|
1694
|
+
node_size=node_size,
|
|
1695
|
+
**kwargs
|
|
1696
|
+
)
|
|
1697
|
+
|
|
1698
|
+
is_3d = len(pos[next(iter(pos))]) == 3
|
|
1699
|
+
if is_3d:
|
|
1700
|
+
for node, coord in pos.items():
|
|
1701
|
+
ax.text(coord[0], coord[1], coord[2], f' {node}', size=10, zorder=1, color='black')
|
|
1702
|
+
else:
|
|
1703
|
+
nx.draw_networkx_labels(nx_graph, pos, ax=ax)
|
|
1704
|
+
|
|
1705
|
+
if is_3d:
|
|
1706
|
+
for u, v in nx_graph.edges():
|
|
1707
|
+
ax.plot(
|
|
1708
|
+
[pos[u][0], pos[v][0]],
|
|
1709
|
+
[pos[u][1], pos[v][1]],
|
|
1710
|
+
[pos[u][2], pos[v][2]],
|
|
1711
|
+
color='gray',
|
|
1712
|
+
alpha=0.5
|
|
1713
|
+
)
|
|
1714
|
+
else:
|
|
1715
|
+
nx.draw_networkx_edges(nx_graph, pos, ax=ax, alpha=0.5)
|
|
1716
|
+
|
|
1717
|
+
ax.set_title("Keçeci Layout: Colored Nodes")
|
|
1718
|
+
return ax
|
|
1719
|
+
|
|
1423
1720
|
# =============================================================================
|
|
1424
1721
|
# 3. INTERNAL DRAWING STYLE IMPLEMENTATIONS
|
|
1425
1722
|
# =============================================================================
|
|
@@ -1523,9 +1820,181 @@ def _draw_internal(nx_graph, ax, style, **kwargs):
|
|
|
1523
1820
|
# =============================================================================
|
|
1524
1821
|
# 4. MAIN USER-FACING DRAWING FUNCTION
|
|
1525
1822
|
# =============================================================================
|
|
1823
|
+
def draw_kececi(
|
|
1824
|
+
graph,
|
|
1825
|
+
pos: Optional[Dict[int, Tuple[float, ...]]] = None,
|
|
1826
|
+
layout: Optional[str] = None,
|
|
1827
|
+
style: str = 'default',
|
|
1828
|
+
ax: Optional[plt.Axes] = None,
|
|
1829
|
+
with_labels: bool = True,
|
|
1830
|
+
node_color: str = 'lightblue',
|
|
1831
|
+
node_size: int = 500,
|
|
1832
|
+
font_weight: str = 'bold',
|
|
1833
|
+
**kwargs
|
|
1834
|
+
) -> plt.Axes:
|
|
1835
|
+
"""
|
|
1836
|
+
Keçeci Layout ile graf çizimi.
|
|
1526
1837
|
|
|
1527
|
-
|
|
1838
|
+
Args:
|
|
1839
|
+
graph: Graf objesi (NetworkX, igraph, vb.).
|
|
1840
|
+
pos: Önceden hesaplanmış koordinatlar (opsiyonel).
|
|
1841
|
+
layout: '2d', 'cylindrical', 'cubic', 'spherical', 'elliptical', 'toric' (opsiyonel).
|
|
1842
|
+
style: 'default', 'weighted', 'colored'.
|
|
1843
|
+
ax: Matplotlib ekseni.
|
|
1844
|
+
with_labels: Düğüm etiketlerini göster.
|
|
1845
|
+
node_color: Düğüm rengi.
|
|
1846
|
+
node_size: Düğüm boyutu.
|
|
1847
|
+
font_weight: Yazı kalınlığı.
|
|
1848
|
+
**kwargs: Ek parametreler.
|
|
1849
|
+
|
|
1850
|
+
Returns:
|
|
1851
|
+
Matplotlib ekseni.
|
|
1528
1852
|
"""
|
|
1853
|
+
nx_graph = to_networkx(graph)
|
|
1854
|
+
|
|
1855
|
+
# Eğer pos verilmemişse, layout'a göre hesapla
|
|
1856
|
+
if pos is None:
|
|
1857
|
+
if layout is None:
|
|
1858
|
+
layout = '2d' # Varsayılan layout
|
|
1859
|
+
|
|
1860
|
+
if layout == '2d':
|
|
1861
|
+
pos = kececi_layout_2d(nx_graph, **kwargs)
|
|
1862
|
+
elif layout == 'cylindrical':
|
|
1863
|
+
pos = kececi_layout_cylindrical(nx_graph, **kwargs)
|
|
1864
|
+
elif layout == 'cubic':
|
|
1865
|
+
pos = kececi_layout_cubic(nx_graph, **kwargs)
|
|
1866
|
+
elif layout == 'spherical':
|
|
1867
|
+
pos = kececi_layout_spherical(nx_graph, **kwargs)
|
|
1868
|
+
elif layout == 'elliptical':
|
|
1869
|
+
pos = kececi_layout_elliptical(nx_graph, **kwargs)
|
|
1870
|
+
elif layout == 'toric':
|
|
1871
|
+
pos = kececi_layout_toric(nx_graph, **kwargs)
|
|
1872
|
+
else:
|
|
1873
|
+
raise ValueError(f"Geçersiz layout: {layout}")
|
|
1874
|
+
|
|
1875
|
+
# 3D için eksen ayarlaması
|
|
1876
|
+
is_3d = len(pos[next(iter(pos))]) == 3
|
|
1877
|
+
if ax is None:
|
|
1878
|
+
fig = plt.figure(figsize=(10, 8))
|
|
1879
|
+
if is_3d:
|
|
1880
|
+
ax = fig.add_subplot(111, projection='3d')
|
|
1881
|
+
else:
|
|
1882
|
+
ax = fig.add_subplot(111)
|
|
1883
|
+
|
|
1884
|
+
# Stile göre çizim yap
|
|
1885
|
+
if style == 'weighted':
|
|
1886
|
+
draw_kececi_weighted(nx_graph, pos, ax, **kwargs)
|
|
1887
|
+
elif style == 'colored':
|
|
1888
|
+
draw_kececi_colored(nx_graph, pos, ax, **kwargs)
|
|
1889
|
+
else: # 'default'
|
|
1890
|
+
nx.draw_networkx_nodes(nx_graph, pos, ax=ax, node_color=node_color, node_size=node_size)
|
|
1891
|
+
|
|
1892
|
+
# Düğüm etiketlerini çiz
|
|
1893
|
+
if with_labels:
|
|
1894
|
+
if is_3d:
|
|
1895
|
+
for node, coord in pos.items():
|
|
1896
|
+
ax.text(coord[0], coord[1], coord[2], f' {node}', size=10, zorder=1, color='black', fontweight=font_weight)
|
|
1897
|
+
else:
|
|
1898
|
+
nx.draw_networkx_labels(nx_graph, pos, ax=ax, font_weight=font_weight)
|
|
1899
|
+
|
|
1900
|
+
# Kenarları çiz
|
|
1901
|
+
if is_3d:
|
|
1902
|
+
for u, v in nx_graph.edges():
|
|
1903
|
+
ax.plot(
|
|
1904
|
+
[pos[u][0], pos[v][0]],
|
|
1905
|
+
[pos[u][1], pos[v][1]],
|
|
1906
|
+
[pos[u][2], pos[v][2]],
|
|
1907
|
+
color='gray',
|
|
1908
|
+
alpha=0.5
|
|
1909
|
+
)
|
|
1910
|
+
else:
|
|
1911
|
+
nx.draw_networkx_edges(nx_graph, pos, ax=ax, alpha=0.5)
|
|
1912
|
+
|
|
1913
|
+
ax.set_title(f"Keçeci Layout: {layout.capitalize() if layout else 'Custom'} ({style})")
|
|
1914
|
+
return ax
|
|
1915
|
+
"""
|
|
1916
|
+
def draw_kececi(
|
|
1917
|
+
graph,
|
|
1918
|
+
layout: str = '2d',
|
|
1919
|
+
style: str = 'default',
|
|
1920
|
+
ax: Optional[plt.Axes] = None,
|
|
1921
|
+
**kwargs
|
|
1922
|
+
) -> plt.Axes:
|
|
1923
|
+
|
|
1924
|
+
Keçeci Layout ile graf çizimi.
|
|
1925
|
+
|
|
1926
|
+
Args:
|
|
1927
|
+
graph: Graf objesi (NetworkX, igraph, vb.).
|
|
1928
|
+
layout: '2d', 'cylindrical', 'cubic', 'spherical', 'elliptical', 'toric'.
|
|
1929
|
+
style: 'default', 'weighted', 'colored'.
|
|
1930
|
+
ax: Matplotlib ekseni.
|
|
1931
|
+
**kwargs: Ek parametreler.
|
|
1932
|
+
|
|
1933
|
+
Returns:
|
|
1934
|
+
Matplotlib ekseni.
|
|
1935
|
+
|
|
1936
|
+
nx_graph = to_networkx(graph)
|
|
1937
|
+
|
|
1938
|
+
# Layout'a göre koordinatları hesapla
|
|
1939
|
+
if layout == '2d':
|
|
1940
|
+
pos = kececi_layout_2d(nx_graph, **kwargs)
|
|
1941
|
+
elif layout == 'cylindrical':
|
|
1942
|
+
pos = kececi_layout_cylindrical(nx_graph, **kwargs)
|
|
1943
|
+
elif layout == 'cubic':
|
|
1944
|
+
pos = kececi_layout_cubic(nx_graph, **kwargs)
|
|
1945
|
+
elif layout == 'spherical':
|
|
1946
|
+
pos = kececi_layout_spherical(nx_graph, **kwargs)
|
|
1947
|
+
elif layout == 'elliptical':
|
|
1948
|
+
pos = kececi_layout_elliptical(nx_graph, **kwargs)
|
|
1949
|
+
elif layout == 'toric':
|
|
1950
|
+
pos = kececi_layout_toric(nx_graph, **kwargs)
|
|
1951
|
+
else:
|
|
1952
|
+
raise ValueError(f"Invalid layout: {layout}")
|
|
1953
|
+
|
|
1954
|
+
# 3D için eksen ayarlaması
|
|
1955
|
+
is_3d = len(pos[next(iter(pos))]) == 3
|
|
1956
|
+
if ax is None:
|
|
1957
|
+
fig = plt.figure(figsize=(10, 8))
|
|
1958
|
+
if is_3d:
|
|
1959
|
+
ax = fig.add_subplot(111, projection='3d')
|
|
1960
|
+
else:
|
|
1961
|
+
ax = fig.add_subplot(111)
|
|
1962
|
+
|
|
1963
|
+
# Stile göre çizim yap
|
|
1964
|
+
if style == 'weighted':
|
|
1965
|
+
draw_kececi_weighted(nx_graph, pos, ax, **kwargs)
|
|
1966
|
+
elif style == 'colored':
|
|
1967
|
+
draw_kececi_colored(nx_graph, pos, ax, **kwargs)
|
|
1968
|
+
else: # 'default'
|
|
1969
|
+
nx.draw_networkx_nodes(nx_graph, pos, ax=ax, **kwargs)
|
|
1970
|
+
|
|
1971
|
+
# Düğüm etiketlerini çiz
|
|
1972
|
+
if is_3d:
|
|
1973
|
+
for node, coord in pos.items():
|
|
1974
|
+
ax.text(coord[0], coord[1], coord[2], f' {node}', size=10, zorder=1, color='black')
|
|
1975
|
+
else:
|
|
1976
|
+
nx.draw_networkx_labels(nx_graph, pos, ax=ax)
|
|
1977
|
+
|
|
1978
|
+
# Kenarları çiz
|
|
1979
|
+
if is_3d:
|
|
1980
|
+
for u, v in nx_graph.edges():
|
|
1981
|
+
ax.plot(
|
|
1982
|
+
[pos[u][0], pos[v][0]],
|
|
1983
|
+
[pos[u][1], pos[v][1]],
|
|
1984
|
+
[pos[u][2], pos[v][2]],
|
|
1985
|
+
color='gray',
|
|
1986
|
+
alpha=0.5
|
|
1987
|
+
)
|
|
1988
|
+
else:
|
|
1989
|
+
nx.draw_networkx_edges(nx_graph, pos, ax=ax, alpha=0.5)
|
|
1990
|
+
|
|
1991
|
+
ax.set_title(f"Keçeci Layout: {layout.capitalize()} ({style})")
|
|
1992
|
+
return ax
|
|
1993
|
+
"""
|
|
1994
|
+
|
|
1995
|
+
"""
|
|
1996
|
+
def draw_kececi(graph, style='curved', ax=None, **kwargs):
|
|
1997
|
+
|
|
1529
1998
|
Draws a graph using the Keçeci Layout with a specified style.
|
|
1530
1999
|
|
|
1531
2000
|
This function automatically handles graphs from different libraries
|
|
@@ -1541,7 +2010,7 @@ def draw_kececi(graph, style='curved', ax=None, **kwargs):
|
|
|
1541
2010
|
|
|
1542
2011
|
Returns:
|
|
1543
2012
|
matplotlib.axis.Axis: The axis object where the graph was drawn.
|
|
1544
|
-
|
|
2013
|
+
|
|
1545
2014
|
nx_graph = to_networkx(graph)
|
|
1546
2015
|
is_3d = (style.lower() == '3d')
|
|
1547
2016
|
|
|
@@ -1559,7 +2028,7 @@ def draw_kececi(graph, style='curved', ax=None, **kwargs):
|
|
|
1559
2028
|
|
|
1560
2029
|
_draw_internal(nx_graph, ax, style.lower(), **kwargs)
|
|
1561
2030
|
return ax
|
|
1562
|
-
|
|
2031
|
+
"""
|
|
1563
2032
|
|
|
1564
2033
|
# =============================================================================
|
|
1565
2034
|
# MODULE TEST CODE
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kececilayout
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Çeşitli graf kütüphaneleri için sıralı-zigzag yerleşimleri sağlayan bir Python paketi.
|
|
5
5
|
Home-page: https://github.com/WhiteSymmetry/kececilayout
|
|
6
6
|
Author: Mehmet Keçeci
|
|
@@ -54,6 +54,7 @@ Requires-Dist: rustworkx; extra == "test"
|
|
|
54
54
|
Requires-Dist: networkit; extra == "test"
|
|
55
55
|
Requires-Dist: graphillion; extra == "test"
|
|
56
56
|
Requires-Dist: graph-tool; extra == "test"
|
|
57
|
+
Requires-Dist: numba; extra == "test"
|
|
57
58
|
Dynamic: author
|
|
58
59
|
Dynamic: home-page
|
|
59
60
|
Dynamic: license-file
|
|
@@ -118,6 +119,12 @@ This algorithm arranges nodes sequentially along a primary axis and offsets them
|
|
|
118
119
|
|
|
119
120
|
=> **v0.2.7**: Curved, transparent, 3D, and `expanding=True` styles supported.
|
|
120
121
|
|
|
122
|
+
**v0.5.0:**
|
|
123
|
+
|
|
124
|
+
layouts = ['2d', 'cylindrical', 'cubic', 'spherical', 'elliptical', 'toric']
|
|
125
|
+
|
|
126
|
+
styles = ['standard', 'default', 'curved', 'helix', '3d', 'weighted', 'colored']
|
|
127
|
+
|
|
121
128
|
---
|
|
122
129
|
|
|
123
130
|
### Installation
|
|
@@ -1958,3 +1965,5 @@ Keçeci, Mehmet. "Keçeci Layout". Open Science Articles (OSAs), Zenodo, 2025. h
|
|
|
1958
1965
|
|
|
1959
1966
|
|
|
1960
1967
|
|
|
1968
|
+
|
|
1969
|
+
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "kececilayout"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.5.0"
|
|
10
10
|
|
|
11
11
|
# Diğer proje bilgileri (isteğe bağlı ama tavsiye edilir)
|
|
12
12
|
authors = [
|
|
@@ -44,6 +44,7 @@ test = [
|
|
|
44
44
|
"networkit",
|
|
45
45
|
"graphillion",
|
|
46
46
|
"graph-tool",
|
|
47
|
+
"numba",
|
|
47
48
|
]
|
|
48
49
|
|
|
49
50
|
[dependency-groups]
|
|
@@ -71,6 +72,3 @@ configuration = "docs/conf.py" # Build konfigürasyonu belirtin
|
|
|
71
72
|
"Homepage" = "https://github.com/WhiteSymmetry/kececilayout"
|
|
72
73
|
"Bug Tracker" = "https://github.com/WhiteSymmetry/kececilayout/issues"
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
@@ -4,7 +4,6 @@ import re
|
|
|
4
4
|
from setuptools import setup, find_packages
|
|
5
5
|
import sys
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
# BU SATIRLAR SORUNUN KALICI ÇÖZÜMÜDÜR.
|
|
9
8
|
# Python'a, README.md dosyasını hangi işletim sisteminde olursa olsun
|
|
10
9
|
# her zaman UTF-8 kodlamasıyla okumasını söylüyoruz.
|
|
@@ -13,7 +12,6 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
13
12
|
|
|
14
13
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
15
14
|
|
|
16
|
-
|
|
17
15
|
def get_version():
|
|
18
16
|
with open('kececilayout/__init__.py', 'r', encoding='utf-8') as f:
|
|
19
17
|
content = f.read()
|
|
@@ -42,7 +40,7 @@ setup(
|
|
|
42
40
|
"cairocffi"
|
|
43
41
|
],
|
|
44
42
|
extras_require={
|
|
45
|
-
"all": ["cairo", "python-louvain", "python-igraph", "networkit", "rustworkx", "graphillion", "graph-tool"],
|
|
43
|
+
"all": ["cairo", "python-louvain", "python-igraph", "networkit", "rustworkx", "graphillion", "graph-tool", "numba"],
|
|
46
44
|
},
|
|
47
45
|
classifiers=[
|
|
48
46
|
"Programming Language :: Python :: 3",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|