Skip to content

Commit

Permalink
merge: Fix some issues in diagrams
Browse files Browse the repository at this point in the history
  • Loading branch information
Wuestengecko committed Jan 8, 2024
2 parents 5026c5d + 55e8cfa commit 1a1d604
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 103 deletions.
2 changes: 1 addition & 1 deletion capellambse/diagram/_styleclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def _physical_component(obj: model.ModelObject) -> str:
assert isinstance(obj, model.pa.PhysicalComponent)
styleclass = _generic_component(obj)
ptrn = re.compile("^(.*)(Component|Actor)$")
nature = obj.nature.name
nature = [obj.nature.name, ""][obj.nature.name == "UNSET"]
return ptrn.sub(rf"\1{nature.capitalize()}\2", styleclass)


Expand Down
21 changes: 21 additions & 0 deletions capellambse/diagram/capstyle.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ class in the form::
"_CAP_StateTransition_Color": RGB(0, 0, 0),
"_CAP_State_Gray": RGB(228, 228, 228),
"_CAP_Unit_LightBrown": RGB(214, 197, 171),
"_CAP_Unset_Gray": RGB(205, 205, 205),
"_CAP_Unset_Gray_min": RGB(234, 234, 234),
"_CAP_Value_LightBrown": RGB(254, 253, 250),
"_CAP_xAB_Activity_Label_Orange": RGB(91, 64, 64),
"_CAP_xAB_Function_Border_Green": RGB(9, 92, 46),
Expand Down Expand Up @@ -768,13 +770,32 @@ class in the form::
"stroke": COLORS["_CAP_Class_Border_Brown"],
},
"Box.PhysicalComponent": {
"fill": [
COLORS["_CAP_Unset_Gray_min"],
COLORS["_CAP_Unset_Gray"],
],
"stroke": COLORS["_CAP_Lifeline_Gray"],
"text_fill": COLORS["black"],
},
"Box.PhysicalNodeComponent": {
"fill": [
COLORS["_CAP_Node_Yellow_min"],
COLORS["_CAP_Node_Yellow"],
],
"stroke": COLORS["_CAP_Node_Yellow_Border"],
"text_fill": COLORS["_CAP_Node_Yellow_Label"],
},
"Box.PhysicalBehaviorComponent": {
"fill": [
COLORS["_CAP_Component_Blue_min"],
COLORS["_CAP_Component_Blue"],
],
"stroke": COLORS["_CAP_Actor_Border_Blue"],
"rx": "10px",
"ry": "10px",
"text_fill": COLORS["black"],
"text_font-style": "italic",
},
"Box.PhysicalFunction": {
"fill": COLORS["_CAP_xAB_Function_Green"],
"stroke": COLORS["_CAP_xAB_Function_Border_Green"],
Expand Down
101 changes: 63 additions & 38 deletions capellambse/svg/drawing.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
"""Debug flag to render helping lines."""
LABEL_ICON_PADDING = 2
"""Default padding between a label's icon and text."""
NUMBER = r"-?\d+(\.\d+)?"
RE_ROTATION = re.compile(
rf"rotate\((?P<angle>{NUMBER}),\s*(?P<x>{NUMBER}),\s*(?P<y>{NUMBER})\) "
f"(?P<tspan_y>{NUMBER})"
)


LabelDict = t.TypedDict(
Expand Down Expand Up @@ -130,6 +135,13 @@ def add_rect(
"class_": class_,
**transform,
}
if rx := getattr(rect_style, "rx", None):
rectparams["rx"] = rx
delattr(rect_style, "rx")
if ry := getattr(rect_style, "ry", None):
rectparams["ry"] = ry
delattr(rect_style, "ry")

if rect_style:
rectparams["style"] = rect_style[""]

Expand Down Expand Up @@ -261,29 +273,11 @@ def _draw_feature_text(
)

def _draw_box_label(self, builder: LabelBuilder) -> container.Group:
"""Draw label text on given object and return label position."""
_, _, _, y_margin = self._draw_label(builder)

if DEBUG:
assert builder.label is not None
assert y_margin is not None

self._draw_circle(
center_=(builder.label["x"], builder.label["y"]),
radius_=3,
obj_style=style.Styling(
self.diagram_class,
"Box",
fill="rgb(239, 41, 41)",
),
text_style=None,
)

"""Draw label text on given object and return the label's group."""
self._draw_label(builder)
return builder.group

def _draw_label(
self, builder: LabelBuilder
) -> tuple[float, float, float, float | int | None]:
def _draw_label(self, builder: LabelBuilder) -> None:
assert isinstance(builder.label["x"], (int, float))
assert isinstance(builder.label["y"], (int, float))
assert isinstance(builder.label["width"], (int, float))
Expand All @@ -294,35 +288,60 @@ def _draw_label(
"insert": (builder.label["x"], builder.label["y"]),
"text_anchor": builder.text_anchor,
"dominant_baseline": "middle",
"style": builder.labelstyle[""],
}
if "class" in builder.label:
textattrs["class_"] = builder.label["class"]

if transform := getattr(builder.labelstyle, "transform", None):
if match := re.search(RE_ROTATION, transform):
if match.group("angle") != "-90":
raise NotImplementedError(
"Angles other than -90 are not supported"
)

x = float(match.group("x"))
y = float(match.group("y"))
if new_y := match.group("tspan_y"):
y = float(new_y)
transform = transform.replace(f" {new_y}", "")

textattrs["transform"] = transform
delattr(builder.labelstyle, "transform")

if labelstyle := builder.labelstyle[""]:
textattrs["style"] = labelstyle

text = self.__drawing.text(**textattrs)
builder.group.add(text)

render_icon = (
f"{builder.class_}Symbol" in decorations.deco_factories
and builder.icon
)
lines = render_hbounded_lines(builder, 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:
text.add(
svgtext.TSpan(
insert=(x, y), text=line, **{"xml:space": "preserve"}
if transform:
params = {
"insert": (x, y),
"text": str(builder.label["text"]),
"xml:space": "preserve",
}
text.add(svgtext.TSpan(**params))
else:
lines = render_hbounded_lines(builder, 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:
text.add(
svgtext.TSpan(
insert=(x, y), text=line, **{"xml:space": "preserve"}
)
)
)
y += lines.line_height
y += lines.line_height

if render_icon:
icon_pos = get_label_icon_position(
builder, lines.text_height, icon_x
)
self.add_label_image(builder, icon_pos)

return x, lines.text_height, lines.max_line_width, builder.y_margin
if render_icon:
icon_pos = get_label_icon_position(
builder, lines.text_height, icon_x
)
self.add_label_image(builder, icon_pos)

def add_label_image(
self, builder: LabelBuilder, pos: tuple[float, float]
Expand All @@ -331,6 +350,12 @@ def add_label_image(
if builder.class_ is None:
builder.class_ = "Error"

if "Human" in builder.class_ and "StickFigure" not in self.deco_cache:
self.__drawing.defs.add(
decorations.deco_factories["StickFigureSymbol"]()
)
self.deco_cache.append("StickFigure")

if builder.class_ not in self.deco_cache:
self.__drawing.defs.add(
decorations.deco_factories[f"{builder.class_}Symbol"]()
Expand Down
112 changes: 48 additions & 64 deletions capellambse/svg/symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,36 @@ def _make_icon_frame(id_: str, color: str) -> container.Symbol:
return symb


def _make_physical_component_symbol(id_: str, color: str) -> container.Symbol:
symb = _make_icon_frame(id_=id_, color=color)
symb.add(
path.Path(
d=(
"m 57.589299,33.276194 c 0,1.410473 -0.249326,2.72122"
" -0.747979,3.93224 -0.484407,1.19676 -1.168275,2.236807"
" -2.051603,3.12014 -1.097038,1.097033 -2.393538,1.923373"
" -3.889499,2.47902 -1.495959,0.541393 -3.383718,0.81209"
" -5.663277,0.81209 h -4.231429 v 11.86083 h -4.23143 v"
" -31.82122 h 8.633826 c 1.909129,0 3.526191,0.163843"
" 4.851185,0.49153 1.324993,0.31344 2.500391,0.812097"
" 3.526192,1.49597 1.211016,0.812087 2.14421,1.82364"
" 2.799583,3.03466 0.669621,1.211013 1.004431,2.742593"
" 1.004431,4.59474 z m -4.402396,0.10687 c 0,-1.09704"
" -0.192339,-2.051607 -0.577016,-2.8637 -0.384675,-0.812093"
" -0.968811,-1.47459 -1.752408,-1.98749 -0.683868,-0.441667"
" -1.467467,-0.755107 -2.350798,-0.94032 -0.869079,-0.19946"
" -1.97324,-0.29919 -3.312483,-0.29919 h -4.188686 v 12.71566"
" h 3.568931 c 1.709669,0 3.098776,-0.149593 4.167321,-0.44878"
" 1.068543,-0.31344 1.937623,-0.80497 2.607242,-1.47459"
" 0.669621,-0.683867 1.139781,-1.403353 1.410478,-2.15846"
" 0.284946,-0.7551 0.427419,-1.60281 0.427419,-2.54313 z"
),
style="fill: #000;stroke-width: 0.1;",
)
)
return symb


@decorations.deco_factories
def entity_symbol(id_: str = "EntitySymbol") -> container.Symbol:
symb = _make_icon_frame(id_=id_, color="#a5b9c8")
Expand Down Expand Up @@ -461,37 +491,40 @@ def initial_pseudo_state_symbol(id_="InitialPseudoStateSymbol"):
def logical_function_symbol(
id_: str = "LogicalFunctionSymbol",
) -> container.Symbol:
return function_symbol(id_, label="LF")
return function_symbol(id_, gradient_url="LF_green", label="LF")


@decorations.deco_factories
def system_function_symbol(
id_: str = "SystemFunctionSymbol",
) -> container.Symbol:
return function_symbol(id_, label="SF")
return function_symbol(id_, gradient_url="SF_green", label="SF")


@decorations.deco_factories
def physical_function_symbol(
id_: str = "PhysicalFunctionSymbol",
) -> container.Symbol:
return function_symbol(id_, label="PF")
return function_symbol(id_, gradient_url="PF_green", label="PF")


@decorations.deco_factories
def operational_activity_symbol(
id_: str = "OperationalActivitySymbol",
) -> container.Symbol:
return function_symbol(id_, ("#f4901d", "white"), "OA")
return function_symbol(
id_, colors=("#f4901d", "white"), gradient_url="OA_orange", label="OA"
)


@decorations.deco_factories
def function_symbol(
id_: str = "FunctionSymbol",
colors: tuple[str, str] = ("#6CB35B", "#ffffff"),
gradient_url="green",
label: str = "F",
) -> container.Symbol:
grad = _make_lgradient("green", stop_colors=colors, end=(1, 0))
grad = _make_lgradient(gradient_url, stop_colors=colors, end=(1, 0))
symb = _make_function_symbol(id_, gradient=grad)
symb.add(
text.Text(
Expand Down Expand Up @@ -1117,7 +1150,7 @@ def _brown_oval(id_: str) -> container.Symbol:
def system_human_actor_symbol(
id_: str = "SystemHumanActorSymbol",
) -> container.Symbol:
return stick_figure_symbol(id_)
return standalone_stick_figure_symbol(id_)


@decorations.deco_factories
Expand Down Expand Up @@ -1195,35 +1228,7 @@ def representation_link_symbol(
def physical_behavior_component_symbol(
id_: str = "PhysicalBehaviorComponentSymbol",
) -> container.Symbol:
symb = _make_icon_frame(id_=id_, color="#a5bde7")
letter = container.Group(transform="scale(0.7,0.7) translate(15, 18)")
letter.add(
path.Path(
d=(
"m 57.589299,33.276194 c 0,1.410473 -0.249326,2.72122"
" -0.747979,3.93224 -0.484407,1.19676 -1.168275,2.236807"
" -2.051603,3.12014 -1.097038,1.097033 -2.393538,1.923373"
" -3.889499,2.47902 -1.495959,0.541393 -3.383718,0.81209"
" -5.663277,0.81209 h -4.231429 v 11.86083 h -4.23143 v"
" -31.82122 h 8.633826 c 1.909129,0 3.526191,0.163843"
" 4.851185,0.49153 1.324993,0.31344 2.500391,0.812097"
" 3.526192,1.49597 1.211016,0.812087 2.14421,1.82364"
" 2.799583,3.03466 0.669621,1.211013 1.004431,2.742593"
" 1.004431,4.59474 z m -4.402396,0.10687 c 0,-1.09704"
" -0.192339,-2.051607 -0.577016,-2.8637 -0.384675,-0.812093"
" -0.968811,-1.47459 -1.752408,-1.98749 -0.683868,-0.441667"
" -1.467467,-0.755107 -2.350798,-0.94032 -0.869079,-0.19946"
" -1.97324,-0.29919 -3.312483,-0.29919 h -4.188686 v 12.71566"
" h 3.568931 c 1.709669,0 3.098776,-0.149593 4.167321,-0.44878"
" 1.068543,-0.31344 1.937623,-0.80497 2.607242,-1.47459"
" 0.669621,-0.683867 1.139781,-1.403353 1.410478,-2.15846"
" 0.284946,-0.7551 0.427419,-1.60281 0.427419,-2.54313 z"
),
style="white-space: pre;inline-size: 15.4494;stroke-width: 0.1;",
)
)
symb.add(letter)
return symb
return _make_physical_component_symbol(id_, color="#a5bde7")


@decorations.deco_factories
Expand Down Expand Up @@ -1284,35 +1289,14 @@ def physical_behavior_human_actor_symbol(
def physical_node_component_symbol(
id_: str = "PhysicalNodeComponentSymbol",
) -> container.Symbol:
symb = _make_icon_frame(id_=id_, color="#ffff00")
letter = container.Group(transform="scale(0.7,0.7) translate(15, 18)")
letter.add(
path.Path(
d=(
"m 57.589299,33.276194 c 0,1.410473 -0.249326,2.72122"
" -0.747979,3.93224 -0.484407,1.19676 -1.168275,2.236807"
" -2.051603,3.12014 -1.097038,1.097033 -2.393538,1.923373"
" -3.889499,2.47902 -1.495959,0.541393 -3.383718,0.81209"
" -5.663277,0.81209 h -4.231429 v 11.86083 h -4.23143 v"
" -31.82122 h 8.633826 c 1.909129,0 3.526191,0.163843"
" 4.851185,0.49153 1.324993,0.31344 2.500391,0.812097"
" 3.526192,1.49597 1.211016,0.812087 2.14421,1.82364"
" 2.799583,3.03466 0.669621,1.211013 1.004431,2.742593"
" 1.004431,4.59474 z m -4.402396,0.10687 c 0,-1.09704"
" -0.192339,-2.051607 -0.577016,-2.8637 -0.384675,-0.812093"
" -0.968811,-1.47459 -1.752408,-1.98749 -0.683868,-0.441667"
" -1.467467,-0.755107 -2.350798,-0.94032 -0.869079,-0.19946"
" -1.97324,-0.29919 -3.312483,-0.29919 h -4.188686 v 12.71566"
" h 3.568931 c 1.709669,0 3.098776,-0.149593 4.167321,-0.44878"
" 1.068543,-0.31344 1.937623,-0.80497 2.607242,-1.47459"
" 0.669621,-0.683867 1.139781,-1.403353 1.410478,-2.15846"
" 0.284946,-0.7551 0.427419,-1.60281 0.427419,-2.54313 z"
),
style="white-space: pre;inline-size: 15.4494;stroke-width: 0.1;",
)
)
symb.add(letter)
return symb
return _make_physical_component_symbol(id_, color="#ffff00")


@decorations.deco_factories
def physical_component_symbol(
id_: str = "PhysicalComponentSymbol",
) -> container.Symbol:
return _make_physical_component_symbol(id_, color="#dbe6f4")


@decorations.deco_factories
Expand Down

0 comments on commit 1a1d604

Please sign in to comment.