Skip to content

Commit

Permalink
refactor: Dynamically create decorations
Browse files Browse the repository at this point in the history
  • Loading branch information
dahbar committed Nov 7, 2023
1 parent 28555bc commit d3100e5
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 160 deletions.
37 changes: 21 additions & 16 deletions capellambse/svg/drawing.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def __init__(self, metadata: generate.DiagramMetadata):

self.__drawing = drawing.Drawing(**superparams)
self.diagram_class = metadata.class_
self.add_static_decorations()
self.deco_cache: list[str] = []
self.add_backdrop(pos=metadata.pos, size=metadata.size)

self.obj_cache: dict[str | None, t.Any] = {}
Expand Down Expand Up @@ -101,14 +101,6 @@ def add_backdrop(
)
self.__drawing.add(self.__backdrop)

def add_static_decorations(self) -> None:
static_deco = style.STATIC_DECORATIONS[
"__GLOBAL__"
] + style.STATIC_DECORATIONS.get(self.diagram_class or "", ())

for name in static_deco:
self.__drawing.defs.add(decorations.deco_factories[name]())

def __repr__(self) -> str:
return self.__drawing._repr_svg_()

Expand Down Expand Up @@ -338,6 +330,12 @@ def add_label_image(
if builder.class_ is None:
builder.class_ = "Error"

if builder.class_ not in self.deco_cache:
self.__drawing.defs.add(
decorations.deco_factories[f"{builder.class_}Symbol"]()
)
self.deco_cache.append(builder.class_)

builder.group.add(
self.__drawing.use(
href=f"#{builder.class_}Symbol",
Expand Down Expand Up @@ -383,15 +381,19 @@ def add_port(
) -> container.Group:
grp = self.__drawing.g(class_=f"Box {class_}", id_=id_)
if class_ in decorations.all_directed_ports:
port_id = "#ErrorSymbol"
port_id = "ErrorSymbol"
if class_ in decorations.function_ports:
port_id = "#PortSymbol"
port_id = "PortSymbol"
elif class_ in decorations.component_ports:
port_id = "#ComponentPortSymbol"
port_id = "ComponentPortSymbol"

if port_id not in self.deco_cache:
self.__drawing.defs.add(decorations.deco_factories[port_id]())
self.deco_cache.append(port_id)

grp.add(
self.__drawing.use(
href=port_id,
href=f"#{port_id}",
insert=pos,
size=size,
transform=self.get_port_transformation(
Expand Down Expand Up @@ -461,6 +463,7 @@ def draw_object(self, obj: cabc.Mapping[str, t.Any]) -> None:
class_: str = style_type + (
f".{mobj['class']}" if "class" in mobj else ""
)

my_styles: dict[str, t.Any] = {
**capstyle.get_style(self.diagram_class, class_),
**mobj.get("style", {}),
Expand Down Expand Up @@ -555,6 +558,11 @@ def _draw_symbol(
assert isinstance(label_, (dict, type(None)))
pos = (x_ + 0.5, y_ + 0.5)
size = (width_, height_)
if class_ not in self.deco_cache:
self.__drawing.defs.add(
decorations.deco_factories[f"{class_}Symbol"]()
)
self.deco_cache.append(class_)

if class_ in decorations.all_ports:
grp = self.add_port(
Expand Down Expand Up @@ -742,9 +750,6 @@ def _draw_edge_label(
text_anchor: str = "start",
y_margin: int | float,
) -> container.Group:
class_ = (
style.get_symbol_styleclass(class_, self.diagram_class) or class_
)
if f"{class_}Symbol" in decorations.deco_factories:
additional_space = (
decorations.icon_size + 2 * decorations.icon_padding
Expand Down
135 changes: 0 additions & 135 deletions capellambse/svg/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,141 +16,6 @@
RE_ELMCLASS = re.compile(r"^([A-Z][a-z_]*)(\.[A-Za-z][A-Za-z0-9_]*)?(:.+)?$")
CUSTOM_STYLE_ATTRS = {"marker-fill"}

# TODO refactor to dynamically determine needed decorations
STATIC_DECORATIONS: dict[str, tuple[str, ...]] = {
"__GLOBAL__": (
"ErrorSymbol",
"RequirementSymbol",
"RepresentationLinkSymbol",
"StickFigureSymbol",
),
"Error": (),
"Class Diagram Blank": ("ClassSymbol",),
"Functional Chain Description": (
"AndControlNodeSymbol",
"IterateControlNodeSymbol",
"OrControlNodeSymbol",
"FunctionalExchangeSymbol",
"FunctionSymbol",
),
"Logical Architecture Blank": (
"ComponentPortSymbol",
"LogicalActorSymbol",
"LogicalComponentSymbol",
"LogicalFunctionSymbol",
"LogicalHumanActorSymbol",
"LogicalHumanComponentSymbol",
"PortSymbol",
"FunctionalExchangeSymbol",
"ComponentExchangeSymbol",
),
"Logical Data Flow Blank": (
"LogicalFunctionSymbol",
"PortSymbol",
"FunctionalExchangeSymbol",
),
"Missions Capabilities Blank": (
"CapabilitySymbol",
"MissionSymbol",
"SystemActorSymbol",
"SystemComponentSymbol",
"SystemHumanActorSymbol",
),
"Mode State Machine": (
"FinalStateSymbol",
"InitialPseudoStateSymbol",
"ModeSymbol",
"StateSymbol",
"TerminatePseudoStateSymbol",
),
"Operational Capabilities Blank": (
"EntitySymbol",
"OperationalActorBoxSymbol",
"OperationalCapabilitySymbol",
),
"Operational Entity Blank": (
"EntitySymbol",
"OperationalActivitySymbol",
"OperationalActorBoxSymbol",
"OperationalExchangeSymbol",
),
"Operational Entity Breakdown": ("OperationalActorSymbol", "EntitySymbol"),
"Operational Process Description": (
"AndControlNodeSymbol",
"IterateControlNodeSymbol",
"OperationalActivitySymbol",
"OrControlNodeSymbol",
"OperationalExchangeSymbol",
),
"Operational Activity Interaction Blank": (
"OperationalActivitySymbol",
"OperationalExchangeSymbol",
),
"Physical Architecture Blank": (
"PhysicalLinkSymbol",
"PhysicalBehaviorComponentSymbol",
"PhysicalBehaviorHumanComponentSymbol",
"PhysicalBehaviorActorSymbol",
"PhysicalBehaviorHumanActorSymbol",
"PhysicalNodeComponentSymbol",
"PhysicalNodeHumanComponentSymbol",
"PhysicalNodeActorSymbol",
"PhysicalNodeHumanActorSymbol",
"ComponentExchangeSymbol",
"ComponentPortSymbol",
"PortSymbol",
),
"Physical Data Flow Blank": (
"FunctionalExchangeSymbol",
"PhysicalFunctionSymbol",
"PortSymbol",
),
"System Architecture Blank": (
"ComponentExchangeSymbol",
"ComponentPortSymbol",
"FunctionalExchangeSymbol",
"PhysicalLinkSymbol",
"SystemActorSymbol",
"SystemComponentSymbol",
"SystemFunctionSymbol",
"SystemHumanActorSymbol",
"PortSymbol",
),
"System Data Flow Blank": (
"FunctionalExchangeSymbol",
"PortSymbol",
"SystemFunctionSymbol",
),
"Contextual Capability": (
"CapabilitySymbol",
"MissionSymbol",
"SystemActorSymbol",
"SystemHumanActorSymbol",
),
}
MODIFY_STYLECLASS = {"FunctionalExchange"}


def get_symbol_styleclass(style: str | None, dstyle: str | None) -> str | None:
if (
style not in MODIFY_STYLECLASS
or dstyle not in STATIC_DECORATIONS
or style in STATIC_DECORATIONS[dstyle]
):
return None

capitals = (dstyle or "").split(" ", maxsplit=1)
assert capitals
layer = capitals[0]
if style.startswith(layer):
return None

scapitals = re.findall(r"[A-Z][^A-Z]*", style)
symbol = f'{layer}{"".join(scapitals[1:])}'
if f"{symbol}Symbol" in STATIC_DECORATIONS[dstyle]:
return symbol
return None


class Styling:
"""Container for style attributes of svg objects.
Expand Down
10 changes: 1 addition & 9 deletions tests/test_svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,7 @@

import capellambse
import capellambse.diagram
from capellambse.svg import (
SVGDiagram,
decorations,
generate,
helpers,
style,
symbols,
)
from capellambse.svg import SVGDiagram, decorations, generate, helpers, symbols

TEST_LAB = "[LAB] Wizzard Education"
TEST_DIAGS = [
Expand All @@ -34,7 +27,6 @@
"[CC] Capability",
"[PAB] A sample vehicle arch",
]
TEST_DECO = set(style.STATIC_DECORATIONS.keys()) - {"__GLOBAL__"}
FREE_SYMBOLS = {
"OperationalCapabilitySymbol",
"AndControlNodeSymbol",
Expand Down

0 comments on commit d3100e5

Please sign in to comment.