extend previous segment to target if last segment is very short

This commit is contained in:
Gavin Nishizawa 2023-01-11 20:21:47 -08:00
parent 25e37fd0c4
commit 54f34d72ad
No known key found for this signature in database
GPG key ID: AE3B177777CE55CD
4 changed files with 55 additions and 4 deletions

View file

@ -30,6 +30,8 @@ var setupJS string
//go:embed dagre.js //go:embed dagre.js
var dagreJS string var dagreJS string
const MIN_SEGMENT_LEN = 10
type ConfigurableOpts struct { type ConfigurableOpts struct {
NodeSep int `json:"nodesep"` NodeSep int `json:"nodesep"`
EdgeSep int `json:"edgesep"` EdgeSep int `json:"edgesep"`
@ -247,6 +249,47 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
} }
} }
// arrowheads can appear broken if segments are very short from dagre routing a point just outside the shape
// to fix this, we try extending the previous segment into the shape instead of having a very short segment
if !start.Equals(points[0]) && startIndex+2 < len(points) {
newStartingSegment := *geo.NewSegment(start, points[startIndex+1])
if newStartingSegment.Length() < MIN_SEGMENT_LEN {
// we don't want a very short segment right next to the source because it will mess up the arrowhead
// instead we want to extend the next segment into the shape border if possible
nextStart := points[startIndex+1]
nextEnd := points[startIndex+2]
// Note: in other direction to extend towards source
nextSegment := *geo.NewSegment(nextStart, nextEnd)
v := nextSegment.ToVector()
extendedStart := nextEnd.ToVector().Add(v.AddLength(MIN_SEGMENT_LEN)).ToPoint()
extended := *geo.NewSegment(nextEnd, extendedStart)
if intersections := edge.Src.Box.Intersections(extended); len(intersections) > 0 {
start = intersections[0]
startIndex += 1
}
}
}
if !end.Equals(points[len(points)-1]) && endIndex-2 >= 0 {
newEndingSegment := *geo.NewSegment(end, points[endIndex-1])
if newEndingSegment.Length() < MIN_SEGMENT_LEN {
// extend the prev segment into the shape border if possible
prevStart := points[endIndex-2]
prevEnd := points[endIndex-1]
prevSegment := *geo.NewSegment(prevStart, prevEnd)
v := prevSegment.ToVector()
extendedEnd := prevStart.ToVector().Add(v.AddLength(MIN_SEGMENT_LEN)).ToPoint()
extended := *geo.NewSegment(prevStart, extendedEnd)
if intersections := edge.Dst.Box.Intersections(extended); len(intersections) > 0 {
end = intersections[0]
endIndex -= 1
}
}
}
srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Attributes.Shape.Value)], edge.Src.Box) srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Attributes.Shape.Value)], edge.Src.Box)
dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Attributes.Shape.Value)], edge.Dst.Box) dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Attributes.Shape.Value)], edge.Dst.Box)

View file

@ -187,12 +187,12 @@ func (p *Point) DistanceToLine(p1, p2 *Point) float64 {
// Moves the given point by Vector // Moves the given point by Vector
func (start *Point) AddVector(v Vector) *Point { func (start *Point) AddVector(v Vector) *Point {
return start.toVector().Add(v).ToPoint() return start.ToVector().Add(v).ToPoint()
} }
// Creates a Vector of the size between start and endpoint, pointing to endpoint // Creates a Vector of the size between start and endpoint, pointing to endpoint
func (start *Point) VectorTo(endpoint *Point) Vector { func (start *Point) VectorTo(endpoint *Point) Vector {
return endpoint.toVector().Minus(start.toVector()) return endpoint.ToVector().Minus(start.ToVector())
} }
func (p *Point) FormattedCoordinates() string { func (p *Point) FormattedCoordinates() string {
@ -205,7 +205,7 @@ func (q *Point) OnSegment(p, r *Point) bool {
} }
// Creates a Vector pointing to point // Creates a Vector pointing to point
func (endpoint *Point) toVector() Vector { func (endpoint *Point) ToVector() Vector {
return []float64{endpoint.X, endpoint.Y} return []float64{endpoint.X, endpoint.Y}
} }

View file

@ -29,7 +29,7 @@ func TestAddVector(t *testing.T) {
func TestToVector(t *testing.T) { func TestToVector(t *testing.T) {
p := &Point{3.5, 6.7} p := &Point{3.5, 6.7}
v := p.toVector() v := p.ToVector()
if v[0] != p.X || v[1] != p.Y { if v[0] != p.X || v[1] != p.Y {
t.Fatalf("Expected Vector (%v) coordinates to match the point (%v)", p, v) t.Fatalf("Expected Vector (%v) coordinates to match the point (%v)", p, v)

View file

@ -114,3 +114,11 @@ func (segment *Segment) GetBounds(segments []*Segment, buffer float64) (float64,
} }
return floor, ceil return floor, ceil
} }
func (segment Segment) Length() float64 {
return EuclideanDistance(segment.Start.X, segment.Start.Y, segment.End.X, segment.End.Y)
}
func (segment Segment) ToVector() Vector {
return NewVector(segment.End.X-segment.Start.X, segment.End.Y-segment.Start.Y)
}