check for overlaps with children

This commit is contained in:
Gavin Nishizawa 2023-07-19 17:31:52 -07:00
parent a8cc241c22
commit 09174c17b8
No known key found for this signature in database
GPG key ID: AE3B177777CE55CD
2 changed files with 257 additions and 15 deletions

View file

@ -317,6 +317,24 @@ func (obj *Object) GetLabelTopLeft() *geo.Point {
return labelTL
}
func (obj *Object) GetIconTopLeft() *geo.Point {
if obj.IconPosition == nil {
return nil
}
s := obj.ToShape()
iconPosition := label.Position(*obj.IconPosition)
var box *geo.Box
if iconPosition.IsOutside() {
box = s.GetBox()
} else {
box = s.GetInnerBox()
}
return iconPosition.GetPointOnBox(box, label.PADDING, d2target.MAX_ICON_SIZE, d2target.MAX_ICON_SIZE)
}
func (edge *Edge) TraceToShape(points []*geo.Point, startIndex, endIndex int) (newStart, newEnd int) {
srcShape := edge.Src.ToShape()
dstShape := edge.Dst.ToShape()

View file

@ -1150,17 +1150,15 @@ func adjustRankSpacing(g *d2graph.Graph, rankSep float64, isHorizontal bool) {
continue
}
} else {
startingRank := startingParentRanks[child]
endingRank := endingParentRanks[child]
if startingRank != rank && endingRank != rank {
if startingParentRanks[child] != rank {
continue
}
}
margin, _ := getSpacing(child)
if isHorizontal {
startPosition = child.TopLeft.X - margin.left
startPosition = child.TopLeft.X - margin.left - padding.left
} else {
startPosition = child.TopLeft.Y - margin.top
startPosition = child.TopLeft.Y - margin.top - padding.top
}
startingAncestorPositions[parent] = math.Min(startingAncestorPositions[parent], startPosition)
}
@ -1185,6 +1183,7 @@ func adjustRankSpacing(g *d2graph.Graph, rankSep float64, isHorizontal bool) {
} else {
endPosition = parent.TopLeft.Y + parent.Height + padding.bottom - rankSep/2.
}
endingAncestorPositions[parent] = math.Max(endingAncestorPositions[parent], endPosition)
for _, child := range parent.ChildrenArray {
if r, has := objectRanks[child]; has {
@ -1192,18 +1191,16 @@ func adjustRankSpacing(g *d2graph.Graph, rankSep float64, isHorizontal bool) {
continue
}
} else {
startingRank := startingParentRanks[child]
endingRank := endingParentRanks[child]
if startingRank != rank && endingRank != rank {
if endingParentRanks[child] != rank {
continue
}
}
margin, _ := getSpacing(child)
if isHorizontal {
endPosition = child.TopLeft.X + child.Width + margin.right
endPosition = child.TopLeft.X + child.Width + margin.right + padding.right
} else {
endPosition = child.TopLeft.Y + child.Height + margin.bottom
endPosition = child.TopLeft.Y + child.Height + margin.bottom + padding.bottom
}
endingAncestorPositions[parent] = math.Max(endingAncestorPositions[parent], endPosition)
}
@ -1383,26 +1380,93 @@ func fitPadding(obj *d2graph.Object) {
fitPadding(child)
}
// we will compute a perfectly fit innerBox merging our padding with children's margin,
// but we need to add padding and margin together if an outside child label will overlap with our inside label
_, padding := getSpacing(obj)
padding.top = math.Max(padding.top, DEFAULT_PADDING)
padding.bottom = math.Max(padding.bottom, DEFAULT_PADDING)
padding.left = math.Max(padding.left, DEFAULT_PADDING)
padding.right = math.Max(padding.right, DEFAULT_PADDING)
// where we are (current*) vs where we want to fit each side to (inner*)
currentTop := obj.TopLeft.Y
currentBottom := obj.TopLeft.Y + obj.Height
currentLeft := obj.TopLeft.X
currentRight := obj.TopLeft.X + obj.Width
innerTop := math.Inf(1)
innerBottom := math.Inf(-1)
innerLeft := math.Inf(1)
innerRight := math.Inf(-1)
// we create boxes for our inside label and icon, and will check against overlaps with any internal boxes
var labelPosition, iconPosition label.Position
var labelBox, iconBox *geo.Box
if obj.HasLabel() && obj.LabelPosition != nil {
labelPosition = label.Position(*obj.LabelPosition)
switch labelPosition {
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight,
label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight,
label.InsideMiddleLeft, label.InsideMiddleRight:
labelTL := obj.GetLabelTopLeft()
if labelTL != nil {
labelBox = geo.NewBox(labelTL, float64(obj.LabelDimensions.Width)+2*label.PADDING, float64(obj.LabelDimensions.Height))
}
}
}
if obj.Icon != nil && shapeType != shape.IMAGE_TYPE && obj.IconPosition != nil {
iconPosition = label.Position(*obj.IconPosition)
switch iconPosition {
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight,
label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight,
label.InsideMiddleLeft, label.InsideMiddleRight:
iconTL := obj.GetIconTopLeft()
if iconTL != nil {
iconBox = geo.NewBox(iconTL, d2target.MAX_ICON_SIZE, d2target.MAX_ICON_SIZE)
}
}
}
// update the inner positions for children's margin and collect the outside boxes that we cannot overlap with
var innerBoxes []geo.Box
for _, child := range obj.ChildrenArray {
margin, _ := getSpacing(child)
dx, dy := child.GetModifierElementAdjustments()
if labelBox != nil || iconBox != nil {
var childLabelBox *geo.Box
var childLabelPosition, childIconPosition label.Position
if child.HasLabel() && child.LabelPosition != nil {
childLabelPosition = label.Position(*child.LabelPosition)
if childLabelPosition.IsOutside() {
childLabelTL := child.GetLabelTopLeft()
childLabelBox = geo.NewBox(
childLabelTL,
float64(child.LabelDimensions.Width),
float64(child.LabelDimensions.Height),
)
innerBoxes = append(innerBoxes, *childLabelBox)
}
}
if child.Icon != nil && child.Shape.Value != d2target.ShapeImage && child.IconPosition != nil {
childIconPosition = label.Position(*child.IconPosition)
if childIconPosition.IsOutside() {
childIconTL := child.GetIconTopLeft()
childIconBox := geo.NewBox(childIconTL, d2target.MAX_ICON_SIZE, d2target.MAX_ICON_SIZE)
innerBoxes = append(innerBoxes, *childIconBox)
}
}
}
innerTop = math.Min(innerTop, child.TopLeft.Y-dy-math.Max(margin.top, padding.top))
innerBottom = math.Max(innerBottom, child.TopLeft.Y+child.Height+math.Max(margin.bottom, padding.bottom))
innerLeft = math.Min(innerLeft, child.TopLeft.X-math.Max(margin.left, padding.left))
innerRight = math.Max(innerRight, child.TopLeft.X+child.Width+dx+math.Max(margin.right, padding.right))
}
// collect edge label boxes and update inner box for internal edges
for _, edge := range obj.Graph.Edges {
if !edge.Src.IsDescendantOf(obj) || !edge.Dst.IsDescendantOf(obj) {
continue
@ -1417,6 +1481,10 @@ func fitPadding(obj *d2graph.Object) {
labelHeight := float64(edge.LabelDimensions.Height)
point, _ := labelPosition.GetPointOnRoute(edge.Route, 2, 0, labelWidth, labelHeight)
if labelBox != nil || iconBox != nil {
innerBoxes = append(innerBoxes, geo.Box{TopLeft: point, Width: labelWidth, Height: labelHeight})
}
innerTop = math.Min(innerTop, point.Y-padding.top)
innerBottom = math.Max(innerBottom, point.Y+labelHeight+padding.bottom)
innerLeft = math.Min(innerLeft, point.X-padding.left)
@ -1430,16 +1498,172 @@ func fitPadding(obj *d2graph.Object) {
}
}
currentTop := obj.TopLeft.Y
currentBottom := obj.TopLeft.Y + obj.Height
currentLeft := obj.TopLeft.X
currentRight := obj.TopLeft.X + obj.Width
// how much do we need to shrink each side
topDelta := innerTop - currentTop
bottomDelta := currentBottom - innerBottom
leftDelta := innerLeft - currentLeft
rightDelta := currentRight - innerRight
if topDelta > 0 || bottomDelta > 0 || leftDelta > 0 || rightDelta > 0 {
var leftOverlap, rightOverlap, topOverlap, bottomOverlap float64
var labelSide, iconSide geo.Orientation
if labelBox != nil {
switch labelPosition {
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
labelSide = geo.Top
case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
labelSide = geo.Bottom
case label.InsideMiddleLeft:
labelSide = geo.Left
case label.InsideMiddleRight:
labelSide = geo.Right
default:
labelSide = geo.NONE
}
// move labelBox to its position with the merged delta and check for overlaps
switch labelSide {
case geo.Top:
if topDelta > 0 {
labelBox.TopLeft.Y += topDelta
}
case geo.Bottom:
if bottomDelta > 0 {
labelBox.TopLeft.Y -= bottomDelta
}
case geo.Left:
if leftDelta > 0 {
labelBox.TopLeft.X += leftDelta
}
case geo.Right:
if rightDelta > 0 {
labelBox.TopLeft.X -= rightDelta
}
}
switch labelSide {
case geo.Top:
if topDelta > 0 {
for _, box := range innerBoxes {
if labelBox.Overlaps(box) {
dy := labelBox.TopLeft.Y + labelBox.Height - box.TopLeft.Y
topOverlap = go2.Max(topOverlap, dy)
}
}
}
case geo.Bottom:
if bottomDelta > 0 {
for _, box := range innerBoxes {
if labelBox.Overlaps(box) {
dy := box.TopLeft.Y + box.Height - labelBox.TopLeft.Y
bottomOverlap = go2.Max(bottomOverlap, dy)
}
}
}
case geo.Left:
if leftDelta > 0 {
for _, box := range innerBoxes {
if labelBox.Overlaps(box) {
dx := labelBox.TopLeft.X + labelBox.Width - box.TopLeft.X
leftOverlap = go2.Max(leftOverlap, dx)
}
}
}
case geo.Right:
if rightDelta > 0 {
for _, box := range innerBoxes {
if labelBox.Overlaps(box) {
dx := box.TopLeft.X + box.Width - labelBox.TopLeft.X
rightOverlap = go2.Max(rightOverlap, dx)
}
}
}
}
}
if iconBox != nil {
switch iconPosition {
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
iconSide = geo.Top
case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
iconSide = geo.Bottom
case label.InsideMiddleLeft:
iconSide = geo.Left
case label.InsideMiddleRight:
iconSide = geo.Right
default:
iconSide = geo.NONE
}
// move iconBox to its position with the merged delta and check for overlaps
switch iconSide {
case geo.Top:
if topDelta > 0 {
iconBox.TopLeft.Y += topDelta
}
case geo.Bottom:
if bottomDelta > 0 {
iconBox.TopLeft.Y -= bottomDelta
}
case geo.Left:
if leftDelta > 0 {
iconBox.TopLeft.X += leftDelta
}
case geo.Right:
if rightDelta > 0 {
iconBox.TopLeft.X -= rightDelta
}
}
switch iconSide {
case geo.Top:
if topDelta > 0 {
for _, box := range innerBoxes {
if iconBox.Overlaps(box) {
dy := iconBox.TopLeft.Y + iconBox.Height - box.TopLeft.Y
topOverlap = go2.Max(topOverlap, dy)
}
}
}
case geo.Bottom:
if bottomDelta > 0 {
for _, box := range innerBoxes {
if iconBox.Overlaps(box) {
dy := box.TopLeft.Y + box.Height - iconBox.TopLeft.Y
bottomOverlap = go2.Max(bottomOverlap, dy)
}
}
}
case geo.Left:
if leftDelta > 0 {
for _, box := range innerBoxes {
if iconBox.Overlaps(box) {
dx := iconBox.TopLeft.X + iconBox.Width - box.TopLeft.X
leftOverlap = go2.Max(leftOverlap, dx)
}
}
}
case geo.Right:
if rightDelta > 0 {
for _, box := range innerBoxes {
if iconBox.Overlaps(box) {
dx := box.TopLeft.X + box.Width - iconBox.TopLeft.X
rightOverlap = go2.Max(rightOverlap, dx)
}
}
}
}
}
if leftOverlap > 0 {
leftDelta -= leftOverlap
}
if rightOverlap > 0 {
rightDelta -= rightOverlap
}
if topOverlap > 0 {
topDelta -= topOverlap
}
if bottomOverlap > 0 {
bottomDelta -= bottomOverlap
}
}
if 0 < topDelta {
topDelta = adjustDeltaForEdges(obj, currentTop, topDelta, false)
if 0 < topDelta {