Skip to content

Commit

Permalink
fix event system
Browse files Browse the repository at this point in the history
  • Loading branch information
zyxkad committed Dec 1, 2023
1 parent 1c3a2d0 commit 43aca1d
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 103 deletions.
55 changes: 44 additions & 11 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,28 @@ import (
"github.com/google/uuid"
)

const (
defaultMinAcc = 1e-3
)

type Config struct {
// MinSpeed means the minimum positive speed
MinSpeed float64
// MaxSpeed means the maximum positive speed
MaxSpeed float64
// MinAccel means the minimum positive acceleration
MinAccel float64
}

// Engine includes a sync.RWMutex which should be locked when operating global things inside a tick
type Engine struct {
sync.RWMutex

// the config should not change while engine running
cfg Config
cfg Config
minSpeedSq, maxSpeedSq float64
minAccelSq float64

// the main anchor object must be invincible and unmovable
mainAnchor *Object
// objects save all the Object instance but not mainAnchor
Expand All @@ -34,6 +43,19 @@ func NewEngine(cfg Config) (e *Engine) {
},
objects: make(map[uuid.UUID]*Object, 10),
}
e.maxSpeedSq = cfg.MaxSpeed * cfg.MaxSpeed
if e.maxSpeedSq <= 0 || e.maxSpeedSq > cSq {
e.maxSpeedSq = cSq
}
e.minSpeedSq = cfg.MinSpeed * cfg.MinSpeed
if cfg.MinAccel > 0 {
e.minAccelSq = cfg.MinAccel * cfg.MinAccel
} else if cfg.MinAccel == 0 {
e.cfg.MinAccel = defaultMinAcc
e.minAccelSq = defaultMinAcc * defaultMinAcc
} else {
e.minAccelSq = cfg.MinAccel
}
return
}

Expand Down Expand Up @@ -74,6 +96,10 @@ func (e *Engine) GetObject(id uuid.UUID) *Object {
return e.objects[id]
}

func (e *Engine) Events() []eventWave {
return e.events
}

