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.
|
||||
|
||||
/*
|
||||
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.
|
||||
|
||||
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
|
||||
| / | \ |
|
||||
| / | \ |
|
||||
------- ------- o
|
||||
D1 D2 DP
|
||||
------- -------
|
||||
D1 D2
|
||||
*/
|
||||
package segdisp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"log"
|
||||
|
||||
"github.com/mum4k/termdash/area"
|
||||
"github.com/mum4k/termdash/canvas"
|
||||
"github.com/mum4k/termdash/canvas/braille"
|
||||
"github.com/mum4k/termdash/cell"
|
||||
"github.com/mum4k/termdash/numbers"
|
||||
"github.com/mum4k/termdash/segdisp/segment"
|
||||
)
|
||||
|
||||
// Segment represents a single segment in the display.
|
||||
|
@ -75,7 +80,6 @@ var segmentNames = map[Segment]string{
|
|||
L: "L",
|
||||
M: "M",
|
||||
N: "M",
|
||||
DP: "DP",
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -97,7 +101,6 @@ const (
|
|||
L
|
||||
M
|
||||
N
|
||||
DP
|
||||
|
||||
segmentMax // Used for validation.
|
||||
)
|
||||
|
@ -108,6 +111,15 @@ type Option interface {
|
|||
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.
|
||||
type option func(*Display)
|
||||
|
||||
|
@ -188,24 +200,65 @@ func (d *Display) ToggleSegment(s Segment) error {
|
|||
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 (
|
||||
// MinColPixels is the smallest valid amount of columns in pixels.
|
||||
MinColPixels = 4 * braille.ColMult
|
||||
// MinRowPixels is the smallest valid amount of rows in pixels.
|
||||
MinRowPixels = 3 * braille.RowMult
|
||||
// MinCols is the smallest valid amount of columns in a cell area.
|
||||
MinCols = 4
|
||||
// MinRowPixels is the smallest valid amount of rows in a cell area.
|
||||
MinRows = 3
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (d *Display) Draw(bc *braille.Canvas, opts ...Option) error {
|
||||
if size := bc.Size(); size.X < MinColPixels || size.Y < MinRowPixels {
|
||||
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)
|
||||
func (d *Display) Draw(cvs *canvas.Canvas, opts ...Option) error {
|
||||
ar, err := Required(cvs.Area())
|
||||
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 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.
|
||||
|
||||
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) {
|
||||
t.Skip()
|
||||
//t.Skip()
|
||||
tests := []struct {
|
||||
desc string
|
||||
cellCanvas image.Rectangle
|
||||
|
@ -986,26 +986,26 @@ func TestMultipleSegments(t *testing.T) {
|
|||
desc: "12-segment display, more spacing",
|
||||
cellCanvas: image.Rect(0, 0, 16, 12),
|
||||
hvSegments: []hvSegment{
|
||||
//{image.Rect(3, 0, 15, 4), SegmentTypeHorizontal}, // A1
|
||||
//{image.Rect(17, 0, 29, 4), SegmentTypeHorizontal}, // A2
|
||||
{image.Rect(3, 0, 15, 4), SegmentTypeHorizontal}, // A1
|
||||
{image.Rect(17, 0, 29, 4), SegmentTypeHorizontal}, // A2
|
||||
|
||||
{image.Rect(0, 3, 4, 23), SegmentTypeVertical}, // F
|
||||
//{image.Rect(14, 3, 18, 23), SegmentTypeVertical}, // J
|
||||
{image.Rect(0, 3, 4, 23), SegmentTypeVertical}, // F
|
||||
{image.Rect(14, 3, 18, 23), SegmentTypeVertical}, // J
|
||||
{image.Rect(28, 3, 32, 23), SegmentTypeVertical}, // B
|
||||
|
||||
//{image.Rect(3, 22, 15, 26), SegmentTypeHorizontal}, // G1
|
||||
//{image.Rect(17, 22, 29, 26), SegmentTypeHorizontal}, // G2
|
||||
{image.Rect(3, 22, 15, 26), SegmentTypeHorizontal}, // G1
|
||||
{image.Rect(17, 22, 29, 26), SegmentTypeHorizontal}, // G2
|
||||
|
||||
{image.Rect(0, 25, 4, 45), SegmentTypeVertical}, // E
|
||||
//{image.Rect(14, 25, 18, 45), SegmentTypeVertical}, // M
|
||||
{image.Rect(0, 25, 4, 45), SegmentTypeVertical}, // E
|
||||
{image.Rect(14, 25, 18, 45), SegmentTypeVertical}, // M
|
||||
{image.Rect(28, 25, 32, 45), SegmentTypeVertical}, // C
|
||||
|
||||
//{image.Rect(3, 44, 15, 48), SegmentTypeHorizontal}, // D1
|
||||
//{image.Rect(17, 44, 29, 48), SegmentTypeHorizontal}, // D2
|
||||
{image.Rect(3, 44, 15, 48), SegmentTypeHorizontal}, // D1
|
||||
{image.Rect(17, 44, 29, 48), SegmentTypeHorizontal}, // D2
|
||||
},
|
||||
diagSegments: []diagSegment{
|
||||
//{image.Rect(4, 4, 14, 22), 4, DiagonalTypeLeftToRight}, // H
|
||||
//{image.Rect(18, 22, 28, 4), 4, DiagonalTypeRightToLeft}, // K
|
||||
{image.Rect(4, 4, 14, 22), 4, DiagonalTypeLeftToRight}, // H
|
||||
{image.Rect(18, 22, 28, 4), 4, DiagonalTypeRightToLeft}, // K
|
||||
{image.Rect(4, 44, 14, 26), 4, DiagonalTypeRightToLeft}, // N
|
||||
{image.Rect(18, 26, 28, 44), 4, DiagonalTypeLeftToRight}, // L
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue