Skip to content

Commit

Permalink
Chapter 3: Expand the canvas size
Browse files Browse the repository at this point in the history
The canvas size is noe larger than the size of the Sense HAT 8X8 LED display.

The HAT LED display is now a floating window that shows only a subset of the picture.

Added three new program flags:
* The -width flag sets the picture width in pixels. The default value is 24 (three windows). The valid values are 8 - 40. a value below 8 will be replaced by 8. A value above 40 will cause error and the application won't start.
* The -height flag sets the picture height in pixels. The default value is 24 (three windows). The valid values are 8 - 40. a value below 8 will be replaced by 8. A value above 40 will cause error and the application won't start.
* The -port sets the application port. The default value is 8080.

Signed-off-by: Nahshon Unna-Tsameret <nahsh.ut@gmail.com>
  • Loading branch information
nunnatsa committed Mar 25, 2021
1 parent 49cd661 commit 1b12aac
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 127 deletions.
4 changes: 2 additions & 2 deletions controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Controller struct {
clientEvents <-chan webapp.ClientEvent
}

func NewController(notifier *notifier.Notifier, clientEvents <-chan webapp.ClientEvent) *Controller {
func NewController(notifier *notifier.Notifier, clientEvents <-chan webapp.ClientEvent, canvasWidth uint8, canvasHeight uint8) *Controller {
je := make(chan hat.Event)
se := make(chan hat.DisplayMessage)

Expand All @@ -33,7 +33,7 @@ func NewController(notifier *notifier.Notifier, clientEvents <-chan webapp.Clien
joystickEvents: je,
screenEvents: se,
done: make(chan bool),
state: state.NewState(),
state: state.NewState(canvasWidth, canvasHeight),
notifier: notifier,
clientEvents: clientEvents,
}
Expand Down
28 changes: 14 additions & 14 deletions controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

func TestControllerStart(t *testing.T) {
s := state.NewState()
s := state.NewState(40, 24)
je := make(chan hat.Event)
se := make(chan hat.DisplayMessage)
n := notifier.NewNotifier()
Expand Down Expand Up @@ -49,54 +49,54 @@ func TestControllerStart(t *testing.T) {
c.Start()

msg := <-se
if msg.CursorX != x {
t.Errorf("msg.CursorX should be %d but it's %d", x, msg.CursorX)
if msg.CursorX != x-s.Window.X {
t.Errorf("msg.CursorX should be %d but it's %d", x-s.Window.X, msg.CursorX)
}

ce <- webapp.ClientEventRegistered(client1)
<-checkNotifications(t, reg1, x, y)
ce <- webapp.ClientEventRegistered(client2)
<-checkNotifications(t, reg2, x, y)

if msg.CursorY != y {
t.Errorf("msg.CursorY should be %d but it's %d", y, msg.CursorY)
if msg.CursorY != y-s.Window.Y {
t.Errorf("msg.CursorY should be %d but it's %d", y-s.Window.Y, msg.CursorY)
}

hatMock.MoveDown()
msg = <-se
if msg.CursorY != y+1 {
t.Errorf("msg.CursorY should be %d but it's %d", y+1, msg.CursorY)
if msg.CursorY != y+1-s.Window.Y {
t.Errorf("msg.CursorY should be %d but it's %d", y+1-s.Window.Y, msg.CursorY)
}
<-checkNotifications(t, reg1, x, y+1)
<-checkNotifications(t, reg2, x, y+1)

hatMock.MoveUp()
msg = <-se
if msg.CursorY != y {
t.Errorf("msg.CursorY should be %d but it's %d", y, msg.CursorY)
if msg.CursorY != y-s.Window.Y {
t.Errorf("msg.CursorY should be %d but it's %d", y-s.Window.Y, msg.CursorY)
}
<-checkNotifications(t, reg1, x, y)
<-checkNotifications(t, reg2, x, y)

hatMock.MoveRight()
msg = <-se
if msg.CursorX != x+1 {
t.Errorf("msg.CursorX should be %d but it's %d", x+1, msg.CursorY)
if msg.CursorX != x+1-s.Window.X {
t.Errorf("msg.CursorX should be %d but it's %d", x+1-s.Window.X, msg.CursorY)
}
<-checkNotifications(t, reg1, x+1, y)
<-checkNotifications(t, reg2, x+1, y)

hatMock.MoveLeft()
msg = <-se
if msg.CursorY != x {
t.Errorf("msg.CursorX should be %d but it's %d", x, msg.CursorY)
if msg.CursorX != x-s.Window.X {
t.Errorf("msg.CursorX should be %d but it's %d", x-s.Window.X, msg.CursorX)
}
<-checkNotifications(t, reg1, x, y)
<-checkNotifications(t, reg2, x, y)

hatMock.Press()
msg = <-se
if !msg.Screen[y][x] {
if !msg.Screen[y-s.Window.Y][x-s.Window.X] {
t.Errorf("msg.Screen[%d][%d] should be set", y, x)
}
<-checkNotifications(t, reg1, x, y, x, y)
Expand Down
45 changes: 41 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"flag"
"fmt"
"log"
"net/http"
Expand All @@ -12,16 +13,52 @@ import (
"github.com/nunnatsa/piHatDraw/controller"
)

func main() {
var (
canvasWidth, canvasHeight uint8
port uint16
)

func init() {
var width, height, prt uint
flag.UintVar(&width, "width", 24, "Canvas width in pixels")
flag.UintVar(&height, "height", 24, "Canvas height in pixels")
flag.UintVar(&prt, "port", 8080, "The application port")

flag.Parse()

if width < 8 {
fmt.Println("The minimum width of the canvas is 8 pixels; setting it for you")
width = 8
}

if width > 40 {
log.Fatal("ERROR: The maximum width of the canvas is 40 pixels")
}
canvasWidth = uint8(width)

if height < 8 {
fmt.Println("The minimum height of the canvas is 8 pixels; setting it for you")
height = 8
}

if height > 40 {
log.Fatal("ERROR: The maximum height of the canvas is 40 pixels")
}
canvasHeight = uint8(height)

port = uint16(prt)
}

func main() {
n := notifier.NewNotifier()

clientEvents := make(chan webapp.ClientEvent)
webApplication := webapp.NewWebApplication(n, 8080, clientEvents)
webApplication := webapp.NewWebApplication(n, port, clientEvents)

server := http.Server{Addr: ":8080", Handler: webApplication.GetMux()}
portStr := fmt.Sprintf(":%d", port)
server := http.Server{Addr: portStr, Handler: webApplication.GetMux()}

control := controller.NewController(n, clientEvents)
control := controller.NewController(n, clientEvents, canvasWidth, canvasHeight)
done := control.Start()
go func() {
<-done
Expand Down
55 changes: 38 additions & 17 deletions state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,50 @@ import (
"github.com/nunnatsa/piHatDraw/common"
)

const (
canvasHeight = common.WindowSize
canvasWidth = common.WindowSize
)

type canvas [][]common.Color

type cursor struct {
X uint8 `json:"x"`
Y uint8 `json:"y"`
}

type window struct {
X uint8 `json:"x"`
Y uint8 `json:"y"`
}

type State struct {
Canvas canvas `json:"canvas,omitempty"`
Cursor cursor `json:"cursor,omitempty"`
Canvas canvas `json:"canvas,omitempty"`
Cursor cursor `json:"cursor,omitempty"`
Window window `json:"window,omitempty"`
canvasWidth uint8
canvasHeight uint8
}

func NewState() *State {
func NewState(canvasWidth, canvasHeight uint8) *State {
c := make([][]common.Color, canvasHeight)
for y := 0; y < canvasHeight; y++ {
for y := uint8(0); y < canvasHeight; y++ {
c[y] = make([]common.Color, canvasWidth)
}

cr := cursor{X: canvasWidth / 2, Y: canvasHeight / 2}
halfWindow := uint8(common.WindowSize / 2)
win := window{X: cr.X - halfWindow, Y: cr.Y - halfWindow}
return &State{
Canvas: c,
Cursor: cursor{X: canvasWidth / 2, Y: canvasHeight / 2},
Canvas: c,
Cursor: cr,
Window: win,
canvasWidth: canvasWidth,
canvasHeight: canvasHeight,
}
}

func (s *State) GoUp() bool {
if s.Cursor.Y > 0 {
s.Cursor.Y--
if s.Cursor.Y < s.Window.Y {
s.Window.Y = s.Cursor.Y
}
return true
}
return false
Expand All @@ -48,31 +60,40 @@ func (s *State) GoUp() bool {
func (s *State) GoLeft() bool {
if s.Cursor.X > 0 {
s.Cursor.X--
if s.Cursor.X < s.Window.X {
s.Window.X = s.Cursor.X
}
return true
}
return false
}

func (s *State) GoDown() bool {
if s.Cursor.Y < canvasHeight-1 {
if s.Cursor.Y < s.canvasHeight-1 {
s.Cursor.Y++
if s.Cursor.Y > s.Window.Y+common.WindowSize-1 {
s.Window.Y++
}
return true
}

return false
}

func (s *State) GoRight() bool {
if s.Cursor.X < canvasWidth-1 {
if s.Cursor.X < s.canvasWidth-1 {
s.Cursor.X++
if s.Cursor.X > s.Window.X+common.WindowSize-1 {
s.Window.X++
}
return true
}

return false
}

func (s *State) PaintPixel() bool {
if s.Cursor.Y >= canvasHeight || s.Cursor.X >= canvasWidth {
if s.Cursor.Y >= s.canvasHeight || s.Cursor.X >= s.canvasWidth {
log.Printf("Error: Cursor (%d, %d) is out of canvas\n", s.Cursor.X, s.Cursor.Y)
return false
}
Expand All @@ -86,10 +107,10 @@ func (s *State) PaintPixel() bool {

func (s State) CreateDisplayMessage() hat.DisplayMessage {
c := make([][]common.Color, common.WindowSize)
for y := 0; y < common.WindowSize; y++ {
for y := uint8(0); y < common.WindowSize; y++ {
c[y] = make([]common.Color, 0, common.WindowSize)
c[y] = append(c[y], s.Canvas[y]...)
c[y] = append(c[y], s.Canvas[s.Window.Y+y][s.Window.X:s.Window.X+common.WindowSize]...)
}

return hat.NewDisplayMessage(c, s.Cursor.X, s.Cursor.Y)
return hat.NewDisplayMessage(c, s.Cursor.X-s.Window.X, s.Cursor.Y-s.Window.Y)
}
27 changes: 18 additions & 9 deletions state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ package state

import "testing"

var (
canvasWidth = uint8(40)
canvasHeight = uint8(24)
)

func TestNewState(t *testing.T) {
s := NewState()

if len(s.Canvas) != canvasHeight {
s := NewState(canvasWidth, canvasHeight)

if len(s.Canvas) != int(canvasHeight) {
t.Errorf("len(s.Canvas should be %d but it's %d", canvasHeight, len(s.Canvas))
}

for i, line := range s.Canvas {
if len(line) != canvasWidth {
if len(line) != int(canvasWidth) {
t.Errorf("line %d length should be %d but it's %d", i, canvasWidth, len(line))
}
}
Expand All @@ -25,11 +31,14 @@ func TestNewState(t *testing.T) {
}

func TestCreateDisplayMessage(t *testing.T) {
s := NewState()
s := NewState(canvasWidth, canvasHeight)

s.Cursor.X = 7
s.Cursor.Y = 3

s.Window.X = 0
s.Window.Y = 0

s.Canvas[4][3] = true

msg := s.CreateDisplayMessage()
Expand All @@ -47,7 +56,7 @@ func TestCreateDisplayMessage(t *testing.T) {
}

func TestStateGoUp(t *testing.T) {
s := NewState()
s := NewState(canvasWidth, canvasHeight)

x := s.Cursor.X
y := s.Cursor.Y
Expand All @@ -71,7 +80,7 @@ func TestStateGoUp(t *testing.T) {
}

func TestStateGoDown(t *testing.T) {
s := NewState()
s := NewState(canvasWidth, canvasHeight)

x := s.Cursor.X
y := s.Cursor.Y
Expand All @@ -95,7 +104,7 @@ func TestStateGoDown(t *testing.T) {
}

func TestStateGoLeft(t *testing.T) {
s := NewState()
s := NewState(canvasWidth, canvasHeight)

x := s.Cursor.X
y := s.Cursor.Y
Expand All @@ -119,7 +128,7 @@ func TestStateGoLeft(t *testing.T) {
}

func TestStateGoRight(t *testing.T) {
s := NewState()
s := NewState(canvasWidth, canvasHeight)

x := s.Cursor.X
y := s.Cursor.Y
Expand All @@ -143,7 +152,7 @@ func TestStateGoRight(t *testing.T) {
}

func TestStatePaintPixel(t *testing.T) {
s := NewState()
s := NewState(canvasWidth, canvasHeight)

if s.Canvas[s.Cursor.Y][s.Cursor.X] {
t.Errorf("s.Canvas[%d][%d] should not be set, but it is", s.Cursor.Y, s.Cursor.X)
Expand Down
Loading

0 comments on commit 1b12aac

Please sign in to comment.