Skip to content

Commit

Permalink
The SegmentDisplay now uses dot segment for dots and colons.
Browse files Browse the repository at this point in the history
  • Loading branch information
mum4k committed Apr 30, 2019
1 parent 939f8fe commit 6159007
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 60 deletions.
33 changes: 25 additions & 8 deletions internal/segdisp/dotseg/attributes.go
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/mum4k/termdash/internal/alignfor"
"github.com/mum4k/termdash/internal/area"
"github.com/mum4k/termdash/internal/segdisp"
"github.com/mum4k/termdash/internal/segdisp/sixteen"
)

// segmentSize given an area for the display determines the size of individual
Expand Down Expand Up @@ -53,30 +54,36 @@ type attributes struct {

// segSize is the width of a vertical or height of a horizontal segment.
segSize int

// sixteen are attributes of a 16-segment display when placed on the same
// area.
sixteen *sixteen.Attributes
}

// newAttributes calculates attributes needed to place the segments for the
// provided pixel area.
func newAttributes(bcAr image.Rectangle) *attributes {
// Dots have double width of normal segments to fill more space in the
// segment display.
segSize := segdisp.SegmentSize(bcAr) * 2

segSize := segdisp.SegmentSize(bcAr)
return &attributes{
bcAr: bcAr,
segSize: segSize,
sixteen: sixteen.NewAttributes(bcAr),
}
}

