Merge pull request #1345 from gavin-ts/grid-only-place-label-if-unplaced

grid: don't overwrite nested graph label positions
This commit is contained in:
gavin-ts 2023-05-31 11:11:04 -07:00 committed by GitHub
commit ba3e43ad5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 3311 additions and 393 deletions

View file

@ -12,3 +12,4 @@
#### Bugfixes ⛑️
- Shadow is cut off when `--pad` is 0. Thank you @LeonardsonCC ! [#1326](https://github.com/terrastruct/d2/pull/1326)
- Fixes grid layout overwriting label placements for nested objects. [#1345](https://github.com/terrastruct/d2/pull/1345)

View file

@ -1817,90 +1817,6 @@ func (g *Graph) ApplyTheme(themeID int64) error {
return nil
}
func (obj *Object) MoveWithDescendants(dx, dy float64) {
obj.TopLeft.X += dx
obj.TopLeft.Y += dy
for _, child := range obj.ChildrenArray {
child.MoveWithDescendants(dx, dy)
}
}
func (obj *Object) MoveWithDescendantsTo(x, y float64) {
dx := x - obj.TopLeft.X
dy := y - obj.TopLeft.Y
obj.MoveWithDescendants(dx, dy)
}
func (parent *Object) removeChild(child *Object) {
delete(parent.Children, strings.ToLower(child.ID))
for i := 0; i < len(parent.ChildrenArray); i++ {
if parent.ChildrenArray[i] == child {
parent.ChildrenArray = append(parent.ChildrenArray[:i], parent.ChildrenArray[i+1:]...)
break
}
}
}
// remove obj and all descendants from graph, as a new Graph
func (g *Graph) ExtractAsNestedGraph(obj *Object) *Graph {
descendantObjects, edges := pluckObjAndEdges(g, obj)
tempGraph := NewGraph()
tempGraph.Root.ChildrenArray = []*Object{obj}
tempGraph.Root.Children[strings.ToLower(obj.ID)] = obj
for _, descendantObj := range descendantObjects {
descendantObj.Graph = tempGraph
}
tempGraph.Objects = descendantObjects
tempGraph.Edges = edges
obj.Parent.removeChild(obj)
obj.Parent = tempGraph.Root
return tempGraph
}
func pluckObjAndEdges(g *Graph, obj *Object) (descendantsObjects []*Object, edges []*Edge) {
for i := 0; i < len(g.Edges); i++ {
edge := g.Edges[i]
if edge.Src == obj || edge.Dst == obj {
edges = append(edges, edge)
g.Edges = append(g.Edges[:i], g.Edges[i+1:]...)
i--
}
}
for i := 0; i < len(g.Objects); i++ {
temp := g.Objects[i]
if temp.AbsID() == obj.AbsID() {
descendantsObjects = append(descendantsObjects, obj)
g.Objects = append(g.Objects[:i], g.Objects[i+1:]...)
for _, child := range obj.ChildrenArray {
subObjects, subEdges := pluckObjAndEdges(g, child)
descendantsObjects = append(descendantsObjects, subObjects...)
edges = append(edges, subEdges...)
}
break
}
}
return descendantsObjects, edges
}
func (g *Graph) InjectNestedGraph(tempGraph *Graph, parent *Object) {
obj := tempGraph.Root.ChildrenArray[0]
obj.MoveWithDescendantsTo(0, 0)
obj.Parent = parent
for _, obj := range tempGraph.Objects {
obj.Graph = g
}
g.Objects = append(g.Objects, tempGraph.Objects...)
parent.Children[strings.ToLower(obj.ID)] = obj
parent.ChildrenArray = append(parent.ChildrenArray, obj)
g.Edges = append(g.Edges, tempGraph.Edges...)
}
func (g *Graph) PrintString() string {
buf := &bytes.Buffer{}
fmt.Fprint(buf, "Objects: [")
@ -1918,54 +1834,6 @@ func (obj *Object) IterDescendants(apply func(parent, child *Object)) {
}
}
// ShiftDescendants moves Object's descendants (not including itself)
// descendants' edges are also moved by the same dx and dy (the whole route is moved if both ends are a descendant)
func (obj *Object) ShiftDescendants(dx, dy float64) {
// also need to shift edges of descendants that are shifted
movedEdges := make(map[*Edge]struct{})
for _, e := range obj.Graph.Edges {
isSrcDesc := e.Src.IsDescendantOf(obj)
isDstDesc := e.Dst.IsDescendantOf(obj)
if isSrcDesc && isDstDesc {
movedEdges[e] = struct{}{}
for _, p := range e.Route {
p.X += dx
p.Y += dy
}
}
}
obj.IterDescendants(func(_, curr *Object) {
curr.TopLeft.X += dx
curr.TopLeft.Y += dy
for _, e := range obj.Graph.Edges {
if _, ok := movedEdges[e]; ok {
continue
}
isSrc := e.Src == curr
isDst := e.Dst == curr
if isSrc && isDst {
for _, p := range e.Route {
p.X += dx
p.Y += dy
}
} else if isSrc {
e.Route[0].X += dx
e.Route[0].Y += dy
} else if isDst {
e.Route[len(e.Route)-1].X += dx
e.Route[len(e.Route)-1].Y += dy
}
if isSrc || isDst {
movedEdges[e] = struct{}{}
}
}
})
}
func (obj *Object) IsMultiple() bool {
return obj.Style.Multiple != nil && obj.Style.Multiple.Value == "true"
}
@ -1973,19 +1841,3 @@ func (obj *Object) IsMultiple() bool {
func (obj *Object) Is3D() bool {
return obj.Style.ThreeDee != nil && obj.Style.ThreeDee.Value == "true"
}
// GetModifierElementAdjustments returns width/height adjustments to account for shapes with 3d or multiple
func (obj *Object) GetModifierElementAdjustments() (dx, dy float64) {
if obj.Is3D() {
if obj.Shape.Value == d2target.ShapeHexagon {
dy = d2target.THREE_DEE_OFFSET / 2
} else {
dy = d2target.THREE_DEE_OFFSET
}
dx = d2target.THREE_DEE_OFFSET
} else if obj.IsMultiple() {
dy = d2target.MULTIPLE_OFFSET
dx = d2target.MULTIPLE_OFFSET
}
return dx, dy
}

191
d2graph/layout.go Normal file
View file

@ -0,0 +1,191 @@
package d2graph
import (
"strings"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/geo"
"oss.terrastruct.com/d2/lib/label"
"oss.terrastruct.com/d2/lib/shape"
)
func (obj *Object) MoveWithDescendants(dx, dy float64) {
obj.TopLeft.X += dx
obj.TopLeft.Y += dy
for _, child := range obj.ChildrenArray {
child.MoveWithDescendants(dx, dy)
}
}
func (obj *Object) MoveWithDescendantsTo(x, y float64) {
dx := x - obj.TopLeft.X
dy := y - obj.TopLeft.Y
obj.MoveWithDescendants(dx, dy)
}
func (parent *Object) removeChild(child *Object) {
delete(parent.Children, strings.ToLower(child.ID))
for i := 0; i < len(parent.ChildrenArray); i++ {
if parent.ChildrenArray[i] == child {
parent.ChildrenArray = append(parent.ChildrenArray[:i], parent.ChildrenArray[i+1:]...)
break
}
}
}
// remove obj and all descendants from graph, as a new Graph
func (g *Graph) ExtractAsNestedGraph(obj *Object) *Graph {
descendantObjects, edges := pluckObjAndEdges(g, obj)
tempGraph := NewGraph()
tempGraph.Root.ChildrenArray = []*Object{obj}
tempGraph.Root.Children[strings.ToLower(obj.ID)] = obj
for _, descendantObj := range descendantObjects {
descendantObj.Graph = tempGraph
}
tempGraph.Objects = descendantObjects
tempGraph.Edges = edges
obj.Parent.removeChild(obj)
obj.Parent = tempGraph.Root
return tempGraph
}
func pluckObjAndEdges(g *Graph, obj *Object) (descendantsObjects []*Object, edges []*Edge) {
for i := 0; i < len(g.Edges); i++ {
edge := g.Edges[i]
if edge.Src == obj || edge.Dst == obj {
edges = append(edges, edge)
g.Edges = append(g.Edges[:i], g.Edges[i+1:]...)
i--
}
}
for i := 0; i < len(g.Objects); i++ {
temp := g.Objects[i]
if temp.AbsID() == obj.AbsID() {
descendantsObjects = append(descendantsObjects, obj)
g.Objects = append(g.Objects[:i], g.Objects[i+1:]...)
for _, child := range obj.ChildrenArray {
subObjects, subEdges := pluckObjAndEdges(g, child)
descendantsObjects = append(descendantsObjects, subObjects...)
edges = append(edges, subEdges...)
}
break
}
}
return descendantsObjects, edges
}
func (g *Graph) InjectNestedGraph(tempGraph *Graph, parent *Object) {
obj := tempGraph.Root.ChildrenArray[0]
obj.MoveWithDescendantsTo(0, 0)
obj.Parent = parent
for _, obj := range tempGraph.Objects {
obj.Graph = g
}
g.Objects = append(g.Objects, tempGraph.Objects...)
parent.Children[strings.ToLower(obj.ID)] = obj
parent.ChildrenArray = append(parent.ChildrenArray, obj)
g.Edges = append(g.Edges, tempGraph.Edges...)
}
// ShiftDescendants moves Object's descendants (not including itself)
// descendants' edges are also moved by the same dx and dy (the whole route is moved if both ends are a descendant)
func (obj *Object) ShiftDescendants(dx, dy float64) {
// also need to shift edges of descendants that are shifted
movedEdges := make(map[*Edge]struct{})
for _, e := range obj.Graph.Edges {
isSrcDesc := e.Src.IsDescendantOf(obj)
isDstDesc := e.Dst.IsDescendantOf(obj)
if isSrcDesc && isDstDesc {
movedEdges[e] = struct{}{}
for _, p := range e.Route {
p.X += dx
p.Y += dy
}
}
}
obj.IterDescendants(func(_, curr *Object) {
curr.TopLeft.X += dx
curr.TopLeft.Y += dy
for _, e := range obj.Graph.Edges {
if _, ok := movedEdges[e]; ok {
continue
}
isSrc := e.Src == curr
isDst := e.Dst == curr
if isSrc && isDst {
for _, p := range e.Route {
p.X += dx
p.Y += dy
}
} else if isSrc {
e.Route[0].X += dx
e.Route[0].Y += dy
} else if isDst {
e.Route[len(e.Route)-1].X += dx
e.Route[len(e.Route)-1].Y += dy
}
if isSrc || isDst {
movedEdges[e] = struct{}{}
}
}
})
}
// GetModifierElementAdjustments returns width/height adjustments to account for shapes with 3d or multiple
func (obj *Object) GetModifierElementAdjustments() (dx, dy float64) {
if obj.Is3D() {
if obj.Shape.Value == d2target.ShapeHexagon {
dy = d2target.THREE_DEE_OFFSET / 2
} else {
dy = d2target.THREE_DEE_OFFSET
}
dx = d2target.THREE_DEE_OFFSET
} else if obj.IsMultiple() {
dy = d2target.MULTIPLE_OFFSET
dx = d2target.MULTIPLE_OFFSET
}
return dx, dy
}
func (obj *Object) ToShape() shape.Shape {
tl := obj.TopLeft
if tl == nil {
tl = geo.NewPoint(0, 0)
}
dslShape := strings.ToLower(obj.Shape.Value)
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape]
contentBox := geo.NewBox(tl, obj.Width, obj.Height)
return shape.NewShape(shapeType, contentBox)
}
func (obj *Object) GetLabelTopLeft() *geo.Point {
if obj.LabelPosition == nil {
return nil
}
s := obj.ToShape()
labelPosition := label.Position(*obj.LabelPosition)
var box *geo.Box
if labelPosition.IsOutside() {
box = s.GetBox()
} else {
box = s.GetInnerBox()
}
labelTL := labelPosition.GetPointOnBox(box, label.PADDING,
float64(obj.LabelDimensions.Width),
float64(obj.LabelDimensions.Height),
)
return labelTL
}

View file

@ -119,9 +119,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
}
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(obj.Width), float64(obj.Height))
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[obj.Shape.Value]
s := shape.NewShape(shapeType, contentBox)
s := obj.ToShape()
iconSize := d2target.GetIconSize(s.GetInnerBox(), string(label.InsideTopLeft))
// Since dagre container labels are pushed up, we don't want a child container to collide
maxContainerLabelHeight = go2.Max(maxContainerLabelHeight, (iconSize+label.PADDING*2)*2)
@ -240,7 +238,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
obj.Width = math.Ceil(dn.Width)
obj.Height = math.Ceil(dn.Height)
if obj.HasLabel() {
if obj.HasLabel() && obj.LabelPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.LabelPosition = go2.Pointer(string(label.OutsideTopCenter))
} else if obj.HasOutsideBottomLabel() {
@ -253,7 +251,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
}
}
if obj.Icon != nil {
if obj.Icon != nil && obj.IconPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.IconPosition = go2.Pointer(string(label.OutsideTopLeft))
obj.LabelPosition = go2.Pointer(string(label.OutsideTopRight))
@ -491,8 +489,8 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
}
}
srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Shape.Value)], edge.Src.Box)
dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Shape.Value)], edge.Dst.Box)
srcShape := edge.Src.ToShape()
dstShape := edge.Dst.ToShape()
// trace the edge to the specific shape's border
points[startIndex] = shape.TraceToShapeBorder(srcShape, start, points[startIndex+1])

