gim: make incoming edges separate along the drawn rectangle edge, instead of all overlapping onto the same point
This commit is contained in:
parent
ed8fa31104
commit
905b182467
3
gg/gg.go
3
gg/gg.go
@ -437,6 +437,9 @@ func Equal(g1, g2 *Graph) bool {
|
||||
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
|
||||
// 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
|
||||
|
@ -11,11 +11,15 @@ type Rect struct {
|
||||
Size XY
|
||||
}
|
||||
|
||||
// Edge 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) Edge(dir XY) int {
|
||||
// Edge describes a straight edge starting at its first XY and ending at its
|
||||
// second
|
||||
type Edge [2]XY
|
||||
|
||||
// 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 {
|
||||
case Up:
|
||||
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
|
||||
// direction (Up/Down/Left/Right)
|
||||
func (r Rect) EdgeMidpoint(dir XY, rounder Rounder) XY {
|
||||
var a, b XY
|
||||
// Edge returns an Edge instance for the edge of the Rect indicated by the given
|
||||
// direction (Up, Down, Left, or Right). secDir indicates the direction the
|
||||
// returned Edge should be pointing (i.e. the order of its XY's) and must be
|
||||
// perpendicular to dir
|
||||
func (r Rect) Edge(dir, secDir XY) Edge {
|
||||
var e Edge
|
||||
switch dir {
|
||||
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:
|
||||
a, b = r.Corner(Left, Down), r.Corner(Right, Down)
|
||||
e[0], e[1] = r.Corner(Left, Down), r.Corner(Right, Down)
|
||||
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:
|
||||
a, b = r.Corner(Right, Up), r.Corner(Right, Down)
|
||||
e[0], e[1] = r.Corner(Right, Up), r.Corner(Right, Down)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported direction: %#v", dir))
|
||||
}
|
||||
|
||||
mid := a.Midpoint(b, rounder)
|
||||
return mid
|
||||
switch secDir {
|
||||
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 {
|
||||
|
@ -12,15 +12,29 @@ func TestRect(t *T) {
|
||||
Size: XY{2, 2},
|
||||
}
|
||||
|
||||
assert.Equal(t, 2, r.Edge(Up))
|
||||
assert.Equal(t, 3, r.Edge(Down))
|
||||
assert.Equal(t, 1, r.Edge(Left))
|
||||
assert.Equal(t, 2, r.Edge(Right))
|
||||
assert.Equal(t, 2, r.EdgeCoord(Up))
|
||||
assert.Equal(t, 3, r.EdgeCoord(Down))
|
||||
assert.Equal(t, 1, r.EdgeCoord(Left))
|
||||
assert.Equal(t, 2, r.EdgeCoord(Right))
|
||||
|
||||
assert.Equal(t, XY{1, 2}, r.Corner(Left, Up))
|
||||
assert.Equal(t, XY{1, 3}, r.Corner(Left, Down))
|
||||
assert.Equal(t, XY{2, 2}, r.Corner(Right, Up))
|
||||
assert.Equal(t, XY{2, 3}, r.Corner(Right, Down))
|
||||
lu := XY{1, 2}
|
||||
ld := XY{1, 3}
|
||||
ru := XY{2, 2}
|
||||
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) {
|
||||
|
40
gim/line.go
40
gim/line.go
@ -39,16 +39,19 @@ var arrows = map[geo.XY]string{
|
||||
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
|
||||
// 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)
|
||||
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))
|
||||
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 {
|
||||
rels[i] *= -1
|
||||
}
|
||||
@ -74,13 +77,22 @@ func (l line) secondaryDir(primary geo.XY) geo.XY {
|
||||
return secondary
|
||||
}
|
||||
|
||||
func (l line) draw(term *terminal.Terminal, dir geo.XY) {
|
||||
from, to := *l[0], *l[1]
|
||||
dirSec := l.secondaryDir(dir)
|
||||
//func (l line) startEnd(flowDir, secFlowDir geo.XY) (geo.XY, geo.XY) {
|
||||
// from, to := *(l.from), *(l.to)
|
||||
// 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)
|
||||
|
||||
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
|
||||
midPrim := along(mid, dir)
|
||||
midPrim := along(mid, flowDir)
|
||||
endSec := along(end, dirSec)
|
||||
for curr := start; curr != end; {
|
||||
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 {
|
||||
curr = curr.Add(dirSec)
|
||||
continue
|
||||
}
|
||||
}
|
||||
curr = curr.Add(dir)
|
||||
curr = curr.Add(flowDir)
|
||||
}
|
||||
|
||||
for i, pt := range pts {
|
||||
var str string
|
||||
switch {
|
||||
case i == 0:
|
||||
str = edgeSegments[dir]
|
||||
str = edgeSegments[flowDir]
|
||||
case i == len(pts)-1:
|
||||
str = arrows[dir]
|
||||
str = arrows[flowDir]
|
||||
default:
|
||||
prev, next := pts[i-1], pts[i+1]
|
||||
seg := [2]geo.XY{
|
||||
|
@ -150,9 +150,9 @@ func (view *view) draw(term *terminal.Terminal) {
|
||||
var lines []line
|
||||
for _, b := range boxes {
|
||||
v := boxesM[b]
|
||||
for _, e := range v.In {
|
||||
for i, e := range v.In {
|
||||
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)
|
||||
}
|
||||
for _, line := range lines {
|
||||
line.draw(term, view.primFlowDir)
|
||||
line.draw(term, view.primFlowDir, view.secFlowDir)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user