mirror of https://github.com/mum4k/termdash.git
Partial implementation of segdisp.
This commit is contained in:
parent
5c02803221
commit
8cdc34fb28
|
@ -13,9 +13,9 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Package segdisp simulates a 16-segment display drawn on a braille canvas.
|
Package segdisp simulates a 16-segment display drawn on a canvas.
|
||||||
|
|
||||||
Given a braille canvas, determines the placement and size of the individual
|
Given a canvas, determines the placement and size of the individual
|
||||||
segments and exposes API that can turn individual segments on and off.
|
segments and exposes API that can turn individual segments on and off.
|
||||||
|
|
||||||
The following outlines segments in the display and their names.
|
The following outlines segments in the display and their names.
|
||||||
|
@ -33,17 +33,22 @@ The following outlines segments in the display and their names.
|
||||||
E | N M L | C
|
E | N M L | C
|
||||||
| / | \ |
|
| / | \ |
|
||||||
| / | \ |
|
| / | \ |
|
||||||
------- ------- o
|
------- -------
|
||||||
D1 D2 DP
|
D1 D2
|
||||||
*/
|
*/
|
||||||
package segdisp
|
package segdisp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/mum4k/termdash/area"
|
||||||
|
"github.com/mum4k/termdash/canvas"
|
||||||
"github.com/mum4k/termdash/canvas/braille"
|
"github.com/mum4k/termdash/canvas/braille"
|
||||||
"github.com/mum4k/termdash/cell"
|
"github.com/mum4k/termdash/cell"
|
||||||
|
"github.com/mum4k/termdash/numbers"
|
||||||
|
"github.com/mum4k/termdash/segdisp/segment"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Segment represents a single segment in the display.
|
// Segment represents a single segment in the display.
|
||||||
|
@ -75,7 +80,6 @@ var segmentNames = map[Segment]string{
|
||||||
L: "L",
|
L: "L",
|
||||||
M: "M",
|
M: "M",
|
||||||
N: "M",
|
N: "M",
|
||||||
DP: "DP",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -97,7 +101,6 @@ const (
|
||||||
L
|
L
|
||||||
M
|
M
|
||||||
N
|
N
|
||||||
DP
|
|
||||||
|
|
||||||
segmentMax // Used for validation.
|
segmentMax // Used for validation.
|
||||||
)
|
)
|
||||||
|
@ -108,6 +111,15 @@ type Option interface {
|
||||||
set(*Display)
|
set(*Display)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllSegments returns all 16 segments in an undefined order.
|
||||||
|
func AllSegments() []Segment {
|
||||||
|
var res []Segment
|
||||||
|
for s := range segmentNames {
|
||||||
|
res = append(res, s)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// option implements Option.
|
// option implements Option.
|
||||||
type option func(*Display)
|
type option func(*Display)
|
||||||
|
|
||||||
|
@ -188,24 +200,65 @@ func (d *Display) ToggleSegment(s Segment) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimum valid size of braille canvas in order to draw the segment display.
|
// Minimum valid size of a cell canvas in order to draw the segment display.
|
||||||
const (
|
const (
|
||||||
// MinColPixels is the smallest valid amount of columns in pixels.
|
// MinCols is the smallest valid amount of columns in a cell area.
|
||||||
MinColPixels = 4 * braille.ColMult
|
MinCols = 4
|
||||||
// MinRowPixels is the smallest valid amount of rows in pixels.
|
// MinRowPixels is the smallest valid amount of rows in a cell area.
|
||||||
MinRowPixels = 3 * braille.RowMult
|
MinRows = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// Draw draws the current state of the segment display onto the canvas.
|
// Draw draws the current state of the segment display onto the canvas.
|
||||||
// The canvas must be at 4x3 cells, or an error will be returned.
|
// The canvas must be at least MinCols x MinRows cells, or an error will be
|
||||||
|
// returned.
|
||||||
// Any options provided to draw overwrite the values provided to New.
|
// Any options provided to draw overwrite the values provided to New.
|
||||||
func (d *Display) Draw(bc *braille.Canvas, opts ...Option) error {
|
func (d *Display) Draw(cvs *canvas.Canvas, opts ...Option) error {
|
||||||
if size := bc.Size(); size.X < MinColPixels || size.Y < MinRowPixels {
|
ar, err := Required(cvs.Area())
|
||||||
return fmt.Errorf("the canvas size %v is too small for the segment display, need at least %d columns and %d rows in pixels", size, MinColPixels, MinRowPixels)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bc, err := braille.New(ar)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("braille.New => %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bcAr := bc.Area()
|
||||||
|
sw := segWidth(bcAr)
|
||||||
|
half := bcAr.Dx() / 2
|
||||||
|
log.Printf("bcAr:%v, sw:%d, half:%d", bcAr, sw, half)
|
||||||
|
|
||||||
|
a1 := image.Rect(sw-1, 0, half-sw/2, sw)
|
||||||
|
a2 := image.Rect(half+sw/2, 0, bcAr.Max.X-1, sw)
|
||||||
|
log.Printf("a1:%v", a1)
|
||||||
|
log.Printf("a2:%v", a2)
|
||||||
|
for _, segAr := range []image.Rectangle{a1, a2} {
|
||||||
|
if err := segment.HV(bc, segAr, segment.SegmentTypeHorizontal); err != nil {
|
||||||
|
return fmt.Errorf("segment.HV => %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine line width.
|
|
||||||
// Determine gap width.
|
// Determine gap width.
|
||||||
// Determine length of short and long segment.
|
// Determine length of short and long segment.
|
||||||
return errors.New("unimplemented")
|
return bc.CopyTo(cvs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required, when given an area of cells, returns either an area of the same
|
||||||
|
// size or a smaller area that is required to draw one display.
|
||||||
|
// Returns a smaller area when the provided area didn;t have the required
|
||||||
|
// aspect ratio.
|
||||||
|
// Returns an error if the area is too small to draw a segment display.
|
||||||
|
func Required(cellArea image.Rectangle) (image.Rectangle, error) {
|
||||||
|
ar := area.WithRatio(cellArea, image.Point{MinCols, MinRows})
|
||||||
|
if ar.Empty() {
|
||||||
|
return image.ZR, fmt.Errorf("cell area %v is to small to draw the segment display, need at least %d x %d cells", cellArea, MinCols, MinRows)
|
||||||
|
}
|
||||||
|
return ar, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// segWidth given an area for the display determines the width of individual segments.
|
||||||
|
func segWidth(ar image.Rectangle) int {
|
||||||
|
// widthPerc is the relative width of a segment to the width of the canvas.
|
||||||
|
const widthPerc = 10
|
||||||
|
return int(numbers.Round(float64(ar.Dx()) * 10 / 100))
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,3 +13,94 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package segdisp
|
package segdisp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mum4k/termdash/area"
|
||||||
|
"github.com/mum4k/termdash/canvas"
|
||||||
|
"github.com/mum4k/termdash/terminal/faketerm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDraw(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
opts []Option
|
||||||
|
drawOpts []Option
|
||||||
|
cellCanvas image.Rectangle
|
||||||
|
// If not nil, called before Draw is called - can set, clear or toggle segments.
|
||||||
|
update func(*Display) error
|
||||||
|
want func(size image.Point) *faketerm.Terminal
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "smallest display, all segments",
|
||||||
|
cellCanvas: image.Rect(0, 0, 4, 3),
|
||||||
|
update: func(d *Display) error {
|
||||||
|
for _, s := range AllSegments() {
|
||||||
|
if err := d.SetSegment(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "16x12, all segments",
|
||||||
|
cellCanvas: image.Rect(0, 0, 16, 12),
|
||||||
|
update: func(d *Display) error {
|
||||||
|
for _, s := range AllSegments() {
|
||||||
|
if err := d.SetSegment(s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
|
d := New(tc.opts...)
|
||||||
|
if tc.update != nil {
|
||||||
|
if err := tc.update(d); err != nil {
|
||||||
|
t.Fatalf("tc.update => unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cvs, err := canvas.New(tc.cellCanvas)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("canvas.New => unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
err := d.Draw(cvs)
|
||||||
|
if (err != nil) != tc.wantErr {
|
||||||
|
t.Errorf("Draw => unexpected error: %v, wantErr: %v", err, tc.wantErr)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size := area.Size(tc.cellCanvas)
|
||||||
|
want := faketerm.MustNew(size)
|
||||||
|
if tc.want != nil {
|
||||||
|
want = tc.want(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := faketerm.New(size)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("faketerm.New => unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if err := cvs.Apply(got); err != nil {
|
||||||
|
t.Fatalf("bc.Apply => unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if diff := faketerm.Diff(want, got); diff != "" {
|
||||||
|
t.Fatalf("Draw => %v", diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -974,7 +974,7 @@ type diagSegment struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultipleSegments(t *testing.T) {
|
func TestMultipleSegments(t *testing.T) {
|
||||||
t.Skip()
|
//t.Skip()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
cellCanvas image.Rectangle
|
cellCanvas image.Rectangle
|
||||||
|
@ -986,26 +986,26 @@ func TestMultipleSegments(t *testing.T) {
|
||||||
desc: "12-segment display, more spacing",
|
desc: "12-segment display, more spacing",
|
||||||
cellCanvas: image.Rect(0, 0, 16, 12),
|
cellCanvas: image.Rect(0, 0, 16, 12),
|
||||||
hvSegments: []hvSegment{
|
hvSegments: []hvSegment{
|
||||||
//{image.Rect(3, 0, 15, 4), SegmentTypeHorizontal}, // A1
|
{image.Rect(3, 0, 15, 4), SegmentTypeHorizontal}, // A1
|
||||||
//{image.Rect(17, 0, 29, 4), SegmentTypeHorizontal}, // A2
|
{image.Rect(17, 0, 29, 4), SegmentTypeHorizontal}, // A2
|
||||||
|
|
||||||
{image.Rect(0, 3, 4, 23), SegmentTypeVertical}, // F
|
{image.Rect(0, 3, 4, 23), SegmentTypeVertical}, // F
|
||||||
//{image.Rect(14, 3, 18, 23), SegmentTypeVertical}, // J
|
{image.Rect(14, 3, 18, 23), SegmentTypeVertical}, // J
|
||||||
{image.Rect(28, 3, 32, 23), SegmentTypeVertical}, // B
|
{image.Rect(28, 3, 32, 23), SegmentTypeVertical}, // B
|
||||||
|
|
||||||
//{image.Rect(3, 22, 15, 26), SegmentTypeHorizontal}, // G1
|
{image.Rect(3, 22, 15, 26), SegmentTypeHorizontal}, // G1
|
||||||
//{image.Rect(17, 22, 29, 26), SegmentTypeHorizontal}, // G2
|
{image.Rect(17, 22, 29, 26), SegmentTypeHorizontal}, // G2
|
||||||
|
|
||||||
{image.Rect(0, 25, 4, 45), SegmentTypeVertical}, // E
|
{image.Rect(0, 25, 4, 45), SegmentTypeVertical}, // E
|
||||||
//{image.Rect(14, 25, 18, 45), SegmentTypeVertical}, // M
|
{image.Rect(14, 25, 18, 45), SegmentTypeVertical}, // M
|
||||||
{image.Rect(28, 25, 32, 45), SegmentTypeVertical}, // C
|
{image.Rect(28, 25, 32, 45), SegmentTypeVertical}, // C
|
||||||
|
|
||||||
//{image.Rect(3, 44, 15, 48), SegmentTypeHorizontal}, // D1
|
{image.Rect(3, 44, 15, 48), SegmentTypeHorizontal}, // D1
|
||||||
//{image.Rect(17, 44, 29, 48), SegmentTypeHorizontal}, // D2
|
{image.Rect(17, 44, 29, 48), SegmentTypeHorizontal}, // D2
|
||||||
},
|
},
|
||||||
diagSegments: []diagSegment{
|
diagSegments: []diagSegment{
|
||||||
//{image.Rect(4, 4, 14, 22), 4, DiagonalTypeLeftToRight}, // H
|
{image.Rect(4, 4, 14, 22), 4, DiagonalTypeLeftToRight}, // H
|
||||||
//{image.Rect(18, 22, 28, 4), 4, DiagonalTypeRightToLeft}, // K
|
{image.Rect(18, 22, 28, 4), 4, DiagonalTypeRightToLeft}, // K
|
||||||
{image.Rect(4, 44, 14, 26), 4, DiagonalTypeRightToLeft}, // N
|
{image.Rect(4, 44, 14, 26), 4, DiagonalTypeRightToLeft}, // N
|
||||||
{image.Rect(18, 26, 28, 44), 4, DiagonalTypeLeftToRight}, // L
|
{image.Rect(18, 26, 28, 44), 4, DiagonalTypeLeftToRight}, // L
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue