File size: 6,049 Bytes
c73b6d7
 
 
 
 
 
991c9f4
c73b6d7
 
 
 
 
 
 
 
991c9f4
 
 
c73b6d7
991c9f4
 
c73b6d7
991c9f4
 
c73b6d7
 
 
991c9f4
 
c73b6d7
 
 
991c9f4
 
c73b6d7
 
 
 
 
991c9f4
 
 
c73b6d7
991c9f4
 
 
c73b6d7
 
991c9f4
c73b6d7
 
 
 
 
991c9f4
c73b6d7
 
 
991c9f4
c73b6d7
 
 
991c9f4
 
c73b6d7
 
 
 
 
991c9f4
c73b6d7
 
 
 
 
991c9f4
c73b6d7
 
 
991c9f4
c73b6d7
991c9f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import graphviz

def add_nodes_and_edges(dot: graphviz.Digraph, parent_id: str, nodes_list: list, current_depth: int, base_color: str):
    """
    Recursively adds nodes and edges to a Graphviz Digraph object,
    applying a color gradient and consistent styling.

    Args:
        dot (graphviz.Digraph): The Graphviz Digraph object to modify.
        parent_id (str): The ID of the parent node for the current set of nodes.
        nodes_list (list): A list of dictionaries, each representing a node
                           with 'id', 'label', 'relationship', and optional 'subnodes'.
        current_depth (int): The current depth in the graph hierarchy (0 for central node).
        base_color (str): The hexadecimal base color for the deepest nodes.
    """
    # Calculate color for current depth, making it lighter
    # This factor determines how quickly the color lightens per level.
    lightening_factor = 0.12 
    
    # Convert base_color hex to RGB for interpolation
    # Ensure base_color is a valid hex string before converting
    if not isinstance(base_color, str) or not base_color.startswith('#') or len(base_color) != 7:
        base_color = '#19191a' # Fallback to default dark if invalid

    base_r = int(base_color[1:3], 16)
    base_g = int(base_color[3:5], 16)
    base_b = int(base_color[5:7], 16)

    # Calculate current node color by blending towards white
    current_r = base_r + int((255 - base_r) * current_depth * lightening_factor)
    current_g = base_g + int((255 - base_g) * current_depth * lightening_factor)
    current_b = base_b + int((255 - base_b) * current_depth * lightening_factor)

    # Clamp values to 255 to stay within valid RGB range
    current_r = min(255, current_r)
    current_g = min(255, current_g)
    current_b = min(255, current_b)
    
    node_fill_color = f'#{current_r:02x}{current_g:02x}{current_b:02x}'

    # Font color: white for dark nodes, black for very light nodes for readability
    font_color = 'white' if current_depth * lightening_factor < 0.6 else 'black'
    
    # Edge colors and font sizes
    edge_color = '#4a4a4a' # Dark gray for lines
    # Font size adjusts based on depth, ensuring a minimum size
    font_size = max(9, 14 - (current_depth * 2)) 
    edge_font_size = max(7, 10 - (current_depth * 1))

    for node in nodes_list:
        node_id = node.get('id')
        label = node.get('label')
        relationship = node.get('relationship')
        
        # Basic validation for node data
        if not all([node_id, label, relationship]):
            raise ValueError(f"Invalid node: {node}")
            
        # Add node with specified style
        dot.node(
            node_id,
            label,
            shape='box', # All nodes are rectangular
            style='filled,rounded', # Filled and rounded corners
            fillcolor=node_fill_color, 
            fontcolor=font_color,
            fontsize=str(font_size) 
        )
        
        # Add edge from parent to current node
        dot.edge(
            parent_id,
            node_id,
            label=relationship,
            color=edge_color,
            fontcolor=edge_color, # Edge label color also dark gray
            fontsize=str(edge_font_size) 
        )
        
        # Recursively call for subnodes
        if 'subnodes' in node:
            add_nodes_and_edges(dot, node_id, node['subnodes'], current_depth + 1, base_color)

# import graphviz

# def add_nodes_and_edges(dot: graphviz.Digraph, parent_id: str, nodes_list: list, current_depth: int, base_color: str):
#     """
#     Recursively adds nodes and edges to a Graphviz Digraph object,
#     applying a color gradient and consistent styling.
#     Args:
#         dot (graphviz.Digraph): The Graphviz Digraph object to modify.
#         parent_id (str): The ID of the parent node for the current set of nodes.
#         nodes_list (list): A list of dictionaries, each representing a node
#                            with 'id', 'label', 'relationship', and optional 'subnodes'.
#         current_depth (int): The current depth in the graph hierarchy (0 for central node).
#         base_color (str): The hexadecimal base color for the deepest nodes.
#     """
#     lightening_factor = 0.06
    
#     if not isinstance(base_color, str) or not base_color.startswith('#') or len(base_color) != 7:
#         base_color = '#BEBEBE'
#     base_r = int(base_color[1:3], 16)
#     base_g = int(base_color[3:5], 16)
#     base_b = int(base_color[5:7], 16)
    
#     current_r = base_r + int((255 - base_r) * current_depth * lightening_factor)
#     current_g = base_g + int((255 - base_g) * current_depth * lightening_factor)
#     current_b = base_b + int((255 - base_b) * current_depth * lightening_factor)
    
#     current_r = min(255, current_r)
#     current_g = min(255, current_g)
#     current_b = min(255, current_b)
    
#     node_fill_color = f'#{current_r:02x}{current_g:02x}{current_b:02x}'
#     font_color = 'black'
    
#     edge_color = '#4a4a4a'
#     font_size = max(9, 14 - (current_depth * 2)) 
#     edge_font_size = max(7, 10 - (current_depth * 1))
    
#     for node in nodes_list:
#         node_id = node.get('id')
#         label = node.get('label')
#         relationship = node.get('relationship')
        
#         if not all([node_id, label, relationship]):
#             raise ValueError(f"Invalid node: {node}")
            
#         dot.node(
#             node_id,
#             label,
#             shape='box',
#             style='filled,rounded',
#             fillcolor=node_fill_color, 
#             fontcolor=font_color,
#             fontsize=str(font_size) 
#         )
        
#         dot.edge(
#             parent_id,
#             node_id,
#             label=relationship,
#             color=edge_color,
#             fontcolor=edge_color,
#             fontsize=str(edge_font_size) 
#         )
        
#         if 'subnodes' in node:
#             add_nodes_and_edges(dot, node_id, node['subnodes'], current_depth + 1, base_color)