Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix state diagrams export #227

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions capellambse/aird/_edge_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import math
import typing as t

from lxml import etree

from capellambse import diagram, helpers

from . import _common as C
Expand Down Expand Up @@ -474,9 +476,7 @@ def state_transition_factory(seb: C.SemanticElementBuilder) -> diagram.Edge:
seb.melodyobjs[0], seb.melodyobjs[0].get("triggers", "")
)
label = ", ".join(
i.get("name", "(unnamed trigger)")
for i in triggers
if i is not None
_get_trigger_expression(i, seb) for i in triggers if i is not None
)

if guard := _guard_condition(seb, "guard"):
Expand All @@ -495,6 +495,29 @@ def state_transition_factory(seb: C.SemanticElementBuilder) -> diagram.Edge:
return edge


def _get_trigger_expression(
trigger: etree._Element, seb: C.SemanticElementBuilder
) -> str:
exprid = trigger.get("expression")
if not exprid:
return ""

change_event_type = trigger.attrib[helpers.ATT_XT]
if change_event_type.endswith("TimeEvent"):
kind = trigger.get("kind", "AT")
elif change_event_type.endswith("ChangeEvent"):
kind = trigger.get("kind", "WHEN")

constraint = seb.melodyloader.follow_link(seb.melodyobjs[0], exprid)
try:
spec = next(constraint.iterchildren("ownedSpecification"))
body = next(spec.iterchildren("bodies"))
except StopIteration:
return ""

return f"({kind}) {body.text}"


