diff --git a/src/backends/pgfplotsx.jl b/src/backends/pgfplotsx.jl index dd9dd8d9..3cc9114b 100644 --- a/src/backends/pgfplotsx.jl +++ b/src/backends/pgfplotsx.jl @@ -512,10 +512,26 @@ function pgfx_add_series!(::Val{:heatmap}, axis, series_opt, series, series_func end function pgfx_add_series!(::Val{:mesh3d}, axis, series_opt, series, series_func, opt) - ptable = join( - [string(i, " ", j, " ", k, "\\\\") for (i, j, k) in zip(opt[:connections]...)], - "\n ", - ) + if opt[:connections] isa Tuple{Array,Array,Array} + # 0-based indexing + ptable = join( + [string(i, " ", j, " ", k, "\\\\") for (i, j, k) in zip(opt[:connections]...)], + "\n ", + ) + elseif typeof(opt[:connections]) <: AbstractVector{NTuple{3, Int}} + # 1-based indexing + ptable = join( + [string(i, " ", j, " ", k, "\\\\") for (i, j, k) in opt[:connections]], + "\n ", + ) + else + throw( + ArgumentError( + "Argument connections has to be either a tuple of three arrays (0-based indexing) + or an AbstractVector{NTuple{3,Int}} (1-based indexing).", + ), + ) + end push!( series_opt, "patch" => nothing, diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index ab09ee80..d88c16e7 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -678,6 +678,7 @@ function plotly_series(plt::Plot, series::Series) if series[:connections] !== nothing if typeof(series[:connections]) <: Tuple{Array,Array,Array} + # 0-based indexing i, j, k = series[:connections] if !(length(i) == length(j) == length(k)) throw( @@ -689,10 +690,17 @@ function plotly_series(plt::Plot, series::Series) plotattributes_out[:i] = i plotattributes_out[:j] = j plotattributes_out[:k] = k + elseif typeof(series[:connections]) <: AbstractVector{NTuple{3, Int}} + # 1-based indexing + i, j, k = broadcast(i -> [ inds[i]-1 for inds in series[:connections]], (1, 2, 3)) + plotattributes_out[:i] = i + plotattributes_out[:j] = j + plotattributes_out[:k] = k else throw( ArgumentError( - "Argument connections has to be a tuple of three arrays.", + "Argument connections has to be either a tuple of three arrays (0-based indexing) + or an AbstractVector{NTuple{3,Int}} (1-based indexing).", ), ) end diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index 6f2002a8..154ce854 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -700,8 +700,13 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series) if st == :mesh3d polygons = if series[:connections] isa AbstractVector{<:AbstractVector{Int}} + # Combination of any polygon types + broadcast(inds -> broadcast(i -> [x[i], y[i], z[i]], inds), series[:connections]) + elseif series[:connections] isa AbstractVector{NTuple{N, Int}} where N + # Only N-gons - connections have to be 1-based (indexing) broadcast(inds -> broadcast(i -> [x[i], y[i], z[i]], inds), series[:connections]) elseif series[:connections] isa NTuple{3,<:AbstractVector{Int}} + # Only triangles - connections have to be 0-based (indexing) ci, cj, ck = series[:connections] if !(length(ci) == length(cj) == length(ck)) throw( diff --git a/src/utils.jl b/src/utils.jl index c90f90e6..ac46302f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1180,16 +1180,12 @@ end _document_argument(S::AbstractString) = _fmt_paragraph("`$S`: " * _arg_desc[Symbol(S)], leadingspaces = 6 + length(S)) -function mesh3d_triangles(x, y, z, cns) - if typeof(cns) <: Tuple{Array,Array,Array} - ci, cj, ck = cns - if !(length(ci) == length(cj) == length(ck)) - throw( - ArgumentError("Argument connections must consist of equally sized arrays."), - ) - end - else - throw(ArgumentError("Argument connections has to be a tuple of three arrays.")) +function mesh3d_triangles(x, y, z, cns::Tuple{Array,Array,Array}) + ci, cj, ck = cns + if !(length(ci) == length(cj) == length(ck)) + throw( + ArgumentError("Argument connections must consist of equally sized arrays."), + ) end X = zeros(eltype(x), 4length(ci)) Y = zeros(eltype(y), 4length(cj)) @@ -1214,6 +1210,30 @@ function mesh3d_triangles(x, y, z, cns) end return X, Y, Z end +function mesh3d_triangles(x, y, z, cns::AbstractVector{NTuple{3, Int}}) + X = zeros(eltype(x), 4length(cns)) + Y = zeros(eltype(y), 4length(cns)) + Z = zeros(eltype(z), 4length(cns)) + @inbounds for I in 1:length(cns) + i = cns[I][1] # connections are 1-based + j = cns[I][2] + k = cns[I][3] + m = 4(I - 1) + 1 + n = m + 1 + o = m + 2 + p = m + 3 + X[m] = X[p] = x[i] + Y[m] = Y[p] = y[i] + Z[m] = Z[p] = z[i] + X[n] = x[j] + Y[n] = y[j] + Z[n] = z[j] + X[o] = x[k] + Y[o] = y[k] + Z[o] = z[k] + end + return X, Y, Z +end # cache joined symbols so they can be looked up instead of constructed each time const _attrsymbolcache = Dict{Symbol,Dict{Symbol,Symbol}}()