From 1b72a33fe57ba68eb5f23519fc08e683e3a0821e Mon Sep 17 00:00:00 2001 From: ewuerger Date: Mon, 13 Jun 2022 12:10:04 +0200 Subject: [PATCH 01/19] model: Add Capability extends and includes attributes Add `.includes` and `.extends` attributes that link to Capabilities which the source Capability includes/extends-to via an Abstract exchange. Refactor generalizations: - Rename `.inheritance` to `.generalizes` - Add `AbstractCapabilityGeneralization` class as intermediate like in the case of includes and extensions. --- capellambse/model/crosslayer/interaction.py | 35 +- capellambse/model/layers/ctx.py | 19 +- capellambse/model/layers/oa.py | 19 +- .../melodymodel/5_2/Melody Model Test.aird | 12546 ++++++++-------- tests/test_model_layers.py | 39 + 5 files changed, 6363 insertions(+), 6295 deletions(-) diff --git a/capellambse/model/crosslayer/interaction.py b/capellambse/model/crosslayer/interaction.py index b8949ee1b..25c472c2d 100644 --- a/capellambse/model/crosslayer/interaction.py +++ b/capellambse/model/crosslayer/interaction.py @@ -5,8 +5,6 @@ XT_CAP2PROC = "org.polarsys.capella.core.data.interaction:FunctionalChainAbstractCapabilityInvolvement" XT_CAP2ACT = "org.polarsys.capella.core.data.interaction:AbstractFunctionAbstractCapabilityInvolvement" -XT_CAP_GEN = "org.polarsys.capella.core.data.interaction:AbstractCapabilityGeneralization" -XT_SCENARIO = "org.polarsys.capella.core.data.interaction:Scenario" XT_CAP_REAL = ( "org.polarsys.capella.core.data.interaction:AbstractCapabilityRealization" ) @@ -15,3 +13,36 @@ @c.xtype_handler(None) class Scenario(c.GenericElement): """A scenario that holds instance roles.""" + + +class Exchange(c.GenericElement): + """An abstract Exchange.""" + + source = c.ParentAccessor(c.GenericElement) + + +@c.xtype_handler(None) +class AbstractCapabilityExtend(Exchange): + """An AbstractCapabilityExtend.""" + + _xmltag = "extends" + + target = c.AttrProxyAccessor(c.GenericElement, "extended") + + +@c.xtype_handler(None) +class AbstractCapabilityInclude(Exchange): + """An AbstractCapabilityInclude.""" + + _xmltag = "includes" + + target = c.AttrProxyAccessor(c.GenericElement, "included") + + +@c.xtype_handler(None) +class AbstractCapabilityGeneralization(Exchange): + """An AbstractCapabilityGeneralization.""" + + _xmltag = "superGeneralizations" + + target = c.AttrProxyAccessor(c.GenericElement, "super") diff --git a/capellambse/model/layers/ctx.py b/capellambse/model/layers/ctx.py index e65a44922..707959d3c 100644 --- a/capellambse/model/layers/ctx.py +++ b/capellambse/model/layers/ctx.py @@ -102,6 +102,15 @@ class Capability(c.GenericElement): _xmltag = "ownedCapabilities" + extends = c.ProxyAccessor( + interaction.AbstractCapabilityExtend, aslist=c.ElementList + ) + includes = c.ProxyAccessor( + interaction.AbstractCapabilityInclude, aslist=c.ElementList + ) + generalizes = c.ProxyAccessor( + interaction.AbstractCapabilityGeneralization, aslist=c.ElementList + ) owned_chains = c.ProxyAccessor(fa.FunctionalChain, aslist=c.ElementList) involved_functions = c.ProxyAccessor( SystemFunction, @@ -210,16 +219,6 @@ class SystemAnalysis(crosslayer.BaseArchitectureLayer): ) # type: ignore[assignment] -c.set_accessor( - Capability, - "inheritance", - c.ProxyAccessor( - Capability, - interaction.XT_CAP_GEN, - follow="super", - aslist=c.ElementList, - ), -) c.set_accessor( SystemFunction, "owner", diff --git a/capellambse/model/layers/oa.py b/capellambse/model/layers/oa.py index 9b75f92e5..0ab9720b5 100644 --- a/capellambse/model/layers/oa.py +++ b/capellambse/model/layers/oa.py @@ -69,6 +69,15 @@ class OperationalCapability(c.GenericElement): _xmltag = "ownedOperationalCapabilities" + extends = c.ProxyAccessor( + interaction.AbstractCapabilityExtend, aslist=c.ElementList + ) + includes = c.ProxyAccessor( + interaction.AbstractCapabilityInclude, aslist=c.ElementList + ) + generalizes = c.ProxyAccessor( + interaction.AbstractCapabilityGeneralization, aslist=c.ElementList + ) involved_activities = c.ProxyAccessor( OperationalActivity, interaction.XT_CAP2ACT, @@ -246,16 +255,6 @@ class OperationalAnalysis(crosslayer.BaseArchitectureLayer): ) # type: ignore[assignment] -c.set_accessor( - OperationalCapability, - "inheritance", - c.ProxyAccessor( - OperationalCapability, - interaction.XT_CAP_GEN, - follow="super", - aslist=c.ElementList, - ), -) c.set_accessor( OperationalActivity, "packages", diff --git a/tests/data/melodymodel/5_2/Melody Model Test.aird b/tests/data/melodymodel/5_2/Melody Model Test.aird index 39eb9d149..601140a2d 100644 --- a/tests/data/melodymodel/5_2/Melody Model Test.aird +++ b/tests/data/melodymodel/5_2/Melody Model Test.aird @@ -4,4032 +4,4032 @@ Melody%20Model%20Test.afm Melody%20Model%20Test.capella - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + -
-
+
+
- - + + -
-
+
+
- - + + - + - + - - + + - - + + - - + + - - + + - + - - - - + + + + - - + + - - + + - - + + - - + + - - + + - - + + - + -
-
+
+
- - + + - - + + - - + + -
-
+
+
- - + + - - + + - + - + - + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - + + + - + - + - + - - - + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - + + + - + - + - + - - - + + + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - + + - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - - - - - + + + + + + - + - + - - + + - - + + - + - + - + - - + + - - + + - + - - + + - - + + - - - + + + - - + + - + - - + + - - - + + + - - + + - - + + - - + + - + - + - - - + + + - - + + - + - - + + - - - + + + - - + + - - + + - - + + - + - + - - - + + + - - + + - + - - + + - - - + + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - - - + + + - - + + - - + + - - + + - + - + - - - + + + - - + + - + - - + + - - - + + + - - + + - - + + - - + + - + - + - - - + + + - - + + - + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + -
+
- + - + - - - - + + + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - - - - - + + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - - + + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - - + + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - - + + + - + - + - - + + - + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - - + + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - - + + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - - - - - + + + + + + - + - + - + - + - + - - - + + + - - + + - - + + - - + + - + - + - - - + + + - - + + - - + + - - + + - + - + - - - + + + - - + + - - + + - - + + - + - + - - - + + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - + - + - - - + + + - - + + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - - + + - + - - + + - - + + - + - - - - - - + + + + + + - + - + - + - + - - + + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - - + + - + - + - + - + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO labelSize - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO uid - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + routingStyle - - + + - + - - + + routingStyle - - - + + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - - + + + - + - - + + routingStyle - - - + + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - - + + + - + - - + + routingStyle - - - + + + - + - - + + routingStyle - - - + + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + - - + + - + - - + + routingStyle - - - + + + - + - - + + - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - + + + - + - + - + - - - + + + - + - + - + - - - + + + - + - + - + - - - - - + + + + + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - - + + - + - - + + - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - + + - - + + - + - - - - - - + + + + + + - + - + - + - + @@ -4038,534 +4038,534 @@ - + - - + + - - + + - + - + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - + + strokeColor size routingStyle - - + + - + - - + + strokeColor size routingStyle - - + + - + - - + + strokeColor size routingStyle - - + + - + - - + + color - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO color - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO borderColor borderSize - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO borderColor borderSize - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO labelAlignment - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO @@ -4573,39 +4573,39 @@ borderColor borderSize labelColor - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO labelFormat strike_through - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO labelFormat underline - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO @@ -4615,8091 +4615,8091 @@ borderSize labelColor italic - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO labelAlignment - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO borderColor borderSize - + - + - - + + strokeColor size routingStyle - - + + - + - - + + strokeColor size - - + + - + - - + + strokeColor size - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - - + + - + - - + + - + strokeColor size - - + + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - + routingStyle - - + + - + - - - - - - + + + + + + - + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + routingStyle - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - - - - - + + + + + + - + - + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - - + + + - - - + + + - + - + - + - + - - - + + + - - - + + + - + - + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - - + + + - - - + + + - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - - + + - - + + - - + + - - + + - + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - - - + + + + - - - + + + - - - + + + - + - + - - - + + + - + - + - + - + - + - + - - - - + + + + - - - + + + - - - + + + - + - + - - - + + + - + - + - + - + - + - + - - - - + + + + - - - + + + - - - + + + - + - + - - - + + + - + - + - + - + - + - + - - - - - - + + + + + + routingStyle - - + + - + - - - - - - + + + + + + - - + + - + - - - - - - + + + + + + - - + + - + - - - + + + - + - + - - - + + + - + - + - - + + - - + + - + - - - + + + - + - + - - - + + + - + - + - - - + + + - + - + - - - + + + - + - + - - - + + + - + - + - - - - - - + + + + + + - - + + - + - - - - - - + + + + + + - + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - + - + - - - - + + + + - + - + - - - - + + + + - + - + - + - + - + - + - - - - - + + + + + - - - - + + + + - - - + + + - + - + - - - - + + + + - + - + - + - + - + - + - - - - - + + + + + - - - - + + + + - - - - + + + + - + - + - - - + + + - + - + - - - + + + - + - + - - - + + + - + - + - + - + - + - + - - - - - + + + + + - - - - + + + + - - - - + + + + - + - + - - - - + + + + - + - + - + - + - + - + - - - - + + + + - - - + + + - - - + + + - + - + - + - + - + - + - - - - - - + + + + + + - - + + - + - - - - - - + + + + + + - - + + - + - - - - - - + + + + + + - - + + - + - - - - - + + + + + - - + + - + - - - - - + + + + + - - + + - + - - - - - + + + + + - - + + - + - - - + + + - + - + - - - + + + - + - + - - + + - - + + - + - - - - - - + + + + + + - + - + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - - - + + + + - - - + + + - - - + + + - + - + - + - + - + - + - - - - + + + + - - - + + + - - - + + + - + - + - + - + - + - + - - - - - - + + + + + + - + - + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - - - - + + + + + - - - - + + + + - - - - + + + + - + - + - + - + - + - + - - - - - - + + + + + + - + - + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - - - + + + + - - - + + + - - - + + + - + - + - + - + - + - + - - - - - + + + + + - + - + - - - - - - + + + + + + - + - + - - + + - - + + - + - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + - + - + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - - - - + + + + + - - - - + + + + - - - - + + + + - + - + - + - + - + - + - - - - - - + + + + + + - + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - - - - - + + + + + + - + - - + + - - + + - + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - - + + - + - + - + - + - - - - - - + + + + + + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - - + + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - - + + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - - + + - - - + + + - + - + backgroundColor - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - + - + - - + + - - - + + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + lineStyle - - - - - - - - - - - - - + + + + + + + + + + + + + - + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - + - - + + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + routingStyle - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - - + + bold - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - - - - - + + + + + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - - + + + + + + - + - + - + - - - - - - + + + + + + - + - + - + - - - - - - + + + + + + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + routingStyle - - + + - + - - - + + + routingStyle - - + + - + - - - + + + routingStyle - - + + - + - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - + + - - + + - + - - + + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - - - - - + + + + + + - + - + - + - + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - + - + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - - + + + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - + - + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - + + - - + + - + - - + + routingStyle strokeColor size - - + + - + - - + + routingStyle strokeColor size - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + routingStyle - - + + - + - - + + - - + + - + - - + + routingStyle - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + color - + - + - - + + strokeColor size - - + + - + - - + + - - + + - + - - + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - - + + italic - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + routingStyle - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + routingStyle - - + + - + - - + + - + - + - - + + italic - + - + - - + + - + - + - - + + - + - + - - + + routingStyle - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + routingStyle - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + italic - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO italic - + - + - - + + - + - + - - + + - + - + - - + + - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - - - + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - - + + - + - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - - - + + + - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - - + + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - - - + + + - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - - + + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - + - + - - + + - + - + - - - + + + - - + + - + - - + + labelColor - - - - - - - - - - - - + + + + + + + + + + + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - - + + + - - - - - - - - - - - - - + + + + + + + + + + + + + @@ -12707,193 +12707,193 @@ - - + + - + - - + + - + - - + + - - + + - - + + - + - - + + - + - - + + - - + + - + - - + + - - + + - - + + - + - + - + - + - - - - - + + + + + - + - + - + - - - - - + + + + + - + - + - + - + - - + + - - + + - - - + + + - + - + backgroundColor - + - + - - + + - - - + + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - + - + - - + + KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + - - + + - + - + - - + + - - - + + + - + - - + + lineStyle - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/tests/test_model_layers.py b/tests/test_model_layers.py index 4e0c68726..a3584d738 100644 --- a/tests/test_model_layers.py +++ b/tests/test_model_layers.py @@ -165,6 +165,45 @@ def test_Capabilities_conditions_markup_escapes(model: MelodyModel): assert markupsafe.escape(elm.precondition.specification) == expected +@pytest.mark.parametrize( + "uuid,trg_uuid,attr_name", + [ + pytest.param( + "3b83b4ba-671a-4de8-9c07-a5c6b1d3c422", + "83d1334f-6180-46c4-a80d-6839341df688", + "extends", + id="[Operational] Extends", + ), + pytest.param( + "53c58b24-3938-4d6a-b84a-bb9bff355a41", + "83d1334f-6180-46c4-a80d-6839341df688", + "includes", + id="[Operational] Includes", + ), + pytest.param( + "9390b7d5-598a-42db-bef8-23677e45ba06", + "562c5128-5acd-45cc-8b49-1d8d686f450a", + "extends", + id="[System] Extends", + ), + pytest.param( + "9390b7d5-598a-42db-bef8-23677e45ba06", + "9390b7d5-598a-42db-bef8-23677e45ba06", + "includes", + id="[System] Includes", + ), + ], +) +def test_Capability_exchange( + model_5_2: MelodyModel, uuid: str, trg_uuid: str, attr_name: str +): + cap = model_5_2.by_uuid(uuid) + expected = model_5_2.by_uuid(trg_uuid) + exchange_targets = (ex.target for ex in getattr(cap, attr_name)) + + assert expected in exchange_targets + + @pytest.mark.parametrize( "uuid,real_uuid,real_attr", [ From 81026c3e7a404d13d6961ce3f703ca1ebe2cea92 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Mon, 13 Jun 2022 13:51:08 +0200 Subject: [PATCH 02/19] tests: Add test cases for Capability generalizations --- tests/test_model_layers.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_model_layers.py b/tests/test_model_layers.py index a3584d738..5d398cd25 100644 --- a/tests/test_model_layers.py +++ b/tests/test_model_layers.py @@ -180,6 +180,12 @@ def test_Capabilities_conditions_markup_escapes(model: MelodyModel): "includes", id="[Operational] Includes", ), + pytest.param( + "30bd2c21-b170-40d3-b476-7c2016b58031", + "84adfa3f-11c9-43d1-801c-b1535fcba802", + "generalizes", + id="[Operational] Generalizes", + ), pytest.param( "9390b7d5-598a-42db-bef8-23677e45ba06", "562c5128-5acd-45cc-8b49-1d8d686f450a", @@ -192,6 +198,12 @@ def test_Capabilities_conditions_markup_escapes(model: MelodyModel): "includes", id="[System] Includes", ), + pytest.param( + "9390b7d5-598a-42db-bef8-23677e45ba06", + "562c5128-5acd-45cc-8b49-1d8d686f450a", + "generalizes", + id="[System] Generalizes", + ), ], ) def test_Capability_exchange( From 483f0a60a7620ef1233111a5a3fe6033f90f448f Mon Sep 17 00:00:00 2001 From: ewuerger Date: Tue, 14 Jun 2022 13:04:32 +0200 Subject: [PATCH 03/19] svg.symbols: Reduce GeneralizationMark size --- capellambse/svg/symbols.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/capellambse/svg/symbols.py b/capellambse/svg/symbols.py index 954fbad42..864244b17 100644 --- a/capellambse/svg/symbols.py +++ b/capellambse/svg/symbols.py @@ -1064,10 +1064,10 @@ def generalization_mark( ) -> container.Marker: style.fill = "#fff" return _make_marker( - (14, 8), - (15, 15), + (7, 4), + (7.5, 7.5), id_=id_, - d="M 0.1275,15 15,7.5 0,0 Z", + d="M 0.1275,7.5 7.5,3.75 0,0 Z", style=style, **kw, ) From 2d69cbc46f3a30a840ce621475d3010ea65f6b72 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Tue, 14 Jun 2022 13:05:01 +0200 Subject: [PATCH 04/19] aird.capstyle: Add missing stroke defaults for [OCB] --- capellambse/aird/capstyle.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/capellambse/aird/capstyle.py b/capellambse/aird/capstyle.py index b1765720e..f0848713f 100644 --- a/capellambse/aird/capstyle.py +++ b/capellambse/aird/capstyle.py @@ -559,6 +559,7 @@ class in the form:: }, "Edge.AbstractCapabilityExtend": { "marker-end": "FineArrowMark", + "stroke": COLORS["black"], }, "Edge.AbstractCapabilityGeneralization": { "marker-end": "GeneralizationMark", @@ -575,6 +576,7 @@ class in the form:: "Edge.Entity": {}, "Edge.EntityOperationalCapabilityInvolvement": { "marker-end": "FineArrowMark", + "stroke": COLORS["black"], }, "Edge.OperationalActor": {}, }, From 87363d1c28b767691b5e5fecaf4b66de5c98aadb Mon Sep 17 00:00:00 2001 From: ewuerger Date: Wed, 15 Jun 2022 13:37:34 +0200 Subject: [PATCH 05/19] svg.drawing: Refactor label drawing - Add LabelBuilder dataclass to reduce parameters to one for _draw_label. - Remove obsolete _draw_annotation --- capellambse/svg/drawing.py | 278 +++++++++++++++++++------------------ 1 file changed, 142 insertions(+), 136 deletions(-) diff --git a/capellambse/svg/drawing.py b/capellambse/svg/drawing.py index 33ed39280..121e5bc14 100644 --- a/capellambse/svg/drawing.py +++ b/capellambse/svg/drawing.py @@ -5,6 +5,7 @@ from __future__ import annotations import collections.abc as cabc +import dataclasses import logging import os import re @@ -36,6 +37,18 @@ ) +@dataclasses.dataclass +class LabelBuilder: + label: LabelDict | None + group: container.Group + labelstyle: style.Styling + class_: str | None = None + y_margin: int | float | None = None + text_anchor: str = "start" + icon: bool = True + icon_size: float | int = decorations.icon_size + + # FIXME: refactor this, so a Drawing contains an "svg drawing". Always prefer composition over inheritance. class Drawing(drawing.Drawing): """The main container that stores all svg elements.""" @@ -128,12 +141,14 @@ def add_rect( y_margin = 5 self._draw_box_label( - label, - grp, - labelstyle=text_style, - class_=class_, - text_anchor=text_anchor, - y_margin=y_margin, + LabelBuilder( + label, + grp, + labelstyle=text_style, + class_=class_, + text_anchor=text_anchor, + y_margin=y_margin, + ) ) elif class_ in decorations.only_icons: icon_size = 50 @@ -141,7 +156,12 @@ def add_rect( pos[0] + (size[0] - icon_size / 4) / 2, pos[1] + (decorations.feature_space - 15) / 2, ) - self.add_label_image(grp, class_, labelpos, icon_size) + self.add_label_image( + LabelBuilder( + label, grp, text_style, class_=class_, icon_size=icon_size + ), + labelpos, + ) if DEBUG: helping_lines = self._draw_rect_helping_lines(pos, size) @@ -195,46 +215,29 @@ def _draw_feature_text( ), } self._draw_box_label( - label, - group, - class_=class_, - labelstyle=labelstyle, - y_margin=7, - icon=False, + LabelBuilder( + label, + group, + class_=class_, + labelstyle=labelstyle, + y_margin=7, + icon=False, + ) ) - def _draw_box_label( - self, - label: LabelDict, - group: container.Group, - *, - class_: str, - labelstyle: style.Styling, - text_anchor: str = "start", - y_margin: int | float | None, - icon: bool = True, - icon_size: float | int = decorations.icon_size, - ) -> container.Group: + def _draw_box_label(self, builder: LabelBuilder) -> container.Group: """Draw label text on given object and return calculated label position.""" - x, text_height, _, y_margin = self._draw_label( - label, - group, - class_=class_, - labelstyle=labelstyle, - text_anchor=text_anchor, - y_margin=y_margin, - icon=icon, - icon_size=icon_size, - ) + x, text_height, _, y_margin = self._draw_label(builder) if DEBUG: - debug_y = int(label["y"]) + y_margin + assert builder.label is not None + debug_y = int(builder.label["y"]) + y_margin debug_y1 = ( - int(label["y"]) - + (int(label["height"]) - decorations.icon_size) / 2 + int(builder.label["y"]) + + (int(builder.label["height"]) - decorations.icon_size) / 2 ) x = ( - int(label["x"]) + int(builder.label["x"]) + decorations.icon_size + 2 * decorations.icon_padding ) @@ -245,10 +248,10 @@ def _draw_box_label( bbox: LabelDict = { "x": x - if text_anchor == "start" - else x - int(label["width"]) / 2, + if builder.text_anchor == "start" + else x - int(builder.label["width"]) / 2, "y": debug_y if debug_y <= debug_y1 else debug_y1, - "width": label["width"], + "width": builder.label["width"], "height": debug_height, } labelstyle = style.Styling( @@ -257,9 +260,11 @@ def _draw_box_label( stroke="rgb(239, 41, 41)", fill="none", ) - self._draw_label_bbox(bbox, group, "Box", obj_style=labelstyle) + self._draw_label_bbox( + bbox, builder.group, "Box", obj_style=labelstyle + ) self._draw_circle( - center_=(label["x"], label["y"]), + center_=(builder.label["x"], builder.label["y"]), radius_=3, obj_style=style.Styling( self.diagram_class, @@ -269,52 +274,47 @@ def _draw_box_label( text_style=None, ) - return group + return builder.group def _draw_label( - self, - label: LabelDict, - group: container.Group, - *, - class_: str, - labelstyle: style.Styling, - text_anchor: str = "start", - icon: bool = True, - icon_size: float | int = decorations.icon_size, - y_margin: int | float | None, + self, builder: LabelBuilder ) -> tuple[float, float, float, float | int]: - assert isinstance(label["x"], (int, float)) - assert isinstance(label["y"], (int, float)) - assert isinstance(label["width"], (int, float)) - assert isinstance(label["height"], (int, float)) - assert "text" not in label or isinstance(label["text"], str) + assert builder.label is not None + assert isinstance(builder.label["x"], (int, float)) + assert isinstance(builder.label["y"], (int, float)) + assert isinstance(builder.label["width"], (int, float)) + assert isinstance(builder.label["height"], (int, float)) + assert "text" not in builder.label or isinstance( + builder.label["text"], str + ) text = self.text( text="", - insert=(label["x"], label["y"]), - class_=label["class"], - text_anchor=text_anchor, + insert=(builder.label["x"], builder.label["y"]), + class_=builder.label["class"], + text_anchor=builder.text_anchor, dominant_baseline="middle", - style=labelstyle[""], + style=builder.labelstyle[""], + ) + builder.group.add(text) + max_text_width = builder.label["width"] + render_icon = ( + f"{builder.class_}Symbol" in decorations.deco_factories + and builder.icon ) - group.add(text) - render_icon = False - max_text_width = label["width"] - if f"{class_}Symbol" in decorations.deco_factories and icon: - render_icon = True ( lines, label_margin, max_text_width, ) = helpers.check_for_horizontal_overflow( - str(label["text"]), - label["width"], + str(builder.label["text"]), + builder.label["width"], decorations.icon_padding if render_icon else 0, - icon_size if render_icon else 0, + builder.icon_size if render_icon else 0, ) lines_to_render = helpers.check_for_vertical_overflow( - lines, label["height"], max_text_width + lines, builder.label["height"], max_text_width ) line_height = max( j for _, j in map(chelpers.extent_func, lines_to_render) @@ -324,24 +324,24 @@ def _draw_label( w for w, _ in map(chelpers.extent_func, lines_to_render) ) assert max_text_width >= max_line_width - if text_anchor == "start": + if builder.text_anchor == "start": x = ( - label["x"] + builder.label["x"] + label_margin - + (icon_size + decorations.icon_padding) * render_icon + + (builder.icon_size + decorations.icon_padding) * render_icon ) - icon_x = label["x"] + label_margin + icon_x = builder.label["x"] + label_margin else: - x = label["x"] + label["width"] / 2 - icon_x = x - max_line_width / 2 - icon_size + x = builder.label["x"] + builder.label["width"] / 2 + icon_x = x - max_line_width / 2 - builder.icon_size dominant_baseline_adjust = ( chelpers.extent_func(lines_to_render[0])[1] / 2 ) - if y_margin is None: - y_margin = (label["height"] - text_height) / 2 + if builder.y_margin is None: + builder.y_margin = (builder.label["height"] - text_height) / 2 - y = label["y"] + y_margin + dominant_baseline_adjust + y = builder.label["y"] + builder.y_margin + dominant_baseline_adjust for line in lines_to_render: text.add( svgtext.TSpan( @@ -351,30 +351,33 @@ def _draw_label( y += line_height if render_icon: - icon_y = label["y"] + y_margin + (text_height - icon_size) / 2 - if icon_x < label["x"] - 2 and label["class"] != "Annotation": - icon_x = label["x"] - 2 + icon_y = ( + builder.label["y"] + + builder.y_margin + + (text_height - builder.icon_size) / 2 + ) + if ( + icon_x < builder.label["x"] - 2 + and builder.label["class"] != "Annotation" + ): + icon_x = builder.label["x"] - 2 - self.add_label_image(group, class_, (icon_x, icon_y), icon_size) + self.add_label_image(builder, (icon_x, icon_y)) - return x, text_height, max_line_width, y_margin + return x, text_height, max_line_width, builder.y_margin def add_label_image( - self, - group: container.Group, - class_: str | None, - pos: tuple[float, float], - icon_size: float | int = decorations.icon_size, + self, builder: LabelBuilder, pos: tuple[float, float] ) -> None: """Add label svgobject to given group.""" - if class_ is None: - class_ = "Error" + if builder.class_ is None: + builder.class_ = "Error" - group.add( + builder.group.add( self.use( - href=f"#{class_}Symbol", + href=f"#{builder.class_}Symbol", insert=pos, - size=(icon_size, icon_size), + size=(builder.icon_size, builder.icon_size), ) ) @@ -449,13 +452,15 @@ def add_port( if label is not None: self._draw_label( - label, - grp, - class_="Annotation", - labelstyle=text_style, - text_anchor="middle", - y_margin=0, - icon=False, + LabelBuilder( + label, + grp, + class_="Annotation", + labelstyle=text_style, + text_anchor="middle", + y_margin=0, + icon=False, + ) ) return grp @@ -600,29 +605,19 @@ def _draw_symbol( self.add(grp) if label_: - self._draw_annotation( - grp=grp, label_=label_, class_=class_, text_style=text_style + label_["class"] = "Annotation" + self._draw_label( + LabelBuilder( + label_, + grp or self.elements[-1], + labelstyle=text_style, + class_=class_, + y_margin=0, + text_anchor="middle", + icon=False, + ) ) - def _draw_annotation( - self, - *, - grp: container.Group = None, - label_: LabelDict, - class_: str, - text_style: style.Styling, - ) -> None: - label_["class"] = "Annotation" - self._draw_label( - label_, - grp or self.elements[-1], - class_=class_, - labelstyle=text_style, - text_anchor="middle", - y_margin=0, - icon=False, - ) - def _draw_box( self, *, @@ -639,7 +634,7 @@ def _draw_box( obj_style: style.Styling, text_style: style.Styling, **kw: t.Any, - ) -> None: + ) -> container.Group: del context_ # FIXME Add context to SVG del kw # Dismiss additional info from json, for e.g.: ports_ pos = (x_ + 0.5, y_ + 0.5) @@ -669,7 +664,7 @@ def _draw_box( features=features_, children=bool(children_), ) - self.add(grp) + return self.add(grp) def _draw_box_symbol( self, @@ -687,7 +682,7 @@ def _draw_box_symbol( ) -> None: del kw - self._draw_box( + grp = self._draw_box( x_=x_, y_=y_, width_=width_, @@ -697,8 +692,17 @@ def _draw_box_symbol( obj_style=obj_style, text_style=text_style, ) - self._draw_annotation( - label_=label_, class_=class_, text_style=text_style + label_["class"] = "Annotation" + self._draw_label( + LabelBuilder( + label_, + grp or self.elements[-1], + labelstyle=text_style, + class_=class_, + y_margin=0, + text_anchor="middle", + icon=False, + ) ) def _draw_circle( @@ -765,12 +769,14 @@ def _draw_edge_label( label["x"] -= additional_space / 2 self._draw_label( - label, - group, - class_=class_, - labelstyle=labelstyle, - text_anchor=text_anchor, - y_margin=y_margin, + LabelBuilder( + label, + group, + labelstyle=labelstyle, + class_=class_, + y_margin=y_margin, + text_anchor=text_anchor, + ) ) return group From 5192016b6fe6151eeb6f912f8c8627ffdd23fafc Mon Sep 17 00:00:00 2001 From: ewuerger Date: Wed, 15 Jun 2022 18:24:10 +0200 Subject: [PATCH 06/19] svg.drawing: Refactor _draw_label Add render_hbounded_lines. --- capellambse/svg/drawing.py | 83 ++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/capellambse/svg/drawing.py b/capellambse/svg/drawing.py index 121e5bc14..edba051f4 100644 --- a/capellambse/svg/drawing.py +++ b/capellambse/svg/drawing.py @@ -287,7 +287,6 @@ def _draw_label( assert "text" not in builder.label or isinstance( builder.label["text"], str ) - text = self.text( text="", insert=(builder.label["x"], builder.label["y"]), @@ -297,64 +296,44 @@ def _draw_label( style=builder.labelstyle[""], ) builder.group.add(text) - max_text_width = builder.label["width"] + render_icon = ( f"{builder.class_}Symbol" in decorations.deco_factories and builder.icon ) + lines = render_hbounded_lines(builder, render_icon) - ( - lines, - label_margin, - 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, - ) - lines_to_render = helpers.check_for_vertical_overflow( - lines, builder.label["height"], max_text_width - ) - line_height = max( - j for _, j in map(chelpers.extent_func, lines_to_render) - ) - text_height = line_height * len(lines_to_render) - max_line_width = max( - w for w, _ in map(chelpers.extent_func, lines_to_render) - ) - assert max_text_width >= max_line_width if builder.text_anchor == "start": x = ( builder.label["x"] - + label_margin + + lines.margin + (builder.icon_size + decorations.icon_padding) * render_icon ) - icon_x = builder.label["x"] + label_margin + icon_x = builder.label["x"] + lines.margin else: x = builder.label["x"] + builder.label["width"] / 2 - icon_x = x - max_line_width / 2 - builder.icon_size + icon_x = x - lines.max_line_width / 2 - builder.icon_size - dominant_baseline_adjust = ( - chelpers.extent_func(lines_to_render[0])[1] / 2 - ) + dominant_baseline_adjust = chelpers.extent_func(lines.lines[0])[1] / 2 if builder.y_margin is None: - builder.y_margin = (builder.label["height"] - text_height) / 2 + builder.y_margin = ( + builder.label["height"] - lines.text_height + ) / 2 y = builder.label["y"] + builder.y_margin + dominant_baseline_adjust - for line in lines_to_render: + for line in lines.lines: text.add( svgtext.TSpan( insert=(x, y), text=line, **{"xml:space": "preserve"} ) ) - y += line_height + y += lines.line_height if render_icon: icon_y = ( builder.label["y"] + builder.y_margin - + (text_height - builder.icon_size) / 2 + + (lines.text_height - builder.icon_size) / 2 ) if ( icon_x < builder.label["x"] - 2 @@ -364,7 +343,7 @@ def _draw_label( self.add_label_image(builder, (icon_x, icon_y)) - return x, text_height, max_line_width, builder.y_margin + return x, lines.text_height, lines.max_line_width, builder.y_margin def add_label_image( self, builder: LabelBuilder, pos: tuple[float, float] @@ -862,3 +841,39 @@ def _draw_rect_helping_lines( ) ) return lines + + +class LinesData(t.NamedTuple): + lines: list[str] + line_height: float + text_height: float + margin: float + max_line_width: float + + +def render_hbounded_lines( + builder: LabelBuilder, render_icon: bool +) -> LinesData: + assert builder.label is not None + ( + lines, + label_margin, + 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, + ) + lines_to_render = helpers.check_for_vertical_overflow( + lines, builder.label["height"], max_text_width + ) + line_height = max(j for _, j in map(chelpers.extent_func, lines_to_render)) + text_height = line_height * len(lines_to_render) + max_line_width = max( + w for w, _ in map(chelpers.extent_func, lines_to_render) + ) + assert max_text_width >= max_line_width + return LinesData( + lines_to_render, line_height, text_height, label_margin, max_line_width + ) From 1504e3040e34ebd47fe028786f039e8464d7fbf0 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Thu, 16 Jun 2022 09:28:13 +0200 Subject: [PATCH 07/19] svg.decorations: Add missing docstrings --- capellambse/svg/decorations.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/capellambse/svg/decorations.py b/capellambse/svg/decorations.py index e736dccd2..85f0870d2 100644 --- a/capellambse/svg/decorations.py +++ b/capellambse/svg/decorations.py @@ -12,8 +12,11 @@ logger = logging.getLogger(__name__) icon_size = 20 +"""Default icon size.""" icon_padding = 2 +"""Default icon padding(right-side).""" feature_space = 24 +"""Default space for feature text.""" function_ports = {"FIP", "FOP"} directed_component_ports = {"CP_IN", "CP_OUT"} From a333dc0c2f9cd9847fbd3e402f08767ea37ccd37 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Thu, 16 Jun 2022 09:29:44 +0200 Subject: [PATCH 08/19] svg.drawing: Refactor _draw_label Outsource essential functions which results in shorter function and more readability. --- capellambse/svg/drawing.py | 124 +++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 47 deletions(-) diff --git a/capellambse/svg/drawing.py b/capellambse/svg/drawing.py index edba051f4..6bc6e5e58 100644 --- a/capellambse/svg/drawing.py +++ b/capellambse/svg/drawing.py @@ -21,6 +21,9 @@ LOGGER = logging.getLogger(__name__) DEBUG = "CAPELLAMBSE_SVG_DEBUG" in os.environ +"""Debug flag to render helping lines.""" +LABEL_ICON_PADDING = 2 +"""Default (right)-padding for label icons.""" LabelDict = t.TypedDict( @@ -35,19 +38,7 @@ }, total=False, ) - - -@dataclasses.dataclass -class LabelBuilder: - label: LabelDict | None - group: container.Group - labelstyle: style.Styling - class_: str | None = None - y_margin: int | float | None = None - text_anchor: str = "start" - icon: bool = True - icon_size: float | int = decorations.icon_size - +"""Stores relevant data of labels.""" # FIXME: refactor this, so a Drawing contains an "svg drawing". Always prefer composition over inheritance. class Drawing(drawing.Drawing): @@ -156,6 +147,14 @@ def add_rect( pos[0] + (size[0] - icon_size / 4) / 2, pos[1] + (decorations.feature_space - 15) / 2, ) + label = { # XXX: Instead of allowing None > more asserts + "x": labelpos[0], + "y": labelpos[1], + "width": 0.0, + "height": 0.0, + "text": "", + "class": "", + } self.add_label_image( LabelBuilder( label, grp, text_style, class_=class_, icon_size=icon_size @@ -231,6 +230,7 @@ def _draw_box_label(self, builder: LabelBuilder) -> container.Group: if DEBUG: assert builder.label is not None + assert y_margin is not None debug_y = int(builder.label["y"]) + y_margin debug_y1 = ( int(builder.label["y"]) @@ -278,8 +278,7 @@ def _draw_box_label(self, builder: LabelBuilder) -> container.Group: def _draw_label( self, builder: LabelBuilder - ) -> tuple[float, float, float, float | int]: - assert builder.label is not None + ) -> tuple[float, float, float, float | int | None]: assert isinstance(builder.label["x"], (int, float)) assert isinstance(builder.label["y"], (int, float)) assert isinstance(builder.label["width"], (int, float)) @@ -302,25 +301,8 @@ def _draw_label( and builder.icon ) lines = render_hbounded_lines(builder, render_icon) - - if builder.text_anchor == "start": - x = ( - builder.label["x"] - + lines.margin - + (builder.icon_size + decorations.icon_padding) * render_icon - ) - icon_x = builder.label["x"] + lines.margin - else: - x = builder.label["x"] + builder.label["width"] / 2 - icon_x = x - lines.max_line_width / 2 - builder.icon_size - - dominant_baseline_adjust = chelpers.extent_func(lines.lines[0])[1] / 2 - if builder.y_margin is None: - builder.y_margin = ( - builder.label["height"] - lines.text_height - ) / 2 - - y = builder.label["y"] + builder.y_margin + dominant_baseline_adjust + 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( @@ -330,18 +312,10 @@ def _draw_label( y += lines.line_height if render_icon: - icon_y = ( - builder.label["y"] - + builder.y_margin - + (lines.text_height - builder.icon_size) / 2 + icon_pos = get_label_icon_position( + builder, lines.text_height, icon_x ) - if ( - icon_x < builder.label["x"] - 2 - and builder.label["class"] != "Annotation" - ): - icon_x = builder.label["x"] - 2 - - self.add_label_image(builder, (icon_x, icon_y)) + self.add_label_image(builder, icon_pos) return x, lines.text_height, lines.max_line_width, builder.y_margin @@ -582,7 +556,6 @@ def _draw_symbol( ) self.add(grp) - if label_: label_["class"] = "Annotation" self._draw_label( @@ -843,6 +816,18 @@ def _draw_rect_helping_lines( return lines +@dataclasses.dataclass +class LabelBuilder: + label: LabelDict + group: container.Group + labelstyle: style.Styling + class_: str | None = None + y_margin: int | float | None = None + text_anchor: str = "start" + icon: bool = True + icon_size: float | int = decorations.icon_size + + class LinesData(t.NamedTuple): lines: list[str] line_height: float @@ -854,7 +839,6 @@ class LinesData(t.NamedTuple): def render_hbounded_lines( builder: LabelBuilder, render_icon: bool ) -> LinesData: - assert builder.label is not None ( lines, label_margin, @@ -877,3 +861,49 @@ def render_hbounded_lines( return LinesData( lines_to_render, line_height, text_height, label_margin, max_line_width ) + + +def get_label_position_x( + builder: LabelBuilder, lines: LinesData, render_icon: bool +) -> tuple[float, float]: + """Return x-coordinate of label-text and icon.""" + if builder.text_anchor == "start": + x = ( + builder.label["x"] + + lines.margin + + (builder.icon_size + decorations.icon_padding) * render_icon + ) + return x, builder.label["x"] + lines.margin + x = builder.label["x"] + builder.label["width"] / 2 + return x, x - lines.max_line_width / 2 - builder.icon_size + + +def get_label_position_y(builder: LabelBuilder, lines: LinesData) -> float: + """Return y-coordinate of label-text.""" + dominant_baseline_adjust = chelpers.extent_func(lines.lines[0])[1] / 2 + if builder.y_margin is None: + builder.y_margin = (builder.label["height"] - lines.text_height) / 2 + return builder.label["y"] + builder.y_margin + dominant_baseline_adjust + + +def get_label_icon_position( + builder: LabelBuilder, text_height: int | float, icon_x: int | float +) -> tuple[float, float]: + """Return icon position for labels. + + Determines mainly y-coordinate and adjusts x-coordinate if + label-class does not equal `Annotation` and x-coordinate + overflows into label-text with `decorations.icon_padding`. + """ + assert builder.y_margin is not None + icon_y = ( + builder.label["y"] + + builder.y_margin + + (text_height - builder.icon_size) / 2 + ) + if ( + icon_x < builder.label["x"] - decorations.icon_padding + and builder.label["class"] != "Annotation" + ): + icon_x = builder.label["x"] - decorations.icon_padding + return icon_x, icon_y From 69d0e774577014ba5c7ea95d2fb28e7fc90c1db6 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Thu, 16 Jun 2022 15:57:46 +0200 Subject: [PATCH 09/19] svg.generate: Add filename parameter to save_drawing --- capellambse/svg/generate.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/capellambse/svg/generate.py b/capellambse/svg/generate.py index 41acf4014..8c7c45593 100644 --- a/capellambse/svg/generate.py +++ b/capellambse/svg/generate.py @@ -108,7 +108,11 @@ def from_json_path(cls, path: str | os.PathLike) -> SVGDiagram: def draw_object(self, obj: ContentsDict) -> None: self.drawing.draw_object(obj) - def save_drawing(self, pretty: bool = False, indent: int = 2) -> None: + def save_drawing( + self, pretty: bool = False, indent: int = 2, filename: str = "" + ) -> None: + if filename: + self.drawing.filename = filename self.drawing.save(pretty=pretty, indent=indent) def to_string(self) -> str: From 648a90d3c97a2943a1bd466b709d959ea0cf28f9 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Fri, 17 Jun 2022 11:25:58 +0200 Subject: [PATCH 10/19] model.diagram: Refactor filters attribute Add filters to class attributes on `AbstractDiagram`. --- capellambse/model/diagram.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/capellambse/model/diagram.py b/capellambse/model/diagram.py index f7079a0ea..d934b4ed2 100644 --- a/capellambse/model/diagram.py +++ b/capellambse/model/diagram.py @@ -6,7 +6,7 @@ import abc import base64 -import collections as cabc +import collections.abc as cabc import importlib.metadata as imm import logging import operator @@ -65,6 +65,8 @@ class AbstractDiagram(metaclass=abc.ABCMeta): * The only top-level element in the diagram **OR** * The element which is considered to be the "element of interest". """ + filters: cabc.MutableSet[str] + """Return a set of currently activated filters on this diagram.""" _model: capellambse.MelodyModel _render: aird.Diagram @@ -346,7 +348,7 @@ def type(self) -> modeltypes.DiagramType: return modeltypes.DiagramType.UNKNOWN @property - def filters(self) -> t.MutableSet[str]: + def filters(self) -> cabc.MutableSet[str]: # type: ignore[override] """Return a set of currently activated filters on this diagram.""" return aird.ActiveFilters(self._model, self) From 26ea45641cd3c59f2a0d296bb771422832cc5adb Mon Sep 17 00:00:00 2001 From: ewuerger Date: Mon, 20 Jun 2022 07:39:30 +0200 Subject: [PATCH 11/19] model: Add Involvements and exploitations interaction: Add `AbstractInvolvement` and `AbstractFunctionAbstractCapabilityInvolvement` fa: `FunctionalChainInvolvementLink` inherits from `AbstractInvolvement` oa: Add `EntityOperationalCapabilityInvolvement` and `.entity_involvements` on `OperationalCapability`. ctx: - Add `CapabilityInvolvement` and `.component_involvements` on `Capability` - Add `MissionInvolvement`, `CapabilityExploitation` and - `.involvements` - `.exploits` - `.exploitations` to `Mission`. - Move `Mission` and `MissionPkg` - Add `.all_capability_exploitations` lookup on layer for - adding `.incoming_exploitations` on `Capability`. --- capellambse/model/crosslayer/fa.py | 9 +-- capellambse/model/crosslayer/interaction.py | 15 ++++ capellambse/model/diagram.py | 2 +- capellambse/model/layers/ctx.py | 90 +++++++++++++++++---- capellambse/model/layers/oa.py | 8 ++ 5 files changed, 100 insertions(+), 24 deletions(-) diff --git a/capellambse/model/crosslayer/fa.py b/capellambse/model/crosslayer/fa.py index c242297e5..6b0b3a52d 100644 --- a/capellambse/model/crosslayer/fa.py +++ b/capellambse/model/crosslayer/fa.py @@ -21,7 +21,7 @@ from .. import common as c from .. import modeltypes -from . import capellacommon, capellacore, information +from . import capellacommon, capellacore, information, interaction if t.TYPE_CHECKING: from . import cs @@ -151,7 +151,7 @@ def owner(self) -> ComponentExchange | None: @c.xtype_handler(None) -class FunctionalChainInvolvementLink(c.GenericElement): +class FunctionalChainInvolvementLink(interaction.AbstractInvolvement): """An element linking a FunctionalChain to an Exchange.""" exchanged_items = c.AttrProxyAccessor( @@ -160,11 +160,8 @@ class FunctionalChainInvolvementLink(c.GenericElement): exchange_context = c.AttrProxyAccessor( capellacore.Constraint, "exchangeContext" ) - involved = c.AttrProxyAccessor(c.GenericElement, "involved") - @property - def name(self) -> str: # type: ignore - return f"[{self.__class__.__name__}] to {self.involved.name} ({self.involved.uuid})" + involved: c.AttrProxyAccessor @c.xtype_handler(None) diff --git a/capellambse/model/crosslayer/interaction.py b/capellambse/model/crosslayer/interaction.py index 25c472c2d..6bb8610dd 100644 --- a/capellambse/model/crosslayer/interaction.py +++ b/capellambse/model/crosslayer/interaction.py @@ -46,3 +46,18 @@ class AbstractCapabilityGeneralization(Exchange): _xmltag = "superGeneralizations" target = c.AttrProxyAccessor(c.GenericElement, "super") + + +class AbstractInvolvement(c.GenericElement): + """An abstract Involvement.""" + + involved = c.AttrProxyAccessor(c.GenericElement, "involved") + + @property + def name(self) -> str: # type: ignore + return f"[{self.__class__.__name__}] to {self.involved.name} ({self.involved.uuid})" + + +@c.xtype_handler(None) +class AbstractFunctionAbstractCapabilityInvolvement(AbstractInvolvement): + """An abstract CapabilityInvolvement linking to SystemFunctions.""" diff --git a/capellambse/model/diagram.py b/capellambse/model/diagram.py index d934b4ed2..690df9dbe 100644 --- a/capellambse/model/diagram.py +++ b/capellambse/model/diagram.py @@ -347,7 +347,7 @@ def type(self) -> modeltypes.DiagramType: LOGGER.warning("Unknown diagram type %r", sc) return modeltypes.DiagramType.UNKNOWN - @property + @property # type: ignore[override] def filters(self) -> cabc.MutableSet[str]: # type: ignore[override] """Return a set of currently activated filters on this diagram.""" return aird.ActiveFilters(self._model, self) diff --git a/capellambse/model/layers/ctx.py b/capellambse/model/layers/ctx.py index 707959d3c..48bcb4758 100644 --- a/capellambse/model/layers/ctx.py +++ b/capellambse/model/layers/ctx.py @@ -17,6 +17,7 @@ XT_ARCH = "org.polarsys.capella.core.data.ctx:SystemAnalysis" XT_CAP_INV = "org.polarsys.capella.core.data.ctx:CapabilityInvolvement" +XT_CAP_EXP = "org.polarsys.capella.core.data.ctx:CapabilityExploitation" @c.xtype_handler(XT_ARCH) @@ -45,23 +46,6 @@ class SystemFunctionPkg(c.GenericElement): packages: c.Accessor -@c.xtype_handler(XT_ARCH) -class Mission(c.GenericElement): - """A mission.""" - - _xmltag = "ownedMissions" - - -@c.xtype_handler(XT_ARCH) -class MissionPkg(c.GenericElement): - """A system mission package that can hold missions.""" - - _xmltag = "ownedMissionPkg" - - missions = c.ProxyAccessor(Mission, aslist=c.ElementList) - packages: c.Accessor - - @c.xtype_handler(XT_ARCH) class SystemComponent(cs.Component): """A system component.""" @@ -96,6 +80,11 @@ class SystemComponentPkg(c.GenericElement): packages: c.Accessor +@c.xtype_handler(XT_ARCH) +class CapabilityInvolvement(interaction.AbstractInvolvement): + """A CapabilityInvolvement.""" + + @c.xtype_handler(XT_ARCH) class Capability(c.GenericElement): """A capability.""" @@ -130,6 +119,9 @@ class Capability(c.GenericElement): follow="involved", aslist=c.MixedElementList, ) + component_involvements = c.ProxyAccessor( + CapabilityInvolvement, aslist=c.ElementList + ) realized_capabilities = c.ProxyAccessor( oa.OperationalCapability, interaction.XT_CAP_REAL, @@ -149,6 +141,54 @@ class Capability(c.GenericElement): packages: c.Accessor +@c.xtype_handler(XT_ARCH) +class MissionInvolvement(interaction.AbstractInvolvement): + """A MissionInvolvement.""" + + _xmltag = "ownedMissionInvolvements" + + +@c.xtype_handler(XT_ARCH) +class CapabilityExploitation(c.GenericElement): + """A CapabilityExploitation.""" + + _xmltag = "ownedCapabilityExploitations" + + capability = c.AttrProxyAccessor(Capability, "capability") + + @property + def name(self) -> str: # type: ignore + return f"[{self.__class__.__name__}] to {self.capability.name} ({self.capability.uuid})" + + +@c.xtype_handler(XT_ARCH) +class Mission(c.GenericElement): + """A mission.""" + + _xmltag = "ownedMissions" + + involvements = c.ProxyAccessor(MissionInvolvement, aslist=c.ElementList) + exploits = c.ProxyAccessor( + Capability, + xtypes=XT_CAP_EXP, + follow="capability", + aslist=c.ElementList, + ) + exploitations = c.ProxyAccessor( + CapabilityExploitation, aslist=c.ElementList + ) + + +@c.xtype_handler(XT_ARCH) +class MissionPkg(c.GenericElement): + """A system mission package that can hold missions.""" + + _xmltag = "ownedMissionPkg" + + missions = c.ProxyAccessor(Mission, aslist=c.ElementList) + packages: c.Accessor + + @c.xtype_handler(XT_ARCH) class CapabilityPkg(c.GenericElement): """A capability package that can hold capabilities.""" @@ -202,6 +242,11 @@ class SystemAnalysis(crosslayer.BaseArchitectureLayer): deep=True, ) + all_capability_exploitations = c.ProxyAccessor( + CapabilityExploitation, + aslist=c.ElementList, + deep=True, + ) all_function_exchanges = c.ProxyAccessor( fa.FunctionalExchange, aslist=c.ElementList, @@ -246,6 +291,17 @@ class SystemAnalysis(crosslayer.BaseArchitectureLayer): aslist=c.ElementList, ), ) +c.set_accessor( + Capability, + "incoming_exploitations", + c.CustomAccessor( + CapabilityExploitation, + operator.attrgetter("_model.sa.all_capability_exploitations"), + elmmatcher=operator.eq, + matchtransform=operator.attrgetter("capability"), + aslist=c.ElementList, + ), +) c.set_accessor( oa.Entity, "realizing_system_components", diff --git a/capellambse/model/layers/oa.py b/capellambse/model/layers/oa.py index 0ab9720b5..38e443915 100644 --- a/capellambse/model/layers/oa.py +++ b/capellambse/model/layers/oa.py @@ -63,6 +63,11 @@ class OperationalProcess(fa.FunctionalChain): """An operational process.""" +@c.xtype_handler(XT_ARCH) +class EntityOperationalCapabilityInvolvement(interaction.AbstractInvolvement): + """An EntityOperationalCapabilityInvolvement.""" + + @c.xtype_handler(XT_ARCH) class OperationalCapability(c.GenericElement): """A capability in the OperationalAnalysis layer.""" @@ -90,6 +95,9 @@ class OperationalCapability(c.GenericElement): follow="involved", aslist=c.MixedElementList, ) + entity_involvements = c.ProxyAccessor( + EntityOperationalCapabilityInvolvement, aslist=c.ElementList + ) involved_processes = c.ProxyAccessor( OperationalProcess, interaction.XT_CAP2PROC, From 992fa26e22cbe616eff557ee804e00429cc193ac Mon Sep 17 00:00:00 2001 From: ewuerger Date: Mon, 20 Jun 2022 10:09:48 +0200 Subject: [PATCH 12/19] Add styling of [MCB] diagrams --- capellambse/aird/capstyle.py | 32 ++++++++++++++++++++++++++++++++ capellambse/svg/style.py | 6 ++++++ 2 files changed, 38 insertions(+) diff --git a/capellambse/aird/capstyle.py b/capellambse/aird/capstyle.py index f0848713f..1701ca9a9 100644 --- a/capellambse/aird/capstyle.py +++ b/capellambse/aird/capstyle.py @@ -503,6 +503,38 @@ class in the form:: "text_fill": COLORS["_CAP_xAB_Function_Border_Green"], }, }, + "Missions Capabilities Blank": { + "Box.SystemComponent": { + "fill": [COLORS["_CAP_Actor_Blue_min"], COLORS["_CAP_Actor_Blue"]], + "stroke": COLORS["_CAP_Actor_Border_Blue"], + "text_fill": COLORS["_CAP_Actor_Blue_label"], + }, + "Edge.AbstractCapabilityExtend": { + "marker-end": "FineArrowMark", + "stroke": COLORS["black"], + "stroke-width": 1, + }, + "Edge.AbstractCapabilityGeneralization": { + "marker-end": "GeneralizationMark", + "stroke": COLORS["black"], + "stroke-width": 1, + }, + "Edge.AbstractCapabilityInclude": { + "marker-end": "FineArrowMark", + "stroke": COLORS["black"], + "stroke-width": 1, + }, + "Edge.CapabilityExploitation": { + "marker-end": "FineArrowMark", + "stroke": COLORS["black"], + "stroke-width": 1, + }, + "Edge.CapabilityInvolvement": { + "marker-end": "FineArrowMark", + "stroke": COLORS["black"], + "stroke-width": 1, + }, + }, "Mode State Machine": { # (from common.odesign) "Box.ChoicePseudoState": { "fill": COLORS["_CAP_ChoicePseudoState_Color"], diff --git a/capellambse/svg/style.py b/capellambse/svg/style.py index d90ed81f3..8f0c679a8 100644 --- a/capellambse/svg/style.py +++ b/capellambse/svg/style.py @@ -57,6 +57,12 @@ "PortSymbol", "FunctionalExchangeSymbol", ), + "Missions Capabilities Blank": ( + "CapabilitySymbol", + "MissionSymbol", + "SystemActorSymbol", + "SystemHumanActorSymbol", + ), "Mode State Machine": ( "FinalStateSymbol", "InitialPseudoStateSymbol", From a22376985a9ddff39d7665c8543c5e06705e14cd Mon Sep 17 00:00:00 2001 From: ewuerger Date: Mon, 20 Jun 2022 15:52:29 +0200 Subject: [PATCH 13/19] aird.capstyle: Add default styling for SystemActor and SystemHumanActor --- capellambse/aird/capstyle.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/capellambse/aird/capstyle.py b/capellambse/aird/capstyle.py index 1701ca9a9..ca78389a1 100644 --- a/capellambse/aird/capstyle.py +++ b/capellambse/aird/capstyle.py @@ -509,6 +509,16 @@ class in the form:: "stroke": COLORS["_CAP_Actor_Border_Blue"], "text_fill": COLORS["_CAP_Actor_Blue_label"], }, + "Box.SystemActor": { + "fill": [COLORS["_CAP_Actor_Blue_min"], COLORS["_CAP_Actor_Blue"]], + "stroke": COLORS["_CAP_Actor_Border_Blue"], + "text_fill": COLORS["_CAP_Actor_Blue_label"], + }, + "Box.SystemHumanActor": { + "fill": [COLORS["_CAP_Actor_Blue_min"], COLORS["_CAP_Actor_Blue"]], + "stroke": COLORS["_CAP_Actor_Border_Blue"], + "text_fill": COLORS["_CAP_Actor_Blue_label"], + }, "Edge.AbstractCapabilityExtend": { "marker-end": "FineArrowMark", "stroke": COLORS["black"], From 9dff0998a5d6e4d193c19e4f83899a777d636675 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Mon, 20 Jun 2022 15:54:47 +0200 Subject: [PATCH 14/19] svg.symbols: Fix OperationalCapability symbol --- capellambse/svg/symbols.py | 56 ++++++-------------------------------- 1 file changed, 9 insertions(+), 47 deletions(-) diff --git a/capellambse/svg/symbols.py b/capellambse/svg/symbols.py index 864244b17..a986e8c73 100644 --- a/capellambse/svg/symbols.py +++ b/capellambse/svg/symbols.py @@ -523,64 +523,26 @@ def _make_function_symbol( @decorations.deco_factories def operational_capability_symbol(id_="OperationalCapabilitySymbol"): - center = (42.2, 251.3) - symb = container.Symbol( - id=id_, - viewBox="0 0 85 150", - ) - symb.add( - _make_rgradient( - "b", - center=center, - r=22.6, - focal=center, - stop_colors=("#fbf9f6", "#d0ab84"), - transform=[4.96, 0, 0, 3.213, -167.2, -731.34], - ) - ) - symb.add( - shapes.Ellipse( - center=(42.2, 75.1), - r=(111.64, 72.13), - style="fill: url(#b); stroke: #674e2c; stroke-width: 5;", - ) + symb = _brown_oval(id_) + grp = container.Group( + transform="matrix(0.99781353,0,0,0.74596554,0.47471941,4.4891996)" ) letters = container.Group( - transform="matrix(8.154,0,0,4.75,-207.423,-144.27)" + transform="matrix(1.1032581,0,0,0.63735306,-10.659712,-9.548313)", + style="fill: black; stroke: none;", ) letters.add( path.Path( - d=( - "m 25.308224,51.460533 q -2.40625,0 -3.921875,-1.5625 -1.515625," - "-1.570312 -1.515625,-4.085937 0,-2.65625 1.539062,-4.296875 " - "1.539063,-1.640625 4.078125,-1.640625 2.398438,0 3.875,1.570312" - " 1.484375,1.570313 1.484375,4.140625 0,2.640625 -1.539062," - "4.257813 -1.53125,1.617187 -4,1.617187 z m 0.109375,-9.414062 q " - "-1.328125,0 -2.109375,1 -0.78125,0.992187 -0.78125,2.632812 " - "0,1.664063 0.78125,2.632813 0.78125,0.96875 2.046875,0.96875 " - "1.304687,0 2.070312,-0.9375 0.765625,-0.945313 0.765625,-2.617188" - " 0,-1.742187 -0.742187,-2.710937 -0.742188,-0.96875 -2.03125," - "-0.96875 z" - ), - style="fill:black;", + d="m 22.557488,35.882454 q -2.845659,0 -4.527185,2.121309 -1.668591,2.12131 -1.668591,5.781862 0,3.647618 1.668591,5.768927 1.681526,2.12131 4.527185,2.12131 2.845659,0 4.501315,-2.12131 1.668591,-2.121309 1.668591,-5.768927 0,-3.660552 -1.668591,-5.781862 -1.655656,-2.121309 -4.501315,-2.121309 z m 0,-2.12131 q 4.061532,0 6.493277,2.729246 2.431745,2.716311 2.431745,7.295235 0,4.56599 -2.431745,7.295236 -2.431745,2.716311 -6.493277,2.716311 -4.074466,0 -6.519146,-2.716311 -2.431745,-2.716311 -2.431745,-7.295236 0,-4.578924 2.431745,-7.295235 2.44468,-2.729246 6.519146,-2.729246 z", ) ) letters.add( path.Path( - d=( - "m 40.730099,50.866783 q -1.226563,0.59375 -3.203125,0.59375" - " -2.578125,0 -4.054688,-1.515625 -1.476562,-1.515625 -1.476562," - "-4.039062 0,-2.6875 1.65625,-4.359375 1.664062,-1.671875 " - "4.3125,-1.671875 1.640625,0 2.765625,0.414062 v 2.429688 q " - "-1.125,-0.671875 -2.5625,-0.671875 -1.578125,0 -2.546875,0.992187" - " -0.96875,0.992188 -0.96875,2.6875 0,1.625 0.914062,2.59375" - " 0.914063,0.960938 2.460938,0.960938 1.476562,0 2.703125," - "-0.71875 z" - ), - style="fill:black;", + d="m 50.031033,35.597888 v 2.755115 q -1.319351,-1.228807 -2.81979,-1.836743 -1.487504,-0.607937 -3.169029,-0.607937 -3.311313,0 -5.070448,2.030766 -1.759134,2.017831 -1.759134,5.846536 0,3.815771 1.759134,5.846536 1.759135,2.017831 5.070448,2.017831 1.681525,0 3.169029,-0.607936 1.500439,-0.607936 2.81979,-1.836744 v 2.729246 q -1.371091,0.931307 -2.910334,1.39696 -1.526308,0.465654 -3.233703,0.465654 -4.384902,0 -6.907191,-2.677507 -2.522289,-2.690441 -2.522289,-7.33404 0,-4.656533 2.522289,-7.334039 2.522289,-2.690442 6.907191,-2.690442 1.733265,0 3.259573,0.465654 1.539243,0.452718 2.884464,1.37109 z", ) ) - symb.add(letters) + grp.add(letters) + symb.add(grp) return symb From 8d1073178e5a49c2bc7f6b51213486cd53a9f99a Mon Sep 17 00:00:00 2001 From: ewuerger Date: Wed, 22 Jun 2022 15:36:20 +0200 Subject: [PATCH 15/19] aird.capstyle: Add missing default style for MissionInvolvements --- capellambse/aird/capstyle.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/capellambse/aird/capstyle.py b/capellambse/aird/capstyle.py index ca78389a1..1016fda69 100644 --- a/capellambse/aird/capstyle.py +++ b/capellambse/aird/capstyle.py @@ -387,6 +387,11 @@ class in the form:: "stroke": COLORS["black"], "stroke-width": 1, }, + "Edge.MissionInvolvement": { + "marker-end": "FineArrowMark", + "stroke": COLORS["black"], + "stroke-width": 1, + }, }, "Error": {}, "Functional Chain Description": { @@ -544,6 +549,11 @@ class in the form:: "stroke": COLORS["black"], "stroke-width": 1, }, + "Edge.MissionInvolvement": { + "marker-end": "FineArrowMark", + "stroke": COLORS["black"], + "stroke-width": 1, + }, }, "Mode State Machine": { # (from common.odesign) "Box.ChoicePseudoState": { From 5654bf56ba99fdaa2ec51f97535fcee8d8031286 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Thu, 23 Jun 2022 09:28:41 +0200 Subject: [PATCH 16/19] Resolve review change requests --- capellambse/model/crosslayer/fa.py | 2 -- capellambse/model/diagram.py | 2 +- capellambse/model/layers/ctx.py | 6 ++---- capellambse/model/layers/la.py | 2 +- capellambse/svg/decorations.py | 4 ++-- capellambse/svg/drawing.py | 34 +++++++++++++++++++----------- capellambse/svg/generate.py | 8 ++++--- 7 files changed, 33 insertions(+), 25 deletions(-) diff --git a/capellambse/model/crosslayer/fa.py b/capellambse/model/crosslayer/fa.py index 6b0b3a52d..1e903e076 100644 --- a/capellambse/model/crosslayer/fa.py +++ b/capellambse/model/crosslayer/fa.py @@ -161,8 +161,6 @@ class FunctionalChainInvolvementLink(interaction.AbstractInvolvement): capellacore.Constraint, "exchangeContext" ) - involved: c.AttrProxyAccessor - @c.xtype_handler(None) class FunctionalChain(c.GenericElement): diff --git a/capellambse/model/diagram.py b/capellambse/model/diagram.py index 690df9dbe..19b61b798 100644 --- a/capellambse/model/diagram.py +++ b/capellambse/model/diagram.py @@ -66,7 +66,7 @@ class AbstractDiagram(metaclass=abc.ABCMeta): * The element which is considered to be the "element of interest". """ filters: cabc.MutableSet[str] - """Return a set of currently activated filters on this diagram.""" + """The filters that are activated for this diagram.""" _model: capellambse.MelodyModel _render: aird.Diagram diff --git a/capellambse/model/layers/ctx.py b/capellambse/model/layers/ctx.py index 48bcb4758..e6abc7a0b 100644 --- a/capellambse/model/layers/ctx.py +++ b/capellambse/model/layers/ctx.py @@ -16,8 +16,6 @@ from . import oa XT_ARCH = "org.polarsys.capella.core.data.ctx:SystemAnalysis" -XT_CAP_INV = "org.polarsys.capella.core.data.ctx:CapabilityInvolvement" -XT_CAP_EXP = "org.polarsys.capella.core.data.ctx:CapabilityExploitation" @c.xtype_handler(XT_ARCH) @@ -115,7 +113,7 @@ class Capability(c.GenericElement): ) involved_components = c.ProxyAccessor( SystemComponent, - XT_CAP_INV, + xtypes=CapabilityInvolvement, follow="involved", aslist=c.MixedElementList, ) @@ -170,7 +168,7 @@ class Mission(c.GenericElement): involvements = c.ProxyAccessor(MissionInvolvement, aslist=c.ElementList) exploits = c.ProxyAccessor( Capability, - xtypes=XT_CAP_EXP, + xtypes=CapabilityExploitation, follow="capability", aslist=c.ElementList, ) diff --git a/capellambse/model/layers/la.py b/capellambse/model/layers/la.py index db362c1ce..3fbe8e01c 100644 --- a/capellambse/model/layers/la.py +++ b/capellambse/model/layers/la.py @@ -110,7 +110,7 @@ class CapabilityRealization(c.GenericElement): ) involved_components = c.ProxyAccessor( LogicalComponent, - ctx.XT_CAP_INV, + xtypes=ctx.CapabilityInvolvement, follow="involved", aslist=c.MixedElementList, ) diff --git a/capellambse/svg/decorations.py b/capellambse/svg/decorations.py index 85f0870d2..ae1d482c9 100644 --- a/capellambse/svg/decorations.py +++ b/capellambse/svg/decorations.py @@ -14,9 +14,9 @@ icon_size = 20 """Default icon size.""" icon_padding = 2 -"""Default icon padding(right-side).""" +"""Default icon padding(left/right side).""" feature_space = 24 -"""Default space for feature text.""" +"""Default margins/padding (top/bot and left/right) for feature text.""" function_ports = {"FIP", "FOP"} directed_component_ports = {"CP_IN", "CP_OUT"} diff --git a/capellambse/svg/drawing.py b/capellambse/svg/drawing.py index 6bc6e5e58..56efc6f0a 100644 --- a/capellambse/svg/drawing.py +++ b/capellambse/svg/drawing.py @@ -23,7 +23,7 @@ DEBUG = "CAPELLAMBSE_SVG_DEBUG" in os.environ """Debug flag to render helping lines.""" LABEL_ICON_PADDING = 2 -"""Default (right)-padding for label icons.""" +"""Default padding between a label's icon and text.""" LabelDict = t.TypedDict( @@ -38,7 +38,7 @@ }, total=False, ) -"""Stores relevant data of labels.""" + # FIXME: refactor this, so a Drawing contains an "svg drawing". Always prefer composition over inheritance. class Drawing(drawing.Drawing): @@ -231,13 +231,13 @@ def _draw_box_label(self, builder: LabelBuilder) -> container.Group: if DEBUG: assert builder.label is not None assert y_margin is not None - debug_y = int(builder.label["y"]) + y_margin + debug_y = builder.label["y"] + y_margin debug_y1 = ( - int(builder.label["y"]) - + (int(builder.label["height"]) - decorations.icon_size) / 2 + builder.label["y"] + + (builder.label["height"] - decorations.icon_size) / 2 ) x = ( - int(builder.label["x"]) + builder.label["x"] + decorations.icon_size + 2 * decorations.icon_padding ) @@ -249,7 +249,7 @@ def _draw_box_label(self, builder: LabelBuilder) -> container.Group: bbox: LabelDict = { "x": x if builder.text_anchor == "start" - else x - int(builder.label["width"]) / 2, + else x - builder.label["width"] / 2, "y": debug_y if debug_y <= debug_y1 else debug_y1, "width": builder.label["width"], "height": debug_height, @@ -889,11 +889,21 @@ def get_label_position_y(builder: LabelBuilder, lines: LinesData) -> float: def get_label_icon_position( builder: LabelBuilder, text_height: int | float, icon_x: int | float ) -> tuple[float, float]: - """Return icon position for labels. - - Determines mainly y-coordinate and adjusts x-coordinate if - label-class does not equal `Annotation` and x-coordinate - overflows into label-text with `decorations.icon_padding`. + """It calculates the position of the icon relative to the label. + + Parameters + ---------- + builder : LabelBuilder + LabelBuilder + text_height : int | float + The height of the text in the label. + icon_x : int | float + The x position of the icon. + + Returns + ------- + icon_coords : tuple[float, float] + The x and y coordinates of the icon. """ assert builder.y_margin is not None icon_y = ( diff --git a/capellambse/svg/generate.py b/capellambse/svg/generate.py index 8c7c45593..e4681b36c 100644 --- a/capellambse/svg/generate.py +++ b/capellambse/svg/generate.py @@ -111,9 +111,11 @@ def draw_object(self, obj: ContentsDict) -> None: def save_drawing( self, pretty: bool = False, indent: int = 2, filename: str = "" ) -> None: - if filename: - self.drawing.filename = filename - self.drawing.save(pretty=pretty, indent=indent) + self.drawing.saveas( + filename=filename or self.drawing.filename, + pretty=pretty, + indent=indent, + ) def to_string(self) -> str: return self.drawing.tostring() From 18bd858d5a761545da453b368212152f40c544cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernst=20W=C3=BCrger?= <50786483+ewuerger@users.noreply.github.com> Date: Thu, 23 Jun 2022 09:33:49 +0200 Subject: [PATCH 17/19] Update capellambse/model/layers/ctx.py Co-authored-by: Martin Lehmann --- capellambse/model/layers/ctx.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/capellambse/model/layers/ctx.py b/capellambse/model/layers/ctx.py index e6abc7a0b..04dcd384d 100644 --- a/capellambse/model/layers/ctx.py +++ b/capellambse/model/layers/ctx.py @@ -292,12 +292,8 @@ class SystemAnalysis(crosslayer.BaseArchitectureLayer): c.set_accessor( Capability, "incoming_exploitations", - c.CustomAccessor( - CapabilityExploitation, - operator.attrgetter("_model.sa.all_capability_exploitations"), - elmmatcher=operator.eq, - matchtransform=operator.attrgetter("capability"), - aslist=c.ElementList, + c.ReferenceSearchingAccessor( + CapabilityExploitation, "capability", aslist=c.ElementList ), ) c.set_accessor( From ca2e0af2acb11f1dc490160034e8cfbda7550537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernst=20W=C3=BCrger?= <50786483+ewuerger@users.noreply.github.com> Date: Thu, 23 Jun 2022 15:03:51 +0200 Subject: [PATCH 18/19] Update drawing.py --- capellambse/svg/drawing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capellambse/svg/drawing.py b/capellambse/svg/drawing.py index 56efc6f0a..09d22ca1e 100644 --- a/capellambse/svg/drawing.py +++ b/capellambse/svg/drawing.py @@ -889,7 +889,7 @@ def get_label_position_y(builder: LabelBuilder, lines: LinesData) -> float: def get_label_icon_position( builder: LabelBuilder, text_height: int | float, icon_x: int | float ) -> tuple[float, float]: - """It calculates the position of the icon relative to the label. + """Calculate the position of the icon relative to the label. Parameters ---------- From 8f29e779e7090ae7b91de99ed8e4b12c2a2aec56 Mon Sep 17 00:00:00 2001 From: Martin Lehmann Date: Fri, 24 Jun 2022 15:44:03 +0200 Subject: [PATCH 19/19] fix(svg): Fix missing space in docstring --- capellambse/svg/decorations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capellambse/svg/decorations.py b/capellambse/svg/decorations.py index ae1d482c9..26235139a 100644 --- a/capellambse/svg/decorations.py +++ b/capellambse/svg/decorations.py @@ -14,7 +14,7 @@ icon_size = 20 """Default icon size.""" icon_padding = 2 -"""Default icon padding(left/right side).""" +"""Default icon padding (left/right side).""" feature_space = 24 """Default margins/padding (top/bot and left/right) for feature text."""