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
|
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
|
||||||
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
40
gim/line.go
40
gim/line.go
@ -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{
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user