View file

@ -407,7 +407,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
obj.Width = math.Ceil(n.Width)
obj.Height = math.Ceil(n.Height)
if obj.HasLabel() {
if obj.HasLabel() && obj.LabelPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
} else if obj.HasOutsideBottomLabel() {
@ -419,7 +419,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
}
}
if obj.Icon != nil {
if obj.Icon != nil && obj.IconPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
obj.LabelPosition = go2.Pointer(string(label.InsideTopRight))
@ -501,8 +501,8 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
}
}
srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Shape.Value)], edge.Src.Box)
dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Shape.Value)], edge.Dst.Box)
srcShape := edge.Src.ToShape()
dstShape := edge.Dst.ToShape()
// trace the edge to the specific shape's border
points[startIndex] = shape.TraceToShapeBorder(srcShape, points[startIndex], points[startIndex+1])

View file

@ -6,13 +6,11 @@ import (
"fmt"
"math"
"sort"
"strings"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/geo"
"oss.terrastruct.com/d2/lib/label"
"oss.terrastruct.com/d2/lib/shape"
"oss.terrastruct.com/util-go/go2"
)
@ -104,42 +102,99 @@ func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph, layout d2graph.L
if obj.GridGap != nil || obj.VerticalGap != nil {
verticalPadding = gd.verticalGap
}
// size shape according to grid
obj.SizeToContent(float64(gd.width), float64(gd.height), float64(2*horizontalPadding), float64(2*verticalPadding))
obj.SizeToContent(gd.width, gd.height, float64(2*horizontalPadding), float64(2*verticalPadding))
// compute where the grid should be placed inside shape
dslShape := strings.ToLower(obj.Shape.Value)
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[dslShape]
s := shape.NewShape(shapeType, geo.NewBox(geo.NewPoint(0, 0), obj.Width, obj.Height))
s := obj.ToShape()
innerBox := s.GetInnerBox()
if innerBox.TopLeft.X != 0 || innerBox.TopLeft.Y != 0 {
gd.shift(innerBox.TopLeft.X, innerBox.TopLeft.Y)
}
// compute how much space the label and icon occupy
var occupiedWidth, occupiedHeight float64
if obj.Icon != nil {
iconSpace := float64(d2target.MAX_ICON_SIZE + 2*label.PADDING)
occupiedWidth = iconSpace
occupiedHeight = iconSpace
}
var dx, dy float64
if obj.LabelDimensions.Width != 0 {
labelWidth := float64(obj.LabelDimensions.Width) + 2*label.PADDING
if labelWidth > obj.Width {
dx = (labelWidth - obj.Width) / 2
obj.Width = labelWidth
}
}
if obj.LabelDimensions.Height != 0 {
labelHeight := float64(obj.LabelDimensions.Height) + 2*label.PADDING
if labelHeight > float64(verticalPadding) {
// if the label doesn't fit within the padding, we need to add more
grow := labelHeight - float64(verticalPadding)
dy = grow
obj.Height += grow
occupiedHeight = math.Max(
occupiedHeight,
float64(obj.LabelDimensions.Height)+2*label.PADDING,
)
}
if obj.LabelDimensions.Width != 0 {
// . ├────┤───────├────┤
// . icon label icon
// with an icon in top left we need 2x the space to fit the label in the center
occupiedWidth *= 2
occupiedWidth += float64(obj.LabelDimensions.Width) + 2*label.PADDING
if occupiedWidth > obj.Width {
dx = (occupiedWidth - obj.Width) / 2
obj.Width = occupiedWidth
}
}
// also check for grid cells with outside top labels or icons
// the first grid object is at the top (and always exists)
topY := gd.objects[0].TopLeft.Y
highestOutside := topY
for _, o := range gd.objects {
// we only want to compute label positions for objects at the top of the grid
if o.TopLeft.Y > topY {
if gd.rowDirected {
// if the grid is rowDirected (row1, row2, etc) we can stop after finishing the first row
break
} else {
// otherwise we continue until the next column
continue
}
}
if o.LabelPosition != nil {
labelPosition := label.Position(*o.LabelPosition)
if labelPosition.IsOutside() {
labelTL := o.GetLabelTopLeft()
if labelTL.Y < highestOutside {
highestOutside = labelTL.Y
}
}
}
if o.IconPosition != nil {
switch label.Position(*o.IconPosition) {
case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
iconSpace := float64(d2target.MAX_ICON_SIZE + label.PADDING)
if topY-iconSpace < highestOutside {
highestOutside = topY - iconSpace
}
}
}
}
if highestOutside < topY {
occupiedHeight += topY - highestOutside + 2*label.PADDING
}
if occupiedHeight > float64(verticalPadding) {
// if the label doesn't fit within the padding, we need to add more
dy = occupiedHeight - float64(verticalPadding)
obj.Height += dy
}
// we need to center children if we have to expand to fit the container label
if dx != 0 || dy != 0 {
gd.shift(dx, dy)
}
}
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
if obj.HasLabel() {
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
}
if obj.Icon != nil {
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
}
gridDiagrams[obj.AbsID()] = gd
for _, o := range gd.objects {
@ -191,10 +246,17 @@ func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*gridDiagram, error) {
// position labels and icons
for _, o := range gd.objects {
if o.Icon != nil {
o.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
// don't overwrite position if nested graph layout positioned label/icon
if o.LabelPosition == nil {
o.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
}
if o.IconPosition == nil {
o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
}
} else {
o.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
if o.LabelPosition == nil {
o.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
}
}
}

View file

@ -2723,6 +2723,7 @@ scenarios: {
loadFromFile(t, "grid_large_checkered"),
loadFromFile(t, "grid_nested"),
loadFromFile(t, "grid_nested_gap0"),
loadFromFile(t, "grid_icon"),
loadFromFile(t, "multiple_offset"),
loadFromFile(t, "multiple_offset_left"),
}

67
e2etests/testdata/files/grid_icon.d2 vendored Normal file
View file

@ -0,0 +1,67 @@
classes: {
2x2: {
grid-rows: 2
grid-columns: 2
}
}
grid w/ container + icon: {
class: 2x2
a
b: {
b child
icon: https://icons.terrastruct.com/dev%2Fgithub.svg
}
c
d
}
grid + icon: {
class: 2x2
icon: https://icons.terrastruct.com/dev%2Fgithub.svg
a
b
c
d
}
grid + icon w/ container: {
class: 2x2
icon: https://icons.terrastruct.com/dev%2Fgithub.svg
a
b: {
b child
}
c
d
}
no label grid w/ container + icon: "" {
class: 2x2
a
b: {
b child
icon: https://icons.terrastruct.com/dev%2Fgithub.svg
}
c
d
}
no label grid + icon: "" {
class: 2x2
icon: https://icons.terrastruct.com/dev%2Fgithub.svg
a
b
c
d
}

1271
e2etests/testdata/stable/grid_icon/dagre/board.exp.json generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

1271
e2etests/testdata/stable/grid_icon/elk/board.exp.json generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -14,7 +14,7 @@
"y": 150
},
"width": 384,
"height": 356,
"height": 388,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
@ -52,7 +52,7 @@
"type": "rectangle",
"pos": {
"x": 60,
"y": 210
"y": 242
},
"width": 53,
"height": 130,
@ -93,7 +93,7 @@
"type": "rectangle",
"pos": {
"x": 153,
"y": 210
"y": 242
},
"width": 171,
"height": 130,
@ -125,7 +125,7 @@
"underline": false,
"labelWidth": 12,
"labelHeight": 31,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPosition": "OUTSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
@ -134,7 +134,7 @@
"type": "rectangle",
"pos": {
"x": 193,
"y": 242
"y": 274
},
"width": 91,
"height": 66,
@ -175,7 +175,7 @@
"type": "rectangle",
"pos": {
"x": 60,
"y": 380
"y": 412
},
"width": 53,
"height": 66,
@ -216,7 +216,7 @@
"type": "rectangle",
"pos": {
"x": 153,
"y": 380
"y": 412
},
"width": 171,
"height": 66,
@ -263,7 +263,7 @@
"y": 0
},
"width": 692,
"height": 656,
"height": 688,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
@ -301,7 +301,7 @@
"type": "rectangle",
"pos": {
"x": 504,
"y": 60
"y": 92
},
"width": 53,
"height": 430,
@ -342,7 +342,7 @@
"type": "rectangle",
"pos": {
"x": 597,
"y": 60
"y": 92
},
"width": 479,
"height": 430,
@ -374,7 +374,7 @@
"underline": false,
"labelWidth": 12,
"labelHeight": 31,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPosition": "OUTSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
@ -383,7 +383,7 @@
"type": "rectangle",
"pos": {
"x": 617,
"y": 123
"y": 155
},
"width": 439,
"height": 335,
@ -424,7 +424,7 @@
"type": "rectangle",
"pos": {
"x": 637,
"y": 183
"y": 215
},
"width": 186,
"height": 240,
@ -465,7 +465,7 @@
"type": "rectangle",
"pos": {
"x": 657,
"y": 246
"y": 278
},
"width": 146,
"height": 140,
@ -506,7 +506,7 @@
"type": "rectangle",
"pos": {
"x": 697,
"y": 283
"y": 315
},
"width": 66,
"height": 66,
@ -547,7 +547,7 @@
"type": "rectangle",
"pos": {
"x": 843,
"y": 183
"y": 215
},
"width": 193,
"height": 240,
@ -588,7 +588,7 @@
"type": "rectangle",
"pos": {
"x": 863,
"y": 246
"y": 278
},
"width": 153,
"height": 140,
@ -629,7 +629,7 @@
"type": "rectangle",
"pos": {
"x": 903,
"y": 283
"y": 315
},
"width": 73,
"height": 66,
@ -670,7 +670,7 @@
"type": "rectangle",
"pos": {
"x": 504,
"y": 530
"y": 562
},
"width": 53,
"height": 66,
@ -711,7 +711,7 @@
"type": "rectangle",
"pos": {
"x": 597,
"y": 530
"y": 562
},
"width": 479,
"height": 66,
@ -755,7 +755,7 @@
],
"pos": {
"x": 1196,
"y": 69
"y": 85
},
"width": 480,
"height": 518,
@ -796,7 +796,7 @@
"type": "rectangle",
"pos": {
"x": 1256,
"y": 129
"y": 145
},
"width": 53,
"height": 292,
@ -840,7 +840,7 @@
],
"pos": {
"x": 1349,
"y": 129
"y": 145
},
"width": 267,
"height": 292,
@ -881,7 +881,7 @@
"type": "rectangle",
"pos": {
"x": 1409,
"y": 189
"y": 205
},
"width": 53,
"height": 66,
@ -922,7 +922,7 @@
"type": "rectangle",
"pos": {
"x": 1502,
"y": 189
"y": 205
},
"width": 54,
"height": 66,
@ -963,7 +963,7 @@
"type": "rectangle",
"pos": {
"x": 1409,
"y": 295
"y": 311
},
"width": 53,
"height": 66,
@ -1004,7 +1004,7 @@
"type": "rectangle",
"pos": {
"x": 1502,
"y": 295
"y": 311
},
"width": 54,
"height": 66,
@ -1045,7 +1045,7 @@
"type": "rectangle",
"pos": {
"x": 1256,
"y": 461
"y": 477
},
"width": 53,
"height": 66,
@ -1086,7 +1086,7 @@
"type": "rectangle",
"pos": {
"x": 1349,
"y": 461
"y": 477
},
"width": 267,
"height": 66,
@ -1127,7 +1127,7 @@
"type": "rectangle",
"pos": {
"x": 1736,
"y": 38
"y": 54
},
"width": 321,
"height": 581,
@ -1168,7 +1168,7 @@
"type": "rectangle",
"pos": {
"x": 1736,
"y": 84
"y": 100
},
"width": 53,
"height": 469,
@ -1209,7 +1209,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
"y": 84
"y": 100
},
"width": 268,
"height": 469,
@ -1250,7 +1250,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
"y": 125
"y": 141
},
"width": 214,
"height": 66,
@ -1291,7 +1291,7 @@
"type": "rectangle",
"pos": {
"x": 2003,
"y": 125
"y": 141
},
"width": 54,
"height": 66,
@ -1332,7 +1332,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
"y": 191
"y": 207
},
"width": 214,
"height": 362,
@ -1373,7 +1373,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
"y": 227
"y": 243
},
"width": 53,
"height": 66,
@ -1414,7 +1414,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
"y": 227
"y": 243
},
"width": 161,
"height": 66,
@ -1455,7 +1455,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
"y": 293
"y": 309
},
"width": 53,
"height": 260,
@ -1496,7 +1496,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
"y": 293
"y": 309
},
"width": 161,
"height": 260,
@ -1537,7 +1537,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
"y": 324
"y": 340
},
"width": 107,
"height": 163,
@ -1578,7 +1578,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
"y": 355
"y": 371
},
"width": 53,
"height": 66,
@ -1619,7 +1619,7 @@
"type": "rectangle",
"pos": {
"x": 1895,
"y": 355
"y": 371
},
"width": 54,
"height": 66,
@ -1660,7 +1660,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
"y": 421
"y": 437
},
"width": 53,
"height": 66,
@ -1701,7 +1701,7 @@
"type": "rectangle",
"pos": {
"x": 1895,
"y": 421
"y": 437
},
"width": 54,
"height": 66,
@ -1742,7 +1742,7 @@
"type": "rectangle",
"pos": {
"x": 1949,
"y": 324
"y": 340
},
"width": 54,
"height": 163,
@ -1783,7 +1783,7 @@
"type": "rectangle",
"pos": {
"x": 1842,
"y": 487
"y": 503
},
"width": 107,
"height": 66,
@ -1824,7 +1824,7 @@
"type": "rectangle",
"pos": {
"x": 1949,
"y": 487
"y": 503
},
"width": 54,
"height": 66,
@ -1865,7 +1865,7 @@
"type": "rectangle",
"pos": {
"x": 2003,
"y": 191
"y": 207
},
"width": 54,
"height": 362,
@ -1906,7 +1906,7 @@
"type": "rectangle",
"pos": {
"x": 1736,
"y": 553
"y": 569
},
"width": 53,
"height": 66,
@ -1947,7 +1947,7 @@
"type": "rectangle",
"pos": {
"x": 1789,
"y": 553
"y": 569
},
"width": 268,
"height": 66,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -125,7 +125,7 @@
"underline": false,
"labelWidth": 12,
"labelHeight": 31,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
@ -374,7 +374,7 @@
"underline": false,
"labelWidth": 12,
"labelHeight": 31,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB