diff --git a/gg/gg.go b/gg/gg.go index 950d3c8..242ff66 100644 --- a/gg/gg.go +++ b/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 diff --git a/gim/geo/rect.go b/gim/geo/rect.go index 5c45fc3..a38f72b 100644 --- a/gim/geo/rect.go +++ b/gim/geo/rect.go @@ -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 { diff --git a/gim/geo/rect_test.go b/gim/geo/rect_test.go index 208cc37..d7301bf 100644 --- a/gim/geo/rect_test.go +++ b/gim/geo/rect_test.go @@ -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) { diff --git a/gim/line.go b/gim/line.go index 7937302..8f241c1 100644 --- a/gim/line.go +++ b/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{ diff --git a/gim/view.go b/gim/view.go index 12c8327..41948ae 100644 --- a/gim/view.go +++ b/gim/view.go @@ -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) } }