Skip to content

Commit

Permalink
created a more nailed down AbstractComponent interface
Browse files Browse the repository at this point in the history
  • Loading branch information
louisponet committed May 6, 2023
1 parent fc034fb commit 8c62cd3
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 60 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Overseer"
uuid = "1ada24be-c16d-4464-9f61-27c2e0f16645"
authors = ["louisponet <[email protected]>"]
version = "0.2.10"
version = "0.3.1"

[deps]
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
Expand Down
156 changes: 97 additions & 59 deletions src/component.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function test_abstractcomponent_interface(::Type{T}) where {T<:AbstractComponent
c[Entity(1)] = TestCompData(1)
c[Entity(2)] = TestCompData(1)
@test Entity(2) in c
@test length(c) == 2 == size(c)[1] == length(c.indices) == length(entity_data(c))
@test length(c) == 2 == size(c)[1] == length(c.indices) == length(data(c))

@test c[Entity(2)] isa TestCompData

Expand All @@ -37,7 +37,7 @@ function test_abstractcomponent_interface(::Type{T}) where {T<:AbstractComponent
c[Entity(2)] = TestCompData(2)

# Needed for order swapping
entity_data(c)[1], entity_data(c)[2] = entity_data(c)[2], entity_data(c)[1]
data(c)[1], data(c)[2] = data(c)[2], data(c)[1]
@test c[Entity(2)] == TestCompData(1)
@test c[Entity(1)] == TestCompData(2)

Expand All @@ -50,29 +50,24 @@ function test_abstractcomponent_interface(::Type{T}) where {T<:AbstractComponent
@test isempty(c)
end

"""
component_type(::Type)
Function that can be overloaded to specify what the default [`AbstractComponent`](@ref) of a given type is. This is mainly used in the various component macros.
"""
component_type(::Type{TC}) where {TC} = Component{TC}
## AbstractComponent Interface: For new components that have a standard component underneath, just overload `component` to point to it

@inline indices_iterator(a::AbstractComponent)::Indices = a.indices
@inline function reverse_indices_iterator(a::AbstractComponent)
return ReverseIndicesIterator(a.indices, i -> true)
end
component(c::AbstractComponent) = MethodError(component, c)

@inline data(c::AbstractComponent) = data(component(c))
@inline indices(c::AbstractComponent) = indices(component(c))
@inline data_index(c::AbstractComponent, args...) = data_index(component(c), args...)
"""
in(entity, component)
Checks whether `entity` has a data entry in `component`.
"""
Base.in(i::Integer, c::AbstractComponent) = in(i, c.indices)
Base.in(i::Integer, c::AbstractComponent) = in(i, indices(c))
Base.in(e::AbstractEntity, c::AbstractComponent) = in(Entity(e).id, c)

Base.length(c::AbstractComponent) = length(c.indices)
Base.size(c::AbstractComponent) = size(c.indices)
Base.isempty(c::AbstractComponent) = isempty(c.indices)
Base.length(c::AbstractComponent) = length(indices(c))
Base.size(c::AbstractComponent) = size(indices(c))
Base.isempty(c::AbstractComponent) = isempty(indices(c))

Base.eltype(::Type{<:AbstractComponent{T}}) where {T} = T

Expand All @@ -83,42 +78,39 @@ Base.delete!(c::AbstractComponent, es::Vector{Entity}) =
end
end

Base.pop!(c::AbstractComponent, args...) = pop!(component(c), args...)

Base.empty!(c::AbstractComponent) = empty!(component(c))

Base.@propagate_inbounds @inline Base.getindex(c::AbstractComponent, i::Integer) = data(c)[data_index(c, i)]

@inline function Base.getindex(c::AbstractComponent, e::AbstractEntity)
@boundscheck if !in(e, c)
throw(BoundsError(c, Entity(e)))
end
return @inbounds data(c)[data_index(c, e)]
end

Base.@propagate_inbounds function Base.getindex(c::AbstractComponent,
I::AbstractVector{<:AbstractEntity})
return map(x -> c[x], I)
end

"""
swap_order!(c, entity1, entity2)
Swaps the order of the data of `entity1` and `entity2`. This is useful to order
multiple components in the same way so that iteration can be performed without
performing checks, i.e. when using [`Groups`](@ref Groups).
"""
function swap_order!(c::AbstractComponent, e1::AbstractEntity, e2::AbstractEntity)
@boundscheck if !in(e1, c)
throw(BoundsError(c, Entity(e1)))
elseif !in(e2, c)
throw(BoundsError(c, Entity(e2)))
end
@inbounds begin
id1, id2 = swap_order!(c.indices, e1.id, e2.id)
edata = entity_data(c)
edata[id1], edata[id2] = edata[id2], edata[id1]
end
end
Base.@propagate_inbounds @inline Base.setindex!(c::AbstractComponent, v, i::Integer) = data(c)[data_index(c, i)] = v
Base.@propagate_inbounds @inline Base.setindex!(c::AbstractComponent, v, e::AbstractEntity) =
setindex!(component(c), v, e)

function Base.permute!(c::AbstractComponent, permvec::AbstractVector{<:Integer})
permute!(entity_data(c), permvec)
permute!(data(c), permvec)
return c
end

function Base.permute!(c::AbstractComponent, permvec::AbstractVector{<:AbstractEntity})
return permute!(c, map(x -> c.indices[x.id], permvec))
return permute!(c, map(x -> indices(c)[x.id], permvec))
end

function Base.sortperm(c::AbstractComponent, args...; kwargs...)
return sortperm(entity_data(c), args...; kwargs...)
return sortperm(data(c), args...; kwargs...)
end

function Base.:(==)(c1::C1, c2::C2) where {C1<:AbstractComponent,C2<:AbstractComponent}
Expand All @@ -134,6 +126,41 @@ end

Base.IndexStyle(::Type{AbstractComponent}) = IndexLinear()

Base.iterate(c::AbstractComponent, args...) = iterate(component(c), args...)

"""
component_type(::Type)
Function that can be overloaded to specify what the default [`AbstractComponent`](@ref) of a given type is. This is mainly used in the various component macros.
"""
component_type(::Type{TC}) where {TC} = Component{TC}

@inline indices_iterator(a::AbstractComponent)::Indices = indices(a)

@inline function reverse_indices_iterator(a::AbstractComponent)
return ReverseIndicesIterator(indices(a), i -> true)
end

"""
swap_order!(c, entity1, entity2)
Swaps the order of the data of `entity1` and `entity2`. This is useful to order
multiple components in the same way so that iteration can be performed without
performing checks, i.e. when using [`Groups`](@ref Groups).
"""
function swap_order!(c::AbstractComponent, e1::AbstractEntity, e2::AbstractEntity)
@boundscheck if !in(e1, c)
throw(BoundsError(c, Entity(e1)))
elseif !in(e2, c)
throw(BoundsError(c, Entity(e2)))
end
@inbounds begin
id1, id2 = swap_order!(indices(c), e1.id, e2.id)
edata = data(c)
edata[id1], edata[id2] = edata[id2], edata[id1]
end
end

"""
The most basic Component type.
Expand All @@ -150,19 +177,15 @@ end

Component{T}() where {T} = Component(Indices(), T[])

entity_data(c::Component) = c.data
@inline indices(c::Component) = c.indices
@inline data(c::Component) = c.data

##### BASE Extensions ####
Base.@propagate_inbounds @inline Base.getindex(c::Component, i::Integer) = c.data[i]
Base.@propagate_inbounds @inline Base.setindex!(c::Component, v, i::Integer) = c.data[i] = v
Base.@propagate_inbounds @inline data_index(c::Component, i::Integer) = i
Base.@propagate_inbounds @inline data_index(c::Component, e::AbstractEntity) = c.indices[e.id]

@inline function Base.getindex(c::Component, e::AbstractEntity)
eid = Entity(e).id
@boundscheck if !in(e, c)
throw(BoundsError(c, Entity(e)))
end
return @inbounds c.data[c.indices[eid]]
end
@inline component(c::Component) = c

##### BASE Extensions ####

@inline function Base.setindex!(c::Component{T}, v::T, e::AbstractEntity) where {T}
eid = Entity(e).id
Expand Down Expand Up @@ -298,6 +321,10 @@ end

PooledComponent{T}() where {T} = PooledComponent{T}(Indices(), Int[], Int[], T[])

@inline component(c::PooledComponent) = c
@inline indices(c::PooledComponent) = c.indices
@inline data(c::PooledComponent) = c.data

"""
pool(pooled_compnent, entity)
pool(pooled_compnent, i)
Expand All @@ -309,19 +336,12 @@ Base.@propagate_inbounds @inline function pool(c::PooledComponent, e::AbstractEn
end
Base.@propagate_inbounds @inline pool(c::PooledComponent, e::Int) = c.pool[c.indices[e]]

entity_data(c::PooledComponent) = c.pool

npools(c::PooledComponent) = length(c.data)
Base.@propagate_inbounds @inline data_index(c::PooledComponent, i::Integer) = c.pool[i]
Base.@propagate_inbounds @inline data_index(c::PooledComponent, e::AbstractEntity) = pool(c, e)

Base.@propagate_inbounds @inline Base.getindex(c::PooledComponent, i::Integer) = c.data[c.pool[i]]
Base.@propagate_inbounds @inline Base.setindex!(c::PooledComponent, v, i::Integer) = c.data[i] = v

@inline function Base.getindex(c::PooledComponent, e::AbstractEntity)
@boundscheck if !in(e, c)
throw(BoundsError(c, Entity(e)))
end
return @inbounds c.data[pool(c, e)]
end
npools(c::PooledComponent) = length(c.data)

Base.@propagate_inbounds @inline function Base.parent(c::PooledComponent, i::Int)
@boundscheck if i > length(c.data)
Expand Down Expand Up @@ -533,3 +553,21 @@ function make_unique!(c::PooledComponent)

return
end

function Base.permute!(c::PooledComponent, permvec::AbstractVector{<:Integer})
permute!(c.pool, permvec)
return c
end

function swap_order!(c::PooledComponent, e1::AbstractEntity, e2::AbstractEntity)
@boundscheck if !in(e1, c)
throw(BoundsError(c, Entity(e1)))
elseif !in(e2, c)
throw(BoundsError(c, Entity(e2)))
end
@inbounds begin
id1, id2 = swap_order!(indices(c), e1.id, e2.id)
edata = c.pool
edata[id1], edata[id2] = edata[id2], edata[id1]
end
end

0 comments on commit 8c62cd3

Please sign in to comment.