Repository: mlange-42/arche
Pull request #61 (2023-02-12 23:11:57)

Clean up filter naming, add Not filter


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ef37fb..ee00fb0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,8 +4,8 @@
 
 ### Features
 
-* Generic queries support optional and excluded components (#53, #56, #58, #59)
-* Logic filters for complex queries (#54, #58)
+* Generic queries support optional, additional and excluded components (#53, #56, #58, #59, #60)
+* Logic filters for complex queries (#54, #58, #61)
 
 ### Other
 
diff --git a/examples/filter/main.go b/examples/filter/main.go
index b2f7ae7..3708001 100644
--- a/examples/filter/main.go
+++ b/examples/filter/main.go
@@ -68,7 +68,7 @@ func main() {
 		fi.All(posID, velID),
 		fi.Or(
 			fi.All(rotID),
-			fi.Not(eleID),
+			fi.NoneOf(eleID),
 		),
 	)
 
diff --git a/filter/filter.go b/filter/filter.go
index b5890fa..2465da8 100644
--- a/filter/filter.go
+++ b/filter/filter.go
@@ -7,10 +7,10 @@ import (
 // Mask is a mask for a combination of components.
 type Mask = ecs.Mask
 
-// ALL is a filter including entities with all the given components
+// ALL matches entities that have all the given components.
 type ALL Mask
 
-// All matches all the given components.
+// All matches entities that have all the given components.
 //
 // Like [And] for combining individual components.
 func All(comps ...ecs.ID) ALL {
@@ -18,8 +18,8 @@ func All(comps ...ecs.ID) ALL {
 }
 
 // Not inverts this filter to exclude entities with all the given components
-func (f ALL) Not() NOT {
-	return NOT(f)
+func (f ALL) Not() NoneOF {
+	return NoneOF(f)
 }
 
 // Matches matches a filter against a bitmask
@@ -27,17 +27,17 @@ func (f ALL) Matches(bits ecs.BitMask) bool {
 	return bits.Contains(f.BitMask)
 }
 
-// ANY is a filter including entities with any of the given components
+// ANY matches entities that have any of the given components.
 type ANY Mask
 
-// Any constructs a NotANY filter
+// Any matches entities that have any of the given components.
 func Any(comps ...ecs.ID) ANY {
 	return ANY(ecs.All(comps...))
 }
 
 // Not inverts this filter to exclude entities with any of the given components
-func (f ANY) Not() NotANY {
-	return NotANY(f)
+func (f ANY) Not() AnyNOT {
+	return AnyNOT(f)
 }
 
 // Matches matches a filter against a bitmask
@@ -45,39 +45,39 @@ func (f ANY) Matches(bits ecs.BitMask) bool {
 	return bits.ContainsAny(f.BitMask)
 }
 
-// NOT is a filter for excluding entities with all given components
-type NOT Mask
+// NoneOF matches entities that are missing all the given components.
+type NoneOF Mask
 
-// Not constructs a NOT filter
-func Not(comps ...ecs.ID) NOT {
-	return NOT(ecs.All(comps...))
+// NoneOf matches entities that are missing all the given components.
+func NoneOf(comps ...ecs.ID) NoneOF {
+	return NoneOF(ecs.All(comps...))
 }
 
 // Matches matches a filter against a bitmask
-func (f NOT) Matches(bits ecs.BitMask) bool {
-	return !bits.Contains(f.BitMask)
+func (f NoneOF) Matches(bits ecs.BitMask) bool {
+	return !bits.ContainsAny(f.BitMask)
 }
 
-// NotANY is a filter for excluding entities with any of the given components
-type NotANY Mask
+// AnyNOT matches entities that are missing any of the given components.
+type AnyNOT Mask
 
-// NotAny constructs a NotANY filter
-func NotAny(comps ...ecs.ID) NotANY {
-	return NotANY(ecs.All(comps...))
+// AnyNot matches entities that are missing any of the given components.
+func AnyNot(comps ...ecs.ID) AnyNOT {
+	return AnyNOT(ecs.All(comps...))
 }
 
 // Matches matches a filter against a bitmask
-func (f NotANY) Matches(bits ecs.BitMask) bool {
-	return !bits.ContainsAny(f.BitMask)
+func (f AnyNOT) Matches(bits ecs.BitMask) bool {
+	return !bits.Contains(f.BitMask)
 }
 
-// AND is a filter for ANDing together components
+// AND combines two filters using AND.
 type AND struct {
 	L ecs.Filter
 	R ecs.Filter
 }
 
-// And constructs a pointer to a AND filter
+// And combines two filters using AND.
 func And(l, r ecs.Filter) *AND {
 	return &AND{L: l, R: r}
 }
@@ -87,13 +87,13 @@ func (f *AND) Matches(bits ecs.BitMask) bool {
 	return f.L.Matches(bits) && f.R.Matches(bits)
 }
 
-// OR is a filter for ORing together components
+// OR combines two filters using OR.
 type OR struct {
 	L ecs.Filter
 	R ecs.Filter
 }
 
-// Or constructs a pointer to a OR filter
+// Or combines two filters using OR.
 func Or(l, r ecs.Filter) *OR {
 	return &OR{L: l, R: r}
 }
@@ -103,13 +103,13 @@ func (f *OR) Matches(bits ecs.BitMask) bool {
 	return f.L.Matches(bits) || f.R.Matches(bits)
 }
 
-// XOR is a filter for XORing together components
+// XOR combines two filters using XOR.
 type XOR struct {
 	L ecs.Filter
 	R ecs.Filter
 }
 
-// XOr constructs a pointer to a XOR filter
+// XOr combines two filters using XOR.
 func XOr(l, r ecs.Filter) *XOR {
 	return &XOR{L: l, R: r}
 }
@@ -118,3 +118,18 @@ func XOr(l, r ecs.Filter) *XOR {
 func (f *XOR) Matches(bits ecs.BitMask) bool {
 	return f.L.Matches(bits) != f.R.Matches(bits)
 }
+
+// NOT inverts a filter. It matches if the inner filter does not.
+type NOT struct {
+	f ecs.Filter
+}
+
+// Not inverts a filter. It matches if the inner filter does not.
+func Not(f ecs.Filter) *NOT {
+	return &NOT{f: f}
+}
+
+// Matches matches a filter against a bitmask
+func (f *NOT) Matches(bits ecs.BitMask) bool {
+	return !f.f.Matches(bits)
+}


Test patch:
diff --git a/filter/filter_test.go b/filter/filter_test.go
index d10aa64..827a2b5 100644
--- a/filter/filter_test.go
+++ b/filter/filter_test.go
@@ -36,6 +36,12 @@ func TestLogicFilters(t *testing.T) {
 	assert.False(t, match(filter, hasB))
 	assert.False(t, match(filter, hasNone))
 
+	filter = Not(All(0, 1))
+	assert.False(t, match(filter, hasAll))
+	assert.True(t, match(filter, hasA))
+	assert.True(t, match(filter, hasB))
+	assert.True(t, match(filter, hasNone))
+
 	filter = Any(0, 1)
 	assert.True(t, match(filter, hasAll))
 	assert.True(t, match(filter, hasA))
@@ -66,50 +72,50 @@ func TestLogicFilters(t *testing.T) {
 	assert.False(t, match(filter, hasB))
 	assert.False(t, match(filter, hasNone))
 
-	filter = NotAny(1)
+	filter = AnyNot(1)
 	assert.False(t, match(filter, hasAll))
 	assert.True(t, match(filter, hasA))
 	assert.False(t, match(filter, hasB))
 	assert.True(t, match(filter, hasNone))
 
-	filter = NotAny(0, 1)
+	filter = AnyNot(0, 1)
 	assert.False(t, match(filter, hasAll))
-	assert.False(t, match(filter, hasA))
-	assert.False(t, match(filter, hasB))
+	assert.True(t, match(filter, hasA))
+	assert.True(t, match(filter, hasB))
 	assert.True(t, match(filter, hasNone))
 
-	filter = Not(0)
+	filter = NoneOf(0)
 	assert.False(t, match(filter, hasAll))
 	assert.False(t, match(filter, hasA))
 	assert.True(t, match(filter, hasB))
 	assert.True(t, match(filter, hasNone))
 
-	filter = Not(0, 1)
+	filter = NoneOf(0, 1)
 	assert.False(t, match(filter, hasAll))
-	assert.True(t, match(filter, hasA))
-	assert.True(t, match(filter, hasB))
+	assert.False(t, match(filter, hasA))
+	assert.False(t, match(filter, hasB))
 	assert.True(t, match(filter, hasNone))
 
-	filter = And(hasA, NotANY(hasB))
+	filter = And(hasA, AnyNOT(hasB))
 	assert.False(t, match(filter, hasAll))
 	assert.True(t, match(filter, hasA))
 	assert.False(t, match(filter, hasB))
 	assert.False(t, match(filter, hasNone))
 
-	filter = Or(hasAll, NotAny(1))
+	filter = Or(hasAll, AnyNot(1))
 	assert.True(t, match(filter, hasAll))
 	assert.True(t, match(filter, hasA))
 	assert.False(t, match(filter, hasB))
 	assert.True(t, match(filter, hasNone))
 
-	filter = Or(Any(0, 1), NotANY(hasB))
+	filter = Or(Any(0, 1), AnyNOT(hasB))
 	assert.True(t, match(filter, hasAll))
 	assert.True(t, match(filter, hasA))
 	assert.True(t, match(filter, hasB))
 	assert.True(t, match(filter, hasNone))
 
-	assert.Equal(t, NotAny(0, 1), Any(0, 1).Not())
-	assert.Equal(t, Not(0, 1), All(0, 1).Not())
+	assert.Equal(t, AnyNot(0, 1), Any(0, 1).Not())
+	assert.Equal(t, NoneOf(0, 1), All(0, 1).Not())
 }
 
 func TestFilter(t *testing.T) {
@@ -169,7 +175,7 @@ func TestFilter(t *testing.T) {
 
 	assert.Panics(t, func() { q.Next() })
 
-	q = w.Query(&AND{L: All(rotID), R: NotANY(All(posID))})
+	q = w.Query(&AND{L: All(rotID), R: AnyNOT(All(posID))})
 
 	cnt = 0
 	for q.Next() {