diff --git a/gim/line.go b/gim/line.go index c7badd5..ebcf8e1 100644 --- a/gim/line.go +++ b/gim/line.go @@ -8,68 +8,26 @@ import ( type line struct { from, to *box fromI, toI int - body string -} - -// 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 secondaryDir(flowDir, start, end geo.XY) geo.XY { - var perpDir geo.XY - perpDir[0], perpDir[1] = flowDir[1], flowDir[0] - return end.Sub(start).Mul(perpDir.Abs()).Unit() + bodyBuf *terminal.Buffer } func (l line) draw(buf *terminal.Buffer, flowDir, secFlowDir geo.XY) { from, to := *(l.from), *(l.to) - start := from.rect().Edge(flowDir, secFlowDir)[0].Add(secFlowDir.Scale(l.fromI*2 + 1)) - end := to.rect().Edge(flowDir.Inv(), secFlowDir)[0].Add(secFlowDir.Scale(l.toI*2 + 1)) - dirSec := secondaryDir(flowDir, start, end) - mid := start.Midpoint(end, rounder) + end := to.rect().Edge(flowDir.Inv(), secFlowDir)[0] + end = end.Add(flowDir.Inv()) + end = end.Add(secFlowDir.Scale(l.toI*2 + 1)) - along := func(xy, dir geo.XY) int { - if dir[0] != 0 { - return xy[0] - } - return xy[1] - } - - // collect the points along the line into an array - var pts []geo.XY - midPrim := along(mid, flowDir) - endSec := along(end, dirSec) - for curr := start; curr != end; { - pts = append(pts, curr) - if prim := along(curr, flowDir); prim == midPrim { - if sec := along(curr, dirSec); sec != endSec { - curr = curr.Add(dirSec) - continue - } - } - curr = curr.Add(flowDir) - } - - // draw each point - for i, pt := range pts { - var r rune - switch { - case i == 0: - r = terminal.SingleLine.Perpendicular(flowDir) - case i == len(pts)-1: - r = terminal.SingleLine.Arrow(flowDir) - default: - prev, next := pts[i-1], pts[i+1] - r = terminal.SingleLine.Segment(prev.Sub(pt), next.Sub(pt)) - } - buf.SetPos(pt) - buf.WriteRune(r) - } + buf.SetPos(start) + buf.WriteRune(terminal.SingleLine.Perpendicular(flowDir)) + buf.DrawLine(start.Add(flowDir), end.Add(flowDir.Inv()), flowDir, terminal.SingleLine) + buf.SetPos(end) + buf.WriteRune(terminal.SingleLine.Arrow(flowDir)) // draw the body - if l.body != "" { - bodyPos := mid.Add(geo.Left.Scale(len(l.body) / 2)) - buf.SetPos(bodyPos) - buf.WriteString(l.body) + if l.bodyBuf != nil { + mid := start.Midpoint(end, rounder) + bodyBufRect := geo.Rect{Size: l.bodyBuf.Size()} + buf.DrawBuffer(bodyBufRect.Centered(mid, rounder).TopLeft, l.bodyBuf) } } diff --git a/gim/terminal/shape.go b/gim/terminal/shape.go index 8981b81..a2fcd85 100644 --- a/gim/terminal/shape.go +++ b/gim/terminal/shape.go @@ -133,3 +133,60 @@ func (b *Buffer) DrawRect(r geo.Rect, ls LineStyle) { b.WriteString(horiz) b.WriteRune(ls.BottomRight) } + +// DrawLine draws a line from the start point to the ending one, primarily +// moving in the given direction, using the given LineStyle to do so. +func (b *Buffer) DrawLine(start, end, dir geo.XY, ls LineStyle) { + // given the "primary" direction the line should be headed, pick 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) + var perpDir geo.XY + perpDir[0], perpDir[1] = dir[1], dir[0] + dirSec := end.Sub(start).Mul(perpDir.Abs()).Unit() + + // TODO gross that this doesn't have some way of discovering the rounder. + // Maybe rounder should just be a global? ugh... + mid := start.Midpoint(end, geo.Round) + + along := func(xy, dir geo.XY) int { + if dir[0] != 0 { + return xy[0] + } + return xy[1] + } + + // collect the points along the line into an array + var pts []geo.XY + var curr geo.XY + midPrim := along(mid, dir) + endSec := along(end, dirSec) + for curr = start; curr != end; { + pts = append(pts, curr) + if prim := along(curr, dir); prim == midPrim { + if sec := along(curr, dirSec); sec != endSec { + curr = curr.Add(dirSec) + continue + } + } + curr = curr.Add(dir) + } + pts = append(pts, curr) // appending end + + // draw each point + for i, pt := range pts { + var prev, next geo.XY + switch { + case i == 0: + prev = pt.Add(dir.Inv()) + next = pts[i+1] + case i == len(pts)-1: + prev = pts[i-1] + next = pt.Add(dir) + default: + prev, next = pts[i-1], pts[i+1] + } + b.SetPos(pt) + b.WriteRune(ls.Segment(prev.Sub(pt), next.Sub(pt))) + } +} diff --git a/gim/view.go b/gim/view.go index 1e55e11..ca869cf 100644 --- a/gim/view.go +++ b/gim/view.go @@ -163,12 +163,14 @@ func (view *view) draw(buf *terminal.Buffer) { for i, e := range v.In { bFrom := boxesMr[e.From] fromI := findFromI(e.From, e) + buf := terminal.NewBuffer() + buf.WriteString(e.Value.V.(string)) lines = append(lines, line{ - from: bFrom, - fromI: fromI, - to: b, - toI: i, - body: e.Value.V.(string), + from: bFrom, + fromI: fromI, + to: b, + toI: i, + bodyBuf: buf, }) } }