kececilayout 0.4.9__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.
@@ -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
@@ -16,9 +33,11 @@ from mpl_toolkits.mplot3d import Axes3D
16
33
  import networkit as nk
17
34
  import networkx as nx
18
35
  import numpy as np # rustworkx
36
+ from numba import jit
19
37
  import platform # graph_tool için
20
38
  import random
21
39
  import rustworkx as rx
40
+ from typing import Any, Dict, List, Optional, Tuple, Union
22
41
  import warnings
23
42
 
24
43
 
@@ -58,6 +77,79 @@ if platform.system() == "Linux":
58
77
  else:
59
78
  gt = None
60
79
 
80
+ """
81
+ @jit(nopython=True)
82
+ def calculate_coordinates(nodes, primary_spacing, secondary_spacing, primary_direction, secondary_start, expanding):
83
+ #Numba ile hızlandırılmış koordinat hesaplama.
84
+ pos = {}
85
+ for i, node_id in enumerate(nodes):
86
+ # Koordinat hesaplama mantığı...
87
+ pos[node_id] = (x, y)
88
+ return pos
89
+ """
90
+
91
+ @jit(nopython=True)
92
+ def calculate_coordinates(
93
+ nodes: list,
94
+ primary_spacing: float,
95
+ secondary_spacing: float,
96
+ primary_direction: str,
97
+ secondary_start: str,
98
+ expanding: bool
99
+ ) -> dict:
100
+ """
101
+ Numba ile hızlandırılmış koordinat hesaplama fonksiyonu.
102
+
103
+ Args:
104
+ nodes: Düğümlerin listesi.
105
+ primary_spacing: Birincil eksendeki düğümler arası mesafe.
106
+ secondary_spacing: İkincil eksendeki zigzag ofseti.
107
+ primary_direction: Birincil yön ('left-to-right', 'right-to-left', 'top_down', 'bottom_up').
108
+ secondary_start: Zigzag'ın başlangıç yönü ('up', 'down', 'left', 'right').
109
+ expanding: Zigzag ofsetinin büyümesi gerekip gerekmediği (True/False).
110
+
111
+ Returns:
112
+ dict: Düğümlerin koordinatlarını içeren sözlük. Örneğin: {0: (x, y), 1: (x, y), ...}.
113
+ """
114
+ pos = {}
115
+ n = len(nodes)
116
+
117
+ for i in range(n):
118
+ node_id = nodes[i]
119
+ primary_coord = 0.0
120
+ secondary_axis = ''
121
+
122
+ # Birincil eksen koordinatını hesapla
123
+ if primary_direction == 'left-to-right':
124
+ primary_coord = i * primary_spacing
125
+ secondary_axis = 'y'
126
+ elif primary_direction == 'right-to-left':
127
+ primary_coord = -i * primary_spacing
128
+ secondary_axis = 'y'
129
+ elif primary_direction == 'top_down':
130
+ primary_coord = -i * primary_spacing
131
+ secondary_axis = 'x'
132
+ elif primary_direction == 'bottom_up':
133
+ primary_coord = i * primary_spacing
134
+ secondary_axis = 'x'
135
+
136
+ # İkincil eksen ofsetini hesapla
137
+ secondary_offset = 0.0
138
+ if i > 0:
139
+ start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
140
+ magnitude = math.ceil(i / 2.0) if expanding else 1.0
141
+ side = 1 if i % 2 != 0 else -1
142
+ secondary_offset = start_multiplier * magnitude * side * secondary_spacing
143
+
144
+ # Koordinatları ata
145
+ if secondary_axis == 'x':
146
+ x, y = (secondary_offset, primary_coord)
147
+ else:
148
+ x, y = (primary_coord, secondary_offset)
149
+
150
+ pos[node_id] = (x, y)
151
+
152
+ return pos
61
153
 
62
154
  def find_max_node_id(edges):
