@@ -35,6 +35,7 @@ def __init__(self, config: argparse.Namespace) -> None:
3535 self .printer : Printer # defined in set_printer
3636 self .file_name = "" # defined in set_printer
3737 self .depth = self .config .max_color_depth
38+ self .max_depth = self .config .max_depth
3839 # default colors are an adaption of the seaborn colorblind palette
3940 self .available_colors = itertools .cycle (self .config .color_palette )
4041 self .used_colors : dict [str , str ] = {}
@@ -53,6 +54,38 @@ def write(self, diadefs: Iterable[ClassDiagram | PackageDiagram]) -> None:
5354 self .write_classes (diagram )
5455 self .save ()
5556
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+
5689 def write_packages (self , diagram : PackageDiagram ) -> None :
5790 """Write a package diagram."""
5891 module_info : dict [str , dict [str , int ]] = {}
@@ -61,6 +94,10 @@ def write_packages(self, diagram: PackageDiagram) -> None:
6194 for module in sorted (diagram .modules (), key = lambda x : x .title ):
6295 module .fig_id = module .node .qname ()
6396
97+ # Filter nodes based on depth
98+ if not self .should_show_node (module .fig_id ):
99+ continue
100+
64101 if self .config .no_standalone and not any (
65102 module in (rel .from_object , rel .to_object )
66103 for rel in diagram .get_relationships ("depends" )
@@ -83,6 +120,10 @@ def write_packages(self, diagram: PackageDiagram) -> None:
83120 from_id = rel .from_object .fig_id
84121 to_id = rel .to_object .fig_id
85122
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+
86127 self .printer .emit_edge (
87128 from_id ,
88129 to_id ,
@@ -96,6 +137,10 @@ def write_packages(self, diagram: PackageDiagram) -> None:
96137 from_id = rel .from_object .fig_id
97138 to_id = rel .to_object .fig_id
98139
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+
99144 self .printer .emit_edge (
100145 from_id ,
101146 to_id ,
@@ -115,6 +160,11 @@ def write_classes(self, diagram: ClassDiagram) -> None:
115160 # sorted to get predictable (hence testable) results
116161 for obj in sorted (diagram .objects , key = lambda x : x .title ):
117162 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+
118168 if self .config .no_standalone and not any (
119169 obj in (rel .from_object , rel .to_object )
120170 for rel_type in ("specialization" , "association" , "aggregation" )
@@ -129,6 +179,12 @@ def write_classes(self, diagram: ClassDiagram) -> None:
129179 )
130180 # inheritance links
131181 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+
132188 self .printer .emit_edge (
133189 rel .from_object .fig_id ,
134190 rel .to_object .fig_id ,
@@ -137,6 +193,12 @@ def write_classes(self, diagram: ClassDiagram) -> None:
137193 associations : dict [str , set [str ]] = defaultdict (set )
138194 # generate associations
139195 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+
140202 associations [rel .from_object .fig_id ].add (rel .to_object .fig_id )
141203 self .printer .emit_edge (
142204 rel .from_object .fig_id ,
@@ -146,6 +208,12 @@ def write_classes(self, diagram: ClassDiagram) -> None:
146208 )
147209 # generate aggregations
148210 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+
149217 if rel .to_object .fig_id in associations [rel .from_object .fig_id ]:
150218 continue
151219 self .printer .emit_edge (
0 commit comments