Repository: mlange-42/arche
Pull request #59 (2023-02-12 20:02:30)

Optionals in generic queries


Patch:
diff --git a/README.md b/README.md
index 35be866..d7cd33c 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
 [Department for Ecological Modelling](https://www.ufz.de/index.php?en=34213) at the
 [Helmholtz Centre for Environmental Research](https://www.ufz.de).
 
-**:warning: Arche is still under rapid development! Be prepared for frequent API changes. :warning:**  
+**:warning: Arche is still under rapid development. Be prepared for frequent API changes! :warning:**  
 
 
 ## Installations
@@ -22,7 +22,8 @@ go get github.com/mlange-42/arche
 
 ## Features
 
-* Minimal API. See the [API docs](https://pkg.go.dev/github.com/mlange-42/arche).
+* Minimal core API. See the [API docs](https://pkg.go.dev/github.com/mlange-42/arche).
+* Optional rich filtering and generic query API.
 * Fast iteration and component access via queries (≈2.5ns iterate + get).
 * Fast random access for components of arbitrary entities. Useful for hierarchies.
 * No systems. Use your own structure.
@@ -80,16 +81,18 @@ func main() {
 		vel.Y = rand.NormFloat64()
 	}
 
+	// Generic queries support up to 8 components.
+	// For more components, use World.Query() directly.
+	query := generic.Query2[Position, Velocity]()
+
 	// Time loop.
 	for t := 0; t < 1000; t++ {
-		// Get a fresh query.
-		// Generic queries support up to 8 components.
-		// For more components, use World.Query()
-		query := generic.Query2[Position, Velocity](&world)
-		// Iterate it
-		for query.Next() {
+		// Get a fresh query iterator.
+		q := query.Build(&world)
+		// Iterate it.
+		for q.Next() {
 			// Component access through a Query.
-			pos, vel := query.GetAll()
+			pos, vel := q.GetAll()
 			// Update component fields.
 			pos.X += vel.X
 			pos.Y += vel.Y
diff --git a/doc.go b/doc.go
index 78d3151..956119d 100644
--- a/doc.go
+++ b/doc.go
@@ -1,4 +1,7 @@
 // Package arche is an archetype-based Entity Component System for Go.
 //
-// See sub-package [github.com/mlange-42/arche/ecs] for Arche's API.
+// See the sub-packages:
+//   - Core API -- [github.com/mlange-42/arche/ecs]
+//   - Generic queries -- [github.com/mlange-42/arche/generic]
+//   - Advanced filters -- [github.com/mlange-42/arche/filter]
 package arche
diff --git a/ecs/archetype.go b/ecs/archetype.go
index 4369fae..59d95db 100644
--- a/ecs/archetype.go
+++ b/ecs/archetype.go
@@ -11,7 +11,7 @@ type archetype struct {
 	Ids  []ID
 	// Indirection to avoid a fixed-size array of storages
 	// Increases access time by 50-100%
-	indices    [maskTotalBits]uint8
+	indices    [MaskTotalBits]uint8
 	entities   storage
 	components []storage
 	toAdd      map[ID]*archetype
diff --git a/ecs/bit_pool.go b/ecs/bit_pool.go
index 046dfe0..0af9a93 100644
--- a/ecs/bit_pool.go
+++ b/ecs/bit_pool.go
@@ -3,7 +3,7 @@ package ecs
 // bitPool is an entityPool implementation using implicit linked lists.
 // Implements https://skypjack.github.io/2019-05-06-ecs-baf-part-3/
 type bitPool struct {
-	bits      [maskTotalBits]uint8
+	bits      [MaskTotalBits]uint8
 	next      uint8
 	length    uint8
 	available uint8
@@ -12,7 +12,7 @@ type bitPool struct {
 // newBitPool creates a new, initialized bit pool
 func newBitPool() bitPool {
 	return bitPool{
-		bits:      [maskTotalBits]uint8{},
+		bits:      [MaskTotalBits]uint8{},
 		next:      0,
 		length:    0,
 		available: 0,
@@ -22,7 +22,7 @@ func newBitPool() bitPool {
 // Get returns a fresh or recycled bit
 func (p *bitPool) Get() uint8 {
 	if p.available == 0 {
-		if p.length >= maskTotalBits {
+		if p.length >= MaskTotalBits {
 			panic("run out of the maximum of 64 bits")
 		}
 		b := p.length
diff --git a/ecs/bitmask.go b/ecs/bitmask.go
index 53a1fdb..abc433e 100644
--- a/ecs/bitmask.go
+++ b/ecs/bitmask.go
@@ -1,11 +1,11 @@
 package ecs
 
-// maskTotalBits is the size of Mask in bits.
+// MaskTotalBits is the size of Mask in bits.
 //
 // It is the maximum number of component types that may exist in any [World].
-const maskTotalBits = 64
+const MaskTotalBits = 64
 
-// BitMask is a bitmask.
+// BitMask is a 64 bit bitmask.
 type BitMask uint64
 
 var nibbleToBitsSet = [16]uint{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}
@@ -13,8 +13,6 @@ var nibbleToBitsSet = [16]uint{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}
 // NewBitMask creates a new bitmask from a list of IDs.
 //
 // If any ID is bigger or equal [MaskTotalBits], it'll not be added to the mask.
-//
-// Implementation taken from https://github.com/marioolofo/go-gameengine-ecs.
 func NewBitMask(ids ...ID) BitMask {
 	var mask BitMask
 	for _, id := range ids {
diff --git a/ecs/doc.go b/ecs/doc.go
index e86e80a..52ceb1e 100644
--- a/ecs/doc.go
+++ b/ecs/doc.go
@@ -1,2 +1,2 @@
-// Package ecs contains all of Arche's API.
+// Package ecs contains Arche's core API.
 package ecs
diff --git a/ecs/query.go b/ecs/query.go
index 335f9bf..3c20ca6 100644
--- a/ecs/query.go
+++ b/ecs/query.go
@@ -6,10 +6,11 @@ import (
 
 // Filter is the interface for logic filters
 type Filter interface {
+	// Matches the filter against a bitmask, i.e. a component composition.
 	Matches(bits BitMask) bool
 }
 
-// Mask is a mask for a combination of components.
+// Mask for a combination of components.
 type Mask struct {
 	BitMask BitMask
 }
@@ -70,9 +71,11 @@ type EntityIter interface {
 // Query is an advanced iterator to iterate entities.
 //
 // Create queries through the [World] using [World.Query].
-// See also the generic alternatives [generic.Query1],  [generic.Query2], etc.
 //
-// For advanced filtering, see package [filter]
+// See also the generic alternatives [github.com/mlange-42/arche/generic.Query1],
+// [github.com/mlange-42/arche/generic.Query2], etc.
+//
+// For advanced filtering, see package [github.com/mlange-42/arche/filter]
 type Query struct {
 	queryIter
 	filter Filter
diff --git a/ecs/registry.go b/ecs/registry.go
index 46b4e8f..6557ed9 100644
--- a/ecs/registry.go
+++ b/ecs/registry.go
@@ -5,7 +5,7 @@ import "reflect"
 // componentRegistry keeps track of component IDs
 type componentRegistry struct {
 	Components map[reflect.Type]ID
-	Types      [maskTotalBits]reflect.Type
+	Types      [MaskTotalBits]reflect.Type
 }
 
 // newComponentRegistry creates a new ComponentRegistry
@@ -31,7 +31,7 @@ func (r *componentRegistry) ComponentID(tp reflect.Type) ID {
 	if id, ok := r.Components[tp]; ok {
 		return id
 	}
-	return r.RegisterComponent(tp, maskTotalBits)
+	return r.RegisterComponent(tp, MaskTotalBits)
 }
 
 // ComponentType returns the type of a component by ID
diff --git a/ecs/world.go b/ecs/world.go
index 21ba465..0f0c572 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -105,7 +105,8 @@ func (w *World) RemEntity(entity Entity) {
 //	    pos.X += 1.0
 //	}
 //
-// For advanced filtering, see package [filter]
+// For the use of generics for queries, see package [github.com/mlange-42/arche/generic].
+// For advanced filtering, see package [github.com/mlange-42/arche/filter].
 func (w *World) Query(filter Filter) Query {
 	lock := w.bitPool.Get()
 	w.locks.Set(ID(lock), true)
@@ -122,7 +123,7 @@ func (w *World) Alive(entity Entity) bool {
 // Returns `nil` if the entity has no such component.
 // Panics when called for an already removed entity.
 //
-// See also [Map.Get] for a generic variant.
+// See also [github.com/mlange-42/arche/generic.Map.Get] for a generic variant.
 func (w *World) Get(entity Entity, comp ID) unsafe.Pointer {
 	index := w.entities[entity.id]
 	return index.arch.Get(int(index.index), comp)
@@ -132,7 +133,7 @@ func (w *World) Get(entity Entity, comp ID) unsafe.Pointer {
 //
 // Panics when called for an already removed entity.
 //
-// See also [Map.Has] for a generic variant.
+// See also [github.com/mlange-42/arche/generic.Map.Has] for a generic variant.
 func (w *World) Has(entity Entity, comp ID) bool {
 	return w.entities[entity.id].arch.HasComponent(comp)
 }
@@ -142,7 +143,7 @@ func (w *World) Has(entity Entity, comp ID) bool {
 // Panics when called on a locked world or for an already removed entity.
 // Do not use during [Query] iteration!
 //
-// See also the generic variants [Add], [Add2], [Add3], ...
+// See also the generic variants [github.com/mlange-42/arche/generic.Add1], [github.com/mlange-42/arche/generic.Add2], [github.com/mlange-42/arche/generic.Add3], ...
 func (w *World) Add(entity Entity, comps ...ID) {
 	w.Exchange(entity, comps, []ID{})
 }
@@ -157,7 +158,7 @@ func (w *World) Add(entity Entity, comps ...ID) {
 // Panics when called on a locked world or for an already removed entity.
 // Do not use during [Query] iteration!
 //
-// See also the generic variants [Assign], [Assign2], [Assign3], ...
+// See also the generic variants [github.com/mlange-42/arche/generic.Assign1], [github.com/mlange-42/arche/generic.Assign2], [github.com/mlange-42/arche/generic.Assign3], ...
 func (w *World) Assign(entity Entity, id ID, comp interface{}) unsafe.Pointer {
 	w.Exchange(entity, []ID{id}, []ID{})
 	return w.copyTo(entity, id, comp)
@@ -174,7 +175,7 @@ func (w *World) Assign(entity Entity, id ID, comp interface{}) unsafe.Pointer {
 //
 // Panics if the entity does not have a component of that type.
 //
-// See also [Map.Set] for a generic variant.
+// See also [github.com/mlange-42/arche/generic.Map.Set] for a generic variant.
 func (w *World) Set(entity Entity, id ID, comp interface{}) unsafe.Pointer {
 	return w.copyTo(entity, id, comp)
 }
@@ -188,7 +189,7 @@ func (w *World) Set(entity Entity, id ID, comp interface{}) unsafe.Pointer {
 // Panics when called on a locked world or for an already removed entity.
 // Do not use during [Query] iteration!
 //
-// See also the generic variants [Assign], [Assign2], [Assign3], ...
+// See also the generic variants [github.com/mlange-42/arche/generic.Assign1], [github.com/mlange-42/arche/generic.Assign2], [github.com/mlange-42/arche/generic.Assign3], ...
 func (w *World) AssignN(entity Entity, comps ...Component) {
 	ids := make([]ID, len(comps))
 	for i, c := range comps {
@@ -206,7 +207,7 @@ func (w *World) AssignN(entity Entity, comps ...Component) {
 //
 // Do not use during [Query] iteration!
 //
-// See also the generic variants [Remove], [Remove2], [Remove3], ...
+// See also the generic variants [github.com/mlange-42/arche/generic.Remove1], [github.com/mlange-42/arche/generic.Remove2], [github.com/mlange-42/arche/generic.Remove3], ...
 func (w *World) Remove(entity Entity, comps ...ID) {
 	w.Exchange(entity, []ID{}, comps)
 }
@@ -337,7 +338,7 @@ func (w *World) createArchetype(mask BitMask) *archetype {
 	types := make([]componentType, count)
 
 	idx := 0
-	for i := 0; i < maskTotalBits; i++ {
+	for i := 0; i < MaskTotalBits; i++ {
 		id := ID(i)
 		if mask.Get(id) {
 			types[idx] = componentType{ID: id, Type: w.registry.Types[id]}
diff --git a/examples/base/main.go b/examples/base/main.go
index ecdb24b..9532926 100644
--- a/examples/base/main.go
+++ b/examples/base/main.go
@@ -44,7 +44,7 @@ func main() {
 
 	// Time loop.
 	for t := 0; t < 1000; t++ {
-		// Get a fresh query.
+		// Get a fresh query iterator.
 		query := world.Query(ecs.All(posID, velID))
 		// Iterate it
 		for query.Next() {
diff --git a/examples/filter/main.go b/examples/filter/main.go
index 3071009..b2f7ae7 100644
--- a/examples/filter/main.go
+++ b/examples/filter/main.go
@@ -74,9 +74,9 @@ func main() {
 
 	// Time loop.
 	for t := 0; t < 1000; t++ {
-		// Get a fresh filter query.
+		// Get a fresh query iterator.
 		query := world.Query(filter)
-		// Iterate it
+		// Iterate it.
 		for query.Next() {
 			// Component access through a Query.
 			pos := (*Position)(query.Get(posID))
diff --git a/examples/generic/main.go b/examples/generic/main.go
index d22b1b9..0a78303 100644
--- a/examples/generic/main.go
+++ b/examples/generic/main.go
@@ -38,15 +38,15 @@ func main() {
 		vel.Y = rand.NormFloat64()
 	}
 
+	// Generic queries support up to 8 components.
+	// For more components, use World.Query() directly.
 	query := generic.Query2[Position, Velocity]()
 
 	// Time loop.
 	for t := 0; t < 1000; t++ {
-		// Get a fresh query.
-		// Generic queries support up to 8 components.
-		// For more components, use World.Query()
+		// Get a fresh query iterator.
 		q := query.Build(&world)
-		// Iterate it
+		// Iterate it.
 		for q.Next() {
 			// Component access through a Query.
 			pos, vel := q.GetAll()
diff --git a/filter/doc.go b/filter/doc.go
new file mode 100644
index 0000000..f30f130
--- /dev/null
+++ b/filter/doc.go
@@ -0,0 +1,2 @@
+// Package filter contains Arche's advanced logic filtering API.
+package filter
diff --git a/filter/filter.go b/filter/filter.go
index b23ffbc..b5890fa 100644
--- a/filter/filter.go
+++ b/filter/filter.go
@@ -7,13 +7,6 @@ import (
 // Mask is a mask for a combination of components.
 type Mask = ecs.Mask
 
-// OneOf matches any of the two components.
-//
-// Like [Or] for combining individual components.
-func OneOf(compA ecs.ID, compB ecs.ID) *OR {
-	return &OR{ecs.All(compA), ecs.All(compB)}
-}
-
 // ALL is a filter including entities with all the given components
 type ALL Mask
 
@@ -43,8 +36,8 @@ func Any(comps ...ecs.ID) ANY {
 }
 
 // Not inverts this filter to exclude entities with any of the given components
-func (f ANY) Not() NOT {
-	return NOT(f)
+func (f ANY) Not() NotANY {
+	return NotANY(f)
 }
 
 // Matches matches a filter against a bitmask
diff --git a/generic/doc.go b/generic/doc.go
new file mode 100644
index 0000000..e5f7cb5
--- /dev/null
+++ b/generic/doc.go
@@ -0,0 +1,2 @@
+// Package generic contains Arche's generic query API.
+package generic
diff --git a/generic/generic.go b/generic/generic.go
index 9e8cd12..8054834 100644
--- a/generic/generic.go
+++ b/generic/generic.go
@@ -16,7 +16,7 @@ type Map[T any] struct {
 //
 // Map provides a type-safe way to access a component type by entity ID.
 //
-// See also [World.Get], [World.Has] and [World.Set].
+// See also [ecs.World.Get], [ecs.World.Has] and [ecs.World.Set].
 func NewMap[T any](w *ecs.World) Map[T] {
 	return Map[T]{
 		id:    ecs.ComponentID[T](w),
@@ -26,14 +26,14 @@ func NewMap[T any](w *ecs.World) Map[T] {
 
 // Get gets the component for the given entity.
 //
-// See also [World.Get].
+// See also [ecs.World.Get].
 func (g *Map[T]) Get(entity ecs.Entity) *T {
 	return (*T)(g.world.Get(entity, g.id))
 }
 
 // Has returns whether the entity has the component.
 //
-// See also [World.Has].
+// See also [ecs.World.Has].
 func (g *Map[T]) Has(entity ecs.Entity) bool {
 	return g.world.Has(entity, g.id)
 }
@@ -42,14 +42,14 @@ func (g *Map[T]) Has(entity ecs.Entity) bool {
 //
 // Panics if the entity does not have a component of that type.
 //
-// See also [World.Set].
+// See also [ecs.World.Set].
 func (g *Map[T]) Set(entity ecs.Entity, comp *T) *T {
 	return (*T)(g.world.Set(entity, g.id, comp))
 }
 
 // Add1 adds a component type to an entity.
 //
-// See also [World.Add1].
+// See also [ecs.World.Add].
 func Add1[A any](w *ecs.World, entity ecs.Entity) *A {
 	id := ecs.ComponentID[A](w)
 	w.Add(entity, id)
@@ -58,7 +58,7 @@ func Add1[A any](w *ecs.World, entity ecs.Entity) *A {
 
 // Add2 adds two component types to an entity.
 //
-// See also [World.Add].
+// See also [ecs.World.Add].
 func Add2[A any, B any](w *ecs.World, entity ecs.Entity) (*A, *B) {
 	idA := ecs.ComponentID[A](w)
 	idB := ecs.ComponentID[B](w)
@@ -68,7 +68,7 @@ func Add2[A any, B any](w *ecs.World, entity ecs.Entity) (*A, *B) {
 
 // Add3 adds three component types to an entity.
 //
-// See also [World.Add].
+// See also [ecs.World.Add].
 func Add3[A any, B any, C any](w *ecs.World, entity ecs.Entity) (*A, *B, *C) {
 	idA := ecs.ComponentID[A](w)
 	idB := ecs.ComponentID[B](w)
@@ -79,7 +79,7 @@ func Add3[A any, B any, C any](w *ecs.World, entity ecs.Entity) (*A, *B, *C) {
 
 // Add4 adds four component types to an entity.
 //
-// See also [World.Add].
+// See also [ecs.World.Add].
 func Add4[A any, B any, C any, D any](w *ecs.World, entity ecs.Entity) (*A, *B, *C, *D) {
 	idA := ecs.ComponentID[A](w)
 	idB := ecs.ComponentID[B](w)
@@ -91,7 +91,7 @@ func Add4[A any, B any, C any, D any](w *ecs.World, entity ecs.Entity) (*A, *B,
 
 // Add5 adds five component types to an entity.
 //
-// See also [World.Add].
+// See also [ecs.World.Add].
 func Add5[A any, B any, C any, D any, E any](w *ecs.World, entity ecs.Entity) (*A, *B, *C, *D, *E) {
 	idA := ecs.ComponentID[A](w)
 	idB := ecs.ComponentID[B](w)
@@ -104,7 +104,7 @@ func Add5[A any, B any, C any, D any, E any](w *ecs.World, entity ecs.Entity) (*
 
 // Assign1 adds a components to an entity.
 //
-// See also [World.Assign1] and [World.AssignN].
+// See also [ecs.World.Assign] and [ecs.World.AssignN].
 func Assign1[A any](w *ecs.World, entity ecs.Entity, a *A) *A {
 	idA := ecs.ComponentID[A](w)
 	w.Assign(entity, idA, a)
@@ -113,7 +113,7 @@ func Assign1[A any](w *ecs.World, entity ecs.Entity, a *A) *A {
 
 // Assign2 adds two components to an entity.
 //
-// See also [World.Assign] and [World.AssignN].
+// See also [ecs.World.Assign] and [ecs.World.AssignN].
 func Assign2[A any, B any](w *ecs.World, entity ecs.Entity, a *A, b *B) (*A, *B) {
 	idA := ecs.ComponentID[A](w)
 	idB := ecs.ComponentID[B](w)
@@ -123,7 +123,7 @@ func Assign2[A any, B any](w *ecs.World, entity ecs.Entity, a *A, b *B) (*A, *B)
 
 // Assign3 adds three components to an entity.
 //
-// See also [World.Assign] and [World.AssignN].
+// See also [ecs.World.Assign] and [ecs.World.AssignN].
 func Assign3[A any, B any, C any](w *ecs.World, entity ecs.Entity, a *A, b *B, c *C) (*A, *B, *C) {
 	idA := ecs.ComponentID[A](w)
 	idB := ecs.ComponentID[B](w)
@@ -134,7 +134,7 @@ func Assign3[A any, B any, C any](w *ecs.World, entity ecs.Entity, a *A, b *B, c
 
 // Assign4 adds four components to an entity.
 //
-// See also [World.Assign] and [World.AssignN].
+// See also [ecs.World.Assign] and [ecs.World.AssignN].
 func Assign4[A any, B any, C any, D any](w *ecs.World, entity ecs.Entity, a *A, b *B, c *C, d *D) (*A, *B, *C, *D) {
 	idA := ecs.ComponentID[A](w)
 	idB := ecs.ComponentID[B](w)
@@ -146,7 +146,7 @@ func Assign4[A any, B any, C any, D any](w *ecs.World, entity ecs.Entity, a *A,
 
 // Assign5 adds four components to an entity.
 //
-// See also [World.Assign] and [World.AssignN].
+// See also [ecs.World.Assign] and [ecs.World.AssignN].
 func Assign5[A any, B any, C any, D any, E any](w *ecs.World, entity ecs.Entity, a *A, b *B, c *C, d *D, e *E) (*A, *B, *C, *D, *E) {
 	idA := ecs.ComponentID[A](w)
 	idB := ecs.ComponentID[B](w)
@@ -159,35 +159,35 @@ func Assign5[A any, B any, C any, D any, E any](w *ecs.World, entity ecs.Entity,
 
 // Remove1 removes a component from an entity.
 //
-// See also [World.Remove1].
+// See also [ecs.World.Remove].
 func Remove1[A any](w *ecs.World, entity ecs.Entity) {
 	w.Remove(entity, ecs.ComponentID[A](w))
 }
 
 // Remove2 removes two components from an entity.
 //
-// See also [World.Remove].
+// See also [ecs.World.Remove].
 func Remove2[A any, B any](w *ecs.World, entity ecs.Entity) {
 	w.Remove(entity, ecs.ComponentID[A](w), ecs.ComponentID[B](w))
 }
 
 // Remove3 removes three components from an entity.
 //
-// See also [World.Remove].
+// See also [ecs.World.Remove].
 func Remove3[A any, B any, C any](w *ecs.World, entity ecs.Entity) {
 	w.Remove(entity, ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w))
 }
 
 // Remove4 removes four components from an entity.
 //
-// See also [World.Remove].
+// See also [ecs.World.Remove].
 func Remove4[A any, B any, C any, D any](w *ecs.World, entity ecs.Entity) {
 	w.Remove(entity, ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w), ecs.ComponentID[D](w))
 }
 
 // Remove5 removes five components from an entity.
 //
-// See also [World.Remove].
+// See also [ecs.World.Remove].
 func Remove5[A any, B any, C any, D any, E any](w *ecs.World, entity ecs.Entity) {
 	w.Remove(entity, ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w), ecs.ComponentID[D](w), ecs.ComponentID[E](w))
 }
diff --git a/generic/query_generic.go b/generic/query_generic.go
index 255aa91..441e8c5 100644
--- a/generic/query_generic.go
+++ b/generic/query_generic.go
@@ -26,22 +26,30 @@ func toMask(w *ecs.World, types []reflect.Type) ecs.Mask {
 	return ecs.Mask{BitMask: mask}
 }
 
-// Q0Builder builds a Q0 query
+func toMaskOptional(w *ecs.World, include []ecs.ID, optional []reflect.Type) ecs.Mask {
+	mask := ecs.NewBitMask(include...)
+	for _, t := range optional {
+		mask.Set(ecs.TypeID(w, t), false)
+	}
+	return ecs.Mask{BitMask: mask}
+}
+
+// Q0Builder builds a [Q0] query
 type Q0Builder struct {
 	exclude []reflect.Type
 }
 
 // Query0 creates a generic query for no components.
 //
-// See also [World.Query].
+// See also [ecs.World.Query].
 func Query0() Q0Builder {
 	return Q0Builder{}
 }
 
-// Not excludes entities with the given components from the query.
+// ButNot excludes entities with the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q0Builder) Not(mask []reflect.Type) Q0Builder {
+func (q Q0Builder) ButNot(mask []reflect.Type) Q0Builder {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -63,35 +71,46 @@ type Q0 struct {
 	ecs.Query
 }
 
-// Q1Builder builds a Q1 query
+// Q1Builder builds a [Q1] query
 type Q1Builder[A any] struct {
-	include reflect.Type
-	exclude []reflect.Type
+	include  []reflect.Type
+	optional []reflect.Type
+	exclude  []reflect.Type
 }
 
 // Query1 creates a generic query for one component.
 //
-// See also [World.Query].
+// See also [ecs.World.Query].
 func Query1[A any]() Q1Builder[A] {
 	return Q1Builder[A]{
-		include: typeOf[A](),
+		include: []reflect.Type{typeOf[A]()},
 	}
 }
 
-// Not excludes entities with the given components from the query.
+// Optional makes some of the query's components optional.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q1Builder[A]) Not(mask []reflect.Type) Q1Builder[A] {
+//
+// Only affects component types that were specified in the query.
+func (q Q1Builder[A]) Optional(mask []reflect.Type) Q1Builder[A] {
+	q.optional = append(q.optional, mask...)
+	return q
+}
+
+// ButNot excludes entities with the given components from the query.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q1Builder[A]) ButNot(mask []reflect.Type) Q1Builder[A] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
 
 // Build builds a Q1 query for iteration.
 func (q Q1Builder[A]) Build(w *ecs.World) Q1[A] {
-	id := ecs.TypeID(w, q.include)
+	id := ecs.TypeID(w, q.include[0])
 	return Q1[A]{
 		w.Query(ecs.MaskPair{
-			Mask:    ecs.All(id),
+			Mask:    toMaskOptional(w, []ecs.ID{id}, q.optional),
 			Exclude: toMask(w, q.exclude),
 		}),
 		id,
@@ -113,25 +132,36 @@ func (q *Q1[A]) Get1() *A {
 
 //////////////////////////////////////////////////////////////////////////
 
-// Q2Builder builds a Q2 query
+// Q2Builder builds a [Q2] query
 type Q2Builder[A any, B any] struct {
-	include []reflect.Type
-	exclude []reflect.Type
+	include  []reflect.Type
+	optional []reflect.Type
+	exclude  []reflect.Type
 }
 
 // Query2 creates a generic query for two components.
 //
-// See also [World.Query].
+// See also [ecs.World.Query].
 func Query2[A any, B any]() Q2Builder[A, B] {
 	return Q2Builder[A, B]{
 		include: []reflect.Type{typeOf[A](), typeOf[B]()},
 	}
 }
 
-// Not excludes entities with the given components from the query.
+// Optional makes some of the query's components optional.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+//
+// Only affects component types that were specified in the query.
+func (q Q2Builder[A, B]) Optional(mask []reflect.Type) Q2Builder[A, B] {
+	q.optional = append(q.optional, mask...)
+	return q
+}
+
+// ButNot excludes entities with the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q2Builder[A, B]) Not(mask []reflect.Type) Q2Builder[A, B] {
+func (q Q2Builder[A, B]) ButNot(mask []reflect.Type) Q2Builder[A, B] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -141,7 +171,7 @@ func (q Q2Builder[A, B]) Build(w *ecs.World) Q2[A, B] {
 	ids := toIds(w, q.include)
 	return Q2[A, B]{
 		w.Query(ecs.MaskPair{
-			Mask:    ecs.All(ids...),
+			Mask:    toMaskOptional(w, ids, q.optional),
 			Exclude: toMask(w, q.exclude),
 		}),
 		ids,
@@ -173,25 +203,36 @@ func (q *Q2[A, B]) Get2() *B {
 
 //////////////////////////////////////////////////////////////////////////
 
-// Q3Builder builds a Q3 query
+// Q3Builder builds a [Q3] query
 type Q3Builder[A any, B any, C any] struct {
-	include []reflect.Type
-	exclude []reflect.Type
+	include  []reflect.Type
+	optional []reflect.Type
+	exclude  []reflect.Type
 }
 
 // Query3 creates a generic query for three components.
 //
-// See also [World.Query].
+// See also [ecs.World.Query].
 func Query3[A any, B any, C any]() Q3Builder[A, B, C] {
 	return Q3Builder[A, B, C]{
 		include: []reflect.Type{typeOf[A](), typeOf[B](), typeOf[C]()},
 	}
 }
 
-// Not excludes entities with the given components from the query.
+// Optional makes some of the query's components optional.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q3Builder[A, B, C]) Not(mask []reflect.Type) Q3Builder[A, B, C] {
+//
+// Only affects component types that were specified in the query.
+func (q Q3Builder[A, B, C]) Optional(mask []reflect.Type) Q3Builder[A, B, C] {
+	q.optional = append(q.optional, mask...)
+	return q
+}
+
+// ButNot excludes entities with the given components from the query.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q3Builder[A, B, C]) ButNot(mask []reflect.Type) Q3Builder[A, B, C] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -201,7 +242,7 @@ func (q Q3Builder[A, B, C]) Build(w *ecs.World) Q3[A, B, C] {
 	ids := toIds(w, q.include)
 	return Q3[A, B, C]{
 		w.Query(ecs.MaskPair{
-			Mask:    ecs.All(ids...),
+			Mask:    toMaskOptional(w, ids, q.optional),
 			Exclude: toMask(w, q.exclude),
 		}),
 		ids,
@@ -238,25 +279,36 @@ func (q *Q3[A, B, C]) Get3() *C {
 
 //////////////////////////////////////////////////////////////////////////
 
-// Q4Builder builds a Q4 query
+// Q4Builder builds a [Q4] query
 type Q4Builder[A any, B any, C any, D any] struct {
-	include []reflect.Type
-	exclude []reflect.Type
+	include  []reflect.Type
+	optional []reflect.Type
+	exclude  []reflect.Type
 }
 
 // Query4 creates a generic query for four components.
 //
-// See also [World.Query].
+// See also [ecs.World.Query].
 func Query4[A any, B any, C any, D any]() Q4Builder[A, B, C, D] {
 	return Q4Builder[A, B, C, D]{
 		include: []reflect.Type{typeOf[A](), typeOf[B](), typeOf[C](), typeOf[D]()},
 	}
 }
 
-// Not excludes entities with the given components from the query.
+// Optional makes some of the query's components optional.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+//
+// Only affects component types that were specified in the query.
+func (q Q4Builder[A, B, C, D]) Optional(mask []reflect.Type) Q4Builder[A, B, C, D] {
+	q.optional = append(q.optional, mask...)
+	return q
+}
+
+// ButNot excludes entities with the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q4Builder[A, B, C, D]) Not(mask []reflect.Type) Q4Builder[A, B, C, D] {
+func (q Q4Builder[A, B, C, D]) ButNot(mask []reflect.Type) Q4Builder[A, B, C, D] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -266,7 +318,7 @@ func (q Q4Builder[A, B, C, D]) Build(w *ecs.World) Q4[A, B, C, D] {
 	ids := toIds(w, q.include)
 	return Q4[A, B, C, D]{
 		w.Query(ecs.MaskPair{
-			Mask:    ecs.All(ids...),
+			Mask:    toMaskOptional(w, ids, q.optional),
 			Exclude: toMask(w, q.exclude),
 		}),
 		ids,
@@ -311,15 +363,16 @@ func (q *Q4[A, B, C, D]) Get4() *D {
 
 //////////////////////////////////////////////////////////////////////////
 
-// Q5Builder builds a Q5 query
+// Q5Builder builds a [Q5] query
 type Q5Builder[A any, B any, C any, D any, E any] struct {
-	include []reflect.Type
-	exclude []reflect.Type
+	include  []reflect.Type
+	optional []reflect.Type
+	exclude  []reflect.Type
 }
 
 // Query5 creates a generic query for five components.
 //
-// See also [World.Query].
+// See also [ecs.World.Query].
 func Query5[A any, B any, C any, D any, E any]() Q5Builder[A, B, C, D, E] {
 	return Q5Builder[A, B, C, D, E]{
 		include: []reflect.Type{
@@ -329,10 +382,20 @@ func Query5[A any, B any, C any, D any, E any]() Q5Builder[A, B, C, D, E] {
 	}
 }
 
-// Not excludes entities with the given components from the query.
+// Optional makes some of the query's components optional.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q5Builder[A, B, C, D, E]) Not(mask []reflect.Type) Q5Builder[A, B, C, D, E] {
+//
+// Only affects component types that were specified in the query.
+func (q Q5Builder[A, B, C, D, E]) Optional(mask []reflect.Type) Q5Builder[A, B, C, D, E] {
+	q.optional = append(q.optional, mask...)
+	return q
+}
+
+// ButNot excludes entities with the given components from the query.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+func (q Q5Builder[A, B, C, D, E]) ButNot(mask []reflect.Type) Q5Builder[A, B, C, D, E] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -342,7 +405,7 @@ func (q Q5Builder[A, B, C, D, E]) Build(w *ecs.World) Q5[A, B, C, D, E] {
 	ids := toIds(w, q.include)
 	return Q5[A, B, C, D, E]{
 		w.Query(ecs.MaskPair{
-			Mask:    ecs.All(ids...),
+			Mask:    toMaskOptional(w, ids, q.optional),
 			Exclude: toMask(w, q.exclude),
 		}),
 		ids,
@@ -393,15 +456,16 @@ func (q *Q5[A, B, C, D, E]) Get5() *E {
 
 //////////////////////////////////////////////////////////////////////////
 
-// Q6Builder builds a Q6 query
+// Q6Builder builds a [Q6] query
 type Q6Builder[A any, B any, C any, D any, E any, F any] struct {
-	include []reflect.Type
-	exclude []reflect.Type
+	include  []reflect.Type
+	optional []reflect.Type
+	exclude  []reflect.Type
 }
 
 // Query6 creates a generic query for six components.
 //
-// See also [World.Query].
+// See also [ecs.World.Query].
 func Query6[A any, B any, C any, D any, E any, F any]() Q6Builder[A, B, C, D, E, F] {
 	return Q6Builder[A, B, C, D, E, F]{
 		include: []reflect.Type{
@@ -411,10 +475,20 @@ func Query6[A any, B any, C any, D any, E any, F any]() Q6Builder[A, B, C, D, E,
 	}
 }
 
-// Not excludes entities with the given components from the query.
+// Optional makes some of the query's components optional.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+//
+// Only affects component types that were specified in the query.
+func (q Q6Builder[A, B, C, D, E, F]) Optional(mask []reflect.Type) Q6Builder[A, B, C, D, E, F] {
+	q.optional = append(q.optional, mask...)
+	return q
+}
+
+// ButNot excludes entities with the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q6Builder[A, B, C, D, E, F]) Not(mask []reflect.Type) Q6Builder[A, B, C, D, E, F] {
+func (q Q6Builder[A, B, C, D, E, F]) ButNot(mask []reflect.Type) Q6Builder[A, B, C, D, E, F] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -424,7 +498,7 @@ func (q Q6Builder[A, B, C, D, E, F]) Build(w *ecs.World) Q6[A, B, C, D, E, F] {
 	ids := toIds(w, q.include)
 	return Q6[A, B, C, D, E, F]{
 		w.Query(ecs.MaskPair{
-			Mask:    ecs.All(ids...),
+			Mask:    toMaskOptional(w, ids, q.optional),
 			Exclude: toMask(w, q.exclude),
 		}),
 		ids,
@@ -481,15 +555,16 @@ func (q *Q6[A, B, C, D, E, F]) Get6() *F {
 
 //////////////////////////////////////////////////////////////////////////
 
-// Q7Builder builds a Q7 query
+// Q7Builder builds a [Q7] query
 type Q7Builder[A any, B any, C any, D any, E any, F any, G any] struct {
-	include []reflect.Type
-	exclude []reflect.Type
+	include  []reflect.Type
+	optional []reflect.Type
+	exclude  []reflect.Type
 }
 
 // Query7 creates a generic query for seven components.
 //
-// See also [World.Query].
+// See also [ecs.World.Query].
 func Query7[A any, B any, C any, D any, E any, F any, G any]() Q7Builder[A, B, C, D, E, F, G] {
 	return Q7Builder[A, B, C, D, E, F, G]{
 		include: []reflect.Type{
@@ -499,10 +574,20 @@ func Query7[A any, B any, C any, D any, E any, F any, G any]() Q7Builder[A, B, C
 	}
 }
 
-// Not excludes entities with the given components from the query.
+// Optional makes some of the query's components optional.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+//
+// Only affects component types that were specified in the query.
+func (q Q7Builder[A, B, C, D, E, F, G]) Optional(mask []reflect.Type) Q7Builder[A, B, C, D, E, F, G] {
+	q.optional = append(q.optional, mask...)
+	return q
+}
+
+// ButNot excludes entities with the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q7Builder[A, B, C, D, E, F, G]) Not(mask []reflect.Type) Q7Builder[A, B, C, D, E, F, G] {
+func (q Q7Builder[A, B, C, D, E, F, G]) ButNot(mask []reflect.Type) Q7Builder[A, B, C, D, E, F, G] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -512,7 +597,7 @@ func (q Q7Builder[A, B, C, D, E, F, G]) Build(w *ecs.World) Q7[A, B, C, D, E, F,
 	ids := toIds(w, q.include)
 	return Q7[A, B, C, D, E, F, G]{
 		w.Query(ecs.MaskPair{
-			Mask:    ecs.All(ids...),
+			Mask:    toMaskOptional(w, ids, q.optional),
 			Exclude: toMask(w, q.exclude),
 		}),
 		ids,
@@ -575,15 +660,16 @@ func (q *Q7[A, B, C, D, E, F, G]) Get7() *G {
 
 //////////////////////////////////////////////////////////////////////////
 
-// Q8Builder builds a Q8 query
+// Q8Builder builds a [Q8] query
 type Q8Builder[A any, B any, C any, D any, E any, F any, G any, H any] struct {
-	include []reflect.Type
-	exclude []reflect.Type
+	include  []reflect.Type
+	optional []reflect.Type
+	exclude  []reflect.Type
 }
 
 // Query8 creates a generic query for eight components.
 //
-// See also [World.Query].
+// See also [ecs.World.Query].
 func Query8[A any, B any, C any, D any, E any, F any, G any, H any]() Q8Builder[A, B, C, D, E, F, G, H] {
 	return Q8Builder[A, B, C, D, E, F, G, H]{
 		include: []reflect.Type{
@@ -593,10 +679,20 @@ func Query8[A any, B any, C any, D any, E any, F any, G any, H any]() Q8Builder[
 	}
 }
 
-// Not excludes entities with the given components from the query.
+// Optional makes some of the query's components optional.
+//
+// Create the required mask with [Mask1], [Mask2], etc.
+//
+// Only affects component types that were specified in the query.
+func (q Q8Builder[A, B, C, D, E, F, G, H]) Optional(mask []reflect.Type) Q8Builder[A, B, C, D, E, F, G, H] {
+	q.optional = append(q.optional, mask...)
+	return q
+}
+
+// ButNot excludes entities with the given components from the query.
 //
 // Create the required mask with [Mask1], [Mask2], etc.
-func (q Q8Builder[A, B, C, D, E, F, G, H]) Not(mask []reflect.Type) Q8Builder[A, B, C, D, E, F, G, H] {
+func (q Q8Builder[A, B, C, D, E, F, G, H]) ButNot(mask []reflect.Type) Q8Builder[A, B, C, D, E, F, G, H] {
 	q.exclude = append(q.exclude, mask...)
 	return q
 }
@@ -606,7 +702,7 @@ func (q Q8Builder[A, B, C, D, E, F, G, H]) Build(w *ecs.World) Q8[A, B, C, D, E,
 	ids := toIds(w, q.include)
 	return Q8[A, B, C, D, E, F, G, H]{
 		w.Query(ecs.MaskPair{
-			Mask:    ecs.All(ids...),
+			Mask:    toMaskOptional(w, ids, q.optional),
 			Exclude: toMask(w, q.exclude),
 		}),
 		ids,


Test patch:
diff --git a/ecs/bit_pool_test.go b/ecs/bit_pool_test.go
index b1b4dea..abaa204 100644
--- a/ecs/bit_pool_test.go
+++ b/ecs/bit_pool_test.go
@@ -9,7 +9,7 @@ import (
 func TestBitPool(t *testing.T) {
 	p := newBitPool()
 
-	for i := 0; i < maskTotalBits; i++ {
+	for i := 0; i < MaskTotalBits; i++ {
 		assert.Equal(t, i, int(p.Get()))
 	}
 
diff --git a/ecs/registry_test.go b/ecs/registry_test.go
index dcc9e1f..7d06fd0 100644
--- a/ecs/registry_test.go
+++ b/ecs/registry_test.go
@@ -13,7 +13,7 @@ func TestComponentRegistry(t *testing.T) {
 	posType := reflect.TypeOf((*position)(nil)).Elem()
 	rotType := reflect.TypeOf((*rotation)(nil)).Elem()
 
-	reg.RegisterComponent(posType, maskTotalBits)
+	reg.RegisterComponent(posType, MaskTotalBits)
 
 	assert.Equal(t, ID(0), reg.ComponentID(posType))
 	assert.Equal(t, ID(1), reg.ComponentID(rotType))
diff --git a/ecs/world_test.go b/ecs/world_test.go
index 613e91e..39a27de 100644
--- a/ecs/world_test.go
+++ b/ecs/world_test.go
@@ -46,6 +46,12 @@ func TestWorldComponents(t *testing.T) {
 	posID := ComponentID[position](&w)
 	rotID := ComponentID[rotation](&w)
 
+	tPosID := TypeID(&w, reflect.TypeOf(position{}))
+	tRotID := TypeID(&w, reflect.TypeOf(rotation{}))
+
+	assert.Equal(t, posID, tPosID)
+	assert.Equal(t, rotID, tRotID)
+
 	e0 := w.NewEntity()
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
@@ -263,7 +269,7 @@ func TestWorldIter(t *testing.T) {
 		assert.Panics(t, func() { query.Next() })
 	}
 
-	for i := 0; i < maskTotalBits-1; i++ {
+	for i := 0; i < MaskTotalBits-1; i++ {
 		query := world.Query(All(posID, rotID))
 		for query.Next() {
 			pos := (*position)(query.Get(posID))
diff --git a/filter/filter_test.go b/filter/filter_test.go
index f22050e..d10aa64 100644
--- a/filter/filter_test.go
+++ b/filter/filter_test.go
@@ -30,6 +30,18 @@ func TestLogicFilters(t *testing.T) {
 	assert.False(t, match(filter, hasB))
 	assert.False(t, match(filter, hasNone))
 
+	filter = All(0, 1)
+	assert.True(t, match(filter, hasAll))
+	assert.False(t, match(filter, hasA))
+	assert.False(t, match(filter, hasB))
+	assert.False(t, match(filter, hasNone))
+
+	filter = Any(0, 1)
+	assert.True(t, match(filter, hasAll))
+	assert.True(t, match(filter, hasA))
+	assert.True(t, match(filter, hasB))
+	assert.False(t, match(filter, hasNone))
+
 	filter = Or(hasA, hasB)
 	assert.True(t, match(filter, hasAll))
 	assert.True(t, match(filter, hasA))
@@ -90,13 +102,14 @@ func TestLogicFilters(t *testing.T) {
 	assert.False(t, match(filter, hasB))
 	assert.True(t, match(filter, hasNone))
 
-	filter = Or(OneOf(0, 1), NotANY(hasB))
+	filter = Or(Any(0, 1), NotANY(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, Or(hasA, hasB), OneOf(0, 1))
+	assert.Equal(t, NotAny(0, 1), Any(0, 1).Not())
+	assert.Equal(t, Not(0, 1), All(0, 1).Not())
 }
 
 func TestFilter(t *testing.T) {
diff --git a/generic/query_generic_test.go b/generic/query_generic_test.go
index 0a7f569..22a7769 100644
--- a/generic/query_generic_test.go
+++ b/generic/query_generic_test.go
@@ -8,6 +8,54 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func TestQueryOptionalNot(t *testing.T) {
+	w := ecs.NewWorld()
+
+	registerAll(&w)
+
+	e0 := w.NewEntity()
+	e1 := w.NewEntity()
+	e2 := w.NewEntity()
+
+	w.Assign(e0, 0, &testStruct0{1})
+	w.Assign(e0, 1, &testStruct1{1})
+
+	w.Assign(e1, 0, &testStruct0{2})
+	w.Assign(e1, 1, &testStruct1{2})
+
+	w.Assign(e2, 0, &testStruct0{3})
+	w.Assign(e2, 1, &testStruct1{3})
+	w.Assign(e2, 9, &testStruct9{})
+
+	query2 := Query2[testStruct0, testStruct1]().Build(&w)
+	cnt := 0
+	for query2.Next() {
+		cnt++
+	}
+	assert.Equal(t, 3, cnt)
+
+	query2 = Query2[testStruct0, testStruct1]().ButNot(Mask1[testStruct9]()).Build(&w)
+	cnt = 0
+	for query2.Next() {
+		cnt++
+	}
+	assert.Equal(t, 2, cnt)
+
+	query3 := Query3[testStruct0, testStruct1, testStruct9]().Build(&w)
+	cnt = 0
+	for query3.Next() {
+		cnt++
+	}
+	assert.Equal(t, 1, cnt)
+
+	query3 = Query3[testStruct0, testStruct1, testStruct9]().Optional(Mask1[testStruct9]()).Build(&w)
+	cnt = 0
+	for query3.Next() {
+		cnt++
+	}
+	assert.Equal(t, 3, cnt)
+}
+
 func TestQuery0(t *testing.T) {
 	w := ecs.NewWorld()
 
@@ -23,7 +71,7 @@ func TestQuery0(t *testing.T) {
 	w.Assign(e2, 9, &testStruct9{})
 
 	cnt := 0
-	query := Query0().Not(Mask1[testStruct9]()).Build(&w)
+	query := Query0().ButNot(Mask1[testStruct9]()).Build(&w)
 
 	for query.Next() {
 		cnt++
@@ -46,7 +94,11 @@ func TestQuery1(t *testing.T) {
 	w.Assign(e2, 9, &testStruct9{})
 
 	cnt := 0
-	query := Query1[testStruct0]().Not(Mask1[testStruct9]()).Build(&w)
+	query :=
+		Query1[testStruct0]().
+			Optional(Mask1[testStruct8]()).
+			ButNot(Mask1[testStruct9]()).
+			Build(&w)
 
 	for query.Next() {
 		c0 := query.Get1()
@@ -76,7 +128,11 @@ func TestQuery2(t *testing.T) {
 	w.Assign(e2, 9, &testStruct9{})
 
 	cnt := 0
-	query := Query2[testStruct0, testStruct1]().Not(Mask1[testStruct9]()).Build(&w)
+	query :=
+		Query2[testStruct0, testStruct1]().
+			Optional(Mask1[testStruct1]()).
+			ButNot(Mask1[testStruct9]()).
+			Build(&w)
 
 	for query.Next() {
 		c1 := query.Get1()
@@ -118,7 +174,8 @@ func TestQuery3(t *testing.T) {
 	cnt := 0
 	query :=
 		Query3[testStruct0, testStruct1, testStruct2]().
-			Not(Mask1[testStruct9]()).
+			Optional(Mask1[testStruct1]()).
+			ButNot(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -168,7 +225,8 @@ func TestQuery4(t *testing.T) {
 	cnt := 0
 	query :=
 		Query4[testStruct0, testStruct1, testStruct2, testStruct3]().
-			Not(Mask1[testStruct9]()).
+			Optional(Mask1[testStruct1]()).
+			ButNot(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -225,7 +283,8 @@ func TestQuery5(t *testing.T) {
 	cnt := 0
 	query :=
 		Query5[testStruct0, testStruct1, testStruct2, testStruct3, testStruct4]().
-			Not(Mask1[testStruct9]()).
+			Optional(Mask1[testStruct1]()).
+			ButNot(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -289,7 +348,8 @@ func TestQuery6(t *testing.T) {
 	cnt := 0
 	query :=
 		Query6[testStruct0, testStruct1, testStruct2, testStruct3, testStruct4, testStruct5]().
-			Not(Mask1[testStruct9]()).
+			Optional(Mask1[testStruct1]()).
+			ButNot(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -363,7 +423,8 @@ func TestQuery7(t *testing.T) {
 			testStruct0, testStruct1, testStruct2, testStruct3, testStruct4,
 			testStruct5, testStruct6,
 		]().
-			Not(Mask1[testStruct9]()).
+			Optional(Mask1[testStruct1]()).
+			ButNot(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {
@@ -436,7 +497,8 @@ func TestQuery8(t *testing.T) {
 			testStruct0, testStruct1, testStruct2, testStruct3,
 			testStruct4, testStruct5, testStruct6, testStruct7,
 		]().
-			Not(Mask1[testStruct9]()).
+			Optional(Mask1[testStruct1]()).
+			ButNot(Mask1[testStruct9]()).
 			Build(&w)
 
 	for query.Next() {