From d45b96278a61b34b3df3e48bb2712de877bdefca Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Fri, 23 Jul 2021 13:31:14 -0400 Subject: [PATCH] Copy values in Update method to avoid data races --- README.md | 4 ++-- examples/opengl/main.go | 6 +++--- examples/tui/main.go | 2 +- harmonica.go | 24 +++++++++++++----------- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 917d1ad..bacd53a 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,8 @@ spring := harmonica.NewSpring(harmonica.FPS(60), 6.0, 0.5) // Animate! for { - spring.Update(&sprite.x, &sprite.xVelocity, targetX) - spring.Update(&sprite.y, &sprite.yVelocity, targetY) + sprite.x, sprite.xVelocity = spring.Update(&sprite.x, &sprite.xVelocity, targetX) + sprite.y, sprite.yVelocity = spring.Update(&sprite.y, &sprite.yVelocity, targetY) time.Sleep(time.Second/60) } ``` diff --git a/examples/opengl/main.go b/examples/opengl/main.go index 09352ed..076a896 100644 --- a/examples/opengl/main.go +++ b/examples/opengl/main.go @@ -255,9 +255,9 @@ func (s *Sprite) Update() { } // 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) + s.X, s.xVel = s.spring.Update(s.X, s.xVel, s.TargetX) + s.Y, s.yVel = s.spring.Update(s.Y, s.yVel, s.TargetY) + s.radius, s.radiusVel = s.spring.Update(s.radius, s.radiusVel, s.TargetRadius) } func (s Sprite) Draw(ctx *gg.Context) { diff --git a/examples/tui/main.go b/examples/tui/main.go index 1ee9b17..955338d 100644 --- a/examples/tui/main.go +++ b/examples/tui/main.go @@ -66,7 +66,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { const targetX = 60 // Update x position (and velocity) with our spring. - m.spring.Update(&m.x, &m.xVel, targetX) + m.x, m.xVel = m.spring.Update(m.x, m.xVel, targetX) // Quit when we're basically at the target position. if math.Abs(m.x-targetX) < 0.01 { diff --git a/harmonica.go b/harmonica.go index cce5afc..da20b99 100644 --- a/harmonica.go +++ b/harmonica.go @@ -9,10 +9,10 @@ // // // Update on every frame. // pos := 0.0 -// targetPos := 100.0 // velocity := 0.0 +// targetPos := 100.0 // someUpdateLoop(func() { -// spring.Update(&pos, &velocity, targetPos) +// pos, velocity = spring.Update(pos, velocity, targetPos) // }) // // For background on the algorithm see: @@ -61,7 +61,7 @@ import ( // // Example: // -// spring := NewSpring(FPS(60), 0.8, 0.98) +// spring := NewSpring(FPS(60), 5.0, 0.2) // func FPS(n int) float64 { return (time.Second / time.Duration(n)).Seconds() @@ -92,13 +92,13 @@ var epsilon = math.Nextafter(1, 2) - 1 // Example: // // // First precompute spring coefficients based on your settings: -// var x, xVel, y, yVel float +// var x, xVel, y, yVel float64 // deltaTime := FPS(60) // s := NewSpring(deltaTime, 5.0, 0.2) // // // Then, in your update loop: -// s.Update(&x, &xVel, 10) // update the X position -// s.Update(&y, &yVel, 20) // update the Y position +// x, xVel = s.Update(x, xVel, 10) // update the X position +// y, yVel = s.Update(y, yVel, 20) // update the Y position // type Spring struct { posPosCoef, posVelCoef float64 @@ -212,10 +212,12 @@ func NewSpring(deltaTime, angularFrequency, dampingRatio float64) (s Spring) { // Update updates position and velocity values against a given target value. // Call this after calling NewSpring to update values. -func (s Spring) Update(pos *float64, vel *float64, equilibriumPos float64) { - oldPos := *pos - equilibriumPos // update in equilibrium relative space - oldVel := *vel +func (s Spring) Update(pos, vel float64, equilibriumPos float64) (newPos, newVel float64) { + oldPos := pos - equilibriumPos // update in equilibrium relative space + oldVel := vel - *pos = oldPos*s.posPosCoef + oldVel*s.posVelCoef + equilibriumPos - *vel = oldPos*s.velPosCoef + oldVel*s.velVelCoef + newPos = oldPos*s.posPosCoef + oldVel*s.posVelCoef + equilibriumPos + newVel = oldPos*s.velPosCoef + oldVel*s.velVelCoef + + return newPos, newVel }