1
0
mirror of https://github.com/mum4k/termdash.git synced 2025-04-25 13:48:50 +08:00

A segment display that can show three dots.

This commit is contained in:
Jakub Sobon 2019-04-29 23:11:55 -04:00
parent a591f95d29
commit 939f8fe199
No known key found for this signature in database
GPG Key ID: F2451A77FB05D3B7
3 changed files with 163 additions and 2 deletions

View File

@ -0,0 +1,116 @@
// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dotseg
// attributes.go calculates attributes needed when determining placement of
// segments.
import (
"fmt"
"image"
"math"
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/internal/alignfor"
"github.com/mum4k/termdash/internal/area"
"github.com/mum4k/termdash/internal/segdisp"
)
// segmentSize given an area for the display determines the size of individual
// segments, i.e. the width of a vertical or the height of a horizontal
// segment.
func segmentSize(ar image.Rectangle) int {
// widthPerc is the relative width of a segment to the width of the canvas.
const widthPerc = 9
s := int(math.Round(float64(ar.Dx()) * widthPerc / 100))
if s > 3 && s%2 == 0 {
// Segments with odd number of pixels in their width/height look
// better, since the spike at the top of their slopes has only one
// pixel.
s++
}
return s
}
// 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 {
// bcAr is the area the attributes were created for.
bcAr image.Rectangle
// segSize is the width of a vertical or height of a horizontal segment.
segSize int
}
// 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
return &attributes{
bcAr: bcAr,
segSize: segSize,
}
}
// segArea returns the area for the specified segment.
func (a *attributes) segArea(seg Segment) (image.Rectangle, error) {
// 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,
)
mid, err := alignfor.Rectangle(a.bcAr, dotAr, align.HorizontalCenter, align.VerticalMiddle)
if err != nil {
return image.ZR, err
}
// 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)))
switch seg {
case D1:
moved, err := area.MoveUp(mid, moveBy)
if err != nil {
return image.ZR, err
}
return moved, nil
case D2:
moved, err := area.MoveDown(mid, moveBy)
if err != nil {
return image.ZR, err
}
return moved, nil
case D3:
bot, err := alignfor.Rectangle(a.bcAr, dotAr, align.HorizontalCenter, align.VerticalBottom)
if err != nil {
return image.ZR, err
}
return bot, nil
default:
return image.ZR, fmt.Errorf("cannot calculate area for %v(%d)", seg, seg)
}
}

View File

@ -44,6 +44,7 @@ import (
"github.com/mum4k/termdash/cell"
"github.com/mum4k/termdash/internal/canvas"
"github.com/mum4k/termdash/internal/segdisp"
"github.com/mum4k/termdash/internal/segdisp/segment"
)
// Segment represents a single segment in the display.
@ -214,10 +215,29 @@ func (d *Display) Draw(cvs *canvas.Canvas, opts ...Option) error {
o.set(d)
}
bc, _, err := segdisp.ToBraille(cvs)
bc, bcAr, err := segdisp.ToBraille(cvs)
if err != nil {
return err
}
return bc.CopyTo(cvs)
attr := newAttributes(bcAr)
for seg, isSet := range d.segments {
if !isSet {
continue
}
ar, err := attr.segArea(seg)
if err != nil {
return err
}
if err := segment.HV(bc, ar, segment.Vertical, segment.CellOpts(d.cellOpts...)); err != nil {
return err
}
}
if err := bc.CopyTo(cvs); err != nil {
return err
}
return nil
//draw.Border(cvs, cvs.Area())
}

View File

@ -22,9 +22,12 @@ import (
"github.com/kylelemons/godebug/pretty"
"github.com/mum4k/termdash/internal/area"
"github.com/mum4k/termdash/internal/canvas"
"github.com/mum4k/termdash/internal/canvas/braille/testbraille"
"github.com/mum4k/termdash/internal/canvas/testcanvas"
"github.com/mum4k/termdash/internal/faketerm"
"github.com/mum4k/termdash/internal/segdisp"
"github.com/mum4k/termdash/internal/segdisp/segment"
"github.com/mum4k/termdash/internal/segdisp/segment/testsegment"
)
func TestDraw(t *testing.T) {
@ -102,6 +105,28 @@ func TestDraw(t *testing.T) {
desc: "empty when no segments set",
cellCanvas: image.Rect(0, 0, segdisp.MinCols, segdisp.MinRows),
},
{
desc: "smallest valid display 6x5, all segments",
cellCanvas: image.Rect(0, 0, segdisp.MinCols, segdisp.MinRows),
update: func(d *Display) error {
for _, seg := range AllSegments() {
if err := d.SetSegment(seg); err != nil {
return err
}
}
return nil
},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
bc := testbraille.MustNew(ft.Area())
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
testbraille.MustApply(bc, ft)
return ft
},
},
}
for _, tc := range tests {