Skip to content

Commit 4959bab

Browse files
authored
Origin as a callable to construct OffsetArrays (#276)
* Origin as a callable to construct OffsetArrays * add tests and broadcasting tip * fix typo * interpolate Int * Constructor test
1 parent d97c1be commit 4959bab

File tree

6 files changed

+93
-12
lines changed

6 files changed

+93
-12
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "OffsetArrays"
22
uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
3-
version = "1.10.8"
3+
version = "1.11.0"
44

55
[deps]
66
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"

docs/src/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ OffsetArray(A, OffsetArrays.Origin(-1, -1))
6363
OffsetArray(OA, OffsetArrays.Origin(-1, -1))
6464
```
6565

66+
An equivalent — but possibly more convenient — way to specify the origin of an array is
67+
68+
```@repl index
69+
OffsetArrays.Origin(-1, -1)(A)
70+
```
71+
6672
Sometimes, it will be convenient to shift the center coordinate of the given array to `(0, 0, ...)`,
6773
`OffsetArrays.centered` is a helper for this very purpose:
6874

src/OffsetArrays.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,12 @@ for FT in (:OffsetArray, :OffsetVector, :OffsetMatrix)
207207
@eval @inline $FT(A::AbstractArray, inds...; kw...) = $FT(A, inds; kw...)
208208
@eval @inline $FT(A::AbstractArray; checkoverflow = false) = $FT(A, ntuple(zero, Val(ndims(A))), checkoverflow = checkoverflow)
209209

210-
@eval @inline $FT(A::AbstractArray, origin::Origin; checkoverflow = true) = $FT(A, origin(A); checkoverflow = checkoverflow)
210+
@eval @inline $FT(A::AbstractArray, origin::Origin; checkoverflow = true) = $FT(A, origin.index .- first.(axes(A)); checkoverflow = checkoverflow)
211211
end
212212

213+
(o::Origin)(A::AbstractArray) = OffsetArray(A, o)
214+
Origin(A::OffsetArray) = Origin(first.(axes(A)))
215+
213216
# conversion-related methods
214217
@inline OffsetArray{T}(M::AbstractArray, I...; kw...) where {T} = OffsetArray{T,ndims(M)}(M, I...; kw...)
215218

src/axes.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ julia> ro[-1]
2222
-1
2323
2424
julia> ro[3]
25-
ERROR: BoundsError: attempt to access 3-element $(IdOffsetRange{Int,UnitRange{Int}}) with indices -1:1 at index [3]
25+
ERROR: BoundsError: attempt to access 3-element IdOffsetRange{$Int, UnitRange{$Int}} with indices -1:1 at index [3]
2626
```
2727
2828
If the range doesn't start at 1, the values may be different from the indices:
@@ -37,7 +37,7 @@ julia> ro[-1]
3737
9
3838
3939
julia> ro[3]
40-
ERROR: BoundsError: attempt to access 3-element $(IdOffsetRange{Int,UnitRange{Int}}) with indices -1:1 at index [3]
40+
ERROR: BoundsError: attempt to access 3-element IdOffsetRange{$Int, UnitRange{$Int}} with indices -1:1 at index [3]
4141
```
4242
4343
# Extended help

src/origin.jl

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,76 @@
33
Origin(origin::Tuple)
44
Origin(origin::CartesianIndex)
55
6-
A helper type to construct OffsetArray with given origin.
6+
A helper type to construct OffsetArray with a given origin. This is not exported.
77
8-
The `origin` of an array is defined as the index of its first element, i.e., `first.(axes(A))`.
8+
The `origin` of an array is defined as the tuple of the first index along each axis, i.e., `first.(axes(A))`.
99
1010
# Example
1111
12-
```jldoctest; setup=:(using OffsetArrays)
12+
```jldoctest origin; setup=:(using OffsetArrays)
1313
julia> a = [1 2; 3 4];
1414
15-
julia> OffsetArray(a, OffsetArrays.Origin(0, 1))
15+
julia> using OffsetArrays: Origin
16+
17+
julia> OffsetArray(a, Origin(0, 1))
1618
2×2 OffsetArray(::$(Array{Int,2}), 0:1, 1:2) with eltype $Int with indices 0:1×1:2:
1719
1 2
1820
3 4
1921
20-
julia> OffsetArray(a, OffsetArrays.Origin(0)) # short notation for `Origin(0, 0)`
22+
julia> OffsetArray(a, Origin(0)) # short notation for `Origin(0, 0)`
23+
2×2 OffsetArray(::$(Array{Int, 2}), 0:1, 0:1) with eltype $Int with indices 0:1×0:1:
24+
1 2
25+
3 4
26+
```
27+
28+
An `Origin` object is callable, and it may shift the origin of an array to the specified point.
29+
30+
```jldoctest origin
31+
julia> b = Origin(0)(a) # shift the origin of the array to (0,0)
2132
2×2 OffsetArray(::$(Array{Int, 2}), 0:1, 0:1) with eltype $Int with indices 0:1×0:1:
2233
1 2
2334
3 4
2435
```
36+
37+
The type `Origin`, when called with an `AbstractArray` as the argument, will return an instance
38+
corresponding ot the origin of the array.
39+
40+
```jldoctest origin
41+
julia> origin_b = Origin(b) # retrieve the origin of the array as an Origin instance
42+
Origin(0, 0)
43+
44+
julia> origin_b(ones(2,2)) # shift the origin of another array to that of b, in this case to (0,0)
45+
2×2 OffsetArray(::$(Array{Float64, 2}), 0:1, 0:1) with eltype Float64 with indices 0:1×0:1:
46+
1.0 1.0
47+
1.0 1.0
48+
```
49+
50+
!!! tip
51+
One may broadcast an `Origin` instance over multiple arrays to shift them all to the same origin.
52+
```jldoctest
53+
julia> using OffsetArrays: Origin
54+
55+
julia> a = [1 2; 3 4]; # origin at (1,1)
56+
57+
julia> b = Origin(2,3)(a); # origin at (2,3)
58+
59+
julia> c = Origin(4)(a); # origin at (4,4)
60+
61+
julia> ao, bo, co = Origin(0).((a, b, c)); # shift all origins to (0,0)
62+
63+
julia> first.(axes(ao)) == first.(axes(bo)) == first.(axes(co)) == (0,0)
64+
true
65+
66+
julia> ao, bo, co = Origin(b).((a, b, c)); # shift all origins to that of b
67+
68+
julia> first.(axes(ao)) == first.(axes(bo)) == first.(axes(co)) == (2,3)
69+
true
70+
71+
julia> ao, bo, co = OffsetArray.((a, b, c), Origin(b)); # another way to do the same
72+
73+
julia> first.(axes(ao)) == first.(axes(bo)) == first.(axes(co)) == (2,3)
74+
true
75+
```
2576
"""
2677
struct Origin{T<:Union{Tuple{Vararg{Int}}, Int}}
2778
index::T
@@ -33,6 +84,8 @@ Origin(I::Number...) = Origin(I)
3384
# Origin(0) != Origin((0, )) but they work the same with broadcasting
3485
Origin(n::Number) = Origin{Int}(Int(n))
3586

36-
(o::Origin)(A::AbstractArray) = o.index .- first.(axes(A))
37-
3887
Base.Broadcast.broadcastable(o::Origin) = Ref(o)
88+
89+
_showidx(index::Integer) = "(" * string(index) * ")"
90+
_showidx(index::Tuple) = string(index)
91+
Base.show(io::IO, o::Origin) = print(io, "Origin", _showidx(o.index))

test/runtests.jl

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using OffsetArrays
2-
using OffsetArrays: IdentityUnitRange, no_offset_view, IIUR
2+
using OffsetArrays: IdentityUnitRange, no_offset_view, IIUR, Origin
33
using Base: Slice
44
using OffsetArrays: IdOffsetRange
55
using Test, Aqua, Documenter
@@ -2594,6 +2594,25 @@ end
25942594

25952595
include("origin.jl")
25962596

2597+
@testset "Origin" begin
2598+
@testset "as a callable" begin
2599+
a = [1 2; 3 4];
2600+
@test OffsetArray(a, Origin(2)) === Origin(2)(a)
2601+
for (index, firstinds) in Any[(1, (1,1)), ((2,3), (2,3))]
2602+
b = Origin(index)(a)
2603+
@test first.(axes(b)) == firstinds
2604+
@test Origin(b) === Origin(firstinds)
2605+
end
2606+
end
2607+
@testset "display" begin
2608+
io = IOBuffer()
2609+
show(io, Origin(1))
2610+
@test String(take!(io)) == "Origin(1)"
2611+
show(io, Origin(1, 1))
2612+
@test String(take!(io)) == "Origin(1, 1)"
2613+
end
2614+
end
2615+
25972616
@testset "misc" begin
25982617
@test OffsetArrays._subtractoffset(Base.OneTo(2), 1) isa AbstractUnitRange{Int}
25992618
@test OffsetArrays._subtractoffset(Base.OneTo(2), 1) == 0:1

0 commit comments

Comments
 (0)