@@ -35,6 +35,7 @@ def __init__(self, config: argparse.Namespace) -> None:
35
35
self .printer : Printer # defined in set_printer
36
36
self .file_name = "" # defined in set_printer
37
37
self .depth = self .config .max_color_depth
38
+ self .max_depth = self .config .max_depth
38
39
# default colors are an adaption of the seaborn colorblind palette
39
40
self .available_colors = itertools .cycle (self .config .color_palette )
40
41
self .used_colors : dict [str , str ] = {}
@@ -53,6 +54,38 @@ def write(self, diadefs: Iterable[ClassDiagram | PackageDiagram]) -> None:
53
54
self .write_classes (diagram )
54
55
self .save ()
55
56
57
+ def should_show_node (self , qualified_name : str , is_class : bool = False ) -> bool :
58
+ """Determine if a node should be shown based on depth settings.
59
+
60
+ Depth is calculated by counting dots in the qualified name:
61
+ - depth 0: top-level packages (no dots)
62
+ - depth 1: first level sub-packages (one dot)
63
+ - depth 2: second level sub-packages (two dots)
64
+
65
+ For classes, depth is measured from their containing module, excluding
66
+ the class name itself from the depth calculation.
67
+ """
68
+ # If no depth limit is set ==> show all nodes
69
+ if self .max_depth is None :
70
+ return True
71
+
72
+ # For classes, we want to measure depth from their containing module
73
+ if is_class :
74
+ # Get the module part (everything before the last dot)
75
+ last_dot = qualified_name .rfind ("." )
76
+ if last_dot == - 1 :
77
+ module_path = ""
78
+ else :
79
+ module_path = qualified_name [:last_dot ]
80
+
81
+ # Count module depth
82
+ module_depth = module_path .count ("." )
83
+ return bool (module_depth <= self .max_depth )
84
+
85
+ # For packages/modules, count full depth
86
+ node_depth = qualified_name .count ("." )
87
+ return bool (node_depth <= self .max_depth )
88
+
56
89
def write_packages (self , diagram : PackageDiagram ) -> None :
57
90
"""Write a package diagram."""
58
91
module_info : dict [str , dict [str , int ]] = {}
@@ -61,6 +94,10 @@ def write_packages(self, diagram: PackageDiagram) -> None:
61
94
for module in sorted (diagram .modules (), key = lambda x : x .title ):
62
95
module .fig_id = module .node .qname ()
63
96
97
+ # Filter nodes based on depth
98
+ if not self .should_show_node (module .fig_id ):
99
+ continue
100
+
64
101
if self .config .no_standalone and not any (
65
102
module in (rel .from_object , rel .to_object )
66
103
for rel in diagram .get_relationships ("depends" )
@@ -83,6 +120,10 @@ def write_packages(self, diagram: PackageDiagram) -> None:
83
120
from_id = rel .from_object .fig_id
84
121
to_id = rel .to_object .fig_id
85
122
123
+ # Filter nodes based on depth ==> skip if either source or target nodes is beyond the max depth
124
+ if not self .should_show_node (from_id ) or not self .should_show_node (to_id ):
125
+ continue
126
+
86
127
self .printer .emit_edge (
87
128
from_id ,
88
129
to_id ,
@@ -96,6 +137,10 @@ def write_packages(self, diagram: PackageDiagram) -> None:
96
137
from_id = rel .from_object .fig_id
97
138
to_id = rel .to_object .fig_id
98
139
140
+ # Filter nodes based on depth ==> skip if either source or target nodes is beyond the max depth
141
+ if not self .should_show_node (from_id ) or not self .should_show_node (to_id ):
142
+ continue
143
+
99
144
self .printer .emit_edge (
100
145
from_id ,
101
146
to_id ,
@@ -115,6 +160,11 @@ def write_classes(self, diagram: ClassDiagram) -> None:
115
160
# sorted to get predictable (hence testable) results
116
161
for obj in sorted (diagram .objects , key = lambda x : x .title ):
117
162
obj .fig_id = obj .node .qname ()
163
+
164
+ # Filter class based on depth setting
165
+ if not self .should_show_node (obj .fig_id , is_class = True ):
166
+ continue
167
+
118
168
if self .config .no_standalone and not any (
119
169
obj in (rel .from_object , rel .to_object )
120
170
for rel_type in ("specialization" , "association" , "aggregation" )
@@ -129,6 +179,12 @@ def write_classes(self, diagram: ClassDiagram) -> None:
129
179
)
130
180
# inheritance links
131
181
for rel in diagram .get_relationships ("specialization" ):
182
+ # Filter nodes based on depth setting
183
+ if not self .should_show_node (
184
+ rel .from_object .fig_id , is_class = True
185
+ ) or not self .should_show_node (rel .to_object .fig_id , is_class = True ):
186
+ continue
187
+
132
188
self .printer .emit_edge (
133
189
rel .from_object .fig_id ,
134
190
rel .to_object .fig_id ,
@@ -137,6 +193,12 @@ def write_classes(self, diagram: ClassDiagram) -> None:
137
193
associations : dict [str , set [str ]] = defaultdict (set )
138
194
# generate associations
139
195
for rel in diagram .get_relationships ("association" ):
196
+ # Filter nodes based on depth setting
197
+ if not self .should_show_node (
198
+ rel .from_object .fig_id , is_class = True
199
+ ) or not self .should_show_node (rel .to_object .fig_id , is_class = True ):
200
+ continue
201
+
140
202
associations [rel .from_object .fig_id ].add (rel .to_object .fig_id )
141
203
self .printer .emit_edge (
142
204
rel .from_object .fig_id ,
@@ -146,6 +208,12 @@ def write_classes(self, diagram: ClassDiagram) -> None:
146
208
)
147
209
# generate aggregations
148
210
for rel in diagram .get_relationships ("aggregation" ):
211
+ # Filter nodes based on depth setting
212
+ if not self .should_show_node (
213
+ rel .from_object .fig_id , is_class = True
214
+ ) or not self .should_show_node (rel .to_object .fig_id , is_class = True ):
215
+ continue
216
+
149
217
if rel .to_object .fig_id in associations [rel .from_object .fig_id ]:
150
218
continue
151
219
self .printer .emit_edge (
0 commit comments