Files
2021-06-17 14:46:08 +09:00

285 lines
7.1 KiB
Go

// Port of Adafruit's "pyportal_boing" demo found here:
// https://github.com/adafruit/Adafruit_ILI9341/blob/master/examples/pyportal_boing
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics"
"tinygo.org/x/drivers/ili9341"
)
const (
BALLWIDTH = 136
BALLHEIGHT = 100
)
const (
SCREENHEIGHT = 240
SCREENWIDTH = 320
)
const (
invBGCOLOR = 0x75AD
invGRIDCOLOR = 0x15A8
invBGSHADOW = 0x8552
invGRIDSHADOW = 0x0C60
invRED = 0x00F8
invWHITE = 0xFFFF
)
const (
BGCOLOR = 0xAD75
GRIDCOLOR = 0xA815
BGSHADOW = 0x5285
GRIDSHADOW = 0x600C
RED = 0xF800
WHITE = 0xFFFF
YBOTTOM = 123 // Ball Y coord at bottom
YBOUNCE = -3.5 // Upward velocity on ball bounce
_debug = false
)
var (
dbg5 = machine.D5
dbg6 = machine.D6
)
var (
frameBuffer = [2][(BALLHEIGHT + 8) * (BALLWIDTH + 8)]uint16{}
startTime int64
frame int64
// Ball coordinates are stored floating-point because screen refresh
// is so quick, whole-pixel movements are just too fast!
ballx float32
bally float32
ballvx float32
ballvy float32
ballframe float32
balloldx float32
balloldy float32
// Color table for ball rotation effect
palette [16]uint16
)
func main() {
// configure backlight
backlight.Configure(machine.PinConfig{machine.PinOutput})
// configure display
display.Configure(ili9341.Config{})
print("width, height == ")
width, height := display.Size()
println(width, height)
backlight.High()
display.SetRotation(ili9341.Rotation270)
DrawBackground()
startTime = time.Now().UnixNano()
frame = 0
ballx = 20.0
bally = YBOTTOM // Current ball position
ballvx = 0.8
ballvy = YBOUNCE // Ball velocity
ballframe = 3 // Ball animation frame #
balloldx = ballx
balloldy = bally // Prior ball position
var bufIdx int8 = 0
for {
dbg5.High()
bufIdx = 1 - bufIdx
balloldx = ballx // Save prior position
balloldy = bally
ballx += ballvx // Update position
bally += ballvy
ballvy += 0.06 // Update Y velocity
if (ballx <= 15) || (ballx >= SCREENWIDTH-BALLWIDTH) {
ballvx *= -1 // Left/right bounce
}
if bally >= YBOTTOM { // Hit ground?
bally = YBOTTOM // Clip and
ballvy = YBOUNCE // bounce up
}
// Determine screen area to update. This is the bounds of the ball's
// prior and current positions, so the old ball is fully erased and new
// ball is fully drawn.
var minx, miny, maxx, maxy, width, height int16
// Determine bounds of prior and new positions
minx = int16(ballx)
if int16(balloldx) < minx {
minx = int16(balloldx)
}
miny = int16(bally)
if int16(balloldy) < miny {
miny = int16(balloldy)
}
maxx = int16(ballx + BALLWIDTH - 1)
if int16(balloldx+BALLWIDTH-1) > maxx {
maxx = int16(balloldx + BALLWIDTH - 1)
}
maxy = int16(bally + BALLHEIGHT - 1)
if int16(balloldy+BALLHEIGHT-1) > maxy {
maxy = int16(balloldy + BALLHEIGHT - 1)
}
width = maxx - minx + 1
height = maxy - miny + 1
// Ball animation frame # is incremented opposite the ball's X velocity
ballframe -= ballvx * 0.5
if ballframe < 0 {
ballframe += 14 // Constrain from 0 to 13
} else if ballframe >= 14 {
ballframe -= 14
}
//// Set 7 palette entries to white, 7 to red, based on frame number.
//// This makes the ball spin
for i := 0; i < 14; i++ {
if (int(ballframe)+i)%14 < 7 {
palette[i+2] = invWHITE
} else {
palette[i+2] = invRED
} // Palette entries 0 and 1 aren't used (clear and shadow, respectively)
}
// Only the changed rectangle is drawn into the 'renderbuf' array...
var c uint16 //, *destPtr;
bx := minx - int16(ballx) // X relative to ball bitmap (can be negative)
by := miny - int16(bally) // Y relative to ball bitmap (can be negative)
bgx := minx // X relative to background bitmap (>= 0)
bgy := miny // Y relative to background bitmap (>= 0)
//var bufIdx int8 = 0
//tft.setAddrWindow(minx, miny, width, height)
dbg5.Low()
dbg6.High()
//fmt.Printf("%d < %d < %d < %d\r\n", by, 0, BALLHEIGHT, height)
y := 0
if by < 0 {
max := -1 * int(by)
for y = 0; y < max; y++ { // For each row...
var bgidxBase = int(bgy)*(SCREENWIDTH) + int(bgx)
var yBase = y * int(width)
for x := 0; x < int(width); x++ {
frameBuffer[bufIdx][yBase+x] = graphics.Background[bgidxBase+x]
}
bgy++
}
}
y2 := y
max := 0
if bx < 0 {
max = -1 * int(bx)
bgy2 := bgy
for y = y2; y < y2+int(BALLHEIGHT); y++ { // For each row...
var bgidxBase = int(bgy2)*(SCREENWIDTH) + int(bgx)
var yBase = y * int(width)
//fmt.Printf("- %d %d %d %d %d %d\r\n", bgy, y, bgx, max, yBase, bgidxBase)
for x := 0; x < int(max); x++ {
//fmt.Printf(" %d %d\r\n", yBase+x, bgidxBase+x)
frameBuffer[bufIdx][yBase+x] = graphics.Background[bgidxBase+x]
}
bgy2++
}
//fmt.Printf("(%d, %d) - (%d, %d)\r\n", bx, 0, -1, BALLHEIGHT-1)
}
{
bgy2 := bgy
//fmt.Printf("(%d, %d) - (%d, %d)\r\n", 0, 0, BALLWIDTH-1, BALLHEIGHT-1)
for y = y2; y < y2+int(BALLHEIGHT); y++ { // For each row...
var bgidxBase = int(bgy2)*(SCREENWIDTH) + int(bgx)
var byBase = (y - y2) * BALLWIDTH
var yBase = y * int(width)
for x := max; x < int(BALLWIDTH)+max; x++ {
//fmt.Printf("%d %d %d %d\r\n", byBase, x, bgidxBase, yBase)
//time.Sleep(1 * time.Millisecond)
// Yes, do ball compositing math...
c = uint16(graphics.Ball[int(byBase)+x-max]) // Get packed value (2 pixels)
if c == 0 { // Outside ball - just draw grid
c = graphics.Background[bgidxBase+x]
} else if c > 1 { // In ball area...
c = palette[c]
} else { // In shadow area...
c = graphics.BackgroundShadow[bgidxBase+x]
}
frameBuffer[bufIdx][yBase+x] = c
}
bgy2++
}
}
{
bgy2 := bgy
for y = y2; y < y2+int(BALLHEIGHT); y++ { // For each row...
var bgidxBase = int(bgy2)*(SCREENWIDTH) + int(bgx)
var yBase = y * int(width)
//fmt.Printf("+ %d %d %d %d %d\r\n", bgy, y, bgx, yBase, bgidxBase)
for x := int(BALLWIDTH) + max; x < int(width); x++ {
frameBuffer[bufIdx][yBase+x] = graphics.Background[bgidxBase+x]
}
bgy2++
}
}
y = y2 + int(BALLHEIGHT)
bgy += BALLHEIGHT
{
for ; y < int(height); y++ { // For each row...
//destPtr = &renderbuf[bufIdx][0];
var bgidxBase = int(bgy)*(SCREENWIDTH) + int(bgx)
var yBase = y * int(width)
for x := 0; x < int(width); x++ {
frameBuffer[bufIdx][yBase+x] = graphics.Background[bgidxBase+x]
}
bgy++
}
}
dbg6.Low()
display.DrawRGBBitmap(minx, miny, frameBuffer[bufIdx][:width*height], width, height)
//time.Sleep(10 * time.Millisecond)
// Show approximate frame rate
frame++
if frame&255 == 0 { // Every 256 frames...
elapsed := (time.Now().UnixNano() - startTime) / int64(time.Second)
if elapsed > 0 {
println(frame/elapsed, " fps")
}
}
}
}
func DrawBackground() {
w, h := display.Size()
var bufIdx int8 = 0
for j := 0; j < int(h); j++ {
bufIdx = 1 - bufIdx
for k := 0; k < int(w); k++ {
frameBuffer[bufIdx][k] = graphics.Background[j*int(w)+k]
}
display.DrawRGBBitmap(0, int16(j), frameBuffer[bufIdx][0:w], w, 1)
time.Sleep(1 * time.Millisecond)
}
}