diff --git a/Sources/FirebladeECS/Family.swift b/Sources/FirebladeECS/Family.swift index 131bb20..7c8b199 100644 --- a/Sources/FirebladeECS/Family.swift +++ b/Sources/FirebladeECS/Family.swift @@ -134,25 +134,52 @@ extension Family.EntityComponentIterator: LazySequenceProtocol { } // MARK: - relatives iterator extension Family { - @inlinable public var descendRelatives: RelativesIterator { - return RelativesIterator(family: self) + @inlinable + public func descendRelatives(from root: Entity) -> RelativesIterator { + return RelativesIterator(family: self, root: root) } public struct RelativesIterator: IteratorProtocol { - @usableFromInline var memberIdsIterator: UnorderedSparseSetIterator @usableFromInline unowned let nexus: Nexus + @usableFromInline let familyTraits: FamilyTraitSet - public init(family: Family) { + @usableFromInline var relatives: [(EntityIdentifier, EntityIdentifier)] + + public init(family: Family, root: Entity) { self.nexus = family.nexus - memberIdsIterator = family.memberIds.makeIterator() + self.familyTraits = family.traits + + // FIXME: this is not the most efficient way to aggregate all parent child tuples + // Problems: + // - allocates new memory + // - needs to be build on every iteration + // - relies on isMember check + self.relatives = [] + self.relatives.reserveCapacity(family.memberIds.count) + aggregateRelativesBreathFirst(root.identifier) + relatives.reverse() + } + + mutating func aggregateRelativesBreathFirst(_ parent: EntityIdentifier) { + guard let children = nexus.parentChildrenMap[parent] else { + return + } + children + .compactMap { child in + guard nexus.isMember(child, in: familyTraits) else { + return nil + } + relatives.append((parent, child)) + return child + } + .forEach { aggregateRelativesBreathFirst($0) } } public mutating func next() -> R.RelativesDescending? { - guard let parentId = memberIdsIterator.next() else { + guard let (parentId, childId) = relatives.popLast() else { return nil } - - return nil + return R.relativesDescending(nexus: nexus, parentId: parentId, childId: childId) } } }