diff --git a/capellambse/aird/__init__.py b/capellambse/aird/__init__.py index d62e07013..e683b1a02 100644 --- a/capellambse/aird/__init__.py +++ b/capellambse/aird/__init__.py @@ -213,7 +213,9 @@ def parse_diagram( def _element_from_xml(ebd: C.ElementBuilder) -> diagram.DiagramElement: """Construct a single diagram element from the model XML.""" - if ebd.data_element.get("element") is not None: + element = ebd.data_element.get("element") + tag = ebd.melodyloader[element].tag if element else None + if element is not None and tag != "ownedRepresentationDescriptors": factory = _semantic.from_xml else: factory = _visual.from_xml diff --git a/capellambse/aird/_visual.py b/capellambse/aird/_visual.py index a8cdd80a6..68494eb9f 100644 --- a/capellambse/aird/_visual.py +++ b/capellambse/aird/_visual.py @@ -65,7 +65,13 @@ def shape_factory(ebd: c.ElementBuilder) -> diagram.Box: assert ebd.target_diagram.styleclass is not None uid = ebd.data_element.attrib[c.ATT_XMID] - label = ebd.data_element.get("description", "") + element = ebd.data_element.get("element") + if element is not None: + label = ebd.melodyloader[element].attrib["name"] + description = ebd.data_element.get("description", "") + else: + label = ebd.data_element.get("description", "") + description = None parent = ebd.data_element.getparent() while parent.tag == "children": parent_uid = parent.attrib.get("element") or parent.attrib.get( @@ -97,14 +103,21 @@ def shape_factory(ebd: c.ElementBuilder) -> diagram.Box: int(layout.attrib.get("width", "0")), int(layout.attrib.get("height", "0")), ) - styleclass = ebd.data_element.attrib["type"] + + if element is not None: + styleclass = "RepresentationLink" + else: + styleclass = "Note" + styleoverrides = _styling.apply_visualelement_styles( ebd.target_diagram.styleclass, f"Box.{styleclass}", ebd.data_element ) + return diagram.Box( pos, size, label=label, + description=description, uuid=uid, parent=parent, styleclass=styleclass, diff --git a/capellambse/diagram/_diagram.py b/capellambse/diagram/_diagram.py index cbd7c4fdd..ef5f198eb 100644 --- a/capellambse/diagram/_diagram.py +++ b/capellambse/diagram/_diagram.py @@ -62,6 +62,7 @@ def __init__( size: diagram.Vec2ish, *, label: Box | str | None = None, + description: str | None = None, uuid: str | None = None, parent: Box | None = None, collapsed: bool = False, @@ -87,6 +88,8 @@ def __init__( Box' label text and contained children. label This box' label text. + description + Optional label text used only by Representation Links. uuid UUID of the semantic element this box represents. parent @@ -124,6 +127,7 @@ def __init__( self.minsize = minsize self.maxsize = maxsize self.label: Box | str | None = label + self.description: str | None = description self.collapsed: bool = collapsed self.features: cabc.MutableSequence[str] | None = features diff --git a/capellambse/diagram/_json_enc.py b/capellambse/diagram/_json_enc.py index 586d05350..8d6c14c5c 100644 --- a/capellambse/diagram/_json_enc.py +++ b/capellambse/diagram/_json_enc.py @@ -69,6 +69,8 @@ def __encode_box(o: diagram.Box) -> object: } if o.label is not None and not o.hidelabel: jsonobj["label"] = _encode_label(o.label) + if o.description is not None: + jsonobj["description"] = o.description if o.styleoverrides: jsonobj["style"] = _encode_styleoverrides(o.styleoverrides) if o.features: diff --git a/capellambse/diagram/capstyle.py b/capellambse/diagram/capstyle.py index 229e8c1a3..dc600f298 100644 --- a/capellambse/diagram/capstyle.py +++ b/capellambse/diagram/capstyle.py @@ -280,6 +280,11 @@ class in the form:: "stroke": RGB(255, 204, 102), "text_fill": RGB(0, 0, 0), }, + "Box.RepresentationLink": { + "fill": RGB(255, 255, 203), + "stroke": RGB(255, 204, 102), + "text_fill": RGB(0, 0, 0), + }, "Box.Requirement": { # ReqVP_Requirement "fill": COLORS["light_purple"], "stroke": COLORS["dark_purple"], diff --git a/capellambse/svg/decorations.py b/capellambse/svg/decorations.py index 9499c3226..204858236 100644 --- a/capellambse/svg/decorations.py +++ b/capellambse/svg/decorations.py @@ -36,6 +36,7 @@ "Class", "Enumeration", "Note", + "RepresentationLink", "OperationalActivity", "PhysicalComponent", } diff --git a/capellambse/svg/drawing.py b/capellambse/svg/drawing.py index 8c29eb705..37db32cb3 100644 --- a/capellambse/svg/drawing.py +++ b/capellambse/svg/drawing.py @@ -120,6 +120,7 @@ def add_rect( *, class_: str = "", label: LabelDict | None = None, + description: str | None = None, features: cabc.Sequence[str] = (), id_: str | None = None, children: bool = False, @@ -143,6 +144,21 @@ def add_rect( rect: shapes.Rect = self.__drawing.rect(**rectparams) grp.add(rect) + if description is not None and label is not None: + new_label: LabelDict = copy.deepcopy(label) + new_label["text"] = description + self._draw_box_label( + LabelBuilder( + new_label, + grp, + labelstyle=text_style, + class_=class_, + text_anchor="middle", + y_margin=None, + icon=False, + ) + ) + if features or class_ in decorations.needs_feature_line: self._draw_feature_line(rect, grp, rect_style) if features: @@ -589,6 +605,7 @@ def _draw_box( children_: cabc.Sequence[str] = (), features_: cabc.Sequence[str] = (), label_: str | LabelDict | None = None, + description_: str | None = None, id_: str, class_: str, obj_style: style.Styling, @@ -621,6 +638,7 @@ def _draw_box( class_=class_, id_=id_, label=label, + description=description_, features=features_, children=bool(children_), ) diff --git a/capellambse/svg/style.py b/capellambse/svg/style.py index 769ccbcb9..ee0819f81 100644 --- a/capellambse/svg/style.py +++ b/capellambse/svg/style.py @@ -21,6 +21,7 @@ "__GLOBAL__": ( "ErrorSymbol", "RequirementSymbol", + "RepresentationLinkSymbol", ), "Error": (), "Class Diagram Blank": ("ClassSymbol",), diff --git a/capellambse/svg/symbols.py b/capellambse/svg/symbols.py index 2c36117fe..a8f378ec7 100644 --- a/capellambse/svg/symbols.py +++ b/capellambse/svg/symbols.py @@ -1144,6 +1144,53 @@ def class_symbol(id_: str = "ClassSymbol") -> container.Symbol: return symb +@decorations.deco_factories +def representation_link_symbol( + id_: str = "RepresentationLinkSymbol", +) -> container.Symbol: + symb = container.Symbol(id=id_, viewBox="0 0 16 16") + grp = symb.add(container.Group(style="stroke-width:0.5;")) + grp.add( + shapes.Rect( + insert=(5.95, 2.96), + size=(4.98, 2.7), + rx=0.5, + style="fill:#d9d297;stroke:#a48a44;", + ) + ) + grp.add( + shapes.Rect( + insert=(1.66, 10.1), + size=(4.98, 2.7), + rx=0.5, + style="fill:#abc0c9;stroke:#557099;", + ) + ) + grp.add( + shapes.Rect( + insert=(10.12, 10.1), + size=(4.98, 2.7), + rx=0.5, + style="fill:#acbd57;stroke:#326f46;", + ) + ) + grp.add( + path.Path( + d="m 8.4526548,7.7355622 -4.3161119,-0.00711 0.00491," + "2.2573969 m 4.3112023,-2.250294 4.3161128,-0.00711 " + "-0.0049,2.2573968", + style="fill:none;stroke:#557099;", + ) + ) + grp.add( + path.Path( + d="m 8.5000114,5.7276095 0.00519,2.0502552", + style="fill:none;stroke:#557099;", + ) + ) + return symb + + @decorations.deco_factories def fine_arrow_mark( id_: str = "FineArrow", *, style: style_.Styling, **kw diff --git a/tests/data/melodymodel/6_0/Melody Model Test.aird b/tests/data/melodymodel/6_0/Melody Model Test.aird index 35b8c5439..5b672cf68 100644 --- a/tests/data/melodymodel/6_0/Melody Model Test.aird +++ b/tests/data/melodymodel/6_0/Melody Model Test.aird @@ -79,7 +79,7 @@ - +
@@ -146,7 +146,7 @@ - +
@@ -158,7 +158,7 @@ - + @@ -170,7 +170,7 @@ - + @@ -1975,16 +1975,27 @@ - - - + + + - - - + + + - - + + + + + + + + + + + + + @@ -2453,22 +2464,6 @@ - - - - - - - - - - - - - - - - @@ -2517,22 +2512,6 @@ - - - - - - - - - - - - - - - - @@ -2869,13 +2848,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -2918,7 +2929,7 @@ - + @@ -3022,14 +3033,20 @@ - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - + + + + + + + + + + + + + + @@ -3183,7 +3200,7 @@ - + @@ -3202,10 +3219,10 @@ - + - + @@ -3635,17 +3652,6 @@ - - - - - routingStyle - - - - - - @@ -3655,16 +3661,6 @@ - - - - - routingStyle - - - - - @@ -3711,6 +3707,25 @@ + + + + + + + + + + + + + + + + + + + @@ -5194,28 +5209,28 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -5265,21 +5280,21 @@ - - - + + + - - + + - - + + - - - - - + + + + + @@ -5308,8 +5323,8 @@ - - + + @@ -5324,7 +5339,7 @@ - + KEEP_LOCATION @@ -5368,11 +5383,11 @@ - - - - - + + + + + @@ -5466,31 +5481,11 @@ - - - - - - - - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - - - - routingStyle + + + - + @@ -8345,26 +8340,6 @@ - - - - - - - - - - - - - - - - - - - - @@ -8379,6 +8354,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -8413,6 +8410,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -8430,6 +8447,22 @@ + + + + + + + + + + + + + + + + @@ -8465,6 +8498,24 @@ + + + + + + + + + + + + + + + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO @@ -8505,29 +8556,6 @@ - - - - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - - KEEP_LOCATION - KEEP_SIZE - KEEP_RATIO - - - - - @@ -8573,6 +8601,39 @@ + + + + + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + + + + @@ -9245,7 +9306,7 @@ - + @@ -9305,7 +9366,7 @@ - + @@ -9382,7 +9443,7 @@ - + @@ -9393,7 +9454,7 @@ - + @@ -9404,7 +9465,7 @@ - + @@ -9464,7 +9525,7 @@ - + @@ -9479,7 +9540,7 @@ - + @@ -9492,6 +9553,32 @@ + + +
+
+ + + + + + + + + +
+ + + + + + + + + + + + @@ -9784,7 +9871,22 @@ - + + + + + + + + + + + + + + + + @@ -9857,7 +9959,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + italic @@ -10150,7 +10252,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -10191,36 +10293,39 @@ - + - - - + + + + - + - + + size routingStyle strokeColor - size - - + + + - + - + + size routingStyle strokeColor - size - - + + + @@ -10376,16 +10481,15 @@ - - + + + + + - - - -