blob: e2bfe76f5a03159fbaa0283715446dec72f672cb [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"image"
"log"
"math"
"time"
_ "image/png"
"golang.org/x/mobile/app"
"golang.org/x/mobile/asset"
"golang.org/x/mobile/event/paint"
"golang.org/x/mobile/event/size"
"golang.org/x/mobile/event/touch"
"golang.org/x/mobile/exp/f32"
"golang.org/x/mobile/exp/sprite"
"golang.org/x/mobile/exp/sprite/clock"
"golang.org/x/mobile/exp/sprite/glsprite"
"golang.org/x/mobile/gl"
)
var (
startTime = time.Now()
eng = glsprite.Engine()
scene *sprite.Node
cards []*sprite.Node
//cardsXY and initialCardsXY are in Pt
cardsXY [][]float32
initialCardsXY [][]float32
curCardIndex = -1
//lastMouseXY is in Px: divide by ppp to get Pt
lastMouseXY = []float32{-1, -1}
cardWidth = float32(50)
cardHeight = float32(50)
//windowSize is in Pt
windowSize = []float32{-1, -1}
//ppp stands for pixels per pt
ppp float32
//to-do: test and fine-tune these thresholds
swipeMoveThreshold = 70
swipeTimeThreshold = .5
swipeStart time.Time
animSpeedScaler = .1
animRotationScaler = .15
)
func main() {
app.Main(func(a app.App) {
var sz size.Event
for e := range a.Events() {
switch e := app.Filter(e).(type) {
case size.Event:
sz = e
oldWidth := windowSize[0]
oldHeight := windowSize[1]
windowSize[0] = float32(sz.WidthPt)
windowSize[1] = float32(sz.HeightPt)
pixelsX := float32(sz.WidthPx)
ppp = pixelsX / windowSize[0]
adjustCards(oldWidth, oldHeight)
case touch.Event:
onTouch(e)
case paint.Event:
onPaint(sz)
a.EndPaint(e)
}
}
})
}
func onTouch(t touch.Event) {
switch t.Type.String() {
case "begin":
for i := len(cards) - 1; i >= 0; i-- {
if t.X/ppp >= getCardX(i) &&
t.Y/ppp >= getCardY(i) &&
t.X/ppp <= cardWidth+getCardX(i) &&
t.Y/ppp <= cardHeight+getCardY(i) {
swipeStart = time.Now()
curCardIndex = i
node := getCardNode(i)
node.Arranger = nil
lastMouseXY[0] = t.X
lastMouseXY[1] = t.Y
return
}
}
case "move":
if curCardIndex > -1 {
eng.SetTransform(cards[curCardIndex], f32.Affine{
{cardWidth, 0, getCardX(curCardIndex) + (t.X-lastMouseXY[0])/ppp},
{0, cardHeight, getCardY(curCardIndex) + (t.Y-lastMouseXY[1])/ppp},
})
setCardX(curCardIndex, getCardX(curCardIndex)+(t.X-lastMouseXY[0])/ppp)
setCardY(curCardIndex, getCardY(curCardIndex)+(t.Y-lastMouseXY[1])/ppp)
lastMouseXY[0] = t.X
lastMouseXY[1] = t.Y
}
case "end":
if curCardIndex > -1 {
xDiff := getCardX(curCardIndex) - getInitialX(curCardIndex)
yDiff := getCardY(curCardIndex) - getInitialY(curCardIndex)
timeDiff := time.Since(swipeStart).Seconds()
if (math.Abs(float64(xDiff))+math.Abs(float64(yDiff))) >= float64(swipeMoveThreshold) && timeDiff <= float64(swipeTimeThreshold) {
animate(xDiff, yDiff, t)
} else {
eng.SetTransform(cards[curCardIndex], f32.Affine{
{cardWidth, 0, getInitialX(curCardIndex)},
{0, cardHeight, getInitialY(curCardIndex)},
})
setCardX(curCardIndex, getInitialX(curCardIndex))
setCardY(curCardIndex, getInitialY(curCardIndex))
}
}
curCardIndex = -1
}
}
func adjustCards(oldWidth, oldHeight float32) {
for i := 0; i < len(cards); i++ {
node := getCardNode(i)
oldX := getCardX(i)
newX := (oldX+cardWidth/2)/oldWidth*windowSize[0] - cardWidth/2
oldY := getCardY(i)
newY := (oldY+cardHeight/2)/oldHeight*windowSize[1] - cardHeight/2
eng.SetTransform(node, f32.Affine{
{cardWidth, 0, newX},
{0, cardHeight, newY},
})
setCardX(i, newX)
setCardY(i, newY)
oldInitialX := getInitialX(i)
newInitialX := (oldInitialX+cardWidth/2)/oldWidth*windowSize[0] - cardWidth/2
oldInitialY := getInitialY(i)
newInitialY := (oldInitialY+cardHeight/2)/oldHeight*windowSize[1] - cardHeight/2
setInitialX(i, newInitialX)
setInitialY(i, newInitialY)
}
}
func animate(xDiff float32, yDiff float32, touch touch.Event) {
index := curCardIndex
curCardIndex = -1
node := getCardNode(index)
startTime := -1
node.Arranger = arrangerFunc(func(eng sprite.Engine, node *sprite.Node, t clock.Time) {
if startTime == -1 {
startTime = int(t)
}
moveSpeed := float32(int(t)-startTime) * float32(animSpeedScaler)
x := getCardX(index)
y := getCardY(index)
if math.Abs(float64(xDiff)) > math.Abs(float64(yDiff)) {
if xDiff > 0 {
x = x + moveSpeed
} else {
x = x - moveSpeed
}
} else {
if yDiff > 0 {
y = y + moveSpeed
} else {
y = y - moveSpeed
}
}
setCardX(index, x)
setCardY(index, y)
position := f32.Affine{
{cardWidth, 0, x + cardWidth/2},
{0, cardHeight, y + cardHeight/2},
}
position.Rotate(&position, float32(t)*float32(animRotationScaler))
position.Translate(&position, -.5, -.5)
eng.SetTransform(node, position)
})
}
func onPaint(sz size.Event) {
if scene == nil {
loadScene()
}
gl.ClearColor(1, 1, 1, 1)
gl.Clear(gl.COLOR_BUFFER_BIT)
now := clock.Time(time.Since(startTime) * 60 / time.Second)
eng.Render(scene, now, sz)
}
func newNode() *sprite.Node {
n := &sprite.Node{}
eng.Register(n)
scene.AppendChild(n)
return n
}
//x and y should be in Pt
func addCard(x float32, y float32, spriteTex sprite.SubTex) {
n := newNode()
eng.SetSubTex(n, spriteTex)
eng.SetTransform(n, f32.Affine{
{cardWidth, 0, x},
{0, cardHeight, y},
})
cards = append(cards, n)
cardsXY = append(cardsXY, []float32{x, y})
initialCardsXY = append(initialCardsXY, []float32{x, y})
}
func getCardX(cardIndex int) float32 {
return cardsXY[cardIndex][0]
}
func setCardX(cardIndex int, newX float32) {
cardsXY[cardIndex][0] = newX
}
func getCardY(cardIndex int) float32 {
return cardsXY[cardIndex][1]
}
func setCardY(cardIndex int, newY float32) {
cardsXY[cardIndex][1] = newY
}
func getCardNode(cardIndex int) *sprite.Node {
return cards[cardIndex]
}
func getInitialX(cardIndex int) float32 {
return initialCardsXY[cardIndex][0]
}
func getInitialY(cardIndex int) float32 {
return initialCardsXY[cardIndex][1]
}
func setInitialX(cardIndex int, newX float32) {
initialCardsXY[cardIndex][0] = newX
}
func setInitialY(cardIndex int, newY float32) {
initialCardsXY[cardIndex][1] = newY
}
func loadScene() {
cards = make([]*sprite.Node, 0)
cardsXY = make([][]float32, 0)
initialCardsXY = make([][]float32, 0)
texs := loadTextures()
scene = &sprite.Node{}
eng.Register(scene)
eng.SetTransform(scene, f32.Affine{
{1, 0, 0},
{0, 1, 0},
})
for _, v := range texs {
x := windowSize[0]/2 - cardWidth/2
y := windowSize[1]/2 - cardHeight/2
addCard(x, y, v)
}
}
func loadTextures() map[string]sprite.SubTex {
allTexs := make(map[string]sprite.SubTex)
files := []string{"Clubs-2.png", "Clubs-3.png", "Clubs-4.png", "Clubs-5.png", "Clubs-6.png", "Clubs-7.png", "Clubs-8.png",
"Clubs-9.png", "Clubs-10.png", "Clubs-Jack.png", "Clubs-Queen.png", "Clubs-King.png", "Clubs-Ace.png",
"Diamonds-2.png", "Diamonds-3.png", "Diamonds-4.png", "Diamonds-5.png", "Diamonds-6.png", "Diamonds-7.png", "Diamonds-8.png",
"Diamonds-9.png", "Diamonds-10.png", "Diamonds-Jack.png", "Diamonds-Queen.png", "Diamonds-King.png", "Diamonds-Ace.png",
"Spades-2.png", "Spades-3.png", "Spades-4.png", "Spades-5.png", "Spades-6.png", "Spades-7.png", "Spades-8.png",
"Spades-9.png", "Spades-10.png", "Spades-Jack.png", "Spades-Queen.png", "Spades-King.png", "Spades-Ace.png",
"Hearts-2.png", "Hearts-3.png", "Hearts-4.png", "Hearts-5.png", "Hearts-6.png", "Hearts-7.png", "Hearts-8.png",
"Hearts-9.png", "Hearts-10.png", "Hearts-Jack.png", "Hearts-Queen.png", "Hearts-King.png", "Hearts-Ace.png"}
for _, f := range files {
a, err := asset.Open(f)
if err != nil {
log.Fatal(err)
}
defer a.Close()
img, _, err := image.Decode(a)
if err != nil {
log.Fatal(err)
}
t, err := eng.LoadTexture(img)
if err != nil {
log.Fatal(err)
}
allTexs[f] = sprite.SubTex{t, image.Rect(0, 0, 100, 100)}
}
return allTexs
}
type arrangerFunc func(e sprite.Engine, n *sprite.Node, t clock.Time)
func (a arrangerFunc) Arrange(e sprite.Engine, n *sprite.Node, t clock.Time) { a(e, n, t) }