mirror of
https://github.com/maaslalani/confetty.git
synced 2024-11-21 15:16:39 +00:00
Remove custom physics package in favour of harmonica
This commit is contained in:
parent
fe126ff4df
commit
f4ce9d632f
@ -4,8 +4,8 @@ import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/harmonica"
|
||||
"github.com/maaslalani/confetty/array"
|
||||
"github.com/maaslalani/confetty/physics"
|
||||
"github.com/maaslalani/confetty/simulation"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
@ -43,11 +43,11 @@ func Spawn(width, height int) []simulation.Particle {
|
||||
y := float64(0)
|
||||
|
||||
p := simulation.Particle{
|
||||
Physics: physics.New(
|
||||
physics.Point{X: x + (float64(width/4) * (rand.Float64() - 0.5)), Y: y},
|
||||
physics.Vector{X: (rand.Float64() - 0.5) * 100, Y: rand.Float64() * 50},
|
||||
physics.Vector(physics.Gravity),
|
||||
framesPerSecond,
|
||||
Physics: harmonica.NewProjectile(
|
||||
harmonica.FPS(framesPerSecond),
|
||||
harmonica.Point{X: x + (float64(width/4) * (rand.Float64() - 0.5)), Y: y, Z: 0},
|
||||
harmonica.Vector{X: (rand.Float64() - 0.5) * 100, Y: rand.Float64() * 50, Z: 0},
|
||||
harmonica.Vector(harmonica.TerminalGravity),
|
||||
),
|
||||
Char: lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color(array.Sample(colors))).
|
||||
|
@ -5,8 +5,8 @@ import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/harmonica"
|
||||
"github.com/maaslalani/confetty/array"
|
||||
"github.com/maaslalani/confetty/physics"
|
||||
"github.com/maaslalani/confetty/simulation"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
@ -48,11 +48,11 @@ func Spawn(width, height int) []simulation.Particle {
|
||||
|
||||
for i := 0; i < numParticles; i++ {
|
||||
p := simulation.Particle{
|
||||
Physics: physics.New(
|
||||
physics.Point{X: x, Y: y},
|
||||
physics.Vector{X: math.Cos(float64(i)) * v, Y: math.Sin(float64(i)) * v / 2},
|
||||
physics.Vector(physics.Gravity),
|
||||
framesPerSecond,
|
||||
Physics: harmonica.NewProjectile(
|
||||
harmonica.FPS(framesPerSecond),
|
||||
harmonica.Point{X: x, Y: y},
|
||||
harmonica.Vector{X: math.Cos(float64(i)) * v, Y: math.Sin(float64(i)) * v / 2},
|
||||
harmonica.Vector(harmonica.TerminalGravity),
|
||||
),
|
||||
Char: lipgloss.NewStyle().Foreground(color).Render(array.Sample(characters)),
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@ -5,6 +5,7 @@ go 1.16
|
||||
require (
|
||||
github.com/charmbracelet/bubbles v0.8.0
|
||||
github.com/charmbracelet/bubbletea v0.14.1
|
||||
github.com/charmbracelet/harmonica v0.1.1-0.20211007155451-1466d67a1d68 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.3.0
|
||||
github.com/spf13/cobra v1.2.1
|
||||
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed
|
||||
|
2
go.sum
2
go.sum
@ -52,6 +52,8 @@ github.com/charmbracelet/bubbles v0.8.0/go.mod h1:5WX1sSSjNCgCrzvRMN/z23HxvWaa+A
|
||||
github.com/charmbracelet/bubbletea v0.13.1/go.mod h1:tp9tr9Dadh0PLhgiwchE5zZJXm5543JYjHG9oY+5qSg=
|
||||
github.com/charmbracelet/bubbletea v0.14.1 h1:pD/bM5LBEH/nDo7nKcgNUgi4uRHQhpWTIHZbG5vuSlc=
|
||||
github.com/charmbracelet/bubbletea v0.14.1/go.mod h1:b5lOf5mLjMg1tRn1HVla54guZB+jvsyV0yYAQja95zE=
|
||||
github.com/charmbracelet/harmonica v0.1.1-0.20211007155451-1466d67a1d68 h1:Gsc/gIm3Fwp9P1hz0tY+7fJR7fASRsGacIErx5+B7uU=
|
||||
github.com/charmbracelet/harmonica v0.1.1-0.20211007155451-1466d67a1d68/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||
github.com/charmbracelet/lipgloss v0.1.2/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg=
|
||||
github.com/charmbracelet/lipgloss v0.3.0 h1:5MysOD6sHr4RP4jkZNWGVIul5GKoOsP12NgbgXPvAlA=
|
||||
github.com/charmbracelet/lipgloss v0.3.0/go.mod h1:VkhdBS2eNAmRkTwRKLJCFhCOVkjntMusBDxv7TXahuk=
|
||||
|
@ -1,101 +0,0 @@
|
||||
package physics
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// Position is the location of an object on a 2-dimensional plane
|
||||
type Position Point
|
||||
|
||||
// Velocity is the velocity vector of an object's motion
|
||||
type Velocity Vector
|
||||
|
||||
// Acceleration is the acceleration vector of an object's motion
|
||||
type Acceleration Vector
|
||||
|
||||
// Gravity is the acceleration of gravity
|
||||
// Downward (+) by g m/s²
|
||||
var Gravity = Acceleration{
|
||||
X: 0,
|
||||
Y: 9.81,
|
||||
}
|
||||
|
||||
// Motion represents an objects motion
|
||||
// it keeps track of the position, velocity, and acceleration
|
||||
type Motion struct {
|
||||
pos Position
|
||||
vel Velocity
|
||||
acc Acceleration
|
||||
}
|
||||
|
||||
// Physics tracks the current motion and initial motion of an object along with
|
||||
// fps to account for the Update in frames rather than per second
|
||||
type Physics struct {
|
||||
current Motion
|
||||
initial Motion
|
||||
fps float64
|
||||
}
|
||||
|
||||
// Vector represents a magnitude and a direction in the form of a Point
|
||||
// from the origin (0, 0)
|
||||
type Vector struct {
|
||||
X float64
|
||||
Y float64
|
||||
}
|
||||
|
||||
// Point is a coordinate on a 2-dimensional plane
|
||||
type Point struct {
|
||||
X float64
|
||||
Y float64
|
||||
}
|
||||
|
||||
// Distance calculates the euclidean distance between two points
|
||||
func (a Point) Distance(b Point) float64 {
|
||||
return math.Sqrt(math.Pow(b.X-a.X, 2) + math.Pow(b.Y-a.Y, 2))
|
||||
}
|
||||
|
||||
// New initialize a physics simulation with simple motion
|
||||
func New(pos Point, vel, acc Vector, fps float64) *Physics {
|
||||
motion := Motion{
|
||||
pos: Position(pos),
|
||||
vel: Velocity(vel),
|
||||
acc: Acceleration(acc),
|
||||
}
|
||||
return &Physics{
|
||||
initial: motion,
|
||||
current: motion,
|
||||
fps: fps,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the current motion back to the initial
|
||||
func (p *Physics) Reset() {
|
||||
p.current = p.initial
|
||||
}
|
||||
|
||||
// Update increases the position of the motion by the velocity
|
||||
// and increases the velocity by the acceleration
|
||||
func (p *Physics) Update() {
|
||||
p.current.pos.X += p.current.vel.X / p.fps
|
||||
p.current.pos.Y += p.current.vel.Y / p.fps
|
||||
|
||||
p.current.vel.X += p.current.acc.X / p.fps
|
||||
p.current.vel.Y += p.current.acc.Y / p.fps
|
||||
}
|
||||
|
||||
// Displacement calculates the displacement between the current position and
|
||||
// its initial position
|
||||
func (p Physics) Displacement() float64 {
|
||||
return Point(p.initial.pos).Distance(Point(p.current.pos))
|
||||
}
|
||||
|
||||
// PosX returns the integer value of the current x coordinate for motion
|
||||
// not to be confused with Posix :D
|
||||
func (p Physics) PosX() int {
|
||||
return int(math.Round(p.current.pos.X))
|
||||
}
|
||||
|
||||
// PosY returns the integer value of the current y coordinate for motion
|
||||
func (p Physics) PosY() int {
|
||||
return int(math.Round(p.current.pos.Y))
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
package physics_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/maaslalani/confetty/physics"
|
||||
)
|
||||
|
||||
const fps = 60
|
||||
|
||||
func simulate(p *Physics, d time.Duration) {
|
||||
frames := int(d.Seconds() * fps)
|
||||
for i := 0; i < frames; i++ {
|
||||
p.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
x := 8
|
||||
y := 20
|
||||
|
||||
physics := New(Point{float64(x), float64(y)}, Vector{1, 1}, Vector(Gravity), 60)
|
||||
if x != physics.PosX() {
|
||||
t.Fatal("x coordinate unexpected")
|
||||
}
|
||||
|
||||
if y != physics.PosY() {
|
||||
t.Fatal("y coordinate unexpected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
physics := New(Point{0, 0}, Vector{5, 5}, Vector(Gravity), float64(fps))
|
||||
|
||||
// coordinates is the location at which the object should be
|
||||
// after i+1 seconds of simulation.
|
||||
coordinates := []Point{
|
||||
{5, 10},
|
||||
{10, 29},
|
||||
{15, 59},
|
||||
{20, 98},
|
||||
{25, 147},
|
||||
{30, 206},
|
||||
{35, 275},
|
||||
}
|
||||
|
||||
for _, c := range coordinates {
|
||||
simulate(physics, time.Second)
|
||||
|
||||
x := physics.PosX()
|
||||
y := physics.PosY()
|
||||
|
||||
if x != int(c.X) {
|
||||
t.Logf("Want: %d, Got: %d", x, int(c.X))
|
||||
t.Fatal("x coordinate unexpected")
|
||||
}
|
||||
if y != int(c.Y) {
|
||||
t.Logf("Want: %d, Got: %d", y, int(c.Y))
|
||||
t.Fatal("y coordinate unexpected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplacement(t *testing.T) {
|
||||
tt := []struct {
|
||||
x int
|
||||
y int
|
||||
vel Vector
|
||||
d float64
|
||||
}{
|
||||
{x: 5, y: 5, vel: Vector{5, 10}, d: 11.180339887498933},
|
||||
{x: 0, y: 0, vel: Vector{1, 1}, d: 1.414213562373097},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
physics := New(Point{float64(tc.x), float64(tc.y)}, tc.vel, Vector{}, float64(fps))
|
||||
|
||||
simulate(physics, time.Second)
|
||||
|
||||
if physics.Displacement() != tc.d {
|
||||
t.Log(physics.Displacement())
|
||||
t.Fatal("expected displacement to be 15")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReset(t *testing.T) {
|
||||
x := 5
|
||||
y := 10
|
||||
vel := Vector{20, 20}
|
||||
|
||||
physics := New(Point{float64(x), float64(y)}, vel, Vector(Gravity), float64(fps))
|
||||
|
||||
simulate(physics, time.Second)
|
||||
|
||||
// store the current position after 1 second of simulation we will simulate 1
|
||||
// second again and ensure that the object reaches the same position, we
|
||||
// don't care what the position is but if the object reaches the same
|
||||
// position again then we ensure that the velocity and acceleration was reset
|
||||
cx := physics.PosX()
|
||||
cy := physics.PosY()
|
||||
|
||||
physics.Reset()
|
||||
|
||||
if physics.PosX() != x {
|
||||
t.Fatal("expected x to be reset")
|
||||
}
|
||||
|
||||
if physics.PosY() != y {
|
||||
t.Fatal("expected y to be reset")
|
||||
}
|
||||
|
||||
// here we are simply checking that the object reaches the same position
|
||||
// after being reset in the same amount of time. This ensures velocity and
|
||||
// acceleration were reset without checking them explicitly
|
||||
simulate(physics, time.Second)
|
||||
|
||||
if physics.PosX() != cx {
|
||||
t.Fatal("expected simulation to be repeatable")
|
||||
}
|
||||
|
||||
if physics.PosY() != cy {
|
||||
t.Fatal("expected simulation to be repeatable")
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/maaslalani/confetty/physics"
|
||||
"github.com/charmbracelet/harmonica"
|
||||
)
|
||||
|
||||
type System struct {
|
||||
@ -14,7 +14,7 @@ type System struct {
|
||||
|
||||
type Particle struct {
|
||||
Char string
|
||||
Physics *physics.Physics
|
||||
Physics *harmonica.Projectile
|
||||
Hidden bool
|
||||
}
|
||||
|
||||
@ -39,8 +39,8 @@ func (s *System) Update() {
|
||||
}
|
||||
|
||||
func (s *System) Visible(p Particle) bool {
|
||||
y := p.Physics.PosY()
|
||||
x := p.Physics.PosX()
|
||||
y := int(p.Physics.Position().Y)
|
||||
x := int(p.Physics.Position().X)
|
||||
return y >= 0 && y < s.Frame.Height-1 && x >= 0 && x < s.Frame.Width-1
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ func (s *System) Render() string {
|
||||
}
|
||||
for _, p := range s.Particles {
|
||||
if s.Visible(p) {
|
||||
plane[p.Physics.PosY()][p.Physics.PosX()] = p.Char
|
||||
plane[int(p.Physics.Position().Y)][int(p.Physics.Position().X)] = p.Char
|
||||
}
|
||||
}
|
||||
for i := range plane {
|
||||
|
Loading…
Reference in New Issue
Block a user