mirror of
https://github.com/charmbracelet/harmonica.git
synced 2026-07-20 02:29:33 -06:00
Just use pixel/pixelgl directly for the visual example
This commit is contained in:
parent
c6198b0130
commit
d7531b76d7
@ -1,136 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/charmbracelet/harmonica"
|
|
||||||
"github.com/h8gi/canvas"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
canvasWidth = 1024
|
|
||||||
canvasHeight = 768
|
|
||||||
fps = 60
|
|
||||||
circleRadius = 100
|
|
||||||
bgColor = "#111111"
|
|
||||||
idleTimeout = time.Second * 5
|
|
||||||
)
|
|
||||||
|
|
||||||
type State int
|
|
||||||
|
|
||||||
const (
|
|
||||||
unstarted State = iota
|
|
||||||
running
|
|
||||||
timeout
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var (
|
|
||||||
circ Circle
|
|
||||||
state State
|
|
||||||
lastClick time.Time
|
|
||||||
)
|
|
||||||
|
|
||||||
c := canvas.NewCanvas(&canvas.CanvasConfig{
|
|
||||||
Width: canvasWidth,
|
|
||||||
Height: canvasHeight,
|
|
||||||
FrameRate: fps,
|
|
||||||
Title: "Harmonica",
|
|
||||||
})
|
|
||||||
|
|
||||||
c.Draw(func(ctx *canvas.Context) {
|
|
||||||
ctx.Clear()
|
|
||||||
ctx.SetHexColor(bgColor)
|
|
||||||
|
|
||||||
switch state {
|
|
||||||
case unstarted:
|
|
||||||
if ctx.IsMouseDragged {
|
|
||||||
const frequency = 0.8
|
|
||||||
const damping = 1.0
|
|
||||||
|
|
||||||
circ = Circle{
|
|
||||||
// Setup a new spring.
|
|
||||||
spring: harmonica.NewSpring(harmonica.FPS(fps), frequency, damping),
|
|
||||||
|
|
||||||
// Set target radius.
|
|
||||||
Radius: circleRadius,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set starting position.
|
|
||||||
circ.SetPos(ctx.Mouse.X, ctx.Mouse.Y)
|
|
||||||
|
|
||||||
lastClick = time.Now()
|
|
||||||
state = running
|
|
||||||
}
|
|
||||||
|
|
||||||
case running:
|
|
||||||
if ctx.IsMouseDragged {
|
|
||||||
lastClick = time.Now()
|
|
||||||
|
|
||||||
if circ.Hidden {
|
|
||||||
circ.SetPos(ctx.Mouse.X, ctx.Mouse.Y)
|
|
||||||
circ.Hidden = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update targets for our spring animation.
|
|
||||||
circ.X = ctx.Mouse.X
|
|
||||||
circ.Y = ctx.Mouse.Y
|
|
||||||
circ.Radius = circleRadius
|
|
||||||
}
|
|
||||||
|
|
||||||
circ.Draw(ctx)
|
|
||||||
|
|
||||||
if time.Now().After(lastClick.Add(idleTimeout)) {
|
|
||||||
state = timeout
|
|
||||||
}
|
|
||||||
|
|
||||||
case timeout:
|
|
||||||
circ.Radius = 0
|
|
||||||
circ.Draw(ctx)
|
|
||||||
if circ.rad == 0 {
|
|
||||||
state = running
|
|
||||||
lastClick = time.Now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Circle struct {
|
|
||||||
// Target values. This is what the spring will use as an equilibrium to
|
|
||||||
// animate towards.
|
|
||||||
X, Y, Radius float64
|
|
||||||
|
|
||||||
x, xVel float64
|
|
||||||
y, yVel float64
|
|
||||||
rad, radVel float64
|
|
||||||
spring harmonica.Spring
|
|
||||||
|
|
||||||
Hidden bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Circle) SetPos(x, y float64) {
|
|
||||||
c.x = x
|
|
||||||
c.y = y
|
|
||||||
c.Hidden = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Circle) Draw(ctx *canvas.Context) {
|
|
||||||
// Update position and radius.
|
|
||||||
c.spring.Update(&c.x, &c.xVel, c.X)
|
|
||||||
c.spring.Update(&c.y, &c.yVel, c.Y)
|
|
||||||
c.spring.Update(&c.rad, &c.radVel, c.Radius)
|
|
||||||
|
|
||||||
if c.rad < 0 {
|
|
||||||
c.rad = 0
|
|
||||||
c.radVel = 0
|
|
||||||
c.Hidden = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const color = "#f1f1f1"
|
|
||||||
|
|
||||||
ctx.Push()
|
|
||||||
ctx.DrawCircle(c.x, c.y, c.rad)
|
|
||||||
ctx.SetHexColor(color)
|
|
||||||
ctx.Fill()
|
|
||||||
ctx.Pop()
|
|
||||||
}
|
|
||||||
@ -5,12 +5,10 @@ go 1.16
|
|||||||
replace github.com/charmbracelet/harmonica => ../
|
replace github.com/charmbracelet/harmonica => ../
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/charmbracelet/bubbletea v0.14.1 // indirect
|
github.com/charmbracelet/bubbletea v0.14.1
|
||||||
github.com/charmbracelet/harmonica v0.0.0-20210709144143-16876a63b30d // indirect
|
github.com/charmbracelet/harmonica v0.0.0-20210709144143-16876a63b30d
|
||||||
github.com/charmbracelet/lipgloss v0.3.0 // indirect
|
github.com/charmbracelet/lipgloss v0.3.0
|
||||||
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f // indirect
|
github.com/faiface/pixel v0.10.0
|
||||||
github.com/h8gi/canvas v0.0.0-20200710004640-a6361a06ab49
|
github.com/fogleman/gg v1.3.0
|
||||||
github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03 // indirect
|
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee // indirect
|
|
||||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff
|
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff
|
||||||
)
|
)
|
||||||
|
|||||||
@ -4,30 +4,28 @@ github.com/charmbracelet/lipgloss v0.3.0 h1:5MysOD6sHr4RP4jkZNWGVIul5GKoOsP12Ngb
|
|||||||
github.com/charmbracelet/lipgloss v0.3.0/go.mod h1:VkhdBS2eNAmRkTwRKLJCFhCOVkjntMusBDxv7TXahuk=
|
github.com/charmbracelet/lipgloss v0.3.0/go.mod h1:VkhdBS2eNAmRkTwRKLJCFhCOVkjntMusBDxv7TXahuk=
|
||||||
github.com/containerd/console v1.0.1 h1:u7SFAJyRqWcG6ogaMAx3KjSTy1e3hT9QxqX7Jco7dRc=
|
github.com/containerd/console v1.0.1 h1:u7SFAJyRqWcG6ogaMAx3KjSTy1e3hT9QxqX7Jco7dRc=
|
||||||
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
|
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380 h1:FvZ0mIGh6b3kOITxUnxS3tLZMh7yEoHo75v3/AgUqg0=
|
github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380 h1:FvZ0mIGh6b3kOITxUnxS3tLZMh7yEoHo75v3/AgUqg0=
|
||||||
github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380/go.mod h1:zqnPFFIuYFFxl7uH2gYByJwIVKG7fRqlqQCbzAnHs9g=
|
github.com/faiface/glhf v0.0.0-20181018222622-82a6317ac380/go.mod h1:zqnPFFIuYFFxl7uH2gYByJwIVKG7fRqlqQCbzAnHs9g=
|
||||||
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 h1:baVdMKlASEHrj19iqjARrPbaRisD7EuZEVJj6ZMLl1Q=
|
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 h1:baVdMKlASEHrj19iqjARrPbaRisD7EuZEVJj6ZMLl1Q=
|
||||||
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3/go.mod h1:VEPNJUlxl5KdWjDvz6Q1l+rJlxF2i6xqDeGuGAxa87M=
|
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3/go.mod h1:VEPNJUlxl5KdWjDvz6Q1l+rJlxF2i6xqDeGuGAxa87M=
|
||||||
github.com/faiface/pixel v0.8.0 h1:phOHW6ixfMAKRamjnvhI6FFI2VRyPEq7+LmmkDGXB/4=
|
github.com/faiface/pixel v0.10.0 h1:EHm3ZdQw2Ck4y51cZqFfqQpwLqNHOoXwbNEc9Dijql0=
|
||||||
github.com/faiface/pixel v0.8.0/go.mod h1:CEUU/s9E82Kqp01Boj1O67KnBskqiLghANqvUJGgDAM=
|
github.com/faiface/pixel v0.10.0/go.mod h1:lU0YYcW77vL0F1CG8oX51GXurymL45MXd57otHNLK7A=
|
||||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
|
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
|
||||||
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
|
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72 h1:b+9H1GAsx5RsjvDFLoS5zkNBzIQMuVKUYQDmxU3N5XE=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7 h1:THttjeRn1iiz69E875U6gAik8KTWk/JYAHoSVpUxBBI=
|
github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7 h1:THttjeRn1iiz69E875U6gAik8KTWk/JYAHoSVpUxBBI=
|
||||||
github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ=
|
github.com/go-gl/mathgl v0.0.0-20190416160123-c4601bc793c7/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
|
|
||||||
github.com/h8gi/canvas v0.0.0-20200710004640-a6361a06ab49 h1:svddC2dRqy+Y1zGH4+AvvCFZLKuPhAfEvlgYSNP4lcg=
|
|
||||||
github.com/h8gi/canvas v0.0.0-20200710004640-a6361a06ab49/go.mod h1:7iSUKfOcA+GiWc+LpxXggMogUFIo8pm1FZcYTUmL3YY=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
|
||||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
@ -35,25 +33,22 @@ github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 h1:y1p/ycavWjGT9Fn
|
|||||||
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
|
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
|
||||||
github.com/muesli/termenv v0.8.1 h1:9q230czSP3DHVpkaPDXGp0TOfAwyjyYwXlUCQxQSaBk=
|
github.com/muesli/termenv v0.8.1 h1:9q230czSP3DHVpkaPDXGp0TOfAwyjyYwXlUCQxQSaBk=
|
||||||
github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0=
|
github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0=
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03/go.mod h1:Z9+Ul5bCbBKnbCvdOWbLqTHhJiYV414CURZJba6L8qA=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff h1:+2zgJKVDVAz/BWSsuniCmU1kLCjL88Z8/kv39xCI9NQ=
|
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff h1:+2zgJKVDVAz/BWSsuniCmU1kLCjL88Z8/kv39xCI9NQ=
|
||||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|||||||
BIN
examples/pixel/JetBrainsMono-Regular.ttf
Normal file
BIN
examples/pixel/JetBrainsMono-Regular.ttf
Normal file
Binary file not shown.
332
examples/pixel/main.go
Normal file
332
examples/pixel/main.go
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/harmonica"
|
||||||
|
"github.com/faiface/pixel"
|
||||||
|
"github.com/faiface/pixel/pixelgl"
|
||||||
|
"github.com/fogleman/gg"
|
||||||
|
"golang.org/x/image/font"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
width = 1024
|
||||||
|
height = 768
|
||||||
|
fontFile = "JetBrainsMono-Regular.ttf"
|
||||||
|
idleTimeout = time.Second * 4
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
pixelgl.Run(Game{}.Run)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Game struct {
|
||||||
|
deltaTime float64
|
||||||
|
frequency float64
|
||||||
|
damping float64
|
||||||
|
win *pixelgl.Window
|
||||||
|
bgColor string
|
||||||
|
textColor string
|
||||||
|
spriteColor string
|
||||||
|
shift bool
|
||||||
|
font font.Face
|
||||||
|
sprite *Sprite
|
||||||
|
lastClick time.Time
|
||||||
|
dirty bool
|
||||||
|
showHelp bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Game) Size() (width, height float64) {
|
||||||
|
return g.win.Bounds().W(), g.win.Bounds().H()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Game) MousePosition() (x, y float64) {
|
||||||
|
return g.win.MousePosition().X, g.win.MousePosition().Y
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Game) Run() {
|
||||||
|
g.frequency = 2.0
|
||||||
|
g.damping = 2.0
|
||||||
|
g.bgColor = "#575BD8"
|
||||||
|
g.textColor = "#827EFF"
|
||||||
|
g.spriteColor = "#FFFDF5"
|
||||||
|
g.showHelp = true
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if g.font, err = gg.LoadFontFace(fontFile, 24); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := pixelgl.WindowConfig{
|
||||||
|
Title: "Harmonica Example",
|
||||||
|
Bounds: pixel.R(0, 0, width, height),
|
||||||
|
VSync: true,
|
||||||
|
Resizable: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.win, err = pixelgl.NewWindow(cfg); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
last := time.Now()
|
||||||
|
for !g.win.Closed() {
|
||||||
|
w, h := g.Size()
|
||||||
|
|
||||||
|
// Get delta time
|
||||||
|
g.deltaTime = time.Since(last).Seconds()
|
||||||
|
last = time.Now()
|
||||||
|
|
||||||
|
g.Update()
|
||||||
|
|
||||||
|
// Use fogleman/gg to render everything
|
||||||
|
ctx := gg.NewContext(int(w), int(h))
|
||||||
|
g.Draw(ctx)
|
||||||
|
canvas := g.win.Canvas()
|
||||||
|
canvas.SetPixels(ctx.Image().(*image.RGBA).Pix)
|
||||||
|
|
||||||
|
// Render
|
||||||
|
g.win.Update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Update() {
|
||||||
|
pressed := g.win.JustPressed
|
||||||
|
released := g.win.JustReleased
|
||||||
|
|
||||||
|
// Handle shift key
|
||||||
|
switch {
|
||||||
|
case pressed(pixelgl.KeyLeftShift), pressed(pixelgl.KeyRightShift):
|
||||||
|
g.shift = true
|
||||||
|
case released(pixelgl.KeyLeftShift), released(pixelgl.KeyRightShift):
|
||||||
|
g.shift = false
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustFreq, adjustDamp := 0.0, 0.0
|
||||||
|
|
||||||
|
// Handle arrow keys to adjust frequency and damping
|
||||||
|
switch {
|
||||||
|
case released(pixelgl.KeyLeft):
|
||||||
|
adjustFreq = -0.1
|
||||||
|
case released(pixelgl.KeyRight):
|
||||||
|
adjustFreq = 0.1
|
||||||
|
case released(pixelgl.KeyUp):
|
||||||
|
adjustDamp = 0.1
|
||||||
|
case released(pixelgl.KeyDown):
|
||||||
|
adjustDamp = -0.1
|
||||||
|
case released(pixelgl.KeySpace):
|
||||||
|
g.showHelp = !g.showHelp
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.shift {
|
||||||
|
adjustDamp *= 10
|
||||||
|
adjustFreq *= 10
|
||||||
|
}
|
||||||
|
|
||||||
|
if adjustFreq != 0 || adjustDamp != 0 {
|
||||||
|
g.frequency = math.Max(0, g.frequency+adjustFreq)
|
||||||
|
g.damping = math.Max(0, g.damping+adjustDamp)
|
||||||
|
g.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if time.Now().After(g.lastClick.Add(idleTimeout)) {
|
||||||
|
if g.sprite != nil {
|
||||||
|
g.sprite.Hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if released(pixelgl.MouseButtonLeft) {
|
||||||
|
s := g.sprite
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
g.sprite = NewSprite(g)
|
||||||
|
} else {
|
||||||
|
x, y := g.MousePosition()
|
||||||
|
s.TargetX = x
|
||||||
|
s.TargetY = y
|
||||||
|
|
||||||
|
switch s.State() {
|
||||||
|
case hiding, gone:
|
||||||
|
s.Show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g.dirty = false
|
||||||
|
g.lastClick = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sprite
|
||||||
|
if g.sprite != nil {
|
||||||
|
g.sprite.Update()
|
||||||
|
}
|
||||||
|
|
||||||
|
g.dirty = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Game) Draw(ctx *gg.Context) {
|
||||||
|
w, h := g.Size()
|
||||||
|
|
||||||
|
// BG
|
||||||
|
ctx.SetHexColor(g.bgColor)
|
||||||
|
ctx.DrawRectangle(0, 0, w, h)
|
||||||
|
ctx.Fill()
|
||||||
|
|
||||||
|
// Help text
|
||||||
|
if g.showHelp {
|
||||||
|
// For some reason our text renders upside down and backwards. Apply a
|
||||||
|
// matrix to fix it.
|
||||||
|
ctx.ScaleAbout(-1, 1, w/2, h/2)
|
||||||
|
ctx.RotateAbout(math.Pi, w/2, h/2)
|
||||||
|
|
||||||
|
// Instructional text
|
||||||
|
ctx.Push()
|
||||||
|
ctx.SetHexColor(g.textColor)
|
||||||
|
ctx.SetFontFace(g.font)
|
||||||
|
ctx.DrawStringAnchored("Click!", w/2, h/2, 0.5, 0.5)
|
||||||
|
ctx.Fill()
|
||||||
|
ctx.Pop()
|
||||||
|
|
||||||
|
// Status
|
||||||
|
str := fmt.Sprintf(
|
||||||
|
"Frequency: %.1f (←/→: adjust) • Damping: %.1f (↑/↓: adjust)",
|
||||||
|
g.frequency, g.damping,
|
||||||
|
)
|
||||||
|
ctx.Push()
|
||||||
|
ctx.SetHexColor(g.textColor)
|
||||||
|
ctx.SetFontFace(g.font)
|
||||||
|
ctx.DrawStringAnchored(str, w/2, h-34, 0.5, 0)
|
||||||
|
ctx.Fill()
|
||||||
|
ctx.Pop()
|
||||||
|
|
||||||
|
// Reset matrix
|
||||||
|
ctx.Identity()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw sprites
|
||||||
|
if g.sprite != nil {
|
||||||
|
g.sprite.Draw(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpriteState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
moving SpriteState = iota
|
||||||
|
stopped
|
||||||
|
hiding
|
||||||
|
gone
|
||||||
|
)
|
||||||
|
|
||||||
|
type Sprite struct {
|
||||||
|
TargetX, TargetY, TargetRadius float64
|
||||||
|
X, xVel float64
|
||||||
|
Y, yVel float64
|
||||||
|
radius, radiusVel float64
|
||||||
|
color string
|
||||||
|
spring harmonica.Spring
|
||||||
|
game *Game
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSprite(g *Game) *Sprite {
|
||||||
|
mouseX, mouseY := g.MousePosition()
|
||||||
|
|
||||||
|
s := &Sprite{
|
||||||
|
X: mouseX,
|
||||||
|
Y: mouseY,
|
||||||
|
TargetX: mouseX,
|
||||||
|
TargetY: mouseY,
|
||||||
|
radius: 0.1,
|
||||||
|
color: g.spriteColor,
|
||||||
|
game: g,
|
||||||
|
}
|
||||||
|
s.computeSpring()
|
||||||
|
s.randomRadius()
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sprite) Update() {
|
||||||
|
if s.State() == gone {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g := s.game
|
||||||
|
|
||||||
|
if g.dirty {
|
||||||
|
// Recompute spring coefficients since our frequency or damping has
|
||||||
|
// changed.
|
||||||
|
s.computeSpring()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate positions based on our spring
|
||||||
|
s.spring.Update(&s.X, &s.xVel, s.TargetX)
|
||||||
|
s.spring.Update(&s.Y, &s.yVel, s.TargetY)
|
||||||
|
s.spring.Update(&s.radius, &s.radiusVel, s.TargetRadius)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Sprite) Draw(ctx *gg.Context) {
|
||||||
|
if s.State() == gone {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Push()
|
||||||
|
ctx.DrawCircle(s.X, s.Y, s.radius)
|
||||||
|
ctx.SetHexColor(s.color)
|
||||||
|
ctx.Fill()
|
||||||
|
ctx.Pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Sprite) State() SpriteState {
|
||||||
|
const precision = 2
|
||||||
|
x := roundFloat(s.X, precision)
|
||||||
|
tX := roundFloat(s.TargetX, precision)
|
||||||
|
y := roundFloat(s.Y, precision)
|
||||||
|
tY := roundFloat(s.TargetY, precision)
|
||||||
|
r := roundFloat(s.radius, precision)
|
||||||
|
tR := roundFloat(s.TargetRadius, precision)
|
||||||
|
|
||||||
|
if r == 0.0 {
|
||||||
|
return gone
|
||||||
|
}
|
||||||
|
if s.TargetRadius == 0 {
|
||||||
|
return hiding
|
||||||
|
}
|
||||||
|
if x == tX && y == tY && r == tR {
|
||||||
|
return stopped
|
||||||
|
}
|
||||||
|
return moving
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sprite) computeSpring() {
|
||||||
|
// Calculate spring coefficients
|
||||||
|
s.spring = harmonica.NewSpring(s.game.deltaTime, s.game.frequency, s.game.damping)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sprite) randomRadius() {
|
||||||
|
s.TargetRadius = rand.Float64()*100.0 + 100.0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sprite) Show() {
|
||||||
|
s.X, s.Y = s.game.MousePosition()
|
||||||
|
if s.radius < 0.1 {
|
||||||
|
s.radius = 0.1
|
||||||
|
}
|
||||||
|
s.computeSpring()
|
||||||
|
s.randomRadius()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sprite) Hide() {
|
||||||
|
// Fixed frequency and damping when hiding
|
||||||
|
s.spring = harmonica.NewSpring(s.game.deltaTime, 3.0, 8.0)
|
||||||
|
s.TargetRadius = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func roundFloat(input float64, decimalPlaces int) float64 {
|
||||||
|
pow := math.Pow(10, float64(decimalPlaces))
|
||||||
|
return math.Round(pow*input) / pow
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user