def sequence_link_factory(seb: C.SemanticElementBuilder) -> diagram.Edge:
"""Create a SequenceLink.

Expand Down
15 changes: 15 additions & 0 deletions capellambse/diagram/capstyle.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ class in the form::
"fill": RGB(255, 255, 203),
"stroke": RGB(255, 204, 102),
"text_fill": RGB(0, 0, 0),
"text_position": "always_top",
},
"Box.Requirement": { # ReqVP_Requirement
"fill": COLORS["light_purple"],
Expand Down Expand Up @@ -326,6 +327,7 @@ class in the form::
"ry": "10px",
"stroke": COLORS["_CAP_Class_Border_Brown"],
"text_fill": COLORS["black"],
"text_position": "always_top",
},
"Box.DataPkg": { # DT_DataPkg
"fill": [
Expand All @@ -339,6 +341,7 @@ class in the form::
"fill": COLORS["_CAP_Class_Brown"],
"stroke": COLORS["_CAP_Datatype_Border_Gray"],
"text_fill": COLORS["black"],
"text_position": "always_top",
},
"Box.ExchangeItem": { # DT_ExchangeItem
"fill": COLORS["_CAP_ExchangeItem_Pinkkish"],
Expand Down Expand Up @@ -439,6 +442,7 @@ class in the form::
"Box.LogicalActor": { # Logical Actors
"fill": [COLORS["_CAP_Actor_Blue_min"], COLORS["_CAP_Actor_Blue"]],
"stroke": COLORS["_CAP_Actor_Border_Blue"],
"text_anchor": "start",
"text_fill": COLORS["_CAP_Actor_Blue_label"],
},
"Box.LogicalComponent": { # LAB Logical Component
Expand All @@ -447,6 +451,7 @@ class in the form::
COLORS["_CAP_Component_Blue"],
],
"stroke": COLORS["_CAP_Component_Border_Blue"],
"text_anchor": "start",
"text_fill": COLORS["_CAP_Component_Label_Blue"],
},
"Box.LogicalHumanActor": { # LAB Logical Human Actor
Expand All @@ -455,6 +460,7 @@ class in the form::
COLORS["_CAP_Component_Blue"],
],
"stroke": COLORS["_CAP_Component_Border_Blue"],
"text_anchor": "start",
"text_fill": COLORS["_CAP_Component_Label_Blue"],
},
"Box.LogicalHumanComponent": { # LAB Logical Human Component
Expand All @@ -463,6 +469,7 @@ class in the form::
COLORS["_CAP_Component_Blue"],
],
"stroke": COLORS["_CAP_Component_Border_Blue"],
"text_anchor": "start",
"text_fill": COLORS["_CAP_Component_Label_Blue"],
},
"Box.LogicalFunction": { # LAB Logical Function
Expand Down Expand Up @@ -584,6 +591,8 @@ class in the form::
COLORS["_CAP_MSM_Mode_Gray"],
],
"stroke": COLORS["dark_gray"],
"text_align": "left",
"text_anchor": "start",
},
"Box.State": {
"fill": COLORS["_CAP_MSM_State_Gray_min"],
Expand All @@ -595,6 +604,8 @@ class in the form::
COLORS["_CAP_MSM_State_Gray"],
],
"stroke": COLORS["dark_gray"],
"text_align": "left",
"text_anchor": "start",
},
"Edge.StateTransition": {
"marker-end": "FineArrowMark",
Expand Down Expand Up @@ -653,6 +664,7 @@ class in the form::
"fill": COLORS["_CAP_Activity_Orange"],
"stroke": COLORS["_CAP_Activity_Border_Orange"],
"text_fill": COLORS["_CAP_xAB_Activity_Label_Orange"],
"text_position": "always_top",
},
"Box.OperationalActor": {
"fill": [
Expand Down Expand Up @@ -695,6 +707,7 @@ class in the form::
"Box.OperationalActivity": {
"fill": COLORS["_CAP_Activity_Orange"],
"stroke": COLORS["_CAP_Activity_Border_Orange"],
"text_position": "always_top",
},
"Edge.OperationalExchange": {
"marker-end": "FineArrowMark",
Expand All @@ -713,6 +726,7 @@ class in the form::
"ry": "10px",
"stroke": COLORS["_CAP_Activity_Border_Orange"],
"text_fill": COLORS["_CAP_xAB_Activity_Label_Orange"],
"text_position": "always_top",
},
"Box.OperationalProcess": {
"stroke": COLORS["black"],
Expand Down Expand Up @@ -751,6 +765,7 @@ class in the form::
],
"stroke": COLORS["_CAP_Node_Yellow_Border"],
"text_fill": COLORS["_CAP_Node_Yellow_Label"],
"text_position": "always_top",
},
"Box.PhysicalFunction": {
"fill": COLORS["_CAP_xAB_Function_Green"],
Expand Down
15 changes: 2 additions & 13 deletions capellambse/svg/decorations.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,18 @@
"""Default icon padding (left/right side)."""
feature_space = 24
"""Default margins/padding (top/bot and left/right) for feature text."""
text_margin = 5
"""Default margins/padding (left and right) for label text."""

function_ports = {"FIP", "FOP"}
directed_component_ports = {"CP_IN", "CP_OUT"}
component_ports = directed_component_ports | {"PP", "CP_INOUT", "CP_UNSET"}
all_ports = function_ports | component_ports
all_directed_ports = directed_component_ports | function_ports
start_aligned = {
"LogicalComponent",
"LogicalActor",
"LogicalHumanActor",
"LogicalHumanComponent",
}
only_icons = {"Requirement"}
DiagramClass = str
FaultyClass = str
PatchClass = str
always_top_label = {
"Class",
"Enumeration",
"Note",
"OperationalActivity",
"PhysicalComponent",
}
needs_feature_line = {"Class", "Enumeration"}


Expand Down
21 changes: 15 additions & 6 deletions capellambse/svg/drawing.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,12 @@ def add_rect(
)

if label:
text_anchor = (
"start" if class_ in decorations.start_aligned else "middle"
defaultstyles = diagram.get_style(
self.diagram_class, text_style._class
)
text_anchor = defaultstyles.get("text_anchor", "middle")
y_margin = None
if children or class_ in decorations.always_top_label:
if children or defaultstyles.get("text_position") == "always_top":
y_margin = 5

self._draw_box_label(
Expand Down Expand Up @@ -324,7 +325,10 @@ def _draw_label(
f"{builder.class_}Symbol" in decorations.deco_factories
and builder.icon
)
lines = render_hbounded_lines(builder, render_icon)
defaultstyles = diagram.get_style(
self.diagram_class, builder.labelstyle._class
)
lines = render_hbounded_lines(builder, defaultstyles, render_icon)
x, icon_x = get_label_position_x(builder, lines, render_icon)
y = get_label_position_y(builder, lines)
for line in lines.lines:
Expand Down Expand Up @@ -873,18 +877,23 @@ class LinesData(t.NamedTuple):


def render_hbounded_lines(
builder: LabelBuilder, render_icon: bool
builder: LabelBuilder, defaultstyles: dict[str, t.Any], render_icon: bool
) -> LinesData:
(
lines,
label_margin,
label_width,
max_text_width,
) = helpers.check_for_horizontal_overflow(
str(builder.label["text"]),
builder.label["width"],
decorations.icon_padding if render_icon else 0,
builder.icon_size if render_icon else 0,
)
if defaultstyles.get("text_align") == "left":
label_margin: int | float = decorations.text_margin
else:
label_margin = (builder.label["width"] - label_width) / 2

lines_to_render = helpers.check_for_vertical_overflow(
lines, builder.label["height"], max_text_width
)
Expand Down
3 changes: 1 addition & 2 deletions capellambse/svg/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ def check_for_horizontal_overflow(
label_width = text_width + icon_size + 2 * icon_padding
assert text_width <= max_text_width
assert label_width <= width
label_margin = (width - label_width) / 2
return (lines, label_margin, max_text_width)
return (lines, label_width, max_text_width)


def check_for_vertical_overflow(
Expand Down
Loading