mirror of
https://github.com/maaslalani/confetty.git
synced 2024-11-14 19:56:43 +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"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/harmonica"
|
||||||
"github.com/maaslalani/confetty/array"
|
"github.com/maaslalani/confetty/array"
|
||||||
"github.com/maaslalani/confetty/physics"
|
|
||||||
"github.com/maaslalani/confetty/simulation"
|
"github.com/maaslalani/confetty/simulation"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
@ -43,11 +43,11 @@ func Spawn(width, height int) []simulation.Particle {
|
|||||||
y := float64(0)
|
y := float64(0)
|
||||||
|
|
||||||
p := simulation.Particle{
|
p := simulation.Particle{
|
||||||
Physics: physics.New(
|
Physics: harmonica.NewProjectile(
|
||||||
physics.Point{X: x + (float64(width/4) * (rand.Float64() - 0.5)), Y: y},
|
harmonica.FPS(framesPerSecond),
|
||||||
physics.Vector{X: (rand.Float64() - 0.5) * 100, Y: rand.Float64() * 50},
|
harmonica.Point{X: x + (float64(width/4) * (rand.Float64() - 0.5)), Y: y, Z: 0},
|
||||||
physics.Vector(physics.Gravity),
|
harmonica.Vector{X: (rand.Float64() - 0.5) * 100, Y: rand.Float64() * 50, Z: 0},
|
||||||
framesPerSecond,
|
harmonica.Vector(harmonica.TerminalGravity),
|
||||||
),
|
),
|
||||||
Char: lipgloss.NewStyle().
|
Char: lipgloss.NewStyle().
|
||||||
Foreground(lipgloss.Color(array.Sample(colors))).
|
Foreground(lipgloss.Color(array.Sample(colors))).
|
||||||
|
@ -5,8 +5,8 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/harmonica"
|
||||||
"github.com/maaslalani/confetty/array"
|
"github.com/maaslalani/confetty/array"
|
||||||
"github.com/maaslalani/confetty/physics"
|
|
||||||
"github.com/maaslalani/confetty/simulation"
|
"github.com/maaslalani/confetty/simulation"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
@ -48,11 +48,11 @@ func Spawn(width, height int) []simulation.Particle {
|
|||||||
|
|
||||||
for i := 0; i < numParticles; i++ {
|
for i := 0; i < numParticles; i++ {
|
||||||
p := simulation.Particle{
|
p := simulation.Particle{
|
||||||
Physics: physics.New(
|
Physics: harmonica.NewProjectile(
|
||||||
physics.Point{X: x, Y: y},
|
harmonica.FPS(framesPerSecond),
|
||||||
physics.Vector{X: math.Cos(float64(i)) * v, Y: math.Sin(float64(i)) * v / 2},
|
harmonica.Point{X: x, Y: y},
|
||||||
physics.Vector(physics.Gravity),
|
harmonica.Vector{X: math.Cos(float64(i)) * v, Y: math.Sin(float64(i)) * v / 2},
|
||||||
framesPerSecond,
|
harmonica.Vector(harmonica.TerminalGravity),
|
||||||
),
|
),
|
||||||
Char: lipgloss.NewStyle().Foreground(color).Render(array.Sample(characters)),
|
Char: lipgloss.NewStyle().Foreground(color).Render(array.Sample(characters)),
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -5,6 +5,7 @@ go 1.16
|
|||||||
require (
|
require (
|
||||||
github.com/charmbracelet/bubbles v0.8.0
|
github.com/charmbracelet/bubbles v0.8.0
|
||||||
github.com/charmbracelet/bubbletea v0.14.1
|
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/charmbracelet/lipgloss v0.3.0
|
||||||
github.com/spf13/cobra v1.2.1
|
github.com/spf13/cobra v1.2.1
|
||||||
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed
|
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.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 h1:pD/bM5LBEH/nDo7nKcgNUgi4uRHQhpWTIHZbG5vuSlc=
|
||||||
github.com/charmbracelet/bubbletea v0.14.1/go.mod h1:b5lOf5mLjMg1tRn1HVla54guZB+jvsyV0yYAQja95zE=
|
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.1.2/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg=
|
||||||
github.com/charmbracelet/lipgloss v0.3.0 h1:5MysOD6sHr4RP4jkZNWGVIul5GKoOsP12NgbgXPvAlA=
|
github.com/charmbracelet/lipgloss v0.3.0 h1:5MysOD6sHr4RP4jkZNWGVIul5GKoOsP12NgbgXPvAlA=
|
||||||
github.com/charmbracelet/lipgloss v0.3.0/go.mod h1:VkhdBS2eNAmRkTwRKLJCFhCOVkjntMusBDxv7TXahuk=
|
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"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/maaslalani/confetty/physics"
|
"github.com/charmbracelet/harmonica"
|
||||||
)
|
)
|
||||||
|
|
||||||
type System struct {
|
type System struct {
|
||||||
@ -14,7 +14,7 @@ type System struct {
|
|||||||
|
|
||||||
type Particle struct {
|
type Particle struct {
|
||||||
Char string
|
Char string
|
||||||
Physics *physics.Physics
|
Physics *harmonica.Projectile
|
||||||
Hidden bool
|
Hidden bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,8 +39,8 @@ func (s *System) Update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *System) Visible(p Particle) bool {
|
func (s *System) Visible(p Particle) bool {
|
||||||
y := p.Physics.PosY()
|
y := int(p.Physics.Position().Y)
|
||||||
x := p.Physics.PosX()
|
x := int(p.Physics.Position().X)
|
||||||
return y >= 0 && y < s.Frame.Height-1 && x >= 0 && x < s.Frame.Width-1
|
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 {
|
for _, p := range s.Particles {
|
||||||
if s.Visible(p) {
|
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 {
|
for i := range plane {
|
||||||
|
Loading…
Reference in New Issue
Block a user