63
155
  """
@@ -170,12 +262,104 @@ def kececi_layout(graph, primary_spacing=1.0, secondary_spacing=1.0,
170
262
  pos[node_id] = (x, y)
171
263
  return pos
172
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
+
173
358
  # =============================================================================
174
359
  # 1. TEMEL LAYOUT HESAPLAMA FONKSİYONU (2D)
175
360
  # Bu fonksiyon sadece koordinatları hesaplar, çizim yapmaz.
176
361
  # 1. LAYOUT CALCULATION FUNCTION (UNIFIED AND IMPROVED)
177
362
  # =============================================================================
178
-
179
363
  def kececi_layout_v4(graph, primary_spacing=1.0, secondary_spacing=1.0,
180
364
  primary_direction='top_down', secondary_start='right',
181
365
  expanding=True):
@@ -268,7 +452,7 @@ def kececi_layout_nx(graph, primary_spacing=1.0, secondary_spacing=1.0,
268
452
  primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
269
453
  secondary_start (str): Initial direction for the zigzag offset.
270
454
  expanding (bool): If True (default), the zigzag offset grows.
271
- If False, the offset is constant (parallel lines). # <-- 2. DOKÜMANTASYON GÜNCELLENDİ
455
+ If False, the offset is constant (parallel lines).
272
456
 
273
457
  Returns:
274
458
  dict: A dictionary of positions keyed by node ID.
@@ -327,7 +511,7 @@ def kececi_layout_networkx(graph, primary_spacing=1.0, secondary_spacing=1.0,
327
511
  primary_direction (str): 'top_down', 'bottom_up', 'left-to-right', 'right-to-left'.
328
512
  secondary_start (str): Initial direction for the zigzag offset.
329
513
  expanding (bool): If True (default), the zigzag offset grows.
330
- If False, the offset is constant (parallel lines). # <-- 2. DOKÜMANTASYON GÜNCELLENDİ
514
+ If False, the offset is constant (parallel lines).
331
515
 
332
516
  Returns:
333
517
  dict: A dictionary of positions keyed by node ID.
@@ -1420,6 +1604,228 @@ def generate_distinct_colors(n):
1420
1604
  colors.append(adjusted_rgb)
1421
1605
  return colors
1422
1606
 
1607
+ # 2D Layout
1608
+ def kececi_layout_2d(
1609
+ nx_graph: nx.Graph,
1610
+ primary_spacing: float = 1.0,
1611
+ secondary_spacing: float = 1.0,
1612
+ primary_direction: str = 'left-to-right',
1613
+ secondary_start: str = 'up',
1614
+ expanding: bool = True
1615
+ ) -> Dict[int, Tuple[float, float]]:
1616
+ pos = {}
1617
+ nodes = sorted(list(nx_graph.nodes()))
1618
+
1619
+ for i, node_id in enumerate(nodes):
1620
+ if primary_direction == 'left-to-right':
1621
+ primary_coord, secondary_axis = i * primary_spacing, 'y'
1622
+ elif primary_direction == 'right-to-left':
1623
+ primary_coord, secondary_axis = i * -primary_spacing, 'y'
1624
+ elif primary_direction == 'top_down':
1625
+ primary_coord, secondary_axis = i * -primary_spacing, 'x'
1626
+ else: # 'bottom_up'
1627
+ primary_coord, secondary_axis = i * primary_spacing, 'x'
1628
+
1629
+ secondary_offset = 0.0
1630
+ if i > 0:
1631
+ start_multiplier = 1.0 if secondary_start in ['right', 'up'] else -1.0
1632
+ magnitude = math.ceil(i / 2.0) if expanding else 1.0
1633
+ side = 1 if i % 2 != 0 else -1
1634
+ secondary_offset = start_multiplier * magnitude * side * secondary_spacing
1635
+
1636
+ x, y = (secondary_offset, primary_coord) if secondary_axis == 'x' else (primary_coord, secondary_offset)
1637
+ pos[node_id] = (x, y)
1638
+
1639
+ return pos
1640
+
1641
+ # Silindirik Layout
1642
+ def kececi_layout_cylindrical(
1643
+ nx_graph: nx.Graph,
1644
+ radius: float = 5.0,
1645
+ height: float = 10.0
1646
+ ) -> Dict[int, Tuple[float, float, float]]:
1647
+ pos_3d = {}
1648
+ nodes = sorted(list(nx_graph.nodes()))
1649
+ num_nodes = len(nodes)
1650
+
1651
+ for i, node_id in enumerate(nodes):
1652
+ theta = 2 * np.pi * i / num_nodes
1653
+ x = radius * np.cos(theta)
1654
+ y = radius * np.sin(theta)
1655
+ z = height * i / num_nodes
1656
+ pos_3d[node_id] = (x, y, z)
1657
+
1658
+ return pos_3d
1659
+
1660
+ # Kübik Layout
1661
+ def kececi_layout_cubic(
1662
+ nx_graph: nx.Graph,
1663
+ size: float = 5.0
1664
+ ) -> Dict[int, Tuple[float, float, float]]:
1665
+ pos_3d = {}
1666
+ nodes = sorted(list(nx_graph.nodes()))
1667
+ num_nodes = len(nodes)
1668
+ cube_size = int(np.cbrt(num_nodes)) + 1
1669
+
1670
+ for i, node_id in enumerate(nodes):
1671
+ x = size * (i % cube_size)
1672
+ y = size * ((i // cube_size) % cube_size)
1673
+ z = size * ((i // (cube_size ** 2)) % cube_size)
1674
+ pos_3d[node_id] = (x, y, z)
1675
+
1676
+ return pos_3d
1677
+
1678
+ # Küresel Layout
1679
+ def kececi_layout_spherical(
1680
+ nx_graph: nx.Graph,
1681
+ radius: float = 5.0
1682
+ ) -> Dict[int, Tuple[float, float, float]]:
1683
+ pos_3d = {}
1684
+ nodes = sorted(list(nx_graph.nodes()))
1685
+ num_nodes = len(nodes)
1686
+
1687
+ for i, node_id in enumerate(nodes):
1688
+ theta = 2 * np.pi * i / num_nodes
1689
+ phi = np.arccos(1 - 2 * (i + 0.5) / num_nodes)
1690
+ x = radius * np.sin(phi) * np.cos(theta)
1691
+ y = radius * np.sin(phi) * np.sin(theta)
1692
+ z = radius * np.cos(phi)
1693
+ pos_3d[node_id] = (x, y, z)
1694
+
1695
+ return pos_3d
1696
+
1697
+ # Eliptik Layout
1698
+ def kececi_layout_elliptical(
1699
+ nx_graph: nx.Graph,
1700
+ a: float = 5.0,
1701
+ b: float = 3.0
1702
+ ) -> Dict[int, Tuple[float, float]]:
1703
+ pos = {}
1704
+ nodes = sorted(list(nx_graph.nodes()))
1705
+ num_nodes = len(nodes)
1706
+
1707
+ for i, node_id in enumerate(nodes):
1708
+ theta = 2 * np.pi * i / num_nodes
1709
+ x = a * np.cos(theta)
1710
+ y = b * np.sin(theta)
1711
+ pos[node_id] = (x, y)
1712
+
1713
+ return pos
1714
+
1715
+ # Torik (Halkasal) Layout
1716
+ def kececi_layout_toric(
1717
+ nx_graph: nx.Graph,
1718
+ major_radius: float = 5.0,
1719
+ minor_radius: float = 2.0
1720
+ ) -> Dict[int, Tuple[float, float, float]]:
1721
+ pos_3d = {}
1722
+ nodes = sorted(list(nx_graph.nodes()))
1723
+ num_nodes = len(nodes)
1724
+
1725
+ for i, node_id in enumerate(nodes):
1726
+ theta = 2 * np.pi * i / num_nodes
1727
+ phi = 2 * np.pi * i / num_nodes
1728
+ x = (major_radius + minor_radius * np.cos(phi)) * np.cos(theta)
1729
+ y = (major_radius + minor_radius * np.cos(phi)) * np.sin(theta)
1730
+ z = minor_radius * np.sin(phi)
1731
+ pos_3d[node_id] = (x, y, z)
1732
+
1733
+ return pos_3d
1734
+
1735
+ # Ağırlıklı Çizim (draw_kececi_weighted)
1736
+ def draw_kececi_weighted(
1737
+ nx_graph: nx.Graph,
1738
+ pos: Dict[int, Tuple[float, ...]],
1739
+ ax: Optional[plt.Axes] = None,
1740
+ node_size: int = 300,
1741
+ edge_width_scale: float = 2.0,
1742
+ **kwargs
1743
+ ) -> plt.Axes:
1744
+ if ax is None:
1745
+ fig = plt.figure(figsize=(10, 8))
1746
+ ax = fig.add_subplot(111)
1747
+
1748
+ weights = nx.get_edge_attributes(nx_graph, 'weight')
1749
+ if not weights:
1750
+ weights = {edge: 1.0 for edge in nx_graph.edges()}
1751
+
1752
+ nx.draw_networkx_nodes(nx_graph, pos, ax=ax, node_size=node_size, **kwargs)
1753
+
1754
+ is_3d = len(pos[next(iter(pos))]) == 3
1755
+ if is_3d:
1756
+ for node, coord in pos.items():
1757
+ ax.text(coord[0], coord[1], coord[2], f' {node}', size=10, zorder=1, color='black')
1758
+ else:
1759
+ nx.draw_networkx_labels(nx_graph, pos, ax=ax)
1760
+
1761
+ for (u, v), weight in weights.items():
1762
+ width = weight * edge_width_scale
1763
+ if is_3d:
1764
+ ax.plot(
1765
+ [pos[u][0], pos[v][0]],
1766
+ [pos[u][1], pos[v][1]],
1767
+ [pos[u][2], pos[v][2]],
1768
+ linewidth=width,
1769
+ color='gray',
1770
+ alpha=0.7
1771
+ )
1772
+ else:
1773
+ ax.plot(
1774
+ [pos[u][0], pos[v][0]],
1775
+ [pos[u][1], pos[v][1]],
1776
+ linewidth=width,
1777
+ color='gray',
1778
+ alpha=0.7
1779
+ )
1780
+
1781
+ ax.set_title("Keçeci Layout: Weighted Edges")
1782
+ return ax
1783
+
1784
+ # Renkli Çizim (draw_kececi_colored)
1785
+ def draw_kececi_colored(
1786
+ nx_graph: nx.Graph,
1787
+ pos: Dict[int, Tuple[float, ...]],
1788
+ ax: Optional[plt.Axes] = None,
1789
+ node_size: int = 300,
1790
+ **kwargs
1791
+ ) -> plt.Axes:
1792
+ if ax is None:
1793
+ fig = plt.figure(figsize=(10, 8))
1794
+ ax = fig.add_subplot(111)
1795
+
1796
+ degrees = dict(nx_graph.degree())
1797
+ max_degree = max(degrees.values()) if degrees else 1
1798
+ node_colors = [plt.cm.viridis(deg / max_degree) for deg in degrees.values()]
1799
+
1800
+ nx.draw_networkx_nodes(
1801
+ nx_graph, pos, ax=ax,
1802
+ node_color=node_colors,
1803
+ node_size=node_size,
1804
+ **kwargs
1805
+ )
1806
+
1807
+ is_3d = len(pos[next(iter(pos))]) == 3
1808
+ if is_3d:
1809
+ for node, coord in pos.items():
1810
+ ax.text(coord[0], coord[1], coord[2], f' {node}', size=10, zorder=1, color='black')
1811
+ else:
1812
+ nx.draw_networkx_labels(nx_graph, pos, ax=ax)
1813
+
1814
+ if is_3d:
1815
+ for u, v in nx_graph.edges():
1816
+ ax.plot(
1817
+ [pos[u][0], pos[v][0]],
1818
+ [pos[u][1], pos[v][1]],
1819
+ [pos[u][2], pos[v][2]],
1820
+ color='gray',
1821
+ alpha=0.5
1822
+ )
1823
+ else:
1824
+ nx.draw_networkx_edges(nx_graph, pos, ax=ax, alpha=0.5)
1825
+
1826
+ ax.set_title("Keçeci Layout: Colored Nodes")
1827
+ return ax
1828
+
1423
1829
  # =============================================================================
1424
1830
  # 3. INTERNAL DRAWING STYLE IMPLEMENTATIONS
1425
1831
  # =============================================================================
@@ -1523,9 +1929,181 @@ def _draw_internal(nx_graph, ax, style, **kwargs):
1523
1929
  # =============================================================================
1524
1930
  # 4. MAIN USER-FACING DRAWING FUNCTION
1525
1931
  # =============================================================================
1932
+ def draw_kececi(
1933
+ graph,
1934
+ pos: Optional[Dict[int, Tuple[float, ...]]] = None,
1935
+ layout: Optional[str] = None,
1936
+ style: str = 'default',
1937
+ ax: Optional[plt.Axes] = None,
1938
+ with_labels: bool = True,
1939
+ node_color: str = 'lightblue',
1940
+ node_size: int = 500,
1941
+ font_weight: str = 'bold',
1942
+ **kwargs
1943
+ ) -> plt.Axes:
1944
+ """
1945
+ Keçeci Layout ile graf çizimi.
1526
1946
 
