gim: make incoming edges separate along the drawn rectangle edge, instead of all overlapping onto the same point

This commit is contained in:
Brian Picciano 2018-03-03 17:32:40 +00:00
parent ed8fa31104
commit 905b182467
5 changed files with 85 additions and 40 deletions

View File

@ -437,6 +437,9 @@ func Equal(g1, g2 *Graph) bool {
return true return true
} }
// TODO Walk, but by edge
// TODO Walk, but without end. AKA FSM
// Walk will traverse the Graph, calling the callback on every Vertex in the // Walk will traverse the Graph, calling the callback on every Vertex in the
// Graph once. If startWith is non-nil then that Vertex will be the first one // Graph once. If startWith is non-nil then that Vertex will be the first one
// passed to the callback and used as the starting point of the traversal. If // passed to the callback and used as the starting point of the traversal. If

View File

@ -11,11 +11,15 @@ type Rect struct {
Size XY Size XY
} }
// Edge returns the coordinate of the edge indicated by the given direction (Up, // Edge describes a straight edge starting at its first XY and ending at its
// Down, Left, or Right). The coordinate will be for the axis applicable to the // second
// direction, so for Left/Right it will be the x coordinate and for Up/Down the type Edge [2]XY
// y.
func (r Rect) Edge(dir XY) int { // EdgeCoord returns the coordinate of the edge indicated by the given direction
// (Up, Down, Left, or Right). The coordinate will be for the axis applicable to
// the direction, so for Left/Right it will be the x coordinate and for Up/Down
// the y.
func (r Rect) EdgeCoord(dir XY) int {
switch dir { switch dir {
case Up: case Up:
return r.TopLeft[1] return r.TopLeft[1]
@ -49,25 +53,37 @@ func (r Rect) Corner(xDir, yDir XY) XY {
} }
} }
// EdgeMidpoint returns the point which is the midpoint of the edge dientified by the // Edge returns an Edge instance for the edge of the Rect indicated by the given
// direction (Up/Down/Left/Right) // direction (Up, Down, Left, or Right). secDir indicates the direction the
func (r Rect) EdgeMidpoint(dir XY, rounder Rounder) XY { // returned Edge should be pointing (i.e. the order of its XY's) and must be
var a, b XY // perpendicular to dir
func (r Rect) Edge(dir, secDir XY) Edge {
var e Edge
switch dir { switch dir {
case Up: case Up:
a, b = r.Corner(Left, Up), r.Corner(Right, Up) e[0], e[1] = r.Corner(Left, Up), r.Corner(Right, Up)
case Down: case Down:
a, b = r.Corner(Left, Down), r.Corner(Right, Down) e[0], e[1] = r.Corner(Left, Down), r.Corner(Right, Down)
case Left: case Left:
a, b = r.Corner(Left, Up), r.Corner(Left, Down) e[0], e[1] = r.Corner(Left, Up), r.Corner(Left, Down)
case Right: case Right:
a, b = r.Corner(Right, Up), r.Corner(Right, Down) e[0], e[1] = r.Corner(Right, Up), r.Corner(Right, Down)
default: default:
panic(fmt.Sprintf("unsupported direction: %#v", dir)) panic(fmt.Sprintf("unsupported direction: %#v", dir))
} }
mid := a.Midpoint(b, rounder) switch secDir {
return mid case Left, Up:
e[0], e[1] = e[1], e[0]
default:
// do nothing
}
return e
}
// Midpoint returns the point which is the midpoint of the Edge
func (e Edge) Midpoint(rounder Rounder) XY {
return e[0].Midpoint(e[1], rounder)
} }
func (r Rect) halfSize(rounder Rounder) XY { func (r Rect) halfSize(rounder Rounder) XY {

View File

@ -12,15 +12,29 @@ func TestRect(t *T) {
Size: XY{2, 2}, Size: XY{2, 2},
} }
assert.Equal(t, 2, r.Edge(Up)) assert.Equal(t, 2, r.EdgeCoord(Up))
assert.Equal(t, 3, r.Edge(Down)) assert.Equal(t, 3, r.EdgeCoord(Down))
assert.Equal(t, 1, r.Edge(Left)) assert.Equal(t, 1, r.EdgeCoord(Left))
assert.Equal(t, 2, r.Edge(Right)) assert.Equal(t, 2, r.EdgeCoord(Right))
assert.Equal(t, XY{1, 2}, r.Corner(Left, Up)) lu := XY{1, 2}
assert.Equal(t, XY{1, 3}, r.Corner(Left, Down)) ld := XY{1, 3}
assert.Equal(t, XY{2, 2}, r.Corner(Right, Up)) ru := XY{2, 2}
assert.Equal(t, XY{2, 3}, r.Corner(Right, Down)) rd := XY{2, 3}
assert.Equal(t, lu, r.Corner(Left, Up))
assert.Equal(t, ld, r.Corner(Left, Down))
assert.Equal(t, ru, r.Corner(Right, Up))
assert.Equal(t, rd, r.Corner(Right, Down))
assert.Equal(t, Edge{lu, ld}, r.Edge(Left, Down))
assert.Equal(t, Edge{ru, rd}, r.Edge(Right, Down))
assert.Equal(t, Edge{lu, ru}, r.Edge(Up, Right))
assert.Equal(t, Edge{ld, rd}, r.Edge(Down, Right))
assert.Equal(t, Edge{ld, lu}, r.Edge(Left, Up))
assert.Equal(t, Edge{rd, ru}, r.Edge(Right, Up))
assert.Equal(t, Edge{ru, lu}, r.Edge(Up, Left))
assert.Equal(t, Edge{rd, ld}, r.Edge(Down, Left))
} }
func TestRectCenter(t *T) { func TestRectCenter(t *T) {

View File

@ -39,16 +39,19 @@ var arrows = map[geo.XY]string{
geo.Right: ">", geo.Right: ">",
} }
type line [2]*box type line struct {
from, to *box
toI int
}
// given the "primary" direction the line should be headed, picks a possible // given the "primary" direction the line should be headed, picks a possible
// secondary one which may be used to detour along the path in order to reach // secondary one which may be used to detour along the path in order to reach
// the destination (in the case that the two boxes are diagonal from each other) // the destination (in the case that the two boxes are diagonal from each other)
func (l line) secondaryDir(primary geo.XY) geo.XY { func (l line) secondaryDir(primary geo.XY) geo.XY {
fromRect, toRect := l[0].rect(), l[1].rect() fromRect, toRect := l.from.rect(), l.to.rect()
rels := make([]int, len(geo.Units)) rels := make([]int, len(geo.Units))
for i, dir := range geo.Units { for i, dir := range geo.Units {
rels[i] = toRect.Edge(dir.Inv()) - fromRect.Edge(dir) rels[i] = toRect.EdgeCoord(dir.Inv()) - fromRect.EdgeCoord(dir)
if dir == geo.Up || dir == geo.Left { if dir == geo.Up || dir == geo.Left {
rels[i] *= -1 rels[i] *= -1
} }
@ -74,13 +77,22 @@ func (l line) secondaryDir(primary geo.XY) geo.XY {
return secondary return secondary
} }
func (l line) draw(term *terminal.Terminal, dir geo.XY) { //func (l line) startEnd(flowDir, secFlowDir geo.XY) (geo.XY, geo.XY) {
from, to := *l[0], *l[1] // from, to := *(l.from), *(l.to)
dirSec := l.secondaryDir(dir) // start := from.rect().EdgeMidpoint(flowDir, rounder) // ezpz
//}
func (l line) draw(term *terminal.Terminal, flowDir, secFlowDir geo.XY) {
from, to := *(l.from), *(l.to)
dirSec := l.secondaryDir(flowDir)
flowDirInv := flowDir.Inv()
start := from.rect().Edge(flowDir, secFlowDir).Midpoint(rounder)
endSlot := l.toI*2 + 1
endSlotXY := geo.XY{endSlot, endSlot}
end := to.rect().Edge(flowDirInv, secFlowDir)[0].Add(secFlowDir.Mul(endSlotXY))
dirInv := dir.Inv()
start := from.rect().EdgeMidpoint(dir, rounder)
end := to.rect().EdgeMidpoint(dirInv, rounder)
mid := start.Midpoint(end, rounder) mid := start.Midpoint(end, rounder)
along := func(xy, dir geo.XY) int { along := func(xy, dir geo.XY) int {
@ -91,26 +103,26 @@ func (l line) draw(term *terminal.Terminal, dir geo.XY) {
} }
var pts []geo.XY var pts []geo.XY
midPrim := along(mid, dir) midPrim := along(mid, flowDir)
endSec := along(end, dirSec) endSec := along(end, dirSec)
for curr := start; curr != end; { for curr := start; curr != end; {
pts = append(pts, curr) pts = append(pts, curr)
if prim := along(curr, dir); prim == midPrim { if prim := along(curr, flowDir); prim == midPrim {
if sec := along(curr, dirSec); sec != endSec { if sec := along(curr, dirSec); sec != endSec {
curr = curr.Add(dirSec) curr = curr.Add(dirSec)
continue continue
} }
} }
curr = curr.Add(dir) curr = curr.Add(flowDir)
} }
for i, pt := range pts { for i, pt := range pts {
var str string var str string
switch { switch {
case i == 0: case i == 0:
str = edgeSegments[dir] str = edgeSegments[flowDir]
case i == len(pts)-1: case i == len(pts)-1:
str = arrows[dir] str = arrows[flowDir]
default: default:
prev, next := pts[i-1], pts[i+1] prev, next := pts[i-1], pts[i+1]
seg := [2]geo.XY{ seg := [2]geo.XY{

View File

@ -150,9 +150,9 @@ func (view *view) draw(term *terminal.Terminal) {
var lines []line var lines []line
for _, b := range boxes { for _, b := range boxes {
v := boxesM[b] v := boxesM[b]
for _, e := range v.In { for i, e := range v.In {
bFrom := boxesMr[e.From] bFrom := boxesMr[e.From]
lines = append(lines, line{bFrom, b}) lines = append(lines, line{from: bFrom, to: b, toI: i})
} }
} }
@ -164,6 +164,6 @@ func (view *view) draw(term *terminal.Terminal) {
b.draw(term) b.draw(term)
} }
for _, line := range lines { for _, line := range lines {
line.draw(term, view.primFlowDir) line.draw(term, view.primFlowDir, view.secFlowDir)
} }
} }