kececilayout 0.5.9__py3-none-any.whl → 0.6.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.
@@ -21,6 +21,8 @@ layouts = ['2d', 'cylindrical', 'cubic', 'spherical', 'elliptical', 'toric']
21
21
  styles = ['standard', 'default', 'curved', 'helix', '3d', 'weighted', 'colored']
22
22
 
23
23
  **v0.5.1:** edge (kececi_layout_edge)
24
+
25
+ **v0.6.0:** periodic table
24
26
  """
25
27
 
26
28
  from collections import defaultdict
@@ -29,6 +31,7 @@ import igraph as ig
29
31
  import itertools # Graphillion için eklendi
30
32
  import math
31
33
  import matplotlib.pyplot as plt
34
+ from matplotlib.colors import hsv_to_rgb
32
35
  from mpl_toolkits.mplot3d import Axes3D
33
36
  import networkit as nk
34
37
  import networkx as nx
@@ -655,7 +658,7 @@ def kececi_layout_ig(graph: "ig.Graph", primary_spacing=1.0, secondary_spacing=1
655
658
  if num_nodes == 0:
656
659
  return []
657
660
 
658
- # Create coordinate list (will be ordered by vertex IDs 0 to N-1)
661
+ # generate coordinate list (will be ordered by vertex IDs 0 to N-1)
659
662
  pos_list = [[0.0, 0.0]] * num_nodes
660
663
  # Since vertex IDs are already 0 to N-1, we can use range directly
661
664
  nodes = range(num_nodes) # Vertex IDs
@@ -719,7 +722,7 @@ def kececi_layout_igraph(graph: "ig.Graph", primary_spacing=1.0, secondary_spaci
719
722
  if num_nodes == 0:
720
723
  return []
721
724
 
722
- # Create coordinate list (will be ordered by vertex IDs 0 to N-1)
725
+ # generate coordinate list (will be ordered by vertex IDs 0 to N-1)
723
726
  pos_list = [[0.0, 0.0]] * num_nodes
724
727
  # Since vertex IDs are already 0 to N-1, we can use range directly
725
728
  nodes = range(num_nodes) # Vertex IDs
@@ -931,7 +934,7 @@ def kececi_layout_gg(graph_set: "gg.GraphSet", primary_spacing=1.0, secondary_sp
931
934
  return {}
932
935
 
933
936
  # Graphillion often uses 1-based node indexing.
934
- # Create the node ID list: 1, 2, ..., num_vertices
937
+ # generate the node ID list: 1, 2, ..., num_vertices
935
938
  nodes = list(range(1, num_vertices + 1))
936
939
 
937
940
  pos = {}
@@ -996,7 +999,7 @@ def kececi_layout_graphillion(graph_set: "gg.GraphSet", primary_spacing=1.0, sec
996
999
  return {}
997
1000
 
998
1001
  # Graphillion often uses 1-based node indexing.
999
- # Create the node ID list: 1, 2, ..., num_vertices
1002
+ # generate the node ID list: 1, 2, ..., num_vertices
1000
1003
  nodes = list(range(1, num_vertices + 1))
1001
1004
 
1002
1005
  pos = {}
@@ -2817,7 +2820,7 @@ def draw_kececi(graph, style='curved', ax=None, **kwargs):
2817
2820
  graph: The graph object to be drawn.
2818
2821
  style (str): The drawing style. Options: 'curved', 'transparent', '3d'.
2819
2822
  ax (matplotlib.axis.Axis, optional): The axis to draw on. If not
2820
- provided, a new figure and axis are created.
2823
+ provided, a new figure and axis are generated.
2821
2824
  **kwargs: Additional keyword arguments passed to both `kececi_layout`
2822
2825
  and the drawing functions (e.g., expanding=True, node_size=500).
2823
2826
 
@@ -2843,6 +2846,1382 @@ def draw_kececi(graph, style='curved', ax=None, **kwargs):
2843
2846
  return ax
2844
2847
  """
2845
2848
 
