117 lines
2.9 KiB
Go
117 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"github.com/google/gxui"
|
|
"github.com/google/gxui/drivers/gl"
|
|
"github.com/google/gxui/math"
|
|
"github.com/google/gxui/themes/dark"
|
|
omath "math"
|
|
"time"
|
|
)
|
|
|
|
//Two pendulums animated
|
|
//Top: Mathematical pendulum with small-angle approxmiation (not appropiate with PHI_ZERO=pi/2)
|
|
//Bottom: Simulated with differential equation phi'' = g/l * sin(phi)
|
|
|
|
const (
|
|
ANIMATION_WIDTH int = 480
|
|
ANIMATION_HEIGHT int = 320
|
|
BALL_RADIUS float32 = 25.0
|
|
METER_PER_PIXEL float64 = 1.0 / 20.0
|
|
PHI_ZERO float64 = omath.Pi * 0.5
|
|
)
|
|
|
|
var (
|
|
l float64 = float64(ANIMATION_HEIGHT) * 0.5
|
|
freq float64 = omath.Sqrt(9.81 / (l * METER_PER_PIXEL))
|
|
)
|
|
|
|
type Pendulum interface {
|
|
GetPhi() float64
|
|
}
|
|
|
|
type mathematicalPendulum struct {
|
|
start time.Time
|
|
}
|
|
|
|
func (p *mathematicalPendulum) GetPhi() float64 {
|
|
if (p.start == time.Time{}) {
|
|
p.start = time.Now()
|
|
}
|
|
t := float64(time.Since(p.start).Nanoseconds()) / omath.Pow10(9)
|
|
return PHI_ZERO * omath.Cos(t*freq)
|
|
}
|
|
|
|
type numericalPendulum struct {
|
|
currentPhi float64
|
|
angAcc float64
|
|
angVel float64
|
|
lastTime time.Time
|
|
}
|
|
|
|
func (p *numericalPendulum) GetPhi() float64 {
|
|
dt := 0.0
|
|
if (p.lastTime != time.Time{}) {
|
|
dt = float64(time.Since(p.lastTime).Nanoseconds()) / omath.Pow10(9)
|
|
}
|
|
p.lastTime = time.Now()
|
|
|
|
p.angAcc = -9.81 / (float64(l) * METER_PER_PIXEL) * omath.Sin(p.currentPhi)
|
|
p.angVel += p.angAcc * dt
|
|
p.currentPhi += p.angVel * dt
|
|
|
|
return p.currentPhi
|
|
}
|
|
|
|
func draw(p Pendulum, canvas gxui.Canvas, x, y int) {
|
|
attachment := math.Point{X: ANIMATION_WIDTH/2 + x, Y: y}
|
|
|
|
phi := p.GetPhi()
|
|
ball := math.Point{X: x + ANIMATION_WIDTH/2 + math.Round(float32(l*omath.Sin(phi))), Y: y + math.Round(float32(l*omath.Cos(phi)))}
|
|
|
|
line := gxui.Polygon{gxui.PolygonVertex{attachment, 0}, gxui.PolygonVertex{ball, 0}}
|
|
|
|
canvas.DrawLines(line, gxui.DefaultPen)
|
|
|
|
m := math.Point{int(BALL_RADIUS), int(BALL_RADIUS)}
|
|
rect := math.Rect{ball.Sub(m), ball.Add(m)}
|
|
canvas.DrawRoundedRect(rect, BALL_RADIUS, BALL_RADIUS, BALL_RADIUS, BALL_RADIUS, gxui.TransparentPen, gxui.CreateBrush(gxui.Yellow))
|
|
}
|
|
|
|
func appMain(driver gxui.Driver) {
|
|
theme := dark.CreateTheme(driver)
|
|
|
|
window := theme.CreateWindow(ANIMATION_WIDTH, 2*ANIMATION_HEIGHT, "Pendulum")
|
|
window.SetBackgroundBrush(gxui.CreateBrush(gxui.Gray50))
|
|
|
|
image := theme.CreateImage()
|
|
|
|
ticker := time.NewTicker(time.Millisecond * 15)
|
|
pendulum := &mathematicalPendulum{}
|
|
pendulum2 := &numericalPendulum{PHI_ZERO, 0.0, 0.0, time.Time{}}
|
|
|
|
go func() {
|
|
for _ = range ticker.C {
|
|
canvas := driver.CreateCanvas(math.Size{ANIMATION_WIDTH, 2 * ANIMATION_HEIGHT})
|
|
canvas.Clear(gxui.White)
|
|
|
|
draw(pendulum, canvas, 0, 0)
|
|
draw(pendulum2, canvas, 0, ANIMATION_HEIGHT)
|
|
|
|
canvas.Complete()
|
|
driver.Call(func() {
|
|
image.SetCanvas(canvas)
|
|
})
|
|
}
|
|
}()
|
|
|
|
window.AddChild(image)
|
|
|
|
window.OnClose(ticker.Stop)
|
|
window.OnClose(driver.Terminate)
|
|
}
|
|
|
|
func main() {
|
|
gl.StartDriver(appMain)
|
|
}
|