diff --git a/collider/collider.go b/collider/collider.go index 9820ad3..6f4af64 100644 --- a/collider/collider.go +++ b/collider/collider.go @@ -6,11 +6,11 @@ import ( "sync" "time" - "github.com/downflux/go-collider/internal/collider" "github.com/downflux/go-collider/internal/kinematics" "github.com/downflux/go-database/agent" "github.com/downflux/go-database/database" "github.com/downflux/go-database/feature" + "github.com/downflux/go-database/filters" "github.com/downflux/go-database/projectile" "github.com/downflux/go-geometry/2d/vector" "github.com/downflux/go-geometry/2d/vector/polar" @@ -83,8 +83,12 @@ func (c *C) generate(d time.Duration) ([]am, []pm) { v.Copy(a.TargetVelocity()) aabb := a.AABB() - ns := c.db.QueryAgents(aabb, func(b agent.RO) bool { return collider.IsSquishableColliding(a, b) }) - fs := c.db.QueryFeatures(aabb, func(f feature.RO) bool { return collider.IsCollidingFeature(a, f) }) + ns := c.db.QueryAgents(aabb, func(b agent.RO) bool { + return filters.AgentIsCollidingNotSquishable(a, b) + }) + fs := c.db.QueryFeatures(aabb, func(f feature.RO) bool { + return filters.AgentIsCollidingWithFeature(a, f) + }) for _, f := range fs { kinematics.SetFeatureCollisionVelocity(a, f, v) diff --git a/go.mod b/go.mod index df8977c..9eefe5f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/downflux/go-bvh v1.0.0 - github.com/downflux/go-database v0.3.3 + github.com/downflux/go-database v0.3.5 github.com/downflux/go-geometry v0.15.4 github.com/google/go-cmp v0.5.9 ) diff --git a/go.sum b/go.sum index 73e10d4..8e6fc11 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,10 @@ github.com/downflux/go-database v0.3.2 h1:LnXY4hRmdeH9oIjTschW9Z+MxGUFGOu5Sz+lX+ github.com/downflux/go-database v0.3.2/go.mod h1:GgparhC7kkqx5VPbyFjE8/zA7o2F/E8P8SR/GwXNs+s= github.com/downflux/go-database v0.3.3 h1:VJis0vWeEbJ0MN/f/kowD0+RSyDajqVh3T2JbZjR0RU= github.com/downflux/go-database v0.3.3/go.mod h1:GgparhC7kkqx5VPbyFjE8/zA7o2F/E8P8SR/GwXNs+s= +github.com/downflux/go-database v0.3.4 h1:MU0JwgdkK7lCTG7GGohBLOcCmErU6vh/Gyf+pZyTPY8= +github.com/downflux/go-database v0.3.4/go.mod h1:GgparhC7kkqx5VPbyFjE8/zA7o2F/E8P8SR/GwXNs+s= +github.com/downflux/go-database v0.3.5 h1:36TZFOI2vbHhGJGRe9MFtpmOco+OEA45glTyArVpzLc= +github.com/downflux/go-database v0.3.5/go.mod h1:GgparhC7kkqx5VPbyFjE8/zA7o2F/E8P8SR/GwXNs+s= github.com/downflux/go-geometry v0.15.2 h1:opm0LgH8p6xIt9od3ZcDxf5HSHP/GOj937s5fUwI1cs= github.com/downflux/go-geometry v0.15.2/go.mod h1:ZJcto0QwYRdoIbi5G4mh5y6v2xUS+d++/cANaO1F9+8= github.com/downflux/go-geometry v0.15.3 h1:Kwr2tiozUQF/Q47pCZDRaPS+MPrV6KsC87Gm0J7p15w= diff --git a/internal/collider/collider.go b/internal/collider/collider.go deleted file mode 100644 index f6c8a66..0000000 --- a/internal/collider/collider.go +++ /dev/null @@ -1,60 +0,0 @@ -package collider - -import ( - "github.com/downflux/go-database/agent" - "github.com/downflux/go-database/feature" - "github.com/downflux/go-database/flags" - "github.com/downflux/go-geometry/2d/vector" - "github.com/downflux/go-geometry/nd/hyperrectangle" - - chr "github.com/downflux/go-collider/internal/geometry/hyperrectangle" -) - -// IsColliding checks if two agents are actually physically overlapping. This -// does not care about the extra logic for e.g. squishing. -func IsColliding(a agent.RO, b agent.RO) bool { - if a.ID() == b.ID() { - return false - } - - m, n := a.Flags(), b.Flags() - - // Agents are allowed to overlap if (only) one of them is in the air. - if (m^n)&flags.FTerrainAir == flags.FTerrainAir { - return false - } - - r := a.Radius() + b.Radius() - if vector.SquaredMagnitude(vector.Sub(a.Position(), b.Position())) > r*r { - return false - } - return true - -} - -func IsSquishableColliding(a agent.RO, b agent.RO) bool { - if IsColliding(a, b) { - // TODO(minkezhang): Check for team. - if a.Flags()&flags.SizeCheck > b.Flags()&flags.SizeCheck { - return false - } - return true - } - return false -} - -func IsCollidingFeature(a agent.RO, f feature.RO) bool { - m, n := a.Flags(), f.Flags() - - // Feature and agent are allowed to overlap if (only) one of them is in - // the air. - if (m^n)&flags.FTerrainAir == flags.FTerrainAir { - return false - } - - if hyperrectangle.Disjoint(a.AABB(), f.AABB()) { - return false - } - - return chr.Collide(f.AABB(), a.Position(), a.Radius()) -} diff --git a/internal/collider/collider_test.go b/internal/collider/collider_test.go deleted file mode 100644 index 1de4686..0000000 --- a/internal/collider/collider_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package collider - -import ( - "testing" - - "github.com/downflux/go-database/agent" - "github.com/downflux/go-database/agent/mock" - "github.com/downflux/go-database/flags" - "github.com/downflux/go-geometry/2d/vector" - "github.com/downflux/go-geometry/2d/vector/polar" -) - -func TestIsColliding(t *testing.T) { - type config struct { - name string - a agent.RO - b agent.RO - want bool - } - - configs := []config{ - func() config { - a := mock.New(1, agent.O{ - Heading: polar.V{1, 0}, - Velocity: vector.V{0, 0}, - Position: vector.V{1, 1}, - Flags: flags.FSizeSmall, - }) - return config{ - name: "NoCollide/SelfID", - a: a, - b: a, - want: false, - } - }(), - func() config { - a := mock.New(1, agent.O{ - Position: vector.V{1, 1}, - Radius: 1, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Flags: flags.FSizeSmall | flags.FTerrainAir | flags.FTerrainAccessibleAir, - }) - b := mock.New(2, agent.O{ - Position: vector.V{1, 1}, - Radius: 1, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Flags: flags.FSizeSmall | flags.FTerrainLand | flags.FTerrainAccessibleLand, - }) - return config{ - name: "NoCollide/ExclusiveAir", - a: a, - b: b, - want: false, - } - }(), - func() config { - a := mock.New(1, agent.O{ - Position: vector.V{1, 1}, - Radius: 1, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Flags: flags.FSizeSmall | flags.FTerrainAir | flags.FTerrainAccessibleAir, - }) - b := mock.New(2, agent.O{ - Position: vector.V{1, 1}, - Radius: 1, - Velocity: vector.V{0, 0}, - Heading: polar.V{1, 0}, - Flags: flags.FSizeSmall | flags.FTerrainAir | flags.FTerrainAccessibleAir, - }) - return config{ - name: "Collide/BothAir", - a: a, - b: b, - want: true, - } - }(), - } - - for _, c := range configs { - t.Run(c.name, func(t *testing.T) { - if got := IsColliding(c.a, c.b); got != c.want { - t.Errorf("IsColliding() = %v, want = %v", got, c.want) - } - }) - } -} diff --git a/internal/geometry/hyperrectangle/hyperrectangle.go b/internal/geometry/hyperrectangle/hyperrectangle.go deleted file mode 100644 index e1c7b8b..0000000 --- a/internal/geometry/hyperrectangle/hyperrectangle.go +++ /dev/null @@ -1,137 +0,0 @@ -package hyperrectangle - -import ( - "fmt" - - "github.com/downflux/go-geometry/2d/hypersphere" - "github.com/downflux/go-geometry/2d/line" - "github.com/downflux/go-geometry/2d/vector" - "github.com/downflux/go-geometry/epsilon" - "github.com/downflux/go-geometry/nd/hyperrectangle" - - vnd "github.com/downflux/go-geometry/nd/vector" -) - -type Side uint64 - -const ( - SideN = 1 << iota - SideE - SideS - SideW - - CornerNE = SideN | SideE - CornerSE = SideS | SideE - CornerSW = SideS | SideW - CornerNW = SideN | SideW -) - -func N(r hyperrectangle.R, v vector.V) vector.V { - vx, vy := v.X(), v.Y() - - xmin, xmax := r.Min().X(vnd.AXIS_X), r.Max().X(vnd.AXIS_X) - ymin, ymax := r.Min().X(vnd.AXIS_Y), r.Max().X(vnd.AXIS_Y) - - var domain Side - if dnorth := vy - ymax; dnorth >= 0 { - domain |= SideN - } - if dsouth := ymin - vy; dsouth >= 0 { - domain |= SideS - } - if deast := vx - xmax; deast >= 0 { - domain |= SideE - } - if dwest := xmin - vx; dwest >= 0 { - domain |= SideW - } - - n := vector.M{0, 0} - n.Copy(v) - - switch domain { - case SideN: - return vector.V{0, 1} - case SideE: - return vector.V{1, 0} - case SideS: - return vector.V{0, -1} - case SideW: - return vector.V{-1, 0} - case CornerNE: - n.Sub(vector.V{xmax, ymax}) - if epsilon.Within(vector.Magnitude(n.V()), 0) { - n.Copy(vector.V{1, 1}) - } - case CornerSE: - n.Sub(vector.V{xmax, ymin}) - if epsilon.Within(vector.Magnitude(n.V()), 0) { - n.Copy(vector.V{1, -1}) - } - case CornerSW: - n.Sub(vector.V{xmin, ymin}) - if epsilon.Within(vector.Magnitude(n.V()), 0) { - n.Copy(vector.V{-1, -1}) - } - case CornerNW: - n.Sub(vector.V{xmin, ymax}) - if epsilon.Within(vector.Magnitude(n.V()), 0) { - n.Copy(vector.V{-1, 1}) - } - default: - panic(fmt.Sprintf("invalid domain: %v", domain)) - } - - n.Unit() - return n.V() -} - -// Collide checks if a circle overlaps an AABB. Note that this can be decomposed -// into three checks -- -// -// 1. if the circle center lies inside the rectangle, -// 1. if the an edge of the rectangle crosses the circle at some point, and -// 1. if the rectangle lies entirely within the circle, but does not overlap the -// circle center -// -// See https://stackoverflow.com/a/402019/873865 for more information. -func Collide(r hyperrectangle.R, p vector.V, radius float64) bool { - if r.In(vnd.V(p)) { - return true - } - - c := *hypersphere.New(p, radius) - - xmin, ymin := r.Min().X(vnd.AXIS_X), r.Min().X(vnd.AXIS_Y) - xmax, ymax := r.Max().X(vnd.AXIS_X), r.Max().X(vnd.AXIS_Y) - - // Check corners. - if c.In(vector.V{xmin, ymin}) { - return true - } - if c.In(vector.V{xmin, ymax}) { - return true - } - if c.In(vector.V{xmax, ymax}) { - return true - } - if c.In(vector.V{xmax, ymin}) { - return true - } - - // Check edges. - if _, _, ok := line.New(vector.V{xmin, ymin}, vector.V{0, 1}).IntersectCircle(c); ok { - return true - } - if _, _, ok := line.New(vector.V{xmin, ymax}, vector.V{1, 0}).IntersectCircle(c); ok { - return true - } - if _, _, ok := line.New(vector.V{xmax, ymax}, vector.V{0, -1}).IntersectCircle(c); ok { - return true - } - if _, _, ok := line.New(vector.V{xmax, ymin}, vector.V{-1, 0}).IntersectCircle(c); ok { - return true - } - - return false -} diff --git a/internal/geometry/hyperrectangle/hyperrectangle_test.go b/internal/geometry/hyperrectangle/hyperrectangle_test.go deleted file mode 100644 index 3474aa8..0000000 --- a/internal/geometry/hyperrectangle/hyperrectangle_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package hyperrectangle - -import ( - "testing" - - "github.com/downflux/go-geometry/2d/vector" - "github.com/downflux/go-geometry/nd/hyperrectangle" - - vnd "github.com/downflux/go-geometry/nd/vector" -) - -func TestN(t *testing.T) { - r := *hyperrectangle.New(vnd.V{0, 0}, vnd.V{10, 10}) - type config struct { - name string - v vector.V - want vector.V - } - - configs := []config{ - { - name: "North", - v: vector.V{5, 20}, - want: vector.V{0, 1}, - }, - { - name: "South", - v: vector.V{5, -10}, - want: vector.V{0, -1}, - }, - { - name: "East", - v: vector.V{20, 5}, - want: vector.V{1, 0}, - }, - { - name: "West", - v: vector.V{-10, 5}, - want: vector.V{-1, 0}, - }, - - { - name: "Corner/NE", - v: vector.V{10, 10}, - want: vector.Unit(vector.V{1, 1}), - }, - { - name: "Corner/SE", - v: vector.V{10, 0}, - want: vector.Unit(vector.V{1, -1}), - }, - { - name: "Corner/SW", - v: vector.V{0, 0}, - want: vector.Unit(vector.V{-1, -1}), - }, - { - name: "Corner/NW", - v: vector.V{0, 10}, - want: vector.Unit(vector.V{-1, 1}), - }, - } - - for _, c := range configs { - t.Run(c.name, func(t *testing.T) { - if got := N(r, c.v); !vector.Within(got, c.want) { - t.Errorf("N() = %v, want = %v", got, c.want) - } - }) - } -} - -func TestCollide(t *testing.T) { - type config struct { - name string - r hyperrectangle.R - p vector.V - radius float64 - want bool - } - - configs := []config{ - { - name: "Center", - r: *hyperrectangle.New(vnd.V{0, 0}, vnd.V{10, 10}), - p: vector.V{5, 5}, - radius: 1, - want: true, - }, - { - name: "Corner", - r: *hyperrectangle.New(vnd.V{0, 0}, vnd.V{10, 10}), - p: vector.V{-1, -1}, - radius: 2, - want: true, - }, - { - name: "Edge", - r: *hyperrectangle.New(vnd.V{0, 0}, vnd.V{10, 10}), - p: vector.V{-1, 5}, - radius: 2, - want: true, - }, - { - name: "Outside", - r: *hyperrectangle.New(vnd.V{0, 0}, vnd.V{10, 10}), - p: vector.V{12, 12}, - radius: 1, - want: false, - }, - } - - for _, c := range configs { - t.Run(c.name, func(t *testing.T) { - if got := Collide(c.r, c.p, c.radius); got != c.want { - t.Errorf("Collide() = %v, want = %v", got, c.want) - } - }) - } -} diff --git a/internal/kinematics/kinematics.go b/internal/kinematics/kinematics.go index 8804114..33c1ae9 100644 --- a/internal/kinematics/kinematics.go +++ b/internal/kinematics/kinematics.go @@ -10,7 +10,7 @@ import ( "github.com/downflux/go-geometry/2d/vector/polar" "github.com/downflux/go-geometry/epsilon" - chr "github.com/downflux/go-collider/internal/geometry/hyperrectangle" + dhr "github.com/downflux/go-database/geometry/hyperrectangle" ) const ( @@ -44,7 +44,7 @@ func ClampCollisionVelocity(a agent.RO, b agent.RO, v vector.M) { } func ClampFeatureCollisionVelocity(a agent.RO, f feature.RO, v vector.M) { - n := chr.N(f.AABB(), a.Position()).M() + n := dhr.Normal(f.AABB(), a.Position()).M() n.Scale(-1) if c := vector.Dot(n.V(), v.V()); c > tolerance { v.SetX(0) @@ -103,7 +103,7 @@ func SetCollisionVelocity(a agent.RO, b agent.RO, v vector.M) { } func SetFeatureCollisionVelocity(a agent.RO, f feature.RO, v vector.M) { - n := chr.N(f.AABB(), a.Position()).M() + n := dhr.Normal(f.AABB(), a.Position()).M() n.Scale(-1) if c := vector.Dot(n.V(), v.V()); c > tolerance { n.Scale(c)