Проблемы с проектированием UML - подобных диаграмм в графвизе
В настоящее время у меня проблемы с проектированием UML-подобных диаграмм на графике. Причиной проблемы является то, что они не являются точно UML-диаграммами. Основное отличие состоит в том, что я использую отступы для добавления иерархии к свойствам объекта. Реализация этих особенностей немного сложна для меня. То, что я пытаюсь достичь, это:
Я обычно использую форму узла под названием record
разработать эти диаграммы. Проблема возникает, когда мне нужно связать две из этих UML-подобных диаграмм точно так же, как отношения в UML, то есть агрегацию, ассоциацию, композицию и т. Д.
Когда у меня есть диаграммы, я не могу установить связь со стрелками, потому что стрелки идут только от случайной части одного узла к другой случайной части другого узла. То, как у меня есть UML-подобные диаграммы, хорошо, но стрелка отношения заставляет ее не быть тем, что я хочу, потому что я хочу, чтобы стрелки переходили из определенной точки одного узла в другую конкретную точку другого узла.
Код DOT, который я использовал для создания этого графика, выглядит так:
digraph G {
fontname = "Bitstream Vera Sans"
fontsize = 8
node [
fontname = "Bitstream Vera Sans"
fontsize = 8
shape = "record"
]
edge [
fontname = "Bitstream Vera Sans"
fontsize = 8
]
Person [
label = "{Person \l\l \ age : int\l \ livesIn : City \l \ \ \ sinceYear : int}"
] // \l -new line, \ -indentation
City [
label = "{City \l \ \ name : string}"
]
Person -> City
}
Я попытался обойти эту проблему, используя горизонтальные линейные деления внутри узлов, хотя я не хотел, чтобы линии. Горизонтальные деления линий позволяют мне сделать это возможным благодаря использованию портов, но они сами создают новую проблему. Проблема, которую они создают, состоит в том, что они избавляются от отступов, которые я хочу и имел в предыдущем графике. Способ, которым я пытался обойти проблемы со стрелками, работает, но создаются новые проблемы - отступы исчезают, а деления горизонтальных линий нельзя сделать невидимыми
,
Код, который я использовал для создания этого графика:
digraph G {
fontname = "Bitstream Vera Sans"
fontsize = 8
node [
fontname = "Bitstream Vera Sans"
fontsize = 8
shape = "record"
penwidth = 0.5
]
edge [
fontname = "Bitstream Vera Sans"
fontsize = 8
]
Person [
label = "{<g0> Person | <g1> age : int | <g2> livesIn : City | <g3> sinceYear : int}"
] // \l -new line, \ -indentation
City [
label = "{<f0> City | <f1> name : string}"
]
Person:<g2> -> City:<f1> [arrowhead = "empty", headlabel = "*"]
}
Эти отступы являются важной частью отношений, поэтому мне интересно, знает ли кто-нибудь, что я могу сделать, чтобы эти отступы были в диаграммах, а также что я могу сделать, чтобы деления горизонтальных линий были невидимыми?
Я буду признателен, если у кого-то есть лучший способ / идея, которая также полностью отличается от того, что я сделал на диаграммах 2 и 3, что поможет мне достичь диаграммы 1.
1 ответ
Ваша первоначальная попытка не была плохой. Я бы сказал, что использование портов, безусловно, путь. Если вы поместите узел в кластер, вы можете использовать границу кластера и скрыть границу узла записи, избавившись от этих разделительных линий.
Как вы заметили, используя обратную косую черту \
больше не работает, чтобы убежать из космоса. Обходной путь должен либо использовать \
вместо этого это избежит пробелов. В качестве альтернативы вы также можете заменить каждый пробел на &nnbsp;
, Любой из них достигнет требуемого эффекта.
Я сделал несколько небольших изменений, чтобы сделать вещи более читабельными, например, поместил свойства Graph в graph
заблокируйте вместо корня графа и переименуйте имена портов в нечто более разумное. Я также удалил все порты, которые не используются.
Конечный результат, который я придумал, был такой:
... и это код DOT, который я использовал:
digraph G {
graph [
compound = true // To clip the head at the cluster border
penwidth = 2 // Make the cluster's borders a bit thicker
rankdir = "LR" // Make the arrow and nodes go from Left to Right
ranksep = 1 // Add a bit more space inbetween nodes
]
node [
color = none // Hide the node's border
fontname = "Bitstream Vera Sans"
height = 0 // Make the node as small as possible (it will grow if it needs more space)
margin = 0 // Remove unneeded whitespace
shape = "record" // So we can use ports
]
edge [
arrowhead = "open"
labelangle = -5 // Place the asteriks closer to the line
labeldistance = 2.5 // Place the asteriks further away from the arrow head
penwidth = 2 // Make the line a bit thicker
]
/* @NOTE: escaping spaces in the label using '\' doesn't work so use ' ' or '\' instead. */
subgraph cluster_Person {
Person [
label = "\N\l | \ \ \ age : int\l | <livesIn> \ \ \ livesIn : City\l | \ \ \ \ \ \ sinceYear : int\l"
]
}
subgraph cluster_City {
City [
label = "<city> \N\l | \ \ \ name : string\l"
]
}
Person:livesIn -> City:city [headlabel = "*", lhead = "cluster_City"] // lhead allows us to point to the cluster's border instead of the node, as long as we add `compound = true` to the graph
}