func (e *Engine) queueEvent(event eventWave) {
e.Lock()
defer e.Unlock()
Expand All @@ -83,42 +109,47 @@ func (e *Engine) queueEvent(event eventWave) {
// Tick will call tick on the main anchor
func (e *Engine) Tick(dt float64) {
e.Lock()
defer e.Unlock()

e.events = append(e.events, e.queued...)
e.queued = e.queued[:0]
e.Unlock()

var wg sync.WaitGroup
// tick objects
e.RLock()
e.tickLocked(&wg, dt)
e.RUnlock()
wg.Wait()
e.Lock()

// save object status
e.saveStatusLocked(&wg)
wg.Wait()
}

func (e *Engine) tickLocked(wg *sync.WaitGroup, dt float64) {
e.RLock()
defer e.RUnlock()

wg.Add(len(e.objects))
for _, o := range e.objects {
go func(o *Object) {
defer wg.Done()
o.tick(dt)
}(o)
}
// wg.Add(len(e.events))
for _, event := range e.events {
// go func() {
// defer wg.Done()
if event.Heavy() {
go func(event eventWave) {
defer wg.Done()
event.Tick(dt, e)
}(event)
}else{
event.Tick(dt, e)
// }()
}
}
}

func (e *Engine) saveStatusLocked(wg *sync.WaitGroup) {
e.Lock()
defer e.Unlock()

wg.Add(len(e.objects))
for _, o := range e.objects {
go func(o *Object) {
Expand All @@ -127,7 +158,9 @@ func (e *Engine) saveStatusLocked(wg *sync.WaitGroup) {
}(o)
}
for i := 0; i < len(e.events); {
if e.events[i].AliveTime() == 0 {
event := e.events[i]
if event.AliveTime() == 0 {
event.OnRemoved()
e.events[i] = e.events[len(e.events)-1]
e.events = e.events[:len(e.events)-1]
} else {
Expand Down
82 changes: 71 additions & 11 deletions event.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package molecular

import (
"math"
"sync"
)

type eventWave interface {
Sender() *Object
// Pos returns the absolute start position when the event was sent
Expand All @@ -8,7 +13,16 @@ type eventWave interface {
AliveTime() float64
MaxSpeed() float64
MaxRadius() float64
// should this event starts from a separate goroutine
Heavy() bool
Tick(dt float64, e *Engine)
OnRemoved()
}

var eventWaveFnPool = sync.Pool{
New: func() any {
return new(eventWaveFn)
},
}

type eventWaveFn struct {
Expand All @@ -17,24 +31,32 @@ type eventWaveFn struct {
alive float64
speed float64
radius, maxRadius float64
heavy bool
on func(receiver *Object)
onRemove func()
triggered, lastTriggered set[*Object]
objsCache []*Object
}

var _ eventWave = (*eventWaveFn)(nil)

func newEventWave(sender *Object, pos Vec3, radius float64, on func(receiver *Object)) eventWave {
return &eventWaveFn{
sender: sender,
pos: pos,
alive: 60 * 60, // 1 hour
speed: C,
maxRadius: radius,
on: on,
triggered: make(set[*Object], 10),
lastTriggered: make(set[*Object], 10),
func newEventWave(sender *Object, pos Vec3, radius float64, on func(receiver *Object), heavy bool) (e *eventWaveFn) {
e = eventWaveFnPool.Get().(*eventWaveFn)
e.sender = sender
e.pos = pos
e.alive = 60 * 60 // 1 hour
e.speed = C
e.radius = 0
e.maxRadius = radius
e.on = on
e.heavy = heavy
if e.triggered == nil {
e.triggered = make(set[*Object], 10)
e.lastTriggered = make(set[*Object], 10)
} else {
e.triggered.Clear()
}
return
}

func (f *eventWaveFn) Sender() *Object {
Expand All @@ -57,6 +79,10 @@ func (f *eventWaveFn) MaxRadius() float64 {
return f.maxRadius
}

func (f *eventWaveFn) Heavy() bool {
return f.heavy
}

func (f *eventWaveFn) Tick(dt float64, e *Engine) {
f.alive -= dt
if f.alive < 0 {
Expand All @@ -73,10 +99,44 @@ func (f *eventWaveFn) Tick(dt float64, e *Engine) {
f.triggered, f.lastTriggered = f.lastTriggered, f.triggered
f.triggered.Clear()
for _, o := range f.objsCache {
if f.lastTriggered.Has(o) {
if f.lastTriggered.Has(o) || o == f.sender {
continue
}
f.triggered.Put(o)
f.on(o)
}
}

func (f *eventWaveFn) OnRemoved() {
f.on = nil
if f.onRemove != nil {
f.onRemove()
f.onRemove = nil
}
eventWaveFnPool.Put(f)
}

func (e *Engine) newGravityWave(sender *Object, center Vec3, f *GravityField) eventWave {
g := gravityStatusPool.Get().(*gravityStatus)
g.f = *f
g.pos = center
g.gone = false
g.c = 1
maxRadius := -1.0
if e.cfg.MinAccel > 0 {
maxRadius = math.Sqrt(G / e.cfg.MinAccel * f.Mass())
}

w := newEventWave(sender, center, maxRadius, func(r *Object) {
r.Lock()
defer r.Unlock()

if last := r.passedGravity[sender]; last != nil {
last.release()
}
g.count()
r.passedGravity[sender] = g
}, false)
w.onRemove = g.release
return w
}
70 changes: 51 additions & 19 deletions force.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package molecular

import (
"math"
"sync"
)

const (
Expand All @@ -10,15 +11,13 @@ const (

type GravityField struct {
mass float64
radius float64
density float64
radius, rSq float64
}

func NewGravityField(mass float64, radius float64, density float64) *GravityField {
func NewGravityField(mass float64, radius float64) *GravityField {
return &GravityField{
mass: mass,
radius: radius,
density: density,
mass: mass,
radius: radius,
}
}

Expand All @@ -36,32 +35,65 @@ func (f *GravityField) Radius() float64 {

func (f *GravityField) SetRadius(radius float64) {
f.radius = radius
}

func (f *GravityField) Density() float64 {
return f.density
}

func (f *GravityField) SetDensity(density float64) {
f.density = density
f.rSq = radius * radius
}

// FieldAt returns the acceleration at the distance due to the gravity field
func (f *GravityField) FieldAt(distance Vec3) Vec3 {
l := distance.Len()
if l == 0 {
lSq := distance.SqLen()
if lSq == 0 {
return ZeroVec
}
distance.Negate()
if l < f.radius {
distance.ScaleN(4 / 3 * G * f.density * math.Pi)
if lSq < f.rSq {
distance.ScaleN(G * f.mass / lSq)
} else {
l := math.Sqrt(lSq)
// normalize 1 / l and G * m / l ^ 2
distance.ScaleN(G * f.mass / (l * l * l))
distance.ScaleN(G * f.mass / (lSq * l))
}
return distance
}

type gravityStatus struct {
f GravityField
pos Vec3

gone bool
c int
}

var gravityStatusPool = sync.Pool{
New: func() any {
return new(gravityStatus)
},
}

func (s *gravityStatus) FieldAt(pos Vec3) Vec3 {
return s.f.FieldAt(pos.Subbed(s.pos))
}

func (s *gravityStatus) clone() (g *gravityStatus) {
g = gravityStatusPool.Get().(*gravityStatus)
*g = *s
g.c = 1
return
}

func (s *gravityStatus) count() {
s.c++
}

func (s *gravityStatus) release() {
s.c--
if s.c < 0 {
panic("gravityStatus.count become less than one")
}
if s.c == 0 {
gravityStatusPool.Put(s)
}
}

// MagnetField represents a simulated magnetic field.
// For easier calculate, it's not the real magnetic field.
// Since the magnetic field disappears easily, the cubic distance is used
Expand Down
16 changes: 12 additions & 4 deletions motion.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import (
)

const (
C = 299792458.0 // The speed of light
C = 299792458.0 // The speed of light
cSq = C * C
)

// See <https://en.wikipedia.org/wiki/Lorentz_factor>
Expand All @@ -17,11 +18,18 @@ func (e *Engine) LorentzFactor(speed float64) float64 {
// It's used for faster calculate in some specific cases
// See <https://en.wikipedia.org/wiki/Lorentz_factor>
func (e *Engine) ReLorentzFactor(speed float64) float64 {
if speed == 0 {
return e.ReLorentzFactorSq(speed * speed)
}

// ReLorentzFactorSq is same as ReLorentzFactor, but require squared speed as input
func (e *Engine) ReLorentzFactorSq(speedSq float64) float64 {
if speedSq == 0 {
return 1
}
n := speed / C
return math.Sqrt(1 - n*n)
if speedSq > e.maxSpeedSq {
speedSq = e.maxSpeedSq
}
return math.Sqrt(1 - speedSq/cSq)
}

// Note: F = dP / dt
Expand Down
Loading

0 comments on commit 43aca1d

Please sign in to comment.