From cc3938bd7bd1583a2d85c0954ce856ac10e86421 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Tue, 19 Nov 2024 17:45:54 +0100 Subject: [PATCH] fix!(context-diagram): Enable derived interfaces per default on `Component`s Also fix bugs: - False symbols bug - Correctly calculate the centerbox size --- capellambse_context_diagrams/__init__.py | 12 ++++-- .../collectors/default.py | 39 +++++++++++-------- capellambse_context_diagrams/context.py | 16 -------- tests/test_context_diagrams.py | 16 ++++---- 4 files changed, 38 insertions(+), 45 deletions(-) diff --git a/capellambse_context_diagrams/__init__.py b/capellambse_context_diagrams/__init__.py index d799aed..4460744 100644 --- a/capellambse_context_diagrams/__init__.py +++ b/capellambse_context_diagrams/__init__.py @@ -94,6 +94,7 @@ def register_classes() -> None: { "display_symbols_as_boxes": True, "display_parent_relation": True, + "display_derived_interfaces": True, "render_styles": styling.BLUE_ACTOR_FNCS, }, ), @@ -112,6 +113,7 @@ def register_classes() -> None: { "display_symbols_as_boxes": True, "display_parent_relation": True, + "display_derived_interfaces": True, "render_styles": styling.BLUE_ACTOR_FNCS, }, ), @@ -127,14 +129,16 @@ def register_classes() -> None: ( pa.PhysicalComponent, DiagramType.PAB, - {"display_parent_relation": True, "display_port_labels": True}, + { + "display_parent_relation": True, + "display_port_labels": True, + "display_derived_interfaces": True, + }, ), ( pa.PhysicalFunction, DiagramType.PAB, - { - "display_parent_relation": True, - }, + {"display_parent_relation": True}, ), ] cap: dict[str, CSSdef] = { diff --git a/capellambse_context_diagrams/collectors/default.py b/capellambse_context_diagrams/collectors/default.py index ad78bb9..c311e0e 100644 --- a/capellambse_context_diagrams/collectors/default.py +++ b/capellambse_context_diagrams/collectors/default.py @@ -23,7 +23,11 @@ from .. import context DerivatorFunction: t.TypeAlias = cabc.Callable[ - [context.ContextDiagram, _elkjs.ELKInputData, _elkjs.ELKInputChild], + [ + context.ContextDiagram, + _elkjs.ELKInputData, + dict[str, _elkjs.ELKInputChild], + ], None, ] @@ -90,13 +94,12 @@ def process_context(self): self.data.children.extend(self.global_boxes.values()) if self.diagram._display_parent_relation: - owner_boxes: dict[str, _elkjs.ELKInputChild] = { - uuid: box for uuid, box in self.made_boxes.items() - } generic.move_parent_boxes_to_owner( - owner_boxes, self.diagram.target, self.data + self.made_boxes, self.diagram.target, self.data + ) + generic.move_edges( + self.made_boxes, self.exchanges.values(), self.data ) - generic.move_edges(owner_boxes, self.exchanges.values(), self.data) if self.diagram._hide_direct_children: self.centerbox.children = [] @@ -327,7 +330,7 @@ def collector( derivator( diagram, data, - processor.made_boxes[diagram.target.uuid], + processor.made_boxes, ) return data @@ -466,7 +469,7 @@ def port_context_collector( def derive_from_functions( diagram: context.ContextDiagram, data: _elkjs.ELKInputData, - centerbox: _elkjs.ELKInputChild, + boxes: dict[str, _elkjs.ELKInputChild], ) -> None: """Derive Components from allocated functions of the context target. @@ -480,8 +483,7 @@ def derive_from_functions( inc, out = port_collector(fnc, diagram.type) ports.extend(inc + out) - context_box_ids = {child.id for child in data.children} - components: dict[str, cs.Component] = {} + derived_components: dict[str, cs.Component] = {} for port in ports: for fex in port.exchanges: if isinstance(port, fa.FunctionOutputPort): @@ -493,12 +495,12 @@ def derive_from_functions( derived_comp = getattr(fex, attr).owner.owner if ( derived_comp == diagram.target - or derived_comp.uuid in context_box_ids + or derived_comp.uuid in boxes ): continue - if derived_comp.uuid not in components: - components[derived_comp.uuid] = derived_comp + if derived_comp.uuid not in derived_components: + derived_components[derived_comp.uuid] = derived_comp except AttributeError: # No owner of owner. pass @@ -506,7 +508,11 @@ def derive_from_functions( # exchanges. Mixed means bidirectional. Just even out bidirectional # interfaces and keep flow direction of others. - for i, (uuid, derived_component) in enumerate(components.items(), 1): + centerbox = boxes[diagram.target.uuid] + i = 0 + for i, (uuid, derived_component) in enumerate( + derived_components.items(), 1 + ): box = makers.make_box( derived_component, no_symbol=diagram._display_symbols_as_boxes, @@ -529,9 +535,8 @@ def derive_from_functions( ) ) - data.children[0].height += ( - makers.PORT_PADDING - + (makers.PORT_SIZE + makers.PORT_PADDING) * len(components) // 2 + centerbox.height += ( + makers.PORT_PADDING + (makers.PORT_SIZE + makers.PORT_PADDING) * i // 2 ) diff --git a/capellambse_context_diagrams/context.py b/capellambse_context_diagrams/context.py index d92bf6c..0d5cace 100644 --- a/capellambse_context_diagrams/context.py +++ b/capellambse_context_diagrams/context.py @@ -393,10 +393,6 @@ def __len__(self) -> int: def _create_diagram(self, params: dict[str, t.Any]) -> cdiagram.Diagram: data = self.elk_input_data(params) assert not isinstance(data, tuple) - if not isinstance(self, ClassTreeDiagram) and has_single_child(data): - self._display_derived_interfaces = True - data = get_elkdata(self, params) - layout = try_to_layout(data) is_legend: bool = params.get("is_legend", False) # type: ignore[assignment] add_context(layout, is_legend) @@ -950,18 +946,6 @@ def calculate_label_position( return (x + width / 2, center_y, tspan_y) -def has_single_child(data: _elkjs.ELKInputData | _elkjs.ELKInputChild) -> bool: - """Checks if ``data`` has a single or no child.""" - if not data.children: - return True - - for child in data.children: - if not has_single_child(child): - return False - - return len(data.children) == 1 - - def _get_all_ports( node: _elkjs.ELKOutputNode, ref: cdiagram.Vector2D ) -> cabc.Iterator[tuple[cdiagram.Vector2D, _elkjs.ELKOutputPort]]: diff --git a/tests/test_context_diagrams.py b/tests/test_context_diagrams.py index 542a4f5..6c3d0fa 100644 --- a/tests/test_context_diagrams.py +++ b/tests/test_context_diagrams.py @@ -114,7 +114,7 @@ def test_context_diagrams_rerender_on_parameter_change( ), pytest.param( [ - (TEST_ACTOR_SIZING_UUID, 41, 41), + (TEST_ACTOR_SIZING_UUID, 40, 40), (TEST_HUMAN_ACTOR_SIZING_UUID, 43, 43), (TEST_CAP_SIZING_UUID, 141, 141), ], @@ -122,23 +122,23 @@ def test_context_diagrams_rerender_on_parameter_change( ), pytest.param( [ - ("e1e48763-7479-4f3a-8134-c82bb6705d58", 126, 201), - ("8df45b70-15cc-4d3a-99e4-593516392c5a", 154, 248), - ("74af6883-25a0-446a-80f3-656f8a490b11", 266, 435), + ("e1e48763-7479-4f3a-8134-c82bb6705d58", 126, 190), + ("8df45b70-15cc-4d3a-99e4-593516392c5a", 154, 234), + ("74af6883-25a0-446a-80f3-656f8a490b11", 266, 412), ], id="LogicalComponent", ), pytest.param( [ - ("0c06cc88-8c77-46f2-8542-c08b1e8edd18", 112, 177), - ("9f1e1875-9ead-4af2-b428-c390786a436a", 112, 177), + ("0c06cc88-8c77-46f2-8542-c08b1e8edd18", 112, 168), + ("9f1e1875-9ead-4af2-b428-c390786a436a", 112, 168), ], id="LogicalFunction", ), pytest.param( [ - ("6241d0c5-65d2-4c0b-b79c-a2a8ed7273f6", 37, 37), - ("344a405e-c7e5-4367-8a9a-41d3d9a27f81", 41, 41), + ("6241d0c5-65d2-4c0b-b79c-a2a8ed7273f6", 36, 36), + ("344a405e-c7e5-4367-8a9a-41d3d9a27f81", 40, 40), ("230c4621-7e0a-4d0a-9db2-d4ba5e97b3df", 42, 60), ], id="SystemComponent Root",