1527
- def draw_kececi(graph, style='curved', ax=None, **kwargs):
1947
+ Args:
1948
+ graph: Graf objesi (NetworkX, igraph, vb.).
1949
+ pos: Önceden hesaplanmış koordinatlar (opsiyonel).
1950
+ layout: '2d', 'cylindrical', 'cubic', 'spherical', 'elliptical', 'toric' (opsiyonel).
1951
+ style: 'default', 'weighted', 'colored'.
1952
+ ax: Matplotlib ekseni.
1953
+ with_labels: Düğüm etiketlerini göster.
1954
+ node_color: Düğüm rengi.
1955
+ node_size: Düğüm boyutu.
1956
+ font_weight: Yazı kalınlığı.
1957
+ **kwargs: Ek parametreler.
1958
+
1959
+ Returns:
1960
+ Matplotlib ekseni.
1528
1961
  """
1962
+ nx_graph = to_networkx(graph)
1963
+
1964
+ # Eğer pos verilmemişse, layout'a göre hesapla
1965
+ if pos is None:
1966
+ if layout is None:
1967
+ layout = '2d' # Varsayılan layout
1968
+
1969
+ if layout == '2d':
1970
+ pos = kececi_layout_2d(nx_graph, **kwargs)
1971
+ elif layout == 'cylindrical':
1972
+ pos = kececi_layout_cylindrical(nx_graph, **kwargs)
1973
+ elif layout == 'cubic':
1974
+ pos = kececi_layout_cubic(nx_graph, **kwargs)
1975
+ elif layout == 'spherical':
1976
+ pos = kececi_layout_spherical(nx_graph, **kwargs)
1977
+ elif layout == 'elliptical':
1978
+ pos = kececi_layout_elliptical(nx_graph, **kwargs)
1979
+ elif layout == 'toric':
1980
+ pos = kececi_layout_toric(nx_graph, **kwargs)
1981
+ else:
1982
+ raise ValueError(f"Geçersiz layout: {layout}")
1983
+
1984
+ # 3D için eksen ayarlaması
1985
+ is_3d = len(pos[next(iter(pos))]) == 3
1986
+ if ax is None:
1987
+ fig = plt.figure(figsize=(10, 8))
1988
+ if is_3d:
1989
+ ax = fig.add_subplot(111, projection='3d')
1990
+ else:
1991
+ ax = fig.add_subplot(111)
1992
+
1993
+ # Stile göre çizim yap
1994
+ if style == 'weighted':
1995
+ draw_kececi_weighted(nx_graph, pos, ax, **kwargs)
1996
+ elif style == 'colored':
1997
+ draw_kececi_colored(nx_graph, pos, ax, **kwargs)
1998
+ else: # 'default'
1999
+ nx.draw_networkx_nodes(nx_graph, pos, ax=ax, node_color=node_color, node_size=node_size)
2000
+
2001
+ # Düğüm etiketlerini çiz
2002
+ if with_labels:
2003
+ if is_3d:
2004
+ for node, coord in pos.items():
2005
+ ax.text(coord[0], coord[1], coord[2], f' {node}', size=10, zorder=1, color='black', fontweight=font_weight)
2006
+ else:
2007
+ nx.draw_networkx_labels(nx_graph, pos, ax=ax, font_weight=font_weight)
2008
+
2009
+ # Kenarları çiz
2010
+ if is_3d:
2011
+ for u, v in nx_graph.edges():
2012
+ ax.plot(
2013
+ [pos[u][0], pos[v][0]],
2014
+ [pos[u][1], pos[v][1]],
2015
+ [pos[u][2], pos[v][2]],
2016
+ color='gray',
2017
+ alpha=0.5
2018
+ )
2019
+ else:
2020
+ nx.draw_networkx_edges(nx_graph, pos, ax=ax, alpha=0.5)
2021
+
2022
+ ax.set_title(f"Keçeci Layout: {layout.capitalize() if layout else 'Custom'} ({style})")
2023
+ return ax
2024
+ """
2025
+ def draw_kececi(
2026
+ graph,
2027
+ layout: str = '2d',
2028
+ style: str = 'default',
2029
+ ax: Optional[plt.Axes] = None,
2030
+ **kwargs
2031
+ ) -> plt.Axes:
2032
+
2033
+ Keçeci Layout ile graf çizimi.
2034
+
2035
+ Args:
2036
+ graph: Graf objesi (NetworkX, igraph, vb.).
2037
+ layout: '2d', 'cylindrical', 'cubic', 'spherical', 'elliptical', 'toric'.
2038
+ style: 'default', 'weighted', 'colored'.
2039
+ ax: Matplotlib ekseni.
2040
+ **kwargs: Ek parametreler.
2041
+
2042
+ Returns:
2043
+ Matplotlib ekseni.
2044
+
2045
+ nx_graph = to_networkx(graph)
2046
+
2047
+ # Layout'a göre koordinatları hesapla
2048
+ if layout == '2d':
2049
+ pos = kececi_layout_2d(nx_graph, **kwargs)
2050
+ elif layout == 'cylindrical':
2051
+ pos = kececi_layout_cylindrical(nx_graph, **kwargs)
2052
+ elif layout == 'cubic':
2053
+ pos = kececi_layout_cubic(nx_graph, **kwargs)
2054
+ elif layout == 'spherical':
2055
+ pos = kececi_layout_spherical(nx_graph, **kwargs)
2056
+ elif layout == 'elliptical':
2057
+ pos = kececi_layout_elliptical(nx_graph, **kwargs)
2058
+ elif layout == 'toric':
2059
+ pos = kececi_layout_toric(nx_graph, **kwargs)
2060
+ else:
2061
+ raise ValueError(f"Invalid layout: {layout}")
2062
+
2063
+ # 3D için eksen ayarlaması
2064
+ is_3d = len(pos[next(iter(pos))]) == 3
2065
+ if ax is None:
2066
+ fig = plt.figure(figsize=(10, 8))
2067
+ if is_3d:
2068
+ ax = fig.add_subplot(111, projection='3d')
2069
+ else:
2070
+ ax = fig.add_subplot(111)
2071
+
2072
+ # Stile göre çizim yap
2073
+ if style == 'weighted':
2074
+ draw_kececi_weighted(nx_graph, pos, ax, **kwargs)
2075
+ elif style == 'colored':
2076
+ draw_kececi_colored(nx_graph, pos, ax, **kwargs)
2077
+ else: # 'default'
2078
+ nx.draw_networkx_nodes(nx_graph, pos, ax=ax, **kwargs)
2079
+
2080
+ # Düğüm etiketlerini çiz
2081
+ if is_3d:
2082
+ for node, coord in pos.items():
2083
+ ax.text(coord[0], coord[1], coord[2], f' {node}', size=10, zorder=1, color='black')
2084
+ else:
2085
+ nx.draw_networkx_labels(nx_graph, pos, ax=ax)
2086
+
2087
+ # Kenarları çiz
2088
+ if is_3d:
2089
+ for u, v in nx_graph.edges():
2090
+ ax.plot(
2091
+ [pos[u][0], pos[v][0]],
2092
+ [pos[u][1], pos[v][1]],
2093
+ [pos[u][2], pos[v][2]],
2094
+ color='gray',
2095
+ alpha=0.5
2096
+ )
2097
+ else:
2098
+ nx.draw_networkx_edges(nx_graph, pos, ax=ax, alpha=0.5)
2099
+
2100
+ ax.set_title(f"Keçeci Layout: {layout.capitalize()} ({style})")
2101
+ return ax
2102
+ """
2103
+
2104
+ """
2105
+ def draw_kececi(graph, style='curved', ax=None, **kwargs):
2106
+
1529
2107
  Draws a graph using the Keçeci Layout with a specified style.
1530
2108
 
1531
2109
  This function automatically handles graphs from different libraries
@@ -1541,7 +2119,7 @@ def draw_kececi(graph, style='curved', ax=None, **kwargs):
1541
2119
 
1542
2120
  Returns:
1543
2121
  matplotlib.axis.Axis: The axis object where the graph was drawn.
1544
- """
2122
+
1545
2123
  nx_graph = to_networkx(graph)
1546
2124
  is_3d = (style.lower() == '3d')
1547
2125
 
@@ -1559,7 +2137,7 @@ def draw_kececi(graph, style='curved', ax=None, **kwargs):
1559
2137
 
1560
2138
  _draw_internal(nx_graph, ax, style.lower(), **kwargs)
1561
2139
  return ax
1562
-
2140
+ """
1563
2141
 
1564
2142
  # =============================================================================
1565
2143
  # MODULE TEST CODE
@@ -1606,3 +2184,6 @@ if __name__ == '__main__':
1606
2184
  draw_kececi(G_test, style='3d', ax=fig_styles.add_subplot(2, 2, (3, 4), projection='3d'))
1607
2185
  plt.tight_layout(rect=[0, 0, 1, 0.96])
1608
2186
  plt.show()
2187
+
2188
+
2189
+