diff --git a/Project.toml b/Project.toml index 88dee08..eff6939 100644 --- a/Project.toml +++ b/Project.toml @@ -7,11 +7,13 @@ version = "1.1.0" ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4" Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" [compat] ColorSchemes = "3" Colors = "0.12" DocStringExtensions = "0.8, 0.9" +GeometryBasics = "0.4" StaticArraysCore = "1.4" julia = "1.6" diff --git a/src/GridVisualizeTools.jl b/src/GridVisualizeTools.jl index ac91403..d95dc73 100644 --- a/src/GridVisualizeTools.jl +++ b/src/GridVisualizeTools.jl @@ -5,6 +5,7 @@ import ColorSchemes using DocStringExtensions: SIGNATURES, TYPEDEF, TYPEDSIGNATURES using StaticArraysCore: SVector +using GeometryBasics: Point include("colors.jl") export region_cmap, bregion_cmap, rgbtuple @@ -12,9 +13,16 @@ export region_cmap, bregion_cmap, rgbtuple include("extraction.jl") export extract_visible_cells3D, extract_visible_bfaces3D +include("linearsimplex.jl") +export LinearSimplex, LinearSimplexIterator +export LinearEdge, LinearTriangle, LinearTetrahedron +export testloop + include("marching.jl") +include("marching_iterators.jl") export marching_tetrahedra, marching_triangles + include("markerpoints.jl") export markerpoints diff --git a/src/linearsimplex.jl b/src/linearsimplex.jl new file mode 100644 index 0000000..d533972 --- /dev/null +++ b/src/linearsimplex.jl @@ -0,0 +1,95 @@ +struct LinearSimplex{D,N,Tv} + points::SVector{N,Point{D,Tv}} + values::SVector{N,Tv} +end + +values(s::LinearSimplex)=s.values +points(s::LinearSimplex)=s.points + + +function LinearSimplex(::Type{Val{D}},points::Union{Vector,Tuple}, values::Union{Vector,Tuple}) where {D} + spoints=SVector{D+1,Point{D,Float32}}(points...) + svalues=SVector{D+1,Float32}(values...) + LinearSimplex(spoints,svalues) +end + +function LinearSimplex(::Type{Val{1}},points::AbstractMatrix, values::AbstractVector) + @views spoints=SVector{2,Point{1,Float32}}(points[:,1],points[:,2]) + svalues=SVector{2,Float32}(values[1],values[2]) + LinearSimplex(spoints,svalues) +end + +function LinearSimplex(::Type{Val{2}},points::AbstractMatrix, values::AbstractVector) + @views spoints=SVector{3,Point{2,Float32}}(points[:,1],points[:,2],points[:,3]) + svalues=SVector{3,Float32}(values[1],values[2],values[3]) + LinearSimplex(spoints,svalues) +end + +function LinearSimplex(::Type{Val{3}},points::AbstractMatrix, values::AbstractVector) + @views spoints=SVector{4,Point{3,Float32}}(points[:,1],points[:,2],points[:,3],points[:,4]) + svalues=SVector{4,Float32}(values[1],values[2],values[3],values[4]) + LinearSimplex(spoints,svalues) +end + + +function LinearSimplex(::Type{Val{1}},points::AbstractMatrix, values::AbstractVector,coordscale) + @views spoints=SVector{2,Point{1,Float32}}(points[:,1]*coordscale,points[:,2]*coordscale) + svalues=SVector{2,Float32}(values[1],values[2]) + LinearSimplex(spoints,svalues) +end + +function LinearSimplex(::Type{Val{2}},points::AbstractMatrix, values::AbstractVector,coordscale) + @views spoints=SVector{3,Point{2,Float32}}(points[:,1]*coordscale,points[:,2]*coordscale,points[:,3]*coordscale) + svalues=SVector{3,Float32}(values[1],values[2],values[3]) + LinearSimplex(spoints,svalues) +end + +function LinearSimplex(::Type{Val{3}},points::AbstractMatrix, values::AbstractVector,coordscale) + @views spoints=SVector{4,Point{3,Float32}}(points[:,1]*coordscale,points[:,2]*coordscale,points[:,3]*coordscale,points[:,4]*coordscale) + svalues=SVector{4,Float32}(values[1],values[2],values[3],values[4]) + LinearSimplex(spoints,svalues) +end + +LinearEdge(points,values)=LinearSimplex(Val{1},points,values) +LinearTriangle(points,values)=LinearSimplex(Val{2},points,values) +LinearTetrahedron(points,values)=LinearSimplex(Val{3},points,values) + + +""" + abstract type LinearSimplexIterator{D} + +Iterator over D-dimensional linear simplices. + +Any subtype `TSub` should comply with the [iteration interface](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-iteration) +and implement +```julia + Base.iterate(vs::TSub{D}, state):: (LinearSimplex{D}, state) where D +``` +The optional methods (in particular, size, length) are not needed. +""" +abstract type LinearSimplexIterator{D} end + +""" + testloop(iterators::AbstractVector{T}) where T<:LinearSimplexIterator + +Sum up all values passed via the iterator. Designed for testing iterators. + +Useful for: +- Consistency test - e.g. when passing constant ones, the sum can be calculated by + other means and compared to the return value of testloop +- Allocation test. `testloop` itself does allocate only a few (<1000) bytes in + very few (<10) allocations. So any allocations beyond this from a + call to `testloop` hint at possibilities to improve an iterator implementation. +""" +function testloop(iterators::AbstractVector{T}) where T<:LinearSimplexIterator + threads=map(iterators) do iterator + begin + local x=0.0 + for vt in iterator + x+=sum(vt.values) + end + x + end + end + sum(fetch.(threads)) +end diff --git a/src/marching_iterators.jl b/src/marching_iterators.jl new file mode 100644 index 0000000..1a85305 --- /dev/null +++ b/src/marching_iterators.jl @@ -0,0 +1,63 @@ +function pushintersection!(intersection_points,triangle::LinearSimplex{2},levels) + f = values(triangle) + coord=points(triangle) + + (n1, n2, n3) = (1, 2, 3) + + f[1] <= f[2] ? (n1, n2) = (1, 2) : (n1, n2) = (2, 1) + f[n2] <= f[3] ? n3 = 3 : (n2, n3) = (3, n2) + f[n1] > f[n2] ? (n1, n2) = (n2, n1) : nothing + + dx31 = coord[n3][1] - coord[n1][1] + dx21 = coord[n2][1] - coord[n1][1] + dx32 = coord[n3][1] - coord[n2][1] + + dy31 = coord[n3][2] - coord[n1][2] + dy21 = coord[n2][2] - coord[n1][2] + dy32 = coord[n3][2] - coord[n2][2] + + df31 = f[n3] != f[n1] ? 1 / (f[n3] - f[n1]) : 0.0 + df21 = f[n2] != f[n1] ? 1 / (f[n2] - f[n1]) : 0.0 + df32 = f[n3] != f[n2] ? 1 / (f[n3] - f[n2]) : 0.0 + + for level ∈ levels + if (f[n1] <= level) && (level < f[n3]) + α = (level - f[n1]) * df31 + x1 = coord[n1][1] + α * dx31 + y1 = coord[n1][2] + α * dy31 + + if (level < f[n2]) + α = (level - f[n1]) * df21 + x2 = coord[n1][1] + α * dx21 + y2 = coord[n1][2] + α * dy21 + else + α = (level - f[n2]) * df32 + x2 = coord[n2][1] + α * dx32 + y2 = coord[n2][2] + α * dy32 + end + push!(intersection_points, Point{2,Float32}(x1, y1)) + push!(intersection_points, Point{2,Float32}(x2, y2)) + end + end +end + +function intersections(triangles::T,levels) where T<:LinearSimplexIterator{2} + local intersection_points = Vector{Point{2,Float32}}(undef, 0) + for triangle in triangles + pushintersection!(intersection_points,triangle,levels) + end + intersection_points +end + +function marching_triangles(triangle_iterators::Vector{T},levels) where T<:LinearSimplexIterator{2} + if Threads.nthreads()==1 + map(triangle_iterators) do triangles + intersections(triangles,levels) + end + else + threads=map(triangle_iterators) do triangles + Threads.@spawn intersections(triangles,levels) + end + fetch.(threads) + end +end