// segArea returns the area for the specified segment.
func (a *attributes) segArea(seg Segment) (image.Rectangle, error) {
// Dots have double width of normal segments to fill more space in the
// segment display.
segSize := a.segSize * 2

// An area representing the dot which gets aligned and moved into position
// below.
dotAr := image.Rect(
a.bcAr.Min.X,
a.bcAr.Min.Y,
a.bcAr.Min.X+a.segSize,
a.bcAr.Min.Y+a.segSize,
a.bcAr.Min.X+segSize,
a.bcAr.Min.Y+segSize,
)
mid, err := alignfor.Rectangle(a.bcAr, dotAr, align.HorizontalCenter, align.VerticalMiddle)
if err != nil {
Expand All @@ -86,7 +93,7 @@ func (a *attributes) segArea(seg Segment) (image.Rectangle, error) {
// moveBySize is the multiplier of segment size to determine by how many
// pixels to move D1 and D2 up and down from the center.
const moveBySize = 1.5
moveBy := int(math.Round(moveBySize * float64(a.segSize)))
moveBy := int(math.Round(moveBySize * float64(segSize)))
switch seg {
case D1:
moved, err := area.MoveUp(mid, moveBy)
Expand All @@ -103,12 +110,22 @@ func (a *attributes) segArea(seg Segment) (image.Rectangle, error) {
return moved, nil

case D3:
// Align at the middle of the bottom.
bot, err := alignfor.Rectangle(a.bcAr, dotAr, align.HorizontalCenter, align.VerticalBottom)
if err != nil {
return image.ZR, err
}

return bot, nil
// Shift up to where the sixteen segment actually places its bottom
// segments.
diff := bot.Min.Y - a.sixteen.VertBotY
// Shift further up by one segment size, since the dots have double width.
diff += a.segSize
moved, err := area.MoveUp(bot, diff)
if err != nil {
return image.ZR, err
}
return moved, nil

default:
return image.ZR, fmt.Errorf("cannot calculate area for %v(%d)", seg, seg)
Expand Down
7 changes: 1 addition & 6 deletions internal/segdisp/dotseg/dotseg.go
Expand Up @@ -234,10 +234,5 @@ func (d *Display) Draw(cvs *canvas.Canvas, opts ...Option) error {
return err
}
}

if err := bc.CopyTo(cvs); err != nil {
return err
}
return nil
//draw.Border(cvs, cvs.Area())
return bc.CopyTo(cvs)
}
2 changes: 1 addition & 1 deletion internal/segdisp/dotseg/dotseg_test.go
Expand Up @@ -122,7 +122,7 @@ func TestDraw(t *testing.T) {

testsegment.MustHV(bc, image.Rect(5, 6, 7, 8), segment.Horizontal) // D1
testsegment.MustHV(bc, image.Rect(5, 12, 7, 14), segment.Horizontal) // D2
testsegment.MustHV(bc, image.Rect(5, 18, 7, 20), segment.Horizontal) // D3
testsegment.MustHV(bc, image.Rect(5, 15, 7, 17), segment.Horizontal) // D3
testbraille.MustApply(bc, ft)
return ft
},
Expand Down
28 changes: 14 additions & 14 deletions internal/segdisp/sixteen/attributes.go
Expand Up @@ -51,10 +51,10 @@ var diaSegType = map[Segment]segment.DiagonalType{
L: segment.LeftToRight,
}

// attributes contains attributes needed to draw the segment display.
// Attributes contains attributes needed to draw the segment display.
// Refer to doc/segment_placement.svg for a visual aid and explanation of the
// usage of the square roots.
type attributes struct {
type Attributes struct {
// segSize is the width of a vertical or height of a horizontal segment.
segSize int

Expand Down Expand Up @@ -92,14 +92,14 @@ type attributes struct {
// vertCenY is the Y coordinate where the area of the segment vertically
// in the center starts, i.e. Y coordinate of G1 and G2.
vertCenY int
// vertBotY is the Y coordinate where the area of the segment vertically
// VertBotY is the Y coordinate where the area of the segment vertically
// at the bottom starts, i.e. Y coordinate of D1 and D2.
vertBotY int
VertBotY int
}

// newAttributes calculates attributes needed to place the segments for the
// NewAttributes calculates attributes needed to place the segments for the
// provided pixel area.
func newAttributes(bcAr image.Rectangle) *attributes {
func NewAttributes(bcAr image.Rectangle) *Attributes {
segSize := segdisp.SegmentSize(bcAr)

// diaPerc is the size of the diaGap in percentage of the segment's size.
Expand Down Expand Up @@ -147,7 +147,7 @@ func newAttributes(bcAr image.Rectangle) *attributes {
vertCenY := horizLeftX + longLen + offset
vertBotY := horizLeftX + longLen + ptp + longLen + offset

return &attributes{
return &Attributes{
segSize: segSize,
diaGap: diaGap,
segPeakDist: segPeakDist,
Expand All @@ -160,12 +160,12 @@ func newAttributes(bcAr image.Rectangle) *attributes {
horizMidX: horizMidX,
horizRightX: horizRightX,
vertCenY: vertCenY,
vertBotY: vertBotY,
VertBotY: vertBotY,
}
}

// hvSegArea returns the area for the specified horizontal or vertical segment.
func (a *attributes) hvSegArea(s Segment) image.Rectangle {
func (a *Attributes) hvSegArea(s Segment) image.Rectangle {
var (
start image.Point
length int
Expand Down Expand Up @@ -218,12 +218,12 @@ func (a *attributes) hvSegArea(s Segment) image.Rectangle {
length = a.longLen

case D1:
start = image.Point{a.horizLeftX, a.vertBotY}
start = image.Point{a.horizLeftX, a.VertBotY}
length = a.shortLen

case D2:
d1 := a.hvSegArea(D1)
start = image.Point{d1.Max.X + a.peakToPeak, a.vertBotY}
start = image.Point{d1.Max.X + a.peakToPeak, a.VertBotY}
length = a.shortLen

default:
Expand All @@ -235,7 +235,7 @@ func (a *attributes) hvSegArea(s Segment) image.Rectangle {

// hvArFromStart given start coordinates of a segment, its length and its type,
// determines its area.
func (a *attributes) hvArFromStart(start image.Point, s Segment, length int) image.Rectangle {
func (a *Attributes) hvArFromStart(start image.Point, s Segment, length int) image.Rectangle {
st := hvSegType[s]
switch st {
case segment.Horizontal:
Expand All @@ -248,7 +248,7 @@ func (a *attributes) hvArFromStart(start image.Point, s Segment, length int) ima
}

// diaSegArea returns the area for the specified diagonal segment.
func (a *attributes) diaSegArea(s Segment) image.Rectangle {
func (a *Attributes) diaSegArea(s Segment) image.Rectangle {
switch s {
case H:
return a.diaBetween(A1, F, J, G1)
Expand All @@ -266,7 +266,7 @@ func (a *attributes) diaSegArea(s Segment) image.Rectangle {

// diaBetween given four segments (two horizontal and two vertical) returns the
// area between them for a diagonal segment.
func (a *attributes) diaBetween(top, left, right, bottom Segment) image.Rectangle {
func (a *Attributes) diaBetween(top, left, right, bottom Segment) image.Rectangle {
topAr := a.hvSegArea(top)
leftAr := a.hvSegArea(left)
rightAr := a.hvSegArea(right)
Expand Down
3 changes: 2 additions & 1 deletion internal/segdisp/sixteen/sixteen.go
Expand Up @@ -137,6 +137,7 @@ var characterSegments = map[rune][]Segment{
'+': {J, G1, G2, M},
',': {N},
'-': {G1, G2},
'.': {D1},
'/': {N, K},

'0': {A1, A2, F, K, B, E, N, C, D1, D2},
Expand Down Expand Up @@ -386,7 +387,7 @@ func (d *Display) Draw(cvs *canvas.Canvas, opts ...Option) error {
return err
}

attr := newAttributes(bcAr)
attr := NewAttributes(bcAr)
var sOpts []segment.Option
if len(d.cellOpts) > 0 {
sOpts = append(sOpts, segment.CellOpts(d.cellOpts...))
Expand Down
14 changes: 7 additions & 7 deletions internal/segdisp/sixteen/sixteen_test.go
Expand Up @@ -893,7 +893,7 @@ func TestSetCharacter(t *testing.T) {
}{
{
desc: "fails on unsupported character",
char: '.',
char: '',
wantErr: true,
},
{
Expand Down Expand Up @@ -1642,15 +1642,15 @@ func TestSupportsChars(t *testing.T) {
},
{
desc: "supports some chars in the string",
str: " w.W :",
str: " wW :",
wantRes: false,
wantUnsupp: []rune{'.'},
wantUnsupp: []rune{''},
},
{
desc: "supports no chars in the string",
str: ".",
str: "",
wantRes: false,
wantUnsupp: []rune{'.'},
wantUnsupp: []rune{''},
},
}

Expand Down Expand Up @@ -1690,12 +1690,12 @@ func TestSanitize(t *testing.T) {
},
{
desc: "some characters are supported",
str: " w.W:",
str: " wW:",
want: " w W:",
},
{
desc: "no characters are supported",
str: ".",
str: "",
want: " ",
},
}
Expand Down
44 changes: 36 additions & 8 deletions widgets/segmentdisplay/segmentdisplay.go
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/mum4k/termdash/internal/attrrange"
"github.com/mum4k/termdash/internal/canvas"
"github.com/mum4k/termdash/internal/segdisp"
"github.com/mum4k/termdash/internal/segdisp/dotseg"
"github.com/mum4k/termdash/internal/segdisp/sixteen"
"github.com/mum4k/termdash/terminal/terminalapi"
"github.com/mum4k/termdash/widgetapi"
Expand Down Expand Up @@ -55,6 +56,10 @@ type SegmentDisplay struct {
// time Draw was called.
lastCanFit int

// dotChars are characters that are drawn using the dot segment.
// All other characters are draws using the 16-segment display.
dotChars map[rune]bool

// mu protects the widget.
mu sync.Mutex

Expand All @@ -71,9 +76,15 @@ func New(opts ...Option) (*SegmentDisplay, error) {
if err := opt.validate(); err != nil {
return nil, err
}

dotChars := map[rune]bool{}
for _, r := range dotseg.SupportedChars() {
dotChars[r] = true
}
return &SegmentDisplay{
wOptsTracker: attrrange.NewTracker(),
opts: opt,
dotChars: dotChars,
}, nil
}

Expand Down Expand Up @@ -225,11 +236,6 @@ func (sd *SegmentDisplay) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
break
}

disp := sixteen.New()
if err := disp.SetCharacter(c); err != nil {
return fmt.Errorf("disp.SetCharacter => %v", err)
}

endX := startX + segAr.segment.Dx()
ar := image.Rect(startX, aligned.Min.Y, endX, aligned.Max.Y)
startX = endX
Expand All @@ -251,9 +257,8 @@ func (sd *SegmentDisplay) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
optRange = or
}
wOpts := sd.givenWOpts[optRange.AttrIdx]

if err := disp.Draw(dCvs, sixteen.CellOpts(wOpts.cellOpts...)); err != nil {
return fmt.Errorf("disp.Draw => %v", err)
if err := sd.drawChar(dCvs, c, wOpts); err != nil {
return err
}

if err := dCvs.CopyTo(cvs); err != nil {
Expand All @@ -263,6 +268,29 @@ func (sd *SegmentDisplay) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
return nil
}

// drawChar draws a single character onto the provided canvas.
func (sd *SegmentDisplay) drawChar(dCvs *canvas.Canvas, c rune, wOpts *writeOptions) error {
if sd.dotChars[c] {
disp := dotseg.New()
if err := disp.SetCharacter(c); err != nil {
return fmt.Errorf("dotseg.Display.SetCharacter => %v", err)
}
if err := disp.Draw(dCvs, dotseg.CellOpts(wOpts.cellOpts...)); err != nil {
return fmt.Errorf("dotseg.Display..Draw => %v", err)
}
return nil
}

disp := sixteen.New()
if err := disp.SetCharacter(c); err != nil {
return fmt.Errorf("sixteen.Display.SetCharacter => %v", err)
}
if err := disp.Draw(dCvs, sixteen.CellOpts(wOpts.cellOpts...)); err != nil {
return fmt.Errorf("sixteen.Display.Draw => %v", err)
}
return nil
}

// Keyboard input isn't supported on the SegmentDisplay widget.
func (*SegmentDisplay) Keyboard(k *terminalapi.Keyboard) error {
return errors.New("the SegmentDisplay widget doesn't support keyboard events")
Expand Down

0 comments on commit 6159007

Please sign in to comment.