| Line | Exclusive | Inclusive | Code |
|---|---|---|---|
| 1 | module StaticArraysCore | ||
| 2 | |||
| 3 | export SArray, SMatrix, SVector | ||
| 4 | export MArray, MMatrix, MVector | ||
| 5 | export SizedArray, SizedMatrix, SizedVector | ||
| 6 | export FieldArray, FieldMatrix, FieldVector | ||
| 7 | export Size | ||
| 8 | |||
| 9 | """ | ||
| 10 | abstract type StaticArray{S, T, N} <: AbstractArray{T, N} end | ||
| 11 | StaticScalar{T} = StaticArray{Tuple{}, T, 0} | ||
| 12 | StaticVector{N,T} = StaticArray{Tuple{N}, T, 1} | ||
| 13 | StaticMatrix{N,M,T} = StaticArray{Tuple{N,M}, T, 2} | ||
| 14 | |||
| 15 | `StaticArray`s are Julia arrays with fixed, known size. | ||
| 16 | |||
| 17 | ## Dev docs | ||
| 18 | |||
| 19 | They must define the following methods: | ||
| 20 | - Constructors that accept a flat tuple of data. | ||
| 21 | - `getindex()` with an integer (linear indexing) (preferably `@inline` with `@boundscheck`). | ||
| 22 | - `Tuple()`, returning the data in a flat Tuple. | ||
| 23 | |||
| 24 | It may be useful to implement: | ||
| 25 | |||
| 26 | - `similar_type(::Type{MyStaticArray}, ::Type{NewElType}, ::Size{NewSize})`, returning a | ||
| 27 | type (or type constructor) that accepts a flat tuple of data. | ||
| 28 | |||
| 29 | For mutable containers you may also need to define the following: | ||
| 30 | |||
| 31 | - `setindex!` for a single element (linear indexing). | ||
| 32 | - `similar(::Type{MyStaticArray}, ::Type{NewElType}, ::Size{NewSize})`. | ||
| 33 | - In some cases, a zero-parameter constructor, `MyStaticArray{...}()` for unintialized data | ||
| 34 | is assumed to exist. | ||
| 35 | |||
| 36 | (see also `SVector`, `SMatrix`, `SArray`, `MVector`, `MMatrix`, `MArray`, `SizedArray`, `FieldVector`, `FieldMatrix` and `FieldArray`) | ||
| 37 | """ | ||
| 38 | abstract type StaticArray{S <: Tuple, T, N} <: AbstractArray{T, N} end | ||
| 39 | const StaticScalar{T} = StaticArray{Tuple{}, T, 0} | ||
| 40 | const StaticVector{N, T} = StaticArray{Tuple{N}, T, 1} | ||
| 41 | const StaticMatrix{N, M, T} = StaticArray{Tuple{N, M}, T, 2} | ||
| 42 | const StaticVecOrMat{T} = Union{StaticVector{<:Any, T}, StaticMatrix{<:Any, <:Any, T}} | ||
| 43 | |||
| 44 | # The ::Tuple variants exist to make sure that anything that calls with a tuple | ||
| 45 | # instead of a Tuple gets through to the constructor, so the user gets a nice | ||
| 46 | # error message | ||
| 47 | tuple_length(T::Tuple) = length(T) | ||
| 48 | tuple_prod(T::Tuple) = prod(T) | ||
| 49 | tuple_minimum(T::Tuple) = minimum(T) | ||
| 50 | |||
| 51 | tuple_svec(::Type{T}) where {T<:Tuple} = T.parameters | ||
| 52 | |||
| 53 | # Julia `Base` provides a function for this purpose since v1.1: `fieldtypes`. | ||
| 54 | # We don't use it yet, though, because it wrecks type inference with Julia | ||
| 55 | # v1.6. | ||
| 56 | Base.@pure tuple_tuple(::Type{T}) where {T<:Tuple} = (tuple_svec(T)...,) | ||
| 57 | |||
| 58 | tuple_length(::Type{<:NTuple{n, Any}}) where {n} = n | ||
| 59 | tuple_prod(::Type{T}) where {T<:Tuple} = mapreduce(Int, *, tuple_tuple(T), init = 1) | ||
| 60 | tuple_minimum(::Type{Tuple{}}) = 0 | ||
| 61 | tuple_minimum(::Type{T}) where {T<:Tuple{Any,Vararg}} = mapreduce(Int, min, tuple_tuple(T), init = typemax(Int)) | ||
| 62 | |||
| 63 | """ | ||
| 64 | size_to_tuple(::Type{S}) where S<:Tuple | ||
| 65 | |||
| 66 | Converts a size given by `Tuple{N, M, ...}` into a tuple `(N, M, ...)`. | ||
| 67 | """ | ||
| 68 | size_to_tuple(::Type{T}) where {T<:Tuple} = tuple_tuple(T) | ||
| 69 | |||
| 70 | # Something doesn't match up type wise | ||
| 71 | @generated function check_array_parameters(::Type{Size}, ::Type{T}, ::Type{Val{N}}, ::Type{Val{L}}) where {Size,T,N,L} | ||
| 72 | if !all(x->isa(x, Int), Size.parameters) | ||
| 73 | return :(throw(ArgumentError("Static Array parameter Size must be a tuple of Ints (e.g. `SArray{Tuple{3,3}}` or `SMatrix{3,3}`)."))) | ||
| 74 | end | ||
| 75 | |||
| 76 | if L != tuple_prod(Size) || L < 0 || tuple_minimum(Size) < 0 || tuple_length(Size) != N | ||
| 77 | return :(throw(ArgumentError("Size mismatch in Static Array parameters. Got size $Size, dimension $N and length $L."))) | ||
| 78 | end | ||
| 79 | |||
| 80 | return nothing | ||
| 81 | end | ||
| 82 | |||
| 83 | # Cast any Tuple to an TupleN{T} | ||
| 84 | @generated function convert_ntuple(::Type{T}, d::NTuple{N,Any}) where {N,T} | ||
| 85 | exprs = ntuple(i -> :(convert(T, d[$i])), Val(N)) | ||
| 86 | return quote | ||
| 87 | Base.@_inline_meta | ||
| 88 | $(Expr(:tuple, exprs...)) | ||
| 89 | end | ||
| 90 | end | ||
| 91 | |||
| 92 | |||
| 93 | """ | ||
| 94 | SArray{S, T, N, L}(x::NTuple{L}) | ||
| 95 | SArray{S, T, N, L}(x1, x2, x3, ...) | ||
| 96 | |||
| 97 | Construct a statically-sized array `SArray`. Since this type is immutable, the data must be | ||
| 98 | provided upon construction and cannot be mutated later. The `S` parameter is a Tuple-type | ||
| 99 | specifying the dimensions, or size, of the array - such as `Tuple{3,4,5}` for a 3×4×5-sized | ||
| 100 | array. The `N` parameter is the dimension of the array; the `L` parameter is the `length` | ||
| 101 | of the array and is always equal to `prod(S)`. Constructors may drop the `L`, `N` and `T` | ||
| 102 | parameters if they are inferrable from the input (e.g. `L` is always inferrable from `S`). | ||
| 103 | |||
| 104 | SArray{S}(a::Array) | ||
| 105 | |||
| 106 | Construct a statically-sized array of dimensions `S` (expressed as a `Tuple{...}`) using | ||
| 107 | the data from `a`. The `S` parameter is mandatory since the size of `a` is unknown to the | ||
| 108 | compiler (the element type may optionally also be specified). | ||
| 109 | """ | ||
| 110 | struct SArray{S <: Tuple, T, N, L} <: StaticArray{S, T, N} | ||
| 111 | data::NTuple{L,T} | ||
| 112 | |||
| 113 | function SArray{S, T, N, L}(x::NTuple{L,T}) where {S<:Tuple, T, N, L} | ||
| 114 | check_array_parameters(S, T, Val{N}, Val{L}) | ||
| 115 | new{S, T, N, L}(x) | ||
| 116 | end | ||
| 117 | |||
| 118 | function SArray{S, T, N, L}(x::NTuple{L,Any}) where {S<:Tuple, T, N, L} | ||
| 119 | check_array_parameters(S, T, Val{N}, Val{L}) | ||
| 120 | new{S, T, N, L}(convert_ntuple(T, x)) | ||
| 121 | end | ||
| 122 | end | ||
| 123 | |||
| 124 | @inline SArray{S,T,N}(x::Tuple) where {S<:Tuple,T,N} = SArray{S,T,N,tuple_prod(S)}(x) | ||
| 125 | |||
| 126 | """ | ||
| 127 | SVector{S, T}(x::NTuple{S, T}) | ||
| 128 | SVector{S, T}(x1, x2, x3, ...) | ||
| 129 | |||
| 130 | Construct a statically-sized vector `SVector`. Since this type is immutable, | ||
| 131 | the data must be provided upon construction and cannot be mutated later. | ||
| 132 | Constructors may drop the `T` and `S` parameters if they are inferrable from the | ||
| 133 | input (e.g. `SVector(1,2,3)` constructs an `SVector{3, Int}`). | ||
| 134 | |||
| 135 | SVector{S}(vec::Vector) | ||
| 136 | |||
| 137 | Construct a statically-sized vector of length `S` using the data from `vec`. | ||
| 138 | The parameter `S` is mandatory since the length of `vec` is unknown to the | ||
| 139 | compiler (the element type may optionally also be specified). | ||
| 140 | """ | ||
| 141 | const SVector{S, T} = SArray{Tuple{S}, T, 1, S} | ||
| 142 | |||
| 143 | """ | ||
| 144 | SMatrix{S1, S2, T, L}(x::NTuple{L, T}) | ||
| 145 | SMatrix{S1, S2, T, L}(x1, x2, x3, ...) | ||
| 146 | |||
| 147 | Construct a statically-sized matrix `SMatrix`. Since this type is immutable, | ||
| 148 | the data must be provided upon construction and cannot be mutated later. The | ||
| 149 | `L` parameter is the `length` of the array and is always equal to `S1 * S2`. | ||
| 150 | Constructors may drop the `L`, `T` and even `S2` parameters if they are inferrable | ||
| 151 | from the input (e.g. `L` is always inferrable from `S1` and `S2`). | ||
| 152 | |||
| 153 | SMatrix{S1, S2}(mat::Matrix) | ||
| 154 | |||
| 155 | Construct a statically-sized matrix of dimensions `S1 × S2` using the data from | ||
| 156 | `mat`. The parameters `S1` and `S2` are mandatory since the size of `mat` is | ||
| 157 | unknown to the compiler (the element type may optionally also be specified). | ||
| 158 | """ | ||
| 159 | const SMatrix{S1, S2, T, L} = SArray{Tuple{S1, S2}, T, 2, L} | ||
| 160 | |||
| 161 | # MArray | ||
| 162 | |||
| 163 | """ | ||
| 164 | MArray{S, T, N, L}(undef) | ||
| 165 | MArray{S, T, N, L}(x::NTuple{L}) | ||
| 166 | MArray{S, T, N, L}(x1, x2, x3, ...) | ||
| 167 | |||
| 168 | |||
| 169 | Construct a statically-sized, mutable array `MArray`. The data may optionally be | ||
| 170 | provided upon construction and can be mutated later. The `S` parameter is a Tuple-type | ||
| 171 | specifying the dimensions, or size, of the array - such as `Tuple{3,4,5}` for a 3×4×5-sized | ||
| 172 | array. The `N` parameter is the dimension of the array; the `L` parameter is the `length` | ||
| 173 | of the array and is always equal to `prod(S)`. Constructors may drop the `L`, `N` and `T` | ||
| 174 | parameters if they are inferrable from the input (e.g. `L` is always inferrable from `S`). | ||
| 175 | |||
| 176 | MArray{S}(a::Array) | ||
| 177 | |||
| 178 | Construct a statically-sized, mutable array of dimensions `S` (expressed as a `Tuple{...}`) | ||
| 179 | using the data from `a`. The `S` parameter is mandatory since the size of `a` is unknown to | ||
| 180 | the compiler (the element type may optionally also be specified). | ||
| 181 | """ | ||
| 182 | mutable struct MArray{S <: Tuple, T, N, L} <: StaticArray{S, T, N} | ||
| 183 | data::NTuple{L,T} | ||
| 184 | |||
| 185 | function MArray{S,T,N,L}(x::NTuple{L,T}) where {S<:Tuple,T,N,L} | ||
| 186 | check_array_parameters(S, T, Val{N}, Val{L}) | ||
| 187 | 1 (2 %) | 1 (2 %) |
1 (2 %)
samples spent in MArray
new{S,T,N,L}(x)
1 (100 %) (ex.), 1 (100 %) (incl.) when called from convert line 204 |
| 188 | end | ||
| 189 | |||
| 190 | function MArray{S,T,N,L}(x::NTuple{L,Any}) where {S<:Tuple,T,N,L} | ||
| 191 | check_array_parameters(S, T, Val{N}, Val{L}) | ||
| 192 | new{S,T,N,L}(convert_ntuple(T, x)) | ||
| 193 | end | ||
| 194 | |||
| 195 | function MArray{S,T,N,L}(::UndefInitializer) where {S<:Tuple,T,N,L} | ||
| 196 | check_array_parameters(S, T, Val{N}, Val{L}) | ||
| 197 | new{S,T,N,L}() | ||
| 198 | end | ||
| 199 | end | ||
| 200 | |||
| 201 | @inline MArray{S,T,N}(x::Tuple) where {S<:Tuple,T,N} = MArray{S,T,N,tuple_prod(S)}(x) | ||
| 202 | |||
| 203 | """ | ||
| 204 | MVector{S,T}(undef) | ||
| 205 | MVector{S,T}(x::NTuple{S, T}) | ||
| 206 | MVector{S,T}(x1, x2, x3, ...) | ||
| 207 | |||
| 208 | Construct a statically-sized, mutable vector `MVector`. Data may optionally be | ||
| 209 | provided upon construction, and can be mutated later. Constructors may drop the | ||
| 210 | `T` and `S` parameters if they are inferrable from the input (e.g. | ||
| 211 | `MVector(1,2,3)` constructs an `MVector{3, Int}`). | ||
| 212 | |||
| 213 | MVector{S}(vec::Vector) | ||
| 214 | |||
| 215 | Construct a statically-sized, mutable vector of length `S` using the data from | ||
| 216 | `vec`. The parameter `S` is mandatory since the length of `vec` is unknown to the | ||
| 217 | compiler (the element type may optionally also be specified). | ||
| 218 | """ | ||
| 219 | const MVector{S, T} = MArray{Tuple{S}, T, 1, S} | ||
| 220 | |||
| 221 | """ | ||
| 222 | MMatrix{S1, S2, T, L}(undef) | ||
| 223 | MMatrix{S1, S2, T, L}(x::NTuple{L, T}) | ||
| 224 | MMatrix{S1, S2, T, L}(x1, x2, x3, ...) | ||
| 225 | |||
| 226 | Construct a statically-sized, mutable matrix `MMatrix`. The data may optionally | ||
| 227 | be provided upon construction and can be mutated later. The `L` parameter is the | ||
| 228 | `length` of the array and is always equal to `S1 * S2`. Constructors may drop | ||
| 229 | the `L`, `T` and even `S2` parameters if they are inferrable from the input | ||
| 230 | (e.g. `L` is always inferrable from `S1` and `S2`). | ||
| 231 | |||
| 232 | MMatrix{S1, S2}(mat::Matrix) | ||
| 233 | |||
| 234 | Construct a statically-sized, mutable matrix of dimensions `S1 × S2` using the data from | ||
| 235 | `mat`. The parameters `S1` and `S2` are mandatory since the size of `mat` is | ||
| 236 | unknown to the compiler (the element type may optionally also be specified). | ||
| 237 | """ | ||
| 238 | const MMatrix{S1, S2, T, L} = MArray{Tuple{S1, S2}, T, 2, L} | ||
| 239 | |||
| 240 | |||
| 241 | # SizedArray | ||
| 242 | |||
| 243 | require_one_based_indexing(A...) = !Base.has_offset_axes(A...) || | ||
| 244 | throw(ArgumentError("offset arrays are not supported but got an array with index other than 1")) | ||
| 245 | |||
| 246 | """ | ||
| 247 | SizedArray{Tuple{dims...}}(array) | ||
| 248 | |||
| 249 | Wraps an `AbstractArray` with a static size, so to take advantage of the (faster) | ||
| 250 | methods defined by the static array package. The size is checked once upon | ||
| 251 | construction to determine if the number of elements (`length`) match, but the | ||
| 252 | array may be reshaped. | ||
| 253 | |||
| 254 | The aliases `SizedVector{N}` and `SizedMatrix{N,M}` are provided as more | ||
| 255 | convenient names for one and two dimensional `SizedArray`s. For example, to | ||
| 256 | wrap a 2x3 array `a` in a `SizedArray`, use `SizedMatrix{2,3}(a)`. | ||
| 257 | """ | ||
| 258 | struct SizedArray{S<:Tuple,T,N,M,TData<:AbstractArray{T,M}} <: StaticArray{S,T,N} | ||
| 259 | data::TData | ||
| 260 | |||
| 261 | function SizedArray{S,T,N,M,TData}(a::TData) where {S<:Tuple,T,N,M,TData<:AbstractArray{T,M}} | ||
| 262 | require_one_based_indexing(a) | ||
| 263 | if size(a) != size_to_tuple(S) && size(a) != (tuple_prod(S),) | ||
| 264 | throw(DimensionMismatch("Dimensions $(size(a)) don't match static size $S")) | ||
| 265 | end | ||
| 266 | return new{S,T,N,M,TData}(a) | ||
| 267 | end | ||
| 268 | |||
| 269 | function SizedArray{S,T,N,1,TData}(::UndefInitializer) where {S<:Tuple,T,N,TData<:AbstractArray{T,1}} | ||
| 270 | return new{S,T,N,1,TData}(TData(undef, tuple_prod(S))) | ||
| 271 | end | ||
| 272 | function SizedArray{S,T,N,N,TData}(::UndefInitializer) where {S<:Tuple,T,N,TData<:AbstractArray{T,N}} | ||
| 273 | return new{S,T,N,N,TData}(TData(undef, size_to_tuple(S)...)) | ||
| 274 | end | ||
| 275 | end | ||
| 276 | |||
| 277 | const SizedVector{S,T} = SizedArray{Tuple{S},T,1,1} | ||
| 278 | |||
| 279 | const SizedMatrix{S1,S2,T} = SizedArray{Tuple{S1,S2},T,2} | ||
| 280 | |||
| 281 | # FieldArray | ||
| 282 | |||
| 283 | """ | ||
| 284 | abstract FieldArray{N, T, D} <: StaticArray{N, T, D} | ||
| 285 | |||
| 286 | Inheriting from this type will make it easy to create your own rank-D tensor types. A `FieldArray` | ||
| 287 | will automatically define `getindex` and `setindex!` appropriately. An immutable | ||
| 288 | `FieldArray` will be as performant as an `SArray` of similar length and element type, | ||
| 289 | while a mutable `FieldArray` will behave similarly to an `MArray`. | ||
| 290 | |||
| 291 | Note that you must define the fields of any `FieldArray` subtype in column major order. If you | ||
| 292 | want to use an alternative ordering you will need to pay special attention in providing your | ||
| 293 | own definitions of `getindex`, `setindex!` and tuple conversion. | ||
| 294 | |||
| 295 | If you define a `FieldArray` which is parametric on the element type you should | ||
| 296 | consider defining `similar_type` as in the `FieldVector` example. | ||
| 297 | |||
| 298 | |||
| 299 | # Example | ||
| 300 | |||
| 301 | struct Stiffness <: FieldArray{Tuple{2,2,2,2}, Float64, 4} | ||
| 302 | xxxx::Float64 | ||
| 303 | yxxx::Float64 | ||
| 304 | xyxx::Float64 | ||
| 305 | yyxx::Float64 | ||
| 306 | xxyx::Float64 | ||
| 307 | yxyx::Float64 | ||
| 308 | xyyx::Float64 | ||
| 309 | yyyx::Float64 | ||
| 310 | xxxy::Float64 | ||
| 311 | yxxy::Float64 | ||
| 312 | xyxy::Float64 | ||
| 313 | yyxy::Float64 | ||
| 314 | xxyy::Float64 | ||
| 315 | yxyy::Float64 | ||
| 316 | xyyy::Float64 | ||
| 317 | yyyy::Float64 | ||
| 318 | end | ||
| 319 | """ | ||
| 320 | abstract type FieldArray{N, T, D} <: StaticArray{N, T, D} end | ||
| 321 | |||
| 322 | """ | ||
| 323 | abstract FieldMatrix{N1, N2, T} <: FieldArray{Tuple{N1, N2}, 2} | ||
| 324 | |||
| 325 | Inheriting from this type will make it easy to create your own rank-two tensor types. A `FieldMatrix` | ||
| 326 | will automatically define `getindex` and `setindex!` appropriately. An immutable | ||
| 327 | `FieldMatrix` will be as performant as an `SMatrix` of similar length and element type, | ||
| 328 | while a mutable `FieldMatrix` will behave similarly to an `MMatrix`. | ||
| 329 | |||
| 330 | Note that the fields of any subtype of `FieldMatrix` must be defined in column | ||
| 331 | major order unless you are willing to implement your own `getindex`. | ||
| 332 | |||
| 333 | If you define a `FieldMatrix` which is parametric on the element type you | ||
| 334 | should consider defining `similar_type` as in the `FieldVector` example. | ||
| 335 | |||
| 336 | # Example | ||
| 337 | |||
| 338 | struct Stress <: FieldMatrix{3, 3, Float64} | ||
| 339 | xx::Float64 | ||
| 340 | yx::Float64 | ||
| 341 | zx::Float64 | ||
| 342 | xy::Float64 | ||
| 343 | yy::Float64 | ||
| 344 | zy::Float64 | ||
| 345 | xz::Float64 | ||
| 346 | yz::Float64 | ||
| 347 | zz::Float64 | ||
| 348 | end | ||
| 349 | |||
| 350 | Note that the fields of any subtype of `FieldMatrix` must be defined in column major order. | ||
| 351 | This means that formatting of constructors for literal `FieldMatrix` can be confusing. For example | ||
| 352 | |||
| 353 | sigma = Stress(1.0, 2.0, 3.0, | ||
| 354 | 4.0, 5.0, 6.0, | ||
| 355 | 7.0, 8.0, 9.0) | ||
| 356 | |||
| 357 | 3×3 Stress: | ||
| 358 | 1.0 4.0 7.0 | ||
| 359 | 2.0 5.0 8.0 | ||
| 360 | 3.0 6.0 9.0 | ||
| 361 | |||
| 362 | will give you the transpose of what the multi-argument formatting suggests. For clarity, | ||
| 363 | you may consider using the alternative | ||
| 364 | |||
| 365 | sigma = Stress(SA[1.0 2.0 3.0; | ||
| 366 | 4.0 5.0 6.0; | ||
| 367 | 7.0 8.0 9.0]) | ||
| 368 | """ | ||
| 369 | abstract type FieldMatrix{N1, N2, T} <: FieldArray{Tuple{N1, N2}, T, 2} end | ||
| 370 | |||
| 371 | """ | ||
| 372 | abstract FieldVector{N, T} <: FieldArray{Tuple{N}, 1} | ||
| 373 | |||
| 374 | Inheriting from this type will make it easy to create your own vector types. A `FieldVector` | ||
| 375 | will automatically define `getindex` and `setindex!` appropriately. An immutable | ||
| 376 | `FieldVector` will be as performant as an `SVector` of similar length and element type, | ||
| 377 | while a mutable `FieldVector` will behave similarly to an `MVector`. | ||
| 378 | |||
| 379 | If you define a `FieldVector` which is parametric on the element type you | ||
| 380 | should consider defining `similar_type` to preserve your array type through | ||
| 381 | array operations as in the example below. | ||
| 382 | |||
| 383 | # Example | ||
| 384 | |||
| 385 | struct Vec3D{T} <: FieldVector{3, T} | ||
| 386 | x::T | ||
| 387 | y::T | ||
| 388 | z::T | ||
| 389 | end | ||
| 390 | |||
| 391 | StaticArrays.similar_type(::Type{<:Vec3D}, ::Type{T}, s::Size{(3,)}) where {T} = Vec3D{T} | ||
| 392 | """ | ||
| 393 | abstract type FieldVector{N, T} <: FieldArray{Tuple{N}, T, 1} end | ||
| 394 | |||
| 395 | # Add a new BroadcastStyle for StaticArrays, derived from AbstractArrayStyle | ||
| 396 | # A constructor that changes the style parameter N (array dimension) is also required | ||
| 397 | struct StaticArrayStyle{N} <: Base.Broadcast.AbstractArrayStyle{N} end | ||
| 398 | StaticArrayStyle{M}(::Val{N}) where {M,N} = StaticArrayStyle{N}() | ||
| 399 | |||
| 400 | """ | ||
| 401 | similar_type(static_array) | ||
| 402 | similar_type(static_array, T) | ||
| 403 | similar_type(array, ::Size) | ||
| 404 | similar_type(array, T, ::Size) | ||
| 405 | |||
| 406 | Returns a constructor for a statically-sized array similar to the input array | ||
| 407 | (or type) `static_array`/`array`, optionally with different element type `T` or size | ||
| 408 | `Size`. If the input `array` is not a `StaticArray` then the `Size` is mandatory. | ||
| 409 | |||
| 410 | This differs from `similar()` in that the resulting array type may not be | ||
| 411 | mutable (or define `setindex!()`), and therefore the returned type may need to | ||
| 412 | be *constructed* with its data. | ||
| 413 | |||
| 414 | Note that the (optional) size *must* be specified as a static `Size` object (so the compiler | ||
| 415 | can infer the result statically). | ||
| 416 | |||
| 417 | New types should define the signature `similar_type(::Type{A},::Type{T},::Size{S}) where {A<:MyType,T,S}` | ||
| 418 | if they wish to overload the default behavior. | ||
| 419 | """ | ||
| 420 | function similar_type end | ||
| 421 | |||
| 422 | """ | ||
| 423 | Dynamic() | ||
| 424 | |||
| 425 | Used to signify that a dimension of an array is not known statically. | ||
| 426 | """ | ||
| 427 | struct Dynamic end | ||
| 428 | |||
| 429 | const StaticDimension = Union{Int, Dynamic} | ||
| 430 | |||
| 431 | """ | ||
| 432 | Size(dims::Int...) | ||
| 433 | |||
| 434 | `Size` is used extensively throughout the `StaticArrays` API to describe _compile-time_ | ||
| 435 | knowledge of the size of an array. The dimensions are stored as a type parameter and are | ||
| 436 | statically propagated by the compiler, resulting in efficient, type-inferrable code. For | ||
| 437 | example, to create a static matrix of zeros, use `A = zeros(SMatrix{3,3})`. The static | ||
| 438 | size of `A` can be obtained by `Size(A)`. (rather than `size(zeros(3,3))`, which returns | ||
| 439 | `Base.Tuple{2,Int}`). | ||
| 440 | |||
| 441 | Note that if dimensions are not known statically (e.g., for standard `Array`s), | ||
| 442 | [`Dynamic()`](@ref) should be used instead of an `Int`. | ||
| 443 | |||
| 444 | Size(a::AbstractArray) | ||
| 445 | Size(::Type{T<:AbstractArray}) | ||
| 446 | |||
| 447 | The `Size` constructor can be used to extract static dimension information from a given | ||
| 448 | array. For example: | ||
| 449 | |||
| 450 | ```julia-repl | ||
| 451 | julia> Size(zeros(SMatrix{3, 4})) | ||
| 452 | Size(3, 4) | ||
| 453 | |||
| 454 | julia> Size(zeros(3, 4)) | ||
| 455 | Size(StaticArrays.Dynamic(), StaticArrays.Dynamic()) | ||
| 456 | ``` | ||
| 457 | |||
| 458 | This has multiple uses, including "trait"-based dispatch on the size of a statically-sized | ||
| 459 | array. For example: | ||
| 460 | |||
| 461 | ```julia | ||
| 462 | det(x::StaticMatrix) = _det(Size(x), x) | ||
| 463 | _det(::Size{(1,1)}, x::StaticMatrix) = x[1,1] | ||
| 464 | _det(::Size{(2,2)}, x::StaticMatrix) = x[1,1]*x[2,2] - x[1,2]*x[2,1] | ||
| 465 | # and other definitions as necessary | ||
| 466 | ``` | ||
| 467 | |||
| 468 | """ | ||
| 469 | struct Size{S} | ||
| 470 | function Size{S}() where {S} | ||
| 471 | new{S::Tuple{Vararg{StaticDimension}}}() | ||
| 472 | end | ||
| 473 | end | ||
| 474 | |||
| 475 | Base.@pure Size(s::Tuple{Vararg{StaticDimension}}) = Size{s}() | ||
| 476 | Base.@pure Size(s::StaticDimension...) = Size{s}() | ||
| 477 | Size(::Type{T}) where {T<:Tuple} = Size{tuple_tuple(T)}() | ||
| 478 | |||
| 479 | Base.show(io::IO, ::Size{S}) where {S} = print(io, "Size", S) | ||
| 480 | |||
| 481 | function missing_size_error(::Type{SA}) where SA | ||
| 482 | error(""" | ||
| 483 | The size of type `$SA` is not known. | ||
| 484 | |||
| 485 | If you were trying to construct (or `convert` to) a `StaticArray` you | ||
| 486 | may need to add the size explicitly as a type parameter so its size is | ||
| 487 | inferrable to the Julia compiler (or performance would be terrible). For | ||
| 488 | example, you might try | ||
| 489 | |||
| 490 | m = zeros(3,3) | ||
| 491 | SMatrix(m) # this error | ||
| 492 | SMatrix{3,3}(m) # correct - size is inferrable | ||
| 493 | SArray{Tuple{3,3}}(m) # correct, note Tuple{3,3} | ||
| 494 | """) | ||
| 495 | end | ||
| 496 | |||
| 497 | Size(a::T) where {T<:AbstractArray} = Size(T) | ||
| 498 | Size(::Type{SA}) where {SA <: StaticArray} = missing_size_error(SA) | ||
| 499 | Size(::Type{SA}) where {SA <: StaticArray{S}} where {S<:Tuple} = @isdefined(S) ? Size(S) : missing_size_error(SA) | ||
| 500 | |||
| 501 | Size(::Type{<:AbstractArray{<:Any, N}}) where {N} = Size(ntuple(_ -> Dynamic(), Val(N))) | ||
| 502 | |||
| 503 | end # module |