2849
+ def draw_kececi_periodic_table(
2850
+ graph,
2851
+ periodic_elements: Dict[int, Tuple[str, int]],
2852
+ layout_type: str = '3d_helix',
2853
+ layout_params: Optional[Dict] = None,
2854
+ ax: Optional[plt.Axes] = None,
2855
+ dimension: str = 'auto', # '2d', '3d', or 'auto'
2856
+ color_scheme: str = 'vibrant', # 'vibrant', 'distinct', 'pastel', 'group', 'period', 'block', 'electronegativity'
2857
+ node_size: Union[int, List[int]] = 1600,
2858
+ font_size: Union[int, List[int]] = 10,
2859
+ edge_style: str = 'standard', # 'standard', 'light', 'bold', 'hidden'
2860
+ label_position: str = 'center', # 'center', 'above', 'below', 'right', 'left'
2861
+ zorder_strategy: str = 'smart', # 'smart', 'fixed', 'z_based'
2862
+ show_legend: bool = False,
2863
+ title: Optional[str] = None,
2864
+ **kwargs
2865
+ ) -> Tuple[plt.Axes, Dict]:
2866
+ """
2867
+ Gelişmiş periyodik tablo çizimi - Hem 2D hem 3D uyumlu.
2868
+ """
2869
+ # Graph'ı NetworkX'e çevir
2870
+ nx_graph = to_networkx(graph)
2871
+ node_count = len(periodic_elements)
2872
+
2873
+ # Dimension belirleme
2874
+ if dimension == 'auto':
2875
+ if '3d' in layout_type.lower():
2876
+ dimension = '3d'
2877
+ else:
2878
+ dimension = '2d'
2879
+
2880
+ # Layout parametreleri
2881
+ if layout_params is None:
2882
+ layout_params = {}
2883
+
2884
+ # Layout hesapla
2885
+ pos = _calculate_layout(nx_graph, layout_type, layout_params, dimension, node_count)
2886
+
2887
+ # Renkleri oluştur
2888
+ node_colors = _generate_colors(node_count, color_scheme, periodic_elements)
2889
+
2890
+ # Etiketleri oluştur
2891
+ custom_labels = _generate_labels(nx_graph, periodic_elements)
2892
+
2893
+ # Eksen oluştur (eğer verilmemişse)
2894
+ if ax is None:
2895
+ fig_size = kwargs.get('figsize', (20, 20) if dimension == '3d' else (16, 16))
2896
+ fig = plt.figure(figsize=fig_size)
2897
+ if dimension == '3d':
2898
+ ax = fig.add_subplot(111, projection='3d')
2899
+ else:
2900
+ ax = fig.add_subplot(111)
2901
+
2902
+ # Çizim sırasını belirle
2903
+ draw_order = _get_draw_order(nx_graph, pos, zorder_strategy, dimension)
2904
+
2905
+ # Edge stilleri
2906
+ edge_config = _get_edge_config(edge_style)
2907
+
2908
+ # Edge'leri çiz (ilk sırada)
2909
+ _draw_edges(nx_graph, pos, ax, edge_config, dimension)
2910
+
2911
+ # Node'ları çiz (çizim sırasına göre)
2912
+ _draw_nodes(nx_graph, pos, node_colors, ax, node_size, draw_order, dimension, **kwargs)
2913
+
2914
+ # Etiketleri çiz
2915
+ _draw_labels(nx_graph, pos, custom_labels, node_colors, ax, font_size,
2916
+ label_position, dimension, **kwargs)
2917
+
2918
+ # Başlık
2919
+ if title is None:
2920
+ title = _generate_title(layout_type, node_count, color_scheme, dimension)
2921
+ ax.set_title(title, fontsize=20, fontweight='bold', pad=25)
2922
+
2923
+ # Eksen ayarları
2924
+ _configure_axes(ax, pos, dimension, layout_params, **kwargs)
2925
+
2926
+ # Açıklama (legend)
2927
+ if show_legend and color_scheme in ['group', 'period', 'block']:
2928
+ _add_legend(ax, color_scheme, dimension, periodic_elements)
2929
+
2930
+ return ax, pos
2931
+
2932
+
2933
+ # Yardımcı fonksiyonlar (öncekiyle aynı)
2934
+ def _calculate_layout(graph, layout_type, params, dimension, node_count=None):
2935
+ """Layout hesapla."""
2936
+ if layout_type == '3d_helix':
2937
+ return kececi_layout_3d_helix_parametric(
2938
+ graph,
2939
+ z_spacing=params.get('z_spacing', 8.0),
2940
+ radius=params.get('radius', 25.0),
2941
+ turns=params.get('turns', 3.0)
2942
+ )
2943
+
2944
+ elif layout_type == '2d_linear':
2945
+ return kececi_layout_2d(
2946
+ graph,
2947
+ primary_spacing=params.get('primary_spacing', 2.0),
2948
+ secondary_spacing=params.get('secondary_spacing', 2.0),
2949
+ primary_direction=params.get('primary_direction', 'left-to-right'),
2950
+ secondary_start=params.get('secondary_start', 'up'),
2951
+ expanding=params.get('expanding', True)
2952
+ )
2953
+
2954
+ elif layout_type == '2d_circular':
2955
+ return nx.circular_layout(graph, scale=params.get('scale', 1.0))
2956
+
2957
+ elif layout_type == '3d_spherical':
2958
+ pos = {}
2959
+ n = len(graph.nodes())
2960
+ for i, node in enumerate(graph.nodes()):
2961
+ phi = np.arccos(1 - 2 * (i + 0.5) / n)
2962
+ theta = np.pi * (1 + 5**0.5) * i
2963
+ radius = params.get('radius', 20.0)
2964
+ pos[node] = (
2965
+ radius * np.sin(phi) * np.cos(theta),
2966
+ radius * np.sin(phi) * np.sin(theta),
2967
+ radius * np.cos(phi)
2968
+ )
2969
+ return pos
2970
+
2971
+ elif layout_type == '2d_spring':
2972
+ return nx.spring_layout(graph, k=params.get('k', 2.0),
2973
+ iterations=params.get('iterations', 50))
2974
+
2975
+ elif layout_type == '2d_grid':
2976
+ return _generate_grid_layout(graph, params, node_count)
2977
+
2978
+ else:
2979
+ return kececi_layout_2d(
2980
+ graph,
2981
+ primary_spacing=params.get('primary_spacing', 2.0),
2982
+ secondary_spacing=params.get('secondary_spacing', 2.0),
2983
+ primary_direction=params.get('primary_direction', 'left-to-right'),
2984
+ secondary_start=params.get('secondary_start', 'up'),
2985
+ expanding=params.get('expanding', True)
2986
+ )
2987
+
2988
+ def _generate_grid_layout(graph, params, node_count):
2989
+ """Manuel grid layout oluştur."""
2990
+ rows = params.get('rows', None)
2991
+ cols = params.get('cols', None)
2992
+ spacing = params.get('spacing', 2.0)
2993
+
2994
+ if rows is None and cols is None:
2995
+ cols = int(np.ceil(np.sqrt(node_count)))
2996
+ rows = int(np.ceil(node_count / cols))
2997
+ elif rows is None:
2998
+ rows = int(np.ceil(node_count / cols))
2999
+ elif cols is None:
3000
+ cols = int(np.ceil(node_count / rows))
3001
+
3002
+ pos = {}
3003
+ for i, node in enumerate(sorted(graph.nodes())):
3004
+ row = i // cols
3005
+ col = i % cols
3006
+
3007
+ x_offset = -(cols - 1) * spacing / 2
3008
+ y_offset = (rows - 1) * spacing / 2
3009
+
3010
+ x = col * spacing + x_offset
3011
+ y = -row * spacing + y_offset
3012
+
3013
+ pos[node] = (x, y)
3014
+
3015
+ return pos
3016
+
3017
+
3018
+ def _generate_colors(node_count, scheme, periodic_elements=None):
3019
+ """Renkleri oluştur."""
3020
+ colors = []
3021
+
3022
+ if scheme == 'vibrant':
3023
+ for i in range(node_count):
3024
+ hue = (i * 0.618033988749895) % 1.0
3025
+ saturation = 0.7 + np.random.random() * 0.3
3026
+ value = 0.8 + np.random.random() * 0.2
3027
+ colors.append(hsv_to_rgb([hue, saturation, value]))
3028
+
3029
+ elif scheme == 'distinct':
3030
+ colors = generate_distinct_colors(node_count)
3031
+
3032
+ elif scheme == 'pastel':
3033
+ for i in range(node_count):
3034
+ hue = i / max(node_count, 1)
3035
+ saturation = 0.4 + np.random.random() * 0.3
3036
+ value = 0.9 + np.random.random() * 0.1
3037
+ colors.append(hsv_to_rgb([hue, saturation, value]))
3038
+
3039
+ elif scheme == 'group' and periodic_elements:
3040
+ colors = _get_group_colors(node_count, periodic_elements)
3041
+
3042
+ elif scheme == 'period' and periodic_elements:
3043
+ colors = _get_period_colors(node_count, periodic_elements)
3044
+
3045
+ elif scheme == 'block' and periodic_elements:
3046
+ colors = _get_block_colors(node_count, periodic_elements)
3047
+
3048
+ elif scheme == 'electronegativity' and periodic_elements:
3049
+ colors = _get_electronegativity_colors(node_count, periodic_elements)
3050
+
3051
+ else:
3052
+ cmap = plt.cm.tab20
3053
+ colors = [cmap(i % 20) for i in range(node_count)]
3054
+
3055
+ return colors
3056
+
3057
+ def _get_group_colors(node_count, periodic_elements):
3058
+ """Gruplara göre renkler."""
3059
+ colors = []
3060
+ group_colors = {
3061
+ 1: (1.0, 0.6, 0.6), # Alkali metals
3062
+ 2: (1.0, 0.8, 0.6), # Alkaline earth
3063
+ 3: (0.8, 1.0, 0.6), # Group 3
3064
+ 4: (0.7, 0.9, 0.8), # Group 4
3065
+ 5: (0.6, 0.9, 0.9), # Group 5
3066
+ 6: (0.6, 0.8, 1.0), # Group 6
3067
+ 7: (0.8, 0.6, 1.0), # Group 7
3068
+ 8: (0.9, 0.9, 0.6), # Group 8
3069
+ 9: (1.0, 0.9, 0.6), # Group 9
3070
+ 10: (0.9, 0.8, 0.7), # Group 10
3071
+ 11: (1.0, 0.8, 0.8), # Group 11
3072
+ 12: (0.8, 1.0, 0.8), # Group 12
3073
+ 13: (0.8, 0.9, 1.0), # Boron group
3074
+ 14: (0.9, 0.8, 1.0), # Carbon group
3075
+ 15: (1.0, 0.8, 0.9), # Nitrogen group
3076
+ 16: (0.8, 1.0, 0.9), # Oxygen group
3077
+ 17: (1.0, 0.9, 0.8), # Halogens
3078
+ 18: (0.9, 0.9, 0.9), # Noble gases
3079
+ }
3080
+
3081
+ lanthanide_color = (0.7, 1.0, 0.7)
3082
+ actinide_color = (1.0, 0.7, 0.7)
3083
+
3084
+ for node_id in range(1, node_count + 1):
3085
+ if node_id in list(range(57, 72)):
3086
+ colors.append(lanthanide_color)
3087
+ elif node_id in list(range(89, 104)):
3088
+ colors.append(actinide_color)
3089
+ else:
3090
+ group = _determine_group(node_id)
3091
+ colors.append(group_colors.get(group, (0.8, 0.8, 0.8)))
3092
+
3093
+ return colors
3094
+
3095
+ def _determine_group(atomic_num):
3096
+ """Atom numarasına göre grup belirle."""
3097
+ if atomic_num <= 2:
3098
+ return atomic_num
3099
+ elif atomic_num <= 10:
3100
+ return atomic_num - 2
3101
+ elif atomic_num <= 18:
3102
+ return atomic_num - 10
3103
+ elif atomic_num <= 36:
3104
+ if atomic_num <= 20:
3105
+ return atomic_num - 18
3106
+ elif atomic_num <= 30:
3107
+ return atomic_num - 20
3108
+ else:
3109
+ return atomic_num - 28
3110
+ elif atomic_num <= 54:
3111
+ if atomic_num <= 38:
3112
+ return atomic_num - 36
3113
+ elif atomic_num <= 48:
3114
+ return atomic_num - 38
3115
+ else:
3116
+ return atomic_num - 46
3117
+ else:
3118
+ return 18
3119
+
3120
+ def _get_period_colors(node_count, periodic_elements):
3121
+ """Periyotlara göre renkler."""
3122
+ colors = []
3123
+ period_colors = [
3124
+ (1.0, 0.7, 0.7), # Period 1
3125
+ (1.0, 0.9, 0.7), # Period 2
3126
+ (0.9, 1.0, 0.7), # Period 3
3127
+ (0.7, 1.0, 0.8), # Period 4
3128
+ (0.7, 0.9, 1.0), # Period 5
3129
+ (0.8, 0.7, 1.0), # Period 6
3130
+ (1.0, 0.7, 0.9), # Period 7
3131
+ ]
3132
+
3133
+ for atomic_num in range(1, node_count + 1):
3134
+ if atomic_num <= 2:
3135
+ period = 0
3136
+ elif atomic_num <= 10:
3137
+ period = 1
3138
+ elif atomic_num <= 18:
3139
+ period = 2
3140
+ elif atomic_num <= 36:
3141
+ period = 3
3142
+ elif atomic_num <= 54:
3143
+ period = 4
3144
+ elif atomic_num <= 86:
3145
+ period = 5
3146
+ else:
3147
+ period = 6
3148
+
3149
+ colors.append(period_colors[period % len(period_colors)])
3150
+
3151
+ return colors
3152
+
3153
+ def _get_block_colors(node_count, periodic_elements):
3154
+ """Bloklara göre renkler."""
3155
+ colors = []
3156
+
3157
+ for atomic_num in range(1, node_count + 1):
3158
+ if atomic_num in [1, 2, 3, 4, 11, 12, 19, 20, 37, 38, 55, 56, 87, 88]:
3159
+ colors.append((1.0, 0.6, 0.6)) # s-block
3160
+ elif atomic_num in (list(range(5, 11)) + list(range(13, 19)) +
3161
+ list(range(31, 37)) + list(range(49, 55)) +
3162
+ list(range(81, 87)) + list(range(113, 119))):
3163
+ colors.append((0.6, 0.8, 1.0)) # p-block
3164
+ elif atomic_num in (list(range(21, 31)) + list(range(39, 49)) +
3165
+ list(range(72, 81)) + list(range(104, 113))):
3166
+ colors.append((0.6, 1.0, 0.6)) # d-block
3167
+ elif atomic_num in list(range(57, 72)) + list(range(89, 104)):
3168
+ colors.append((1.0, 0.6, 1.0)) # f-block
3169
+ else:
3170
+ colors.append((0.8, 0.8, 0.8))
3171
+
3172
+ return colors
3173
+
3174
+ def _get_electronegativity_colors(node_count, periodic_elements):
3175
+ """Elektronegativiteye göre renkler."""
3176
+ colors = []
3177
+ electronegativity_data = {
3178
+ 1: 2.20, 3: 0.98, 4: 1.57, 5: 2.04, 6: 2.55, 7: 3.04, 8: 3.44,
3179
+ 9: 3.98, 11: 0.93, 12: 1.31, 13: 1.61, 14: 1.90, 15: 2.19,
3180
+ 16: 2.58, 17: 3.16, 19: 0.82, 20: 1.00, 21: 1.36, 22: 1.54,
3181
+ 23: 1.63, 24: 1.66, 25: 1.55, 26: 1.83, 27: 1.88, 28: 1.91, 29: 1.90,
3182
+ 30: 1.65, 31: 1.81, 32: 2.01, 33: 2.18, 34: 2.55, 35: 2.96,
3183
+ 37: 0.82, 38: 0.95, 39: 1.22, 40: 1.33, 41: 1.60, 42: 2.16, 43: 1.90,
3184
+ 44: 2.20, 45: 2.28, 46: 2.20, 47: 1.93, 48: 1.69, 49: 1.78, 50: 1.96,
3185
+ 51: 2.05, 52: 2.10, 53: 2.66, 55: 0.79, 56: 0.89, 57: 1.10,
3186
+ 58: 1.12, 59: 1.13, 60: 1.14, 62: 1.17, 63: 1.20, 64: 1.20, 65: 1.20,
3187
+ 66: 1.22, 67: 1.23, 68: 1.24, 69: 1.25, 70: 1.10, 71: 1.27, 72: 1.30,
3188
+ 73: 1.50, 74: 2.36, 75: 1.90, 76: 2.20, 77: 2.20, 78: 2.28, 79: 2.54,
3189
+ 80: 2.00, 81: 1.62, 82: 1.87, 83: 2.02, 84: 2.00, 85: 2.20,
3190
+ 87: 0.70, 88: 0.89, 89: 1.10, 90: 1.30, 91: 1.50, 92: 1.38, 93: 1.36,
3191
+ 94: 1.28, 95: 1.30, 96: 1.30, 97: 1.30, 98: 1.30, 99: 1.30, 100: 1.30,
3192
+ 101: 1.30, 102: 1.30, 103: 1.30
3193
+ }
3194
+
3195
+ for atomic_num in range(1, node_count + 1):
3196
+ en = electronegativity_data.get(atomic_num, 1.5)
3197
+ if en < 1.0:
3198
+ color = (0.0, 0.0, 0.8)
3199
+ elif en < 1.5:
3200
+ color = (0.0, 0.5, 1.0)
3201
+ elif en < 2.0:
3202
+ color = (0.0, 0.8, 0.8)
3203
+ elif en < 2.5:
3204
+ color = (0.5, 1.0, 0.5)
3205
+ elif en < 3.0:
3206
+ color = (1.0, 0.8, 0.0)
3207
+ elif en < 3.5:
3208
+ color = (1.0, 0.5, 0.0)
3209
+ else:
3210
+ color = (1.0, 0.0, 0.0)
3211
+
3212
+ colors.append(color)
3213
+
3214
+ return colors
3215
+
3216
+
3217
+ def _generate_labels(graph, periodic_elements):
3218
+ """Etiketleri oluştur."""
3219
+ return {node_id: f"{periodic_elements[node_id][0]}\n{periodic_elements[node_id][1]}"
3220
+ for node_id in graph.nodes()}
3221
+
3222
+ def _get_draw_order(graph, pos, strategy, dimension):
3223
+ """Çizim sırasını belirle."""
3224
+ if strategy == 'fixed':
3225
+ return list(graph.nodes())
3226
+ elif strategy == 'z_based' and dimension == '3d':
3227
+ return sorted(graph.nodes(), key=lambda n: pos[n][2], reverse=True)
3228
+ elif strategy == 'smart':
3229
+ nodes = list(graph.nodes())
3230
+ if len(nodes) == 0:
3231
+ return nodes
3232
+
3233
+ if dimension == '3d':
3234
+ positions = np.array([pos[n] for n in nodes])
3235
+ center = np.mean(positions, axis=0)
3236
+ distances = np.linalg.norm(positions - center, axis=1)
3237
+ else:
3238
+ positions = np.array([pos[n] for n in nodes])
3239
+ center = np.mean(positions, axis=0)
3240
+ distances = np.linalg.norm(positions - center, axis=1)
3241
+
3242
+ sorted_indices = np.argsort(distances)[::-1]
3243
+ return [nodes[i] for i in sorted_indices]
3244
+ else:
3245
+ return list(graph.nodes())
3246
+
3247
+ def _get_edge_config(style):
3248
+ """Edge stilini belirle."""
3249
+ configs = {
3250
+ 'standard': {'color': 'gray', 'alpha': 0.5, 'width': 1.0},
3251
+ 'light': {'color': 'lightgray', 'alpha': 0.3, 'width': 0.8},
3252
+ 'bold': {'color': 'black', 'alpha': 0.7, 'width': 2.0},
3253
+ 'hidden': {'color': 'none', 'alpha': 0.0, 'width': 0.0}
3254
+ }
3255
+ return configs.get(style, configs['standard'])
3256
+
3257
+
3258
+ def _draw_edges(graph, pos, ax, config, dimension):
3259
+ """Edge'leri çiz."""
3260
+ if config['color'] == 'none':
3261
+ return
3262
+
3263
+ for u, v in graph.edges():
3264
+ if dimension == '3d':
3265
+ ax.plot(
3266
+ [pos[u][0], pos[v][0]],
3267
+ [pos[u][1], pos[v][1]],
3268
+ [pos[u][2], pos[v][2]],
3269
+ color=config['color'],
3270
+ alpha=config['alpha'],
3271
+ linewidth=config['width'],
3272
+ zorder=1
3273
+ )
3274
+ else:
3275
+ ax.plot(
3276
+ [pos[u][0], pos[v][0]],
3277
+ [pos[u][1], pos[v][1]],
3278
+ color=config['color'],
3279
+ alpha=config['alpha'],
3280
+ linewidth=config['width'],
3281
+ zorder=1
3282
+ )
3283
+
3284
+ def _draw_nodes(graph, pos, colors, ax, node_size, draw_order, dimension, **kwargs):
3285
+ """Node'ları çiz."""
3286
+ edge_width = kwargs.get('edge_width', 2.0)
3287
+ alpha = kwargs.get('node_alpha', 1.0)
3288
+
3289
+ for node_id in draw_order:
3290
+ if dimension == '3d':
3291
+ x, y, z = pos[node_id]
3292
+ ax.scatter(x, y, z,
3293
+ s=node_size if isinstance(node_size, int) else node_size[node_id-1],
3294
+ c=[colors[node_id-1]],
3295
+ edgecolors='black',
3296
+ linewidths=edge_width,
3297
+ alpha=alpha,
3298
+ depthshade=False if kwargs.get('no_depth_shade', False) else True,
3299
+ zorder=10)
3300
+ else:
3301
+ x, y = pos[node_id]
3302
+ ax.scatter(x, y,
3303
+ s=node_size if isinstance(node_size, int) else node_size[node_id-1],
3304
+ c=[colors[node_id-1]],
3305
+ edgecolors='black',
3306
+ linewidths=edge_width,
3307
+ alpha=alpha,
3308
+ zorder=10)
3309
+
3310
+ def _draw_labels(graph, pos, labels, colors, ax, font_size, position, dimension, **kwargs):
3311
+ """Etiketleri çiz."""
3312
+ label_offset = kwargs.get('label_offset', 0.0)
3313
+
3314
+ for node_id in graph.nodes():
3315
+ if dimension == '3d':
3316
+ x, y, z = pos[node_id]
3317
+ if position == 'above':
3318
+ z += label_offset
3319
+ elif position == 'below':
3320
+ z -= label_offset
3321
+ elif position == 'right':
3322
+ x += label_offset
3323
+ elif position == 'left':
3324
+ x -= label_offset
3325
+
3326
+ bg_color = colors[node_id-1]
3327
+ text_color = get_text_color_for_bg(bg_color)
3328
+
3329
+ ax.text(x, y, z,
3330
+ labels[node_id],
3331
+ fontsize=font_size if isinstance(font_size, int) else font_size[node_id-1],
3332
+ fontweight='bold',
3333
+ color=text_color,
3334
+ ha='center',
3335
+ va='center',
3336
+ zorder=1000)
3337
+ else:
3338
+ x, y = pos[node_id]
3339
+ if position == 'above':
3340
+ y += label_offset
3341
+ elif position == 'below':
3342
+ y -= label_offset
3343
+ elif position == 'right':
3344
+ x += label_offset
3345
+ elif position == 'left':
3346
+ x -= label_offset
3347
+
3348
+ bg_color = colors[node_id-1]
3349
+ text_color = get_text_color_for_bg(bg_color)
3350
+
3351
+ ax.text(x, y,
3352
+ labels[node_id],
3353
+ fontsize=font_size if isinstance(font_size, int) else font_size[node_id-1],
3354
+ fontweight='bold',
3355
+ color=text_color,
3356
+ ha='center',
3357
+ va='center',
3358
+ zorder=1000)
3359
+
3360
+ def _generate_title(layout_type, node_count, color_scheme, dimension):
3361
+ """Başlık oluştur."""
3362
+ dim_text = "3D" if dimension == '3d' else "2D"
3363
+ scheme_text = color_scheme.capitalize()
3364
+
3365
+ layout_names = {
3366
+ '3d_helix': 'Heliks Layout',
3367
+ '2d_linear': 'Lineer Layout',
3368
+ '2d_circular': 'Dairesel Layout',
3369
+ '3d_spherical': 'Küresel Layout',
3370
+ '2d_spring': 'Yay Layout',
3371
+ '2d_grid': 'Grid Layout'
3372
+ }
3373
+
3374
+ layout_name = layout_names.get(layout_type, layout_type.replace('_', ' ').title())
3375
+
3376
+ title = f"Keçeci Layout ile Periyodik Tablo\n"
3377
+ title += f"{dim_text} {layout_name}\n"
3378
+ title += f"({node_count} Element, {scheme_text} Renk Şeması)"
3379
+
3380
+ return title
3381
+
3382
+ def _configure_axes(ax, pos, dimension, layout_params, **kwargs):
3383
+ """Eksenleri yapılandır."""
3384
+ ax.set_axis_off()
3385
+
3386
+ if dimension == '3d':
3387
+ elev = kwargs.get('elevation', -25)
3388
+ azim = kwargs.get('azimuth', 15)
3389
+ ax.view_init(elev=elev, azim=azim)
3390
+
3391
+ positions = list(pos.values())
3392
+ if positions:
3393
+ xs, ys, zs = zip(*positions)
3394
+
3395
+ padding = kwargs.get('padding', 0.2)
3396
+ x_range = max(xs) - min(xs)
3397
+ y_range = max(ys) - min(ys)
3398
+ z_range = max(zs) - min(zs)
3399
+
3400
+ x_range = max(x_range, 10)
3401
+ y_range = max(y_range, 10)
3402
+ z_range = max(z_range, 10)
3403
+
3404
+ ax.set_xlim(min(xs) - x_range*padding, max(xs) + x_range*padding)
3405
+ ax.set_ylim(min(ys) - y_range*padding, max(ys) + y_range*padding)
3406
+ ax.set_zlim(min(zs) - z_range*padding, max(zs) + z_range*padding)
3407
+ else:
3408
+ ax.set_aspect('equal')
3409
+ ax.autoscale_view()
3410
+
3411
+ def _add_legend(ax, color_scheme, dimension, periodic_elements):
3412
+ """Renk şeması açıklaması ekle."""
3413
+ legend_text = f"Renk Şeması: {color_scheme.capitalize()}\n"
3414
+
3415
+ if color_scheme == 'group':
3416
+ legend_text += "• Kırmızı: Alkali Metaller\n"
3417
+ legend_text += "• Turuncu: Toprak Alkali\n"
3418
+ legend_text += "• Yeşil: Geçiş Metalleri\n"
3419
+ legend_text += "• Mavi: Ametaller\n"
3420
+ legend_text += "• Mor: Halojenler\n"
3421
+ legend_text += "• Gri: Soygazlar"
3422
+
3423
+ elif color_scheme == 'period':
3424
+ legend_text += "• Her periyot farklı renk\n"
3425
+ legend_text += "• 7 periyot, 7 renk"
3426
+
3427
+ elif color_scheme == 'block':
3428
+ legend_text += "• Kırmızı: s-blok\n"
3429
+ legend_text += "• Mavi: p-blok\n"
3430
+ legend_text += "• Yeşil: d-blok\n"
3431
+ legend_text += "• Mor: f-blok"
3432
+
3433
+ if dimension == '3d':
3434
+ ax.text2D(0.02, 0.98, legend_text, transform=ax.transAxes,
3435
+ fontsize=9, verticalalignment='top',
3436
+ bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
3437
+ else:
3438
+ ax.text(0.02, 0.98, legend_text, transform=ax.transAxes,
3439
+ fontsize=9, verticalalignment='top',
3440
+ bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
3441
+
3442
+ # Ana fonksiyonlar
3443
+ def quick_periodic_table_3d():
3444
+ """
3445
+ Hızlı 3D periyodik tablo görselleştirmesi.
3446
+ Boş şablonlar oluşmaz.
3447
+ """
3448
+ # Önceki figürleri temizle
3449
+ plt.close('all')
3450
+
3451
+ # Periyodik tabloyu yükle
3452
+ periodic_elements, _ = generate_complete_periodic_table()
3453
+
3454
+ # Graf oluştur
3455
+ node_count = len(periodic_elements)
3456
+ G = nx.DiGraph()
3457
+ G.add_nodes_from(range(1, node_count + 1))
3458
+ for i in range(1, node_count):
3459
+ G.add_edge(i, i + 1)
3460
+
3461
+ # Tek figür oluştur
3462
+ fig = plt.figure(figsize=(20, 20))
3463
+ ax = fig.add_subplot(111, projection='3d')
3464
+
3465
+ ax, pos = draw_kececi_periodic_table(
3466
+ G,
3467
+ periodic_elements,
3468
+ layout_type='3d_helix',
3469
+ color_scheme='vibrant',
3470
+ node_size=2000,
3471
+ font_size=10,
3472
+ edge_style='light',
3473
+ title="Keçeci Layout ile Periyodik Tablo\n3D Heliks Layout",
3474
+ elevation=-25,
3475
+ azimuth=15
3476
+ )
3477
+
3478
+ plt.tight_layout()
3479
+ plt.show()
3480
+
3481
+ # Kullanıcıya bilgi ver
3482
+ print(f"\n3D periyodik tablo oluşturuldu!")
3483
+ print(f"Toplam {node_count} element gösteriliyor.")
3484
+ print("Grafı kapatmak için figür penceresini kapatın.")
3485
+
3486
+ return ax, pos
3487
+
3488
+ def generate_comparison_figure():
3489
+ """
3490
+ 4 farklı görseli bir figürde karşılaştırma.
3491
+ Boş şablon oluşmaz.
3492
+ """
3493
+ # Önceki figürleri temizle
3494
+ plt.close('all')
3495
+
3496
+ # Periyodik tabloyu yükle
3497
+ periodic_elements, _ = generate_complete_periodic_table()
3498
+
3499
+ # Graf oluştur
3500
+ node_count = len(periodic_elements)
3501
+ G = nx.DiGraph()
3502
+ G.add_nodes_from(range(1, node_count + 1))
3503
+ for i in range(1, node_count):
3504
+ G.add_edge(i, i + 1)
3505
+
3506
+ # 2x2 grid şeklinde 4 alt figür oluştur
3507
+ fig = plt.figure(figsize=(24, 20))
3508
+ fig.suptitle('Keçeci Layout ile Periyodik Tablo Görselleştirme Karşılaştırması',
3509
+ fontsize=24, fontweight='bold', y=0.98)
3510
+
3511
+ # 1. 3D Heliks Layout
3512
+ ax1 = fig.add_subplot(221, projection='3d')
3513
+ ax1, pos1 = draw_kececi_periodic_table(
3514
+ G,
3515
+ periodic_elements,
3516
+ layout_type='3d_helix',
3517
+ layout_params={'z_spacing': 6.0, 'radius': 20.0, 'turns': 3.0},
3518
+ ax=ax1,
3519
+ color_scheme='vibrant',
3520
+ node_size=1200,
3521
+ font_size=8,
3522
+ edge_style='light',
3523
+ label_position='center',
3524
+ title="3D Heliks Layout\n(Vibrant Renkler)",
3525
+ elevation=-25,
3526
+ azimuth=15,
3527
+ figsize=None # Figsize'ı burada kullanmıyoruz
3528
+ )
3529
+
3530
+ # 2. 2D Linear Layout
3531
+ ax2 = fig.add_subplot(222)
3532
+ ax2, pos2 = draw_kececi_periodic_table(
3533
+ G,
3534
+ periodic_elements,
3535
+ layout_type='2d_linear',
3536
+ layout_params={'primary_spacing': 2.5, 'secondary_spacing': 2.5},
3537
+ ax=ax2,
3538
+ color_scheme='group',
3539
+ dimension='2d',
3540
+ node_size=800,
3541
+ font_size=7,
3542
+ edge_style='standard',
3543
+ show_legend=False,
3544
+ title="2D Linear Layout\n(Grup Renkleri)"
3545
+ )
3546
+
3547
+ # 3. 2D Grid Layout
3548
+ ax3 = fig.add_subplot(223)
3549
+ ax3, pos3 = draw_kececi_periodic_table(
3550
+ G,
3551
+ periodic_elements,
3552
+ layout_type='2d_grid',
3553
+ layout_params={'rows': 9, 'cols': 14, 'spacing': 2.2},
3554
+ ax=ax3,
3555
+ color_scheme='period',
3556
+ dimension='2d',
3557
+ node_size=600,
3558
+ font_size=6,
3559
+ edge_style='light',
3560
+ show_legend=False,
3561
+ title="2D Grid Layout\n(Periyot Renkleri)"
3562
+ )
3563
+
3564
+ # 4. 2D Circular Layout
3565
+ ax4 = fig.add_subplot(224)
3566
+ ax4, pos4 = draw_kececi_periodic_table(
3567
+ G,
3568
+ periodic_elements,
3569
+ layout_type='2d_circular',
3570
+ layout_params={'scale': 2.0},
3571
+ ax=ax4,
3572
+ color_scheme='block',
3573
+ dimension='2d',
3574
+ node_size=800,
3575
+ font_size=7,
3576
+ edge_style='light',
3577
+ show_legend=False,
3578
+ title="2D Dairesel Layout\n(Blok Renkleri)"
3579
+ )
3580
+
3581
+ plt.tight_layout(rect=[0, 0, 1, 0.96])
3582
+ plt.show()
3583
+
3584
+ return fig, (ax1, ax2, ax3, ax4)
3585
+
3586
+ def save_periodic_table_visualization(
3587
+ filename: str = "periodic_table_kececi",
3588
+ format: str = "png",
3589
+ dpi: int = 300,
3590
+ layout_type: str = "3d_helix",
3591
+ color_scheme: str = "vibrant"
3592
+ ):
3593
+ """
3594
+ Periyodik tablo görselleştirmesini kaydet.
3595
+
3596
+ Parameters:
3597
+ -----------
3598
+ filename : str
3599
+ Kaydedilecek dosyanın adı (uzantı olmadan)
3600
+ format : str
3601
+ Kayıt formatı: 'png', 'jpg', 'svg', 'pdf'
3602
+ dpi : int
3603
+ Çözünürlük (dots per inch)
3604
+ layout_type : str
3605
+ Layout tipi
3606
+ color_scheme : str
3607
+ Renk şeması
3608
+ """
3609
+ # Önceki figürleri temizle
3610
+ plt.close('all')
3611
+
3612
+ # Periyodik tabloyu yükle
3613
+ periodic_elements, _ = generate_complete_periodic_table()
3614
+
3615
+ # Graf oluştur
3616
+ node_count = len(periodic_elements)
3617
+ G = nx.DiGraph()
3618
+ G.add_nodes_from(range(1, node_count + 1))
3619
+ for i in range(1, node_count):
3620
+ G.add_edge(i, i + 1)
3621
+
3622
+ # Figür oluştur
3623
+ if '3d' in layout_type.lower():
3624
+ fig = plt.figure(figsize=(16, 16))
3625
+ ax = fig.add_subplot(111, projection='3d')
3626
+ else:
3627
+ fig = plt.figure(figsize=(14, 14))
3628
+ ax = fig.add_subplot(111)
3629
+
3630
+ # Görseli çiz
3631
+ ax, pos = draw_kececi_periodic_table(
3632
+ G,
3633
+ periodic_elements,
3634
+ layout_type=layout_type,
3635
+ ax=ax,
3636
+ color_scheme=color_scheme,
3637
+ node_size=1500 if '3d' in layout_type.lower() else 1000,
3638
+ font_size=9 if '3d' in layout_type.lower() else 8,
3639
+ edge_style='light',
3640
+ show_legend=True if color_scheme in ['group', 'period', 'block'] else False
3641
+ )
3642
+
3643
+ # Kaydet
3644
+ full_filename = f"{filename}.{format}"
3645
+ plt.savefig(full_filename, dpi=dpi, bbox_inches='tight',
3646
+ facecolor='white', edgecolor='none')
3647
+ plt.close()
3648
+
3649
+ print(f"Görsel kaydedildi: {full_filename}")
3650
+ return full_filename
3651
+
3652
+ def highlight_elements(element_symbols: List[str],
3653
+ highlight_color: Tuple[float, float, float] = (1.0, 0.0, 0.0),
3654
+ **kwargs):
3655
+ """
3656
+ Belirli elementleri vurgula.
3657
+
3658
+ Parameters:
3659
+ -----------
3660
+ element_symbols : List[str]
3661
+ Vurgulanacak element sembolleri
3662
+ highlight_color : Tuple[float, float, float]
3663
+ Vurgulama rengi (RGB)
3664
+ **kwargs : diğer parametreler draw_kececi_periodic_table'a aktarılır
3665
+ """
3666
+ # Önceki figürleri temizle
3667
+ plt.close('all')
3668
+
3669
+ # Periyodik tabloyu yükle
3670
+ periodic_elements, _ = generate_complete_periodic_table()
3671
+
3672
+ # Vurgulanacak elementlerin atom numaralarını bul
3673
+ highlight_indices = []
3674
+ element_symbols_found = []
3675
+ valid_symbols = []
3676
+
3677
+ # Element sembolünden isim eşleştirmesi için sabit sözlük
3678
+ # Güncellenmiş Türkçe element isimleri
3679
+ element_name_map = {
3680
+ 'H': 'Hidrojen', 'He': 'Helyum', 'Li': 'Lityum', 'Be': 'Berilyum',
3681
+ 'B': 'Bor', 'C': 'Karbon', 'N': 'Azot', 'O': 'Oksijen', 'F': 'Flor',
3682
+ 'Ne': 'Neon', 'Na': 'Sodyum', 'Mg': 'Magnezyum', 'Al': 'Alüminyum',
3683
+ 'Si': 'Silisyum', 'P': 'Fosfor', 'S': 'Kükürt', 'Cl': 'Klor',
3684
+ 'Ar': 'Argon', 'K': 'Potasyum', 'Ca': 'Kalsiyum', 'Sc': 'Skandiyum',
3685
+ 'Ti': 'Titanyum', 'V': 'Vanadyum', 'Cr': 'Krom', 'Mn': 'Mangan',
3686
+ 'Fe': 'Demir', 'Co': 'Kobalt', 'Ni': 'Nikel', 'Cu': 'Bakır',
3687
+ 'Zn': 'Çinko', 'Ga': 'Galyum', 'Ge': 'Germanyum', 'As': 'Arsenik',
3688
+ 'Se': 'Selenyum', 'Br': 'Brom', 'Kr': 'Kripton', 'Rb': 'Rubidyum',
3689
+ 'Sr': 'Stronsiyum', 'Y': 'İtriyum', 'Zr': 'Zirkonyum', 'Nb': 'Niyobyum',
3690
+ 'Mo': 'Molibden', 'Tc': 'Teknesyum', 'Ru': 'Rutenyum', 'Rh': 'Rodyum',
3691
+ 'Pd': 'Paladyum', 'Ag': 'Gümüş', 'Cd': 'Kadmiyum', 'In': 'İndiyum',
3692
+ 'Sn': 'Kalay', 'Sb': 'Antimon', 'Te': 'Tellür', 'I': 'İyot',
3693
+ 'Xe': 'Ksenon', 'Cs': 'Sezyum', 'Ba': 'Baryum', 'La': 'Lantan',
3694
+ 'Ce': 'Seryum', 'Pr': 'Praseodim', 'Nd': 'Neodimyum', 'Pm': 'Prometyum',
3695
+ 'Sm': 'Samaryum', 'Eu': 'Europyum', 'Gd': 'Gadolinyum', 'Tb': 'Terbiyum',
3696
+ 'Dy': 'Disprozyum', 'Ho': 'Holmiyum', 'Er': 'Erbiyum', 'Tm': 'Tulyum',
3697
+ 'Yb': 'İterbiyum', 'Lu': 'Lutesyum', 'Hf': 'Hafniyum', 'Ta': 'Tantal',
3698
+ 'W': 'Tungsten', 'Re': 'Renyum', 'Os': 'Osmiyum', 'Ir': 'İridyum',
3699
+ 'Pt': 'Platin', 'Au': 'Altın', 'Hg': 'Cıva', 'Tl': 'Talyum',
3700
+ 'Pb': 'Kurşun', 'Bi': 'Bizmut', 'Po': 'Polonyum', 'At': 'Astatin',
3701
+ 'Rn': 'Radon', 'Fr': 'Fransiyum', 'Ra': 'Radyum', 'Ac': 'Aktinyum',
3702
+ 'Th': 'Toryum', 'Pa': 'Protaktinyum', 'U': 'Uranyum', 'Np': 'Neptünyum',
3703
+ 'Pu': 'Plütonyum', 'Am': 'Amerikyum', 'Cm': 'Küriyum', 'Bk': 'Berkelyum',
3704
+ 'Cf': 'Kaliforniyum', 'Es': 'Einsteinyum', 'Fm': 'Fermiyum', 'Md': 'Mendelevyum',
3705
+ 'No': 'Nobelyum', 'Lr': 'Lawrensiyum', 'Rf': 'Rutherfordiyum', 'Db': 'Dubniyum',
3706
+ 'Sg': 'Seaborgiyum', 'Bh': 'Bohriyum', 'Hs': 'Hassiyum', 'Mt': 'Meitneriyum',
3707
+ 'Ds': 'Darmstadtiyum', 'Rg': 'Röntgenyum', 'Cn': 'Kopernikyum', 'Nh': 'Nihonyum',
3708
+ 'Fl': 'Flerovyum', 'Mc': 'Moscovyum', 'Lv': 'Livermoryum', 'Ts': 'Tenesin',
3709
+ 'Og': 'Oganesson/Oganesyan'
3710
+ }
3711
+
3712
+ for input_symbol in element_symbols:
3713
+ input_symbol_clean = str(input_symbol).strip()
3714
+ found = False
3715
+ for atomic_num, (sym, atomic_num_in_tuple) in periodic_elements.items():
3716
+ # Periyodik tablodaki sembol ile kullanıcının girdiği sembolü karşılaştır
3717
+ # Büyük/küçük harf duyarsız karşılaştırma
3718
+ if sym.upper() == input_symbol_clean.upper():
3719
+ highlight_indices.append(atomic_num - 1) # 0-based index
3720
+ # Orijinal sembolü (büyük/küçük harf korunarak) ekle
3721
+ element_symbols_found.append(sym) # Burada sym kullanıyoruz (periyodik tablodaki orijinal)
3722
+ valid_symbols.append(sym) # Orijinal sembolü sakla
3723
+ found = True
3724
+ break
3725
+
3726
+ if not found:
3727
+ print(f"Uyarı: '{input_symbol_clean}' elementi bulunamadı!")
3728
+
3729
+ if not highlight_indices:
3730
+ print("Vurgulanacak geçerli element bulunamadı!")
3731
+ return None, None
3732
+
3733
+ # Element isimlerini bul
3734
+ element_names = [element_name_map.get(sym, sym) for sym in element_symbols_found]
3735
+
3736
+ print(f"Vurgulanan elementler: {', '.join(element_names)}")
3737
+
3738
+ # Graf oluştur
3739
+ node_count = len(periodic_elements)
3740
+ G = nx.DiGraph()
3741
+ G.add_nodes_from(range(1, node_count + 1))
3742
+ for i in range(1, node_count):
3743
+ G.add_edge(i, i + 1)
3744
+
3745
+ # Özel renk şeması oluştur
3746
+ colors = []
3747
+ for i in range(1, node_count + 1):
3748
+ if (i - 1) in highlight_indices:
3749
+ colors.append(highlight_color)
3750
+ else:
3751
+ # Gri tonlarında diğer elementler
3752
+ colors.append((0.9, 0.9, 0.9))
3753
+
3754
+ # Layout tipini belirle
3755
+ layout_type = kwargs.get('layout_type', '3d_helix')
3756
+ dimension = '3d' if '3d' in layout_type.lower() else '2d'
3757
+
3758
+ # Figür oluştur
3759
+ if dimension == '3d':
3760
+ fig = plt.figure(figsize=(16, 16))
3761
+ ax = fig.add_subplot(111, projection='3d')
3762
+ else:
3763
+ fig = plt.figure(figsize=(14, 14))
3764
+ ax = fig.add_subplot(111)
3765
+
3766
+ # Layout hesapla
3767
+ layout_params = kwargs.get('layout_params', {})
3768
+ pos = _calculate_layout(G, layout_type, layout_params, dimension, node_count)
3769
+
3770
+ # Özel etiketler oluştur
3771
+ custom_labels = _generate_labels(G, periodic_elements)
3772
+
3773
+ # Node boyutlarını ayarla (vurgulananlar daha büyük)
3774
+ node_sizes = []
3775
+ for i in range(1, node_count + 1):
3776
+ if (i - 1) in highlight_indices:
3777
+ node_sizes.append(kwargs.get('highlight_size', 2000))
3778
+ else:
3779
+ node_sizes.append(kwargs.get('normal_size', 800))
3780
+
3781
+ # Font boyutlarını ayarla
3782
+ font_sizes = []
3783
+ for i in range(1, node_count + 1):
3784
+ if (i - 1) in highlight_indices:
3785
+ font_sizes.append(kwargs.get('highlight_font_size', 12))
3786
+ else:
3787
+ font_sizes.append(kwargs.get('normal_font_size', 7))
3788
+
3789
+ # Çizim sırasını belirle
3790
+ draw_order = _get_draw_order(G, pos, 'smart', dimension)
3791
+
3792
+ # Edge stilini al
3793
+ edge_config = _get_edge_config(kwargs.get('edge_style', 'light'))
3794
+
3795
+ # Çiz
3796
+ _draw_edges(G, pos, ax, edge_config, dimension)
3797
+ _draw_nodes(G, pos, colors, ax, node_sizes, draw_order, dimension,
3798
+ edge_width=kwargs.get('edge_width', 2.0),
3799
+ node_alpha=kwargs.get('node_alpha', 1.0))
3800
+
3801
+ # Etiketleri çiz
3802
+ _draw_labels(G, pos, custom_labels, colors, ax, font_sizes,
3803
+ kwargs.get('label_position', 'center'), dimension,
3804
+ label_offset=kwargs.get('label_offset', 0.0))
3805
+
3806
+ # Başlık
3807
+ title = kwargs.get('title')
3808
+ if title is None:
3809
+ # Orijinal sembolleri (büyük/küçük harf korunarak) kullan
3810
+ element_symbols_str = ', '.join(element_symbols_found)
3811
+ element_names_str = ', '.join(element_names)
3812
+ title = f"Keçeci Layout ile Vurgulanan Elementler\nHighlighted Elements with Keçeci Layout:\n: {element_names_str} ({element_symbols_str})\n"
3813
+ title += f"Layout: {layout_type}, Toplam/Total {node_count} Element"
3814
+
3815
+ ax.set_title(title, fontsize=18, fontweight='bold', pad=20)
3816
+
3817
+ # Eksen ayarları
3818
+ _configure_axes(ax, pos, dimension, layout_params,
3819
+ elevation=kwargs.get('elevation', -25),
3820
+ azimuth=kwargs.get('azimuth', 15))
3821
+
3822
+ plt.tight_layout()
3823
+
3824
+ # Açıklama ekle
3825
+ if dimension == '3d':
3826
+ ax.text2D(0.02, 0.98,
3827
+ f"Kırmızı: {', '.join(element_names)}\nGri: Diğer elementler",
3828
+ transform=ax.transAxes,
3829
+ fontsize=10, verticalalignment='top',
3830
+ bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
3831
+ else:
3832
+ ax.text(0.02, 0.98,
3833
+ f"Kırmızı: {', '.join(element_names)}\nGri: Diğer elementler",
3834
+ transform=ax.transAxes,
3835
+ fontsize=10, verticalalignment='top',
3836
+ bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
3837
+
3838
+ plt.show()
3839
+
3840
+ print(f"\nVurgulama tamamlandı!")
3841
+ print(f"Toplam {node_count} element, {len(highlight_indices)} element vurgulandı.")
3842
+ print("Grafı kapatmak için figür penceresini kapatın.")
3843
+
3844
+ return ax, pos
3845
+
3846
+ def demo_periodic_table_visualizations():
3847
+ """
3848
+ Periyodik tablo görselleştirmelerinin demo gösterimi.
3849
+ Her görsel ayrı ayrı gösterilir, boş şablonlar oluşmaz.
3850
+ """
3851
+ # Tüm figürleri temizle
3852
+ plt.close('all')
3853
+
3854
+ # Periyodik tabloyu yükle
3855
+ print("Periyodik tablo yükleniyor...")
3856
+ periodic_elements, _ = generate_complete_periodic_table()
3857
+
3858
+ # Graf oluştur
3859
+ node_count = len(periodic_elements)
3860
+ G = nx.DiGraph()
3861
+ G.add_nodes_from(range(1, node_count + 1))
3862
+ for i in range(1, node_count):
3863
+ G.add_edge(i, i + 1)
3864
+
3865
+ print("=" * 70)
3866
+ print("PERİYODİK TABLO GÖRSELLEŞTİRME DEMOLARI")
3867
+ print("=" * 70)
3868
+ print(f"Toplam {node_count} element gösterilecek.")
3869
+ print("Her görsel 5 saniye boyunca gösterilecek...")
3870
+
3871
+ demos = [
3872
+ {
3873
+ "name": "3D Heliks Layout",
3874
+ "layout_type": "3d_helix",
3875
+ "color_scheme": "vibrant",
3876
+ "params": {'z_spacing': 8.0, 'radius': 25.0, 'turns': 3.0},
3877
+ "figsize": (20, 20),
3878
+ "projection": '3d'
3879
+ },
3880
+ {
3881
+ "name": "2D Linear Layout",
3882
+ "layout_type": "2d_linear",
3883
+ "color_scheme": "group",
3884
+ "params": {'primary_spacing': 3.0, 'secondary_spacing': 3.0},
3885
+ "figsize": (16, 16),
3886
+ "projection": None
3887
+ },
3888
+ {
3889
+ "name": "2D Grid Layout",
3890
+ "layout_type": "2d_grid",
3891
+ "color_scheme": "period",
3892
+ "params": {'rows': 9, 'cols': 14, 'spacing': 3.0},
3893
+ "figsize": (18, 18),
3894
+ "projection": None
3895
+ },
3896
+ {
3897
+ "name": "2D Circular Layout",
3898
+ "layout_type": "2d_circular",
3899
+ "color_scheme": "block",
3900
+ "params": {'scale': 2.0},
3901
+ "figsize": (16, 16),
3902
+ "projection": None
3903
+ }
3904
+ ]
3905
+
3906
+ for i, demo in enumerate(demos, 1):
3907
+ print(f"\n{i}. {demo['name']} ({demo['color_scheme']} renkler) oluşturuluyor...")
3908
+
3909
+ if demo['projection'] == '3d':
3910
+ fig = plt.figure(figsize=demo['figsize'])
3911
+ ax = fig.add_subplot(111, projection='3d')
3912
+ else:
3913
+ fig = plt.figure(figsize=demo['figsize'])
3914
+ ax = fig.add_subplot(111)
3915
+
3916
+ ax, pos = draw_kececi_periodic_table(
3917
+ G,
3918
+ periodic_elements,
3919
+ layout_type=demo['layout_type'],
3920
+ layout_params=demo['params'],
3921
+ ax=ax,
3922
+ color_scheme=demo['color_scheme'],
3923
+ node_size=2000 if demo['projection'] == '3d' else 1000,
3924
+ font_size=10 if demo['projection'] == '3d' else 8,
3925
+ edge_style='light',
3926
+ show_legend=True if demo['color_scheme'] in ['group', 'period', 'block'] else False,
3927
+ title=f"Keçeci Layout ile\n {i}/4: {demo['name']}\n{demo['color_scheme'].capitalize()} Renk Şeması"
3928
+ )
3929
+
3930
+ plt.tight_layout()
3931
+ plt.show(block=False)
3932
+ plt.pause(5)
3933
+ plt.close(fig)
3934
+
3935
+ print("\n" + "=" * 70)
3936
+ print("TÜM DEMOLAR TAMAMLANDI!")
3937
+ print("=" * 70)
3938
+
3939
+ return True
3940
+
3941
+ def get_element_info(element_symbol: str) -> Dict[str, Any]:
3942
+ """
3943
+ Element sembolünden element bilgilerini getir.
3944
+
3945
+ Parameters:
3946
+ -----------
3947
+ element_symbol : str
3948
+ Element sembolü (örn: 'H', 'He', 'Fe')
3949
+
3950
+ Returns:
3951
+ --------
3952
+ dict : Element bilgileri
3953
+ """
3954
+ # Periyodik tabloyu yükle
3955
+ periodic_elements, element_dict = generate_complete_periodic_table()
3956
+
3957
+ # Element sembolünden atom numarasını bul
3958
+ atomic_num_found = None
3959
+ original_symbol = None
3960
+
3961
+ for atomic_num, (sym, atomic_num_in_tuple) in periodic_elements.items():
3962
+ if sym.upper() == element_symbol.upper():
3963
+ atomic_num_found = atomic_num
3964
+ original_symbol = sym # Orijinal sembolü sakla
3965
+ break
3966
+
3967
+ if atomic_num_found is None:
3968
+ raise ValueError(f"Element bulunamadı: {element_symbol}")
3969
+
3970
+ # Element ismini bul - Güncellenmiş Türkçe isimler
3971
+ element_name_map = {
3972
+ 'H': 'Hidrojen', 'He': 'Helyum', 'Li': 'Lityum', 'Be': 'Berilyum',
3973
+ 'B': 'Bor', 'C': 'Karbon', 'N': 'Azot', 'O': 'Oksijen', 'F': 'Flor',
3974
+ 'Ne': 'Neon', 'Na': 'Sodyum', 'Mg': 'Magnezyum', 'Al': 'Alüminyum',
3975
+ 'Si': 'Silisyum', 'P': 'Fosfor', 'S': 'Kükürt', 'Cl': 'Klor',
3976
+ 'Ar': 'Argon', 'K': 'Potasyum', 'Ca': 'Kalsiyum', 'Sc': 'Skandiyum',
3977
+ 'Ti': 'Titanyum', 'V': 'Vanadyum', 'Cr': 'Krom', 'Mn': 'Mangan',
3978
+ 'Fe': 'Demir', 'Co': 'Kobalt', 'Ni': 'Nikel', 'Cu': 'Bakır',
3979
+ 'Zn': 'Çinko', 'Ga': 'Galyum', 'Ge': 'Germanyum', 'As': 'Arsenik',
3980
+ 'Se': 'Selenyum', 'Br': 'Brom', 'Kr': 'Kripton', 'Rb': 'Rubidyum',
3981
+ 'Sr': 'Stronsiyum', 'Y': 'İtriyum', 'Zr': 'Zirkonyum', 'Nb': 'Niyobyum',
3982
+ 'Mo': 'Molibden', 'Tc': 'Teknesyum', 'Ru': 'Rutenyum', 'Rh': 'Rodyum',
3983
+ 'Pd': 'Paladyum', 'Ag': 'Gümüş', 'Cd': 'Kadmiyum', 'In': 'İndiyum',
3984
+ 'Sn': 'Kalay', 'Sb': 'Antimon', 'Te': 'Tellür', 'I': 'İyot',
3985
+ 'Xe': 'Ksenon', 'Cs': 'Sezyum', 'Ba': 'Baryum', 'La': 'Lantan',
3986
+ 'Ce': 'Seryum', 'Pr': 'Praseodim', 'Nd': 'Neodimyum', 'Pm': 'Prometyum',
3987
+ 'Sm': 'Samaryum', 'Eu': 'Europyum', 'Gd': 'Gadolinyum', 'Tb': 'Terbiyum',
3988
+ 'Dy': 'Disprozyum', 'Ho': 'Holmiyum', 'Er': 'Erbiyum', 'Tm': 'Tulyum',
3989
+ 'Yb': 'İterbiyum', 'Lu': 'Lutesyum', 'Hf': 'Hafniyum', 'Ta': 'Tantal',
3990
+ 'W': 'Tungsten', 'Re': 'Renyum', 'Os': 'Osmiyum', 'Ir': 'İridyum',
3991
+ 'Pt': 'Platin', 'Au': 'Altın', 'Hg': 'Cıva', 'Tl': 'Talyum',
3992
+ 'Pb': 'Kurşun', 'Bi': 'Bizmut', 'Po': 'Polonyum', 'At': 'Astatin',
3993
+ 'Rn': 'Radon', 'Fr': 'Fransiyum', 'Ra': 'Radyum', 'Ac': 'Aktinyum',
3994
+ 'Th': 'Toryum', 'Pa': 'Protaktinyum', 'U': 'Uranyum', 'Np': 'Neptünyum',
3995
+ 'Pu': 'Plütonyum', 'Am': 'Amerikyum', 'Cm': 'Küriyum', 'Bk': 'Berkelyum',
3996
+ 'Cf': 'Kaliforniyum', 'Es': 'Einsteinyum', 'Fm': 'Fermiyum', 'Md': 'Mendelevyum',
3997
+ 'No': 'Nobelyum', 'Lr': 'Lawrensiyum', 'Rf': 'Rutherfordiyum', 'Db': 'Dubniyum',
3998
+ 'Sg': 'Seaborgiyum', 'Bh': 'Bohriyum', 'Hs': 'Hassiyum', 'Mt': 'Meitneriyum',
3999
+ 'Ds': 'Darmstadtiyum', 'Rg': 'Röntgenyum', 'Cn': 'Kopernikyum', 'Nh': 'Nihonyum',
4000
+ 'Fl': 'Flerovyum', 'Mc': 'Moscovyum', 'Lv': 'Livermoryum', 'Ts': 'Tenesin',
4001
+ 'Og': 'Oganesson/Oganesyan'
4002
+ }
4003
+
4004
+ element_name = element_name_map.get(original_symbol.upper(), original_symbol)
4005
+
4006
+ # Yuri Oganessian hakkında ek bilgi
4007
+ additional_info = ""
4008
+ if original_symbol.upper() == 'OG':
4009
+ additional_info = "\n Not: Element, Rus-Armeni fizikçi Yuri Oganessian (Юрий Оганесян) onuruna adlandırılmıştır."
4010
+
4011
+ # Grup ve periyot bilgilerini hesapla
4012
+ group = _determine_group(atomic_num_found)
4013
+
4014
+ # Periyot hesapla
4015
+ if atomic_num_found <= 2:
4016
+ period = 1
4017
+ elif atomic_num_found <= 10:
4018
+ period = 2
4019
+ elif atomic_num_found <= 18:
4020
+ period = 3
4021
+ elif atomic_num_found <= 36:
4022
+ period = 4
4023
+ elif atomic_num_found <= 54:
4024
+ period = 5
4025
+ elif atomic_num_found <= 86:
4026
+ period = 6
4027
+ else:
4028
+ period = 7
4029
+
4030
+ # Blok bilgisi
4031
+ if atomic_num_found in [1, 2, 3, 4, 11, 12, 19, 20, 37, 38, 55, 56, 87, 88]:
4032
+ block = "s"
4033
+ elif atomic_num_found in (list(range(5, 11)) + list(range(13, 19)) +
4034
+ list(range(31, 37)) + list(range(49, 55)) +
4035
+ list(range(81, 87)) + list(range(113, 119))):
4036
+ block = "p"
4037
+ elif atomic_num_found in (list(range(21, 31)) + list(range(39, 49)) +
4038
+ list(range(72, 81)) + list(range(104, 113))):
4039
+ block = "d"
4040
+ elif atomic_num_found in list(range(57, 72)) + list(range(89, 104)):
4041
+ block = "f"
4042
+ else:
4043
+ block = "unknown"
4044
+
4045
+ # Elektronegativite verisi
4046
+ electronegativity_data = {
4047
+ 1: 2.20, 2: None, 3: 0.98, 4: 1.57, 5: 2.04, 6: 2.55, 7: 3.04, 8: 3.44,
4048
+ 9: 3.98, 10: None, 11: 0.93, 12: 1.31, 13: 1.61, 14: 1.90, 15: 2.19,
4049
+ 16: 2.58, 17: 3.16, 18: None, 19: 0.82, 20: 1.00, 21: 1.36, 22: 1.54,
4050
+ 23: 1.63, 24: 1.66, 25: 1.55, 26: 1.83, 27: 1.88, 28: 1.91, 29: 1.90,
4051
+ 30: 1.65, 31: 1.81, 32: 2.01, 33: 2.18, 34: 2.55, 35: 2.96, 36: None,
4052
+ 37: 0.82, 38: 0.95, 39: 1.22, 40: 1.33, 41: 1.60, 42: 2.16, 43: 1.90,
4053
+ 44: 2.20, 45: 2.28, 46: 2.20, 47: 1.93, 48: 1.69, 49: 1.78, 50: 1.96,
4054
+ 51: 2.05, 52: 2.10, 53: 2.66, 54: None, 55: 0.79, 56: 0.89, 57: 1.10,
4055
+ 58: 1.12, 59: 1.13, 60: 1.14, 61: 1.13, 62: 1.17, 63: 1.20, 64: 1.20,
4056
+ 65: 1.20, 66: 1.22, 67: 1.23, 68: 1.24, 69: 1.25, 70: 1.10, 71: 1.27,
4057
+ 72: 1.30, 73: 1.50, 74: 2.36, 75: 1.90, 76: 2.20, 77: 2.20, 78: 2.28,
4058
+ 79: 2.54, 80: 2.00, 81: 1.62, 82: 1.87, 83: 2.02, 84: 2.00, 85: 2.20,
4059
+ 86: None, 87: 0.70, 88: 0.89, 89: 1.10, 90: 1.30, 91: 1.50, 92: 1.38,
4060
+ 93: 1.36, 94: 1.28, 95: 1.30, 96: 1.30, 97: 1.30, 98: 1.30, 99: 1.30,
4061
+ 100: 1.30, 101: 1.30, 102: 1.30, 103: 1.30, 118: None # Oganesson
4062
+ }
4063
+
4064
+ result = {
4065
+ 'atomic_number': atomic_num_found,
4066
+ 'symbol': original_symbol, # Orijinal sembolü döndür
4067
+ 'name': element_name,
4068
+ 'group': group,
4069
+ 'period': period,
4070
+ 'block': block,
4071
+ 'electronegativity': electronegativity_data.get(atomic_num_found, None)
4072
+ }
4073
+
4074
+ # Ek bilgiyi de döndürelim
4075
+ result['additional_info'] = additional_info
4076
+
4077
+ return result
4078
+
4079
+ def custom_visualization():
4080
+ """
4081
+ Özelleştirilmiş görselleştirme menüsü.
4082
+ """
4083
+ print("\n" + "=" * 70)
4084
+ print("Custom Visualization")
4085
+ print("=" * 70)
4086
+
4087
+ # Periyodik tabloyu yükle
4088
+ periodic_elements, _ = generate_complete_periodic_table()
4089
+
4090
+ # Graf oluştur
4091
+ node_count = len(periodic_elements)
4092
+ G = nx.DiGraph()
4093
+ G.add_nodes_from(range(1, node_count + 1))
4094
+ for i in range(1, node_count):
4095
+ G.add_edge(i, i + 1)
4096
+
4097
+ # Layout seçenekleri
4098
+ layouts = {
4099
+ '1': ('3d_helix', 'Heliks Layout'),
4100
+ '2': ('2d_linear', 'Linear Layout'),
4101
+ '3': ('2d_grid', 'Grid Layout'),
4102
+ '4': ('2d_circular', 'Dairesel Layout'),
4103
+ '5': ('2d_spring', 'Yay Layout')
4104
+ }
4105
+
4106
+ print("\nLayout Tipleri:")
4107
+ for key, (_, name) in layouts.items():
4108
+ print(f"{key}. {name}")
4109
+
4110
+ layout_choice = input("\nLayout tipi seçin (1-5): ").strip()
4111
+ if layout_choice not in layouts:
4112
+ print("Varsayılan olarak 3D Heliks seçildi.")
4113
+ layout_type = '3d_helix'
4114
+ else:
4115
+ layout_type, layout_name = layouts[layout_choice]
4116
+
4117
+ # Renk şeması seçenekleri
4118
+ color_schemes = {
4119
+ '1': 'vibrant',
4120
+ '2': 'distinct',
4121
+ '3': 'pastel',
4122
+ '4': 'group',
4123
+ '5': 'period',
4124
+ '7': 'electronegativity',
4125
+ }
4126
+
4127
+ print("\nRenk Şemaları:")
4128
+ print("1. Vibrant (Canlı renkler)")
4129
+ print("2. Distinct (Farklı renkler)")
4130
+ print("3. Pastel (Pastel tonlar)")
4131
+ print("4. Group (Gruplara göre)")
4132
+ print("5. Period (Periyotlara göre)")
4133
+ print("6. Block (Bloklara göre)")
4134
+ print("7. electronegativity (Electronegativitiye göre)")
4135
+
4136
+ color_choice = input("\nRenk şeması seçin (1-7): ").strip()
4137
+ if color_choice not in color_schemes:
4138
+ print("Varsayılan olarak Vibrant seçildi.")
4139
+ color_scheme = 'vibrant'
4140
+ else:
4141
+ color_scheme = color_schemes[color_choice]
4142
+
4143
+ # Boyut seçimi
4144
+ try:
4145
+ node_size = int(input(f"\nNode boyutu (varsayılan: 1600): ") or "1600")
4146
+ except:
4147
+ node_size = 1600
4148
+
4149
+ try:
4150
+ font_size = int(input(f"Font boyutu (varsayılan: 10): ") or "10")
4151
+ except:
4152
+ font_size = 10
4153
+
4154
+ # 3D için özel parametreler
4155
+ if '3d' in layout_type:
4156
+ fig = plt.figure(figsize=(20, 20))
4157
+ ax = fig.add_subplot(111, projection='3d')
4158
+ else:
4159
+ fig = plt.figure(figsize=(16, 16))
4160
+ ax = fig.add_subplot(111)
4161
+
4162
+ # Özel başlık
4163
+ custom_title = input("\nÖzel başlık (boş bırakırsanız otomatik oluşturulur): ").strip()
4164
+
4165
+ # Çizim yap
4166
+ ax, pos = draw_kececi_periodic_table(
4167
+ G,
4168
+ periodic_elements,
4169
+ layout_type=layout_type,
4170
+ ax=ax,
4171
+ color_scheme=color_scheme,
4172
+ node_size=node_size,
4173
+ font_size=font_size,
4174
+ title=custom_title if custom_title else None,
4175
+ show_legend=(color_scheme in ['vibrant', 'distinct', 'pastel', 'group', 'period', 'block', 'electronegativity'])
4176
+ )
4177
+
4178
+ plt.tight_layout()
4179
+ plt.show()
4180
+
4181
+ print(f"\n{layout_name} ile {color_scheme} renk şeması başarıyla oluşturuldu!")
4182
+ return ax, pos
4183
+
4184
+ def debug_periodic_table_structure():
4185
+ """Periyodik tablo veri yapısını kontrol et."""
4186
+ print("Periyodik tablo veri yapısı kontrol ediliyor...")
4187
+ periodic_elements, _ = generate_complete_periodic_table()
4188
+
4189
+ print(f"\nToplam element sayısı: {len(periodic_elements)}")
4190
+
4191
+ # İlk 5 elementi göster
4192
+ print("\nİlk 5 element:")
4193
+ for i, (atomic_num, value) in enumerate(list(periodic_elements.items())[:5]):
4194
+ print(f" Atom numarası {atomic_num}:")
4195
+ print(f" Değer: {value}")
4196
+ print(f" Tip: {type(value)}")
4197
+
4198
+ if isinstance(value, tuple):
4199
+ print(f" Tuple uzunluğu: {len(value)}")
4200
+ for j, item in enumerate(value):
4201
+ print(f" Item {j}: {item} (tip: {type(item)})")
4202
+
4203
+ # Rastgele bir element kontrolü
4204
+ print("\nRastgele element kontrolü (atom numarası 26 - Demir):")
4205
+ if 26 in periodic_elements:
4206
+ value = periodic_elements[26]
4207
+ print(f" Değer: {value}")
4208
+ print(f" Tip: {type(value)}")
4209
+ if isinstance(value, tuple) and len(value) >= 2:
4210
+ print(f" Sembol: {value[0]}")
4211
+ print(f" İsim: {value[1]}")
4212
+
4213
+ def _generate_labels(graph, periodic_elements):
4214
+ """Etiketleri oluştur."""
4215
+ labels = {}
4216
+ for node_id in graph.nodes():
4217
+ if node_id in periodic_elements:
4218
+ sym, atomic_num = periodic_elements[node_id]
4219
+ # Etiket formatı: Sembol\nAtom Numarası
4220
+ labels[node_id] = f"{sym}\n{atomic_num}"
4221
+ else:
4222
+ labels[node_id] = str(node_id)
4223
+ return labels
4224
+
2846
4225
  # =============================================================================
2847
4226
  # MODULE TEST CODE
2848
4227
  # =============================================================================
@@ -2890,12 +4269,3 @@ if __name__ == '__main__':
2890
4269
  plt.show()
2891
4270
 
2892
4271
 
2893
-
2894
-
2895
-
2896
-
2897
-
2898
-
2899
-
2900